failproofai 1.0.9-beta.0 → 1.0.9-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 +5 -5
- 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/build-manifest.json +2 -2
- 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 +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
- 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/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
- 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 +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +1 -1
- 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/build-manifest.json +2 -2
- 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/build-manifest.json +2 -2
- 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/build-manifest.json +2 -2
- 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/app/settings/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
- 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]__04usis8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__12e7nhs._.js → [root-of-the-server]__1-bm5zf._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15578wp._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m_svbe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0tmz862._.js → [root-of-the-server]__1ox4w55._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1p3iz2d._.js → _0n6zrjv._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1gb0ifp._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_0n0lja6._.js → _1pa3k5k._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +1 -1
- 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/app_settings_02tf1h4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
- 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 +23 -23
- package/.next/standalone/.next/static/chunks/{06jxjnw8bfczc.js → 0542cuk_va8g1.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1h0if8ky362as.js → 0h--izcpk__m9.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0snylg3y1i47a.js → 1srecm9k-z_i1.js} +1 -1
- package/.next/standalone/.next/static/chunks/{03001k9jy-kst.js → 28cmd5molgwa3.js} +1 -1
- package/.next/standalone/.next/static/chunks/{06k7gsfnb7jkh.js → 2b59i-n9l-4of.js} +1 -1
- package/.next/standalone/.next/static/chunks/{42m0f2hh8vfb1.js → 2k5vhij_w1g82.js} +1 -1
- package/.next/standalone/.next/static/chunks/2o1fy-waqn1he.js +6 -0
- package/.next/standalone/.next/static/chunks/{3rq7fjj9we0v3.js → 30z-240_lrczt.js} +1 -1
- package/.next/standalone/.next/static/chunks/38gpf5tb_rd0m.js +1 -0
- package/.next/standalone/.next/static/chunks/3nyf50dub1vuk.js +1 -0
- package/.next/standalone/.next/static/chunks/{0qejem4-zt-hy.js → 42-l25bbfo0ec.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-38nzuvp95_2rv.js → turbopack-11dn2pegmg9_9.js} +1 -1
- package/.next/standalone/fp-cloud-cli/uv.lock +3 -3
- package/.next/standalone/node_modules/@next/env/package.json +1 -1
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +10 -10
- package/.next/standalone/package.json +11 -11
- package/.next/standalone/sdk/python/uv.lock +18 -18
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +932 -725
- package/dist/worker.mjs +4 -1
- package/package.json +11 -11
- package/src/hooks/integrations.ts +152 -22
- package/src/hooks/jev-activity.ts +5 -0
- package/src/hooks/policy-reviewability.ts +4 -2
- package/src/hooks/safe-config-write.ts +119 -0
- package/.next/standalone/.next/static/chunks/0247hc9ath5t-.js +0 -1
- package/.next/standalone/.next/static/chunks/2qsrrrl2maqsb.js +0 -1
- package/.next/standalone/.next/static/chunks/3ns-egfh8i8i5.js +0 -6
- /package/.next/standalone/.next/static/{z1C0kJVF0NCtWn5R88hW3 → D1BjwOkHNPetPo47DhV4_}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{z1C0kJVF0NCtWn5R88hW3 → D1BjwOkHNPetPo47DhV4_}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{z1C0kJVF0NCtWn5R88hW3 → D1BjwOkHNPetPo47DhV4_}/_ssgManifest.js +0 -0
package/dist/worker.mjs
CHANGED
|
@@ -1162,7 +1162,7 @@ function instruct(reason) {
|
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
1164
|
// package.json
|
|
1165
|
-
var version = "1.0.9-beta.
|
|
1165
|
+
var version = "1.0.9-beta.2";
|
|
1166
1166
|
var init_package = () => {};
|
|
1167
1167
|
|
|
1168
1168
|
// src/hooks/policy-types.ts
|
|
@@ -4172,8 +4172,11 @@ function sanitizeJevActivity(entry) {
|
|
|
4172
4172
|
drop("evaluator");
|
|
4173
4173
|
if (!(typeof out.jevDecision === "string" && DECISIONS.has(out.jevDecision)))
|
|
4174
4174
|
drop("jevDecision");
|
|
4175
|
+
const unknownMode = out.jevMode !== undefined && !(typeof out.jevMode === "string" && MODES.has(out.jevMode));
|
|
4175
4176
|
if (!(typeof out.jevMode === "string" && MODES.has(out.jevMode)))
|
|
4176
4177
|
drop("jevMode");
|
|
4178
|
+
if (unknownMode)
|
|
4179
|
+
drop("jevCleared");
|
|
4177
4180
|
if (Array.isArray(out.jevCleared)) {
|
|
4178
4181
|
const names = [
|
|
4179
4182
|
...new Set(out.jevCleared.filter(isJevPolicyName))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "1.0.9-beta.
|
|
3
|
+
"version": "1.0.9-beta.2",
|
|
4
4
|
"description": "Observability and enforcement for AI agent harnesses. 40 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs",
|
|
@@ -81,14 +81,14 @@
|
|
|
81
81
|
"access": "public"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@anthropic-ai/sdk": "^0.
|
|
84
|
+
"@anthropic-ai/sdk": "^0.128.0",
|
|
85
85
|
"@mdx-js/mdx": "^3.1.1",
|
|
86
86
|
"@tailwindcss/postcss": "^4.3.1",
|
|
87
87
|
"@tanstack/react-virtual": "^3.14.10",
|
|
88
88
|
"@testing-library/jest-dom": "^7.0.0",
|
|
89
89
|
"@testing-library/react": "^16.3.2",
|
|
90
90
|
"@testing-library/user-event": "^14.6.5",
|
|
91
|
-
"@types/node": "26.6.
|
|
91
|
+
"@types/node": "26.6.2",
|
|
92
92
|
"@types/react": "19.3.0",
|
|
93
93
|
"@types/react-dom": "^19.2.3",
|
|
94
94
|
"@vitejs/plugin-react": "^6.1.0",
|
|
@@ -122,15 +122,15 @@
|
|
|
122
122
|
"browserslist": "4.28.8"
|
|
123
123
|
},
|
|
124
124
|
"optionalDependencies": {
|
|
125
|
-
"@failproofai/failproofaid-linux-x64": "1.0.9-beta.
|
|
126
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.9-beta.
|
|
127
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.9-beta.
|
|
128
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.9-beta.
|
|
125
|
+
"@failproofai/failproofaid-linux-x64": "1.0.9-beta.2",
|
|
126
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.9-beta.2",
|
|
127
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.9-beta.2",
|
|
128
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.9-beta.2"
|
|
129
129
|
},
|
|
130
130
|
"failproofaidBinaries": {
|
|
131
|
-
"linux-x64": "
|
|
132
|
-
"linux-arm64": "
|
|
133
|
-
"darwin-x64": "
|
|
134
|
-
"darwin-arm64": "
|
|
131
|
+
"linux-x64": "cd0a7124ae94ae78753cda6ffad985ba30be80443d8bc93d617ce507ba04e211",
|
|
132
|
+
"linux-arm64": "925446186bbfa827275356a9dc80b7d5351a6dcce9d926731419bbd91c1f98ba",
|
|
133
|
+
"darwin-x64": "cd35a6549f402b6c04848772074edb110a54a242fe7488792e59f8e8a00af475",
|
|
134
|
+
"darwin-arm64": "587b045accada4947a556e1bc1d748780a846c2ef95cf8dafb973a23fdbcb173"
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -25,6 +25,7 @@ import { resolve, dirname, basename } from "node:path";
|
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
26
|
import { homedir } from "node:os";
|
|
27
27
|
import { parseDocument, type Document } from "yaml";
|
|
28
|
+
import { writeConfigFileAtomic } from "./safe-config-write";
|
|
28
29
|
import { listHermesProfiles, hermesRoot } from "../../lib/hermes-profiles";
|
|
29
30
|
import { listOpenClawProfiles } from "../../lib/openclaw-profiles";
|
|
30
31
|
import {
|
|
@@ -65,32 +66,82 @@ import {
|
|
|
65
66
|
|
|
66
67
|
// ── Generic helpers ─────────────────────────────────────────────────────────
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* The error every config reader throws for a file it cannot read faithfully.
|
|
71
|
+
*
|
|
72
|
+
* These files belong to the user's agent. A config that exists but does not
|
|
73
|
+
* parse is never treated as empty: writing our entries into an "empty" document
|
|
74
|
+
* would replace every other setting in it, and the agent would come back without
|
|
75
|
+
* its model, keys or channels. Refusing leaves the file exactly as the user has
|
|
76
|
+
* it and says which file and why.
|
|
77
|
+
*/
|
|
78
|
+
export class UnreadableAgentConfigError extends Error {
|
|
79
|
+
constructor(readonly path: string, reason: string) {
|
|
80
|
+
super(
|
|
81
|
+
`Refusing to modify ${path}: ${reason}. ` +
|
|
82
|
+
`failproofai never rewrites a config it could not parse — fix or move the file, then retry.`,
|
|
83
|
+
);
|
|
84
|
+
this.name = "UnreadableAgentConfigError";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
function readJsonFile(path: string): Record<string, unknown> {
|
|
69
89
|
if (!existsSync(path)) return {};
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
let raw: string;
|
|
91
|
+
try {
|
|
92
|
+
raw = readFileSync(path, "utf8");
|
|
93
|
+
} catch (err) {
|
|
94
|
+
throw new UnreadableAgentConfigError(path, `it could not be read (${err instanceof Error ? err.message : String(err)})`);
|
|
95
|
+
}
|
|
96
|
+
if (raw.trim() === "") return {};
|
|
97
|
+
let parsed: unknown;
|
|
98
|
+
try {
|
|
99
|
+
parsed = JSON.parse(raw);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
throw new UnreadableAgentConfigError(path, `it is not valid JSON (${err instanceof Error ? err.message : String(err)})`);
|
|
102
|
+
}
|
|
103
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
104
|
+
throw new UnreadableAgentConfigError(path, "its top level is not a JSON object");
|
|
105
|
+
}
|
|
106
|
+
return parsed as Record<string, unknown>;
|
|
72
107
|
}
|
|
73
108
|
|
|
74
109
|
function writeJsonFile(path: string, data: Record<string, unknown>): void {
|
|
75
|
-
|
|
76
|
-
writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
110
|
+
writeConfigFileAtomic(path, JSON.stringify(data, null, 2) + "\n");
|
|
77
111
|
}
|
|
78
112
|
|
|
79
113
|
/** Read a YAML file as a `Document` so writes round-trip the user's other keys +
|
|
80
|
-
* comments (a plain parse→stringify would strip comments).
|
|
81
|
-
*
|
|
82
|
-
*
|
|
114
|
+
* comments (a plain parse→stringify would strip comments). Missing or empty →
|
|
115
|
+
* an empty Document; unreadable or unparseable → {@link UnreadableAgentConfigError},
|
|
116
|
+
* never an empty Document that the next write would put in the user's place.
|
|
117
|
+
* Used by the Hermes integration, whose config lives in `~/.hermes/config.yaml`. */
|
|
83
118
|
function readYamlDoc(path: string): Document {
|
|
119
|
+
if (!existsSync(path)) return parseDocument("");
|
|
120
|
+
let text: string;
|
|
84
121
|
try {
|
|
85
|
-
|
|
122
|
+
text = readFileSync(path, "utf8");
|
|
123
|
+
} catch (err) {
|
|
124
|
+
throw new UnreadableAgentConfigError(path, `it could not be read (${err instanceof Error ? err.message : String(err)})`);
|
|
125
|
+
}
|
|
126
|
+
const doc = parseDocument(text);
|
|
127
|
+
if (doc.errors.length > 0) {
|
|
128
|
+
const first = doc.errors[0]?.message.split("\n")[0] ?? "syntax error";
|
|
129
|
+
throw new UnreadableAgentConfigError(path, `it does not parse as YAML (${first})`);
|
|
130
|
+
}
|
|
131
|
+
return doc;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** {@link readYamlDoc} for read-only callers (status, health): an unreadable file is `null`. */
|
|
135
|
+
function inspectYamlDoc(path: string): Document | null {
|
|
136
|
+
try {
|
|
137
|
+
return readYamlDoc(path);
|
|
86
138
|
} catch {
|
|
87
|
-
return
|
|
139
|
+
return null;
|
|
88
140
|
}
|
|
89
141
|
}
|
|
90
142
|
|
|
91
143
|
function writeYamlDoc(path: string, doc: Document): void {
|
|
92
|
-
|
|
93
|
-
writeFileSync(path, doc.toString(), "utf8");
|
|
144
|
+
writeConfigFileAtomic(path, doc.toString());
|
|
94
145
|
}
|
|
95
146
|
|
|
96
147
|
function isMarkedHook(hook: unknown): boolean {
|
|
@@ -1127,7 +1178,7 @@ export const opencode: Integration = {
|
|
|
1127
1178
|
// (a) Write the shim file. mkdirSync is recursive so the plugins/ dir
|
|
1128
1179
|
// is created on first install.
|
|
1129
1180
|
mkdirSync(dirname(pluginPath), { recursive: true });
|
|
1130
|
-
|
|
1181
|
+
writeConfigFileAtomic(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope));
|
|
1131
1182
|
|
|
1132
1183
|
// (b) Merge our entry into the plugin array idempotently. Replace any
|
|
1133
1184
|
// existing failproofai-marked entry; otherwise append.
|
|
@@ -1422,7 +1473,30 @@ const HERMES_PLUGIN_SOURCE_DIR = "hermes-plugin";
|
|
|
1422
1473
|
function getHermesPluginSourcePath(): string {
|
|
1423
1474
|
const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
1424
1475
|
if (fromEnv) return resolve(fromEnv, HERMES_PLUGIN_SOURCE_DIR);
|
|
1425
|
-
return
|
|
1476
|
+
return findHermesPluginSourcePath(dirname(fileURLToPath(import.meta.url)));
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* The package's `hermes-plugin/`, found by walking up from `startDir`.
|
|
1481
|
+
*
|
|
1482
|
+
* The CLI entry point sets `FAILPROOFAI_PACKAGE_ROOT`, so this is only the
|
|
1483
|
+
* fallback — but a fixed number of parents is right for exactly one layout:
|
|
1484
|
+
* three from `src/hooks/` is the package root, three from the bundled
|
|
1485
|
+
* `dist/cli.mjs` is the package's PARENT. Walking up to the first directory
|
|
1486
|
+
* that really holds the plugin's manifest works from source, from `dist/`, and
|
|
1487
|
+
* from the dashboard's standalone build alike. When none is found the old
|
|
1488
|
+
* answer is returned, so the install still fails loudly on a missing asset.
|
|
1489
|
+
*/
|
|
1490
|
+
export function findHermesPluginSourcePath(startDir: string): string {
|
|
1491
|
+
let dir = resolve(startDir);
|
|
1492
|
+
for (let i = 0; i < 8; i++) {
|
|
1493
|
+
const candidate = resolve(dir, HERMES_PLUGIN_SOURCE_DIR);
|
|
1494
|
+
if (existsSync(resolve(candidate, "plugin.yaml"))) return candidate;
|
|
1495
|
+
const parent = dirname(dir);
|
|
1496
|
+
if (parent === dir) break;
|
|
1497
|
+
dir = parent;
|
|
1498
|
+
}
|
|
1499
|
+
return resolve(startDir, "..", "..", HERMES_PLUGIN_SOURCE_DIR);
|
|
1426
1500
|
}
|
|
1427
1501
|
|
|
1428
1502
|
export function hermesPluginPathForSettings(settingsPath: string): string {
|
|
@@ -1599,11 +1673,16 @@ export function installHermesPlugin(
|
|
|
1599
1673
|
if (linked && (existing.kind === "absent" || existing.kind === "link")) {
|
|
1600
1674
|
try {
|
|
1601
1675
|
renameSync(temporary, destination);
|
|
1676
|
+
return "linked";
|
|
1602
1677
|
} catch (err) {
|
|
1603
|
-
|
|
1604
|
-
|
|
1678
|
+
// Windows cannot always rename a junction over another junction; the
|
|
1679
|
+
// backup swap below moves the old link aside first, so try that before
|
|
1680
|
+
// giving up. With nothing there to move aside, the rename is the error.
|
|
1681
|
+
if (existing.kind !== "link") {
|
|
1682
|
+
rmSync(temporary, { force: true });
|
|
1683
|
+
throw err;
|
|
1684
|
+
}
|
|
1605
1685
|
}
|
|
1606
|
-
return "linked";
|
|
1607
1686
|
}
|
|
1608
1687
|
|
|
1609
1688
|
const backup = `${destination}.backup-${suffix}`;
|
|
@@ -1669,26 +1748,44 @@ function removeLegacyHermesHooks(doc: Document): number {
|
|
|
1669
1748
|
}
|
|
1670
1749
|
|
|
1671
1750
|
function hermesConfigState(settingsPath: string): {
|
|
1751
|
+
/** Hermes will load it: listed in `plugins.enabled` AND not in `plugins.disabled`. */
|
|
1672
1752
|
pluginEnabled: boolean;
|
|
1753
|
+
/** Listed in `plugins.enabled`, whatever `plugins.disabled` says. */
|
|
1754
|
+
pluginListed: boolean;
|
|
1755
|
+
/** Listed in `plugins.disabled`, which Hermes checks first and which wins. */
|
|
1756
|
+
pluginDisabled: boolean;
|
|
1673
1757
|
legacyShellHookPresent: boolean;
|
|
1758
|
+
unreadable?: boolean;
|
|
1674
1759
|
} {
|
|
1675
1760
|
if (!existsSync(settingsPath)) {
|
|
1676
|
-
return { pluginEnabled: false, legacyShellHookPresent: false };
|
|
1761
|
+
return { pluginEnabled: false, pluginListed: false, pluginDisabled: false, legacyShellHookPresent: false };
|
|
1762
|
+
}
|
|
1763
|
+
const doc = inspectYamlDoc(settingsPath);
|
|
1764
|
+
if (!doc) {
|
|
1765
|
+
return { pluginEnabled: false, pluginListed: false, pluginDisabled: false, legacyShellHookPresent: false, unreadable: true };
|
|
1677
1766
|
}
|
|
1678
|
-
const doc = readYamlDoc(settingsPath);
|
|
1679
1767
|
const js = (doc.toJS() ?? {}) as {
|
|
1680
1768
|
hooks?: Record<string, HermesHookEntry[]>;
|
|
1681
1769
|
plugins?: HermesPluginsConfig;
|
|
1682
1770
|
};
|
|
1683
1771
|
const enabled = js.plugins?.enabled;
|
|
1772
|
+
const disabled = js.plugins?.disabled;
|
|
1684
1773
|
const legacyShellHookPresent =
|
|
1685
1774
|
!!js.hooks &&
|
|
1686
1775
|
typeof js.hooks === "object" &&
|
|
1687
1776
|
Object.values(js.hooks).some(
|
|
1688
1777
|
(entries) => Array.isArray(entries) && entries.some((entry) => isMarkedHook(entry)),
|
|
1689
1778
|
);
|
|
1779
|
+
// Hermes gates `plugins.disabled` before `plugins.enabled`
|
|
1780
|
+
// (hermes_cli/plugins_discovery.py `gate_manifest`), so a plugin in both
|
|
1781
|
+
// lists never loads. Treating that as enabled reported a switched-off plugin
|
|
1782
|
+
// healthy and let `update` skip it as "already current".
|
|
1783
|
+
const pluginListed = Array.isArray(enabled) && enabled.includes(HERMES_PLUGIN_ID);
|
|
1784
|
+
const pluginDisabled = Array.isArray(disabled) && disabled.includes(HERMES_PLUGIN_ID);
|
|
1690
1785
|
return {
|
|
1691
|
-
pluginEnabled:
|
|
1786
|
+
pluginEnabled: pluginListed && !pluginDisabled,
|
|
1787
|
+
pluginListed,
|
|
1788
|
+
pluginDisabled,
|
|
1692
1789
|
legacyShellHookPresent,
|
|
1693
1790
|
};
|
|
1694
1791
|
}
|
|
@@ -1763,7 +1860,30 @@ export async function migrateHermesProfiles(opts: {
|
|
|
1763
1860
|
const base = { name: profile.name, home: profile.home };
|
|
1764
1861
|
const legacy = config.legacyShellHookPresent;
|
|
1765
1862
|
|
|
1766
|
-
|
|
1863
|
+
// A config.yaml that does not parse is never edited. Whether it is ours to
|
|
1864
|
+
// report is read from the raw text: it cannot be parsed to find out.
|
|
1865
|
+
if (config.unreadable) {
|
|
1866
|
+
let raw = "";
|
|
1867
|
+
try {
|
|
1868
|
+
raw = readFileSync(settingsPath, "utf8");
|
|
1869
|
+
} catch {
|
|
1870
|
+
raw = "";
|
|
1871
|
+
}
|
|
1872
|
+
const mentionsUs = ours || raw.includes(FAILPROOFAI_HOOK_MARKER) || /failproofai/.test(raw);
|
|
1873
|
+
results.push(
|
|
1874
|
+
mentionsUs
|
|
1875
|
+
? {
|
|
1876
|
+
...base,
|
|
1877
|
+
status: "failed",
|
|
1878
|
+
detail: `${settingsPath} does not parse as YAML; left untouched`,
|
|
1879
|
+
legacyShellHooksRemain: /--hook\s+\S+\s+--cli\s+hermes|__failproofai_hook__/.test(raw),
|
|
1880
|
+
}
|
|
1881
|
+
: { ...base, status: "untouched", detail: "no failproofai integration", legacyShellHooksRemain: false },
|
|
1882
|
+
);
|
|
1883
|
+
continue;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
if (!legacy && !ours && !config.pluginListed && !config.pluginDisabled) {
|
|
1767
1887
|
results.push({ ...base, status: "untouched", detail: "no failproofai integration", legacyShellHooksRemain: false });
|
|
1768
1888
|
continue;
|
|
1769
1889
|
}
|
|
@@ -1844,12 +1964,16 @@ export interface HermesProfileHealth {
|
|
|
1844
1964
|
/** How the plugin is installed: a link into the package, a copy, or neither. */
|
|
1845
1965
|
pluginMode: "link" | "copy" | null;
|
|
1846
1966
|
pluginEnabled: boolean;
|
|
1967
|
+
/** failproofai is listed in `plugins.disabled`, so Hermes will not load it. */
|
|
1968
|
+
pluginDisabled: boolean;
|
|
1847
1969
|
legacyShellHookPresent: boolean;
|
|
1848
1970
|
/**
|
|
1849
1971
|
* Still enforced only by legacy config.yaml shell hooks. Hermes cron jobs run
|
|
1850
1972
|
* with none of those, so their tool calls go unchecked.
|
|
1851
1973
|
*/
|
|
1852
1974
|
cronUnchecked: boolean;
|
|
1975
|
+
/** config.yaml exists but does not parse; failproofai will not touch it. */
|
|
1976
|
+
configUnreadable: boolean;
|
|
1853
1977
|
healthy: boolean;
|
|
1854
1978
|
}
|
|
1855
1979
|
|
|
@@ -1868,9 +1992,11 @@ export function hermesProfileHealth(): HermesProfileHealth[] {
|
|
|
1868
1992
|
pluginInstalled,
|
|
1869
1993
|
pluginMode: plugin.kind === "link" || plugin.kind === "copy" ? plugin.kind : null,
|
|
1870
1994
|
pluginEnabled: config.pluginEnabled,
|
|
1995
|
+
pluginDisabled: config.pluginDisabled,
|
|
1871
1996
|
legacyShellHookPresent: config.legacyShellHookPresent,
|
|
1872
1997
|
cronUnchecked: config.legacyShellHookPresent && !pluginWorks,
|
|
1873
|
-
|
|
1998
|
+
configUnreadable: config.unreadable === true,
|
|
1999
|
+
healthy: pluginWorks && !config.legacyShellHookPresent && config.unreadable !== true,
|
|
1874
2000
|
};
|
|
1875
2001
|
});
|
|
1876
2002
|
}
|
|
@@ -1881,6 +2007,9 @@ export function hermesProfileStatusRows(): Array<[string, string]> {
|
|
|
1881
2007
|
.filter((profile) => existsSync(profile.home))
|
|
1882
2008
|
.map((profile) => {
|
|
1883
2009
|
const label = "hermes/" + profile.name;
|
|
2010
|
+
if (profile.configUnreadable) {
|
|
2011
|
+
return [label, `UNHEALTHY — ${profile.settingsPath} does not parse; failproofai leaves it untouched until it is fixed`];
|
|
2012
|
+
}
|
|
1884
2013
|
if (profile.cronUnchecked) {
|
|
1885
2014
|
// Not a cosmetic leftover: the shell-hook integration does not reach
|
|
1886
2015
|
// cron jobs at all, so this profile has an unenforced path.
|
|
@@ -1891,7 +2020,8 @@ export function hermesProfileStatusRows(): Array<[string, string]> {
|
|
|
1891
2020
|
}
|
|
1892
2021
|
const problems: string[] = [];
|
|
1893
2022
|
if (!profile.pluginInstalled) problems.push("plugin files missing or incomplete");
|
|
1894
|
-
if (
|
|
2023
|
+
if (profile.pluginDisabled) problems.push("plugin disabled (listed in plugins.disabled)");
|
|
2024
|
+
else if (!profile.pluginEnabled) problems.push("plugin not enabled");
|
|
1895
2025
|
if (profile.legacyShellHookPresent) problems.push("legacy shell hook also present");
|
|
1896
2026
|
if (problems.length > 0) return [label, "UNHEALTHY — " + problems.join("; ")];
|
|
1897
2027
|
return [
|
|
@@ -236,7 +236,12 @@ export function sanitizeJevActivity<T extends JevActivityFields>(entry: T): T {
|
|
|
236
236
|
|
|
237
237
|
if (!(typeof out.evaluator === "string" && EVALUATORS.has(out.evaluator))) drop("evaluator");
|
|
238
238
|
if (!(typeof out.jevDecision === "string" && DECISIONS.has(out.jevDecision))) drop("jevDecision");
|
|
239
|
+
// A mode this build does not know (a row written before the mode was named
|
|
240
|
+
// `observe`) cannot say whether its clears were enforced, so they are not
|
|
241
|
+
// counted as anything: dropping only the mode would file them as enforced.
|
|
242
|
+
const unknownMode = out.jevMode !== undefined && !(typeof out.jevMode === "string" && MODES.has(out.jevMode));
|
|
239
243
|
if (!(typeof out.jevMode === "string" && MODES.has(out.jevMode))) drop("jevMode");
|
|
244
|
+
if (unknownMode) drop("jevCleared");
|
|
240
245
|
|
|
241
246
|
if (Array.isArray(out.jevCleared)) {
|
|
242
247
|
const names = [
|
|
@@ -61,7 +61,7 @@ import { resolve } from "node:path";
|
|
|
61
61
|
import { discoverPolicyFiles } from "./custom-hooks-loader";
|
|
62
62
|
import { customPoliciesDir } from "./fp-home";
|
|
63
63
|
import { configuredCustomPolicyPaths, findProjectConfigDir, readMergedHooksConfig } from "./hooks-config";
|
|
64
|
-
import {
|
|
64
|
+
import { hasInstalledRegexPacks, readInstalledPacks } from "./pack-manifest";
|
|
65
65
|
import { resolvePolicyAuthority } from "./policy-authority";
|
|
66
66
|
import { POLICY_CATALOG } from "./policy-catalog";
|
|
67
67
|
import { normalizePolicyName } from "./policy-registry";
|
|
@@ -167,7 +167,9 @@ export function surveyReviewableCoverage(cwd?: string): ReviewableCoverage {
|
|
|
167
167
|
*/
|
|
168
168
|
let reviewers: ReadonlySet<string> | undefined;
|
|
169
169
|
try {
|
|
170
|
-
|
|
170
|
+
// Only a pack with regex policies retires the shim, exactly as handler.ts
|
|
171
|
+
// decides it: a Jev-only pack (FailproofAI/jev-policies) replaces none.
|
|
172
|
+
packsInstalled = hasInstalledRegexPacks();
|
|
171
173
|
const packs = readInstalledPacks().packs;
|
|
172
174
|
for (const pack of packs) {
|
|
173
175
|
const selected = pack.enabled;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crash-safe writes for configuration files that belong to someone else.
|
|
3
|
+
*
|
|
4
|
+
* Every agent integration edits a file the user's agent reads on startup:
|
|
5
|
+
* `~/.claude/settings.json`, `~/.hermes/config.yaml`, `~/.openclaw/openclaw.json`
|
|
6
|
+
* and the rest. A plain `writeFileSync` truncates that file first and fills it
|
|
7
|
+
* second, so a crash, a full disk or a killed process in between leaves the agent
|
|
8
|
+
* with a half-written config — for Hermes that is a gateway that will not start.
|
|
9
|
+
* And a later install then refuses to touch the file because it no longer parses,
|
|
10
|
+
* so nothing repairs it.
|
|
11
|
+
*
|
|
12
|
+
* So the new content never goes near the live file until it is complete and on
|
|
13
|
+
* disk:
|
|
14
|
+
*
|
|
15
|
+
* 1. write it to a uniquely named temporary file in the SAME directory (a
|
|
16
|
+
* rename is only atomic within one filesystem),
|
|
17
|
+
* 2. fsync that file,
|
|
18
|
+
* 3. copy the current file to `<name>.failproofai-backup` — the last version
|
|
19
|
+
* before failproofai changed it, for a person to restore by hand,
|
|
20
|
+
* 4. rename the temporary file over the live one (atomic on POSIX, and a
|
|
21
|
+
* replacing move on Windows), then fsync the directory so the rename
|
|
22
|
+
* itself survives a power cut.
|
|
23
|
+
*
|
|
24
|
+
* Any failure before step 4 leaves the live file exactly as it was and removes
|
|
25
|
+
* the temporary file. The file keeps its permission bits (Hermes keeps
|
|
26
|
+
* `config.yaml` at 0600), and a config that is a symlink — a dotfiles checkout —
|
|
27
|
+
* is written through to its target rather than replaced by a regular file.
|
|
28
|
+
*/
|
|
29
|
+
import {
|
|
30
|
+
chmodSync,
|
|
31
|
+
closeSync,
|
|
32
|
+
copyFileSync,
|
|
33
|
+
existsSync,
|
|
34
|
+
fsyncSync,
|
|
35
|
+
lstatSync,
|
|
36
|
+
mkdirSync,
|
|
37
|
+
openSync,
|
|
38
|
+
realpathSync,
|
|
39
|
+
renameSync,
|
|
40
|
+
rmSync,
|
|
41
|
+
statSync,
|
|
42
|
+
writeSync,
|
|
43
|
+
} from "node:fs";
|
|
44
|
+
import { randomBytes } from "node:crypto";
|
|
45
|
+
import { basename, dirname, join } from "node:path";
|
|
46
|
+
|
|
47
|
+
/** Where the copy of the previous version is kept, beside the file itself. */
|
|
48
|
+
export function configBackupPath(path: string): string {
|
|
49
|
+
return `${resolveWriteTarget(path)}.failproofai-backup`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Hooks for tests to simulate an interruption at the one step that matters. */
|
|
53
|
+
export interface AtomicWriteDeps {
|
|
54
|
+
rename?: (from: string, to: string) => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A symlinked config is written through to what it points at. */
|
|
58
|
+
function resolveWriteTarget(path: string): string {
|
|
59
|
+
try {
|
|
60
|
+
if (lstatSync(path).isSymbolicLink()) return realpathSync(path);
|
|
61
|
+
} catch {
|
|
62
|
+
// Absent or unreadable: write to the path as given.
|
|
63
|
+
}
|
|
64
|
+
return path;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function writeConfigFileAtomic(path: string, content: string, deps: AtomicWriteDeps = {}): void {
|
|
68
|
+
const target = resolveWriteTarget(path);
|
|
69
|
+
const dir = dirname(target);
|
|
70
|
+
mkdirSync(dir, { recursive: true });
|
|
71
|
+
|
|
72
|
+
let mode: number | undefined;
|
|
73
|
+
try {
|
|
74
|
+
mode = statSync(target).mode & 0o777;
|
|
75
|
+
} catch {
|
|
76
|
+
mode = undefined; // a new file: the process umask decides, as writeFileSync would
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const temporary = join(dir, `.${basename(target)}.failproofai-${process.pid}-${randomBytes(4).toString("hex")}.tmp`);
|
|
80
|
+
let fd: number | undefined;
|
|
81
|
+
try {
|
|
82
|
+
fd = openSync(temporary, "wx", mode ?? 0o666);
|
|
83
|
+
const bytes = Buffer.from(content, "utf8");
|
|
84
|
+
let written = 0;
|
|
85
|
+
while (written < bytes.length) written += writeSync(fd, bytes, written, bytes.length - written);
|
|
86
|
+
fsyncSync(fd);
|
|
87
|
+
closeSync(fd);
|
|
88
|
+
fd = undefined;
|
|
89
|
+
// `open` applies the umask; the live file's own bits are what should survive.
|
|
90
|
+
if (mode !== undefined) chmodSync(temporary, mode);
|
|
91
|
+
|
|
92
|
+
if (existsSync(target)) copyFileSync(target, `${target}.failproofai-backup`);
|
|
93
|
+
|
|
94
|
+
(deps.rename ?? renameSync)(temporary, target);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (fd !== undefined) {
|
|
97
|
+
try {
|
|
98
|
+
closeSync(fd);
|
|
99
|
+
} catch {
|
|
100
|
+
// already closed or never opened
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
rmSync(temporary, { force: true });
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Make the rename itself durable. Not supported on every platform (Windows
|
|
108
|
+
// cannot open a directory); the file content is already safe either way.
|
|
109
|
+
try {
|
|
110
|
+
const dirFd = openSync(dir, "r");
|
|
111
|
+
try {
|
|
112
|
+
fsyncSync(dirFd);
|
|
113
|
+
} finally {
|
|
114
|
+
closeSync(dirFd);
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
// best effort
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,34578,e=>{"use strict";var t=e.i(95187);let a=(0,t.createServerReference)("60b2a3a075bc71a6052007070cfceb60df6ec81cc5",t.callServer,void 0,t.findSourceMapURL,"searchHookActivityAction");e.s(["searchHookActivityAction",0,a])},73520,e=>{"use strict";var t=e.i(43476),a=e.i(24071),r=e.i(67927);e.s(["default",0,function({currentPage:e,totalPages:l,onPageChange:o}){if(l<=1)return null;let s=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>o(e-1),disabled:1===e,className:s(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(l<=7)return Array.from({length:l},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(l-1,e+1);e<=3&&(r=Math.min(5,l-1)),e>=l-2&&(a=Math.max(2,l-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<l-1&&t.push("ellipsis-end"),t.push(l),t})().map(a=>"string"==typeof a?(0,t.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},a):(0,t.jsx)("button",{onClick:()=>o(a),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${e===a?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${a}`,"aria-current":e===a?"page":void 0,children:a},a))}),(0,t.jsxs)("button",{onClick:()=>o(e+1),disabled:e===l,className:s(e===l),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},24578,e=>{"use strict";let t={claude:{id:"claude",label:"Claude Code",badgeClasses:"bg-orange-500/10 text-orange-400 border-orange-500/20"},codex:{id:"codex",label:"OpenAI Codex",badgeClasses:"bg-purple-500/10 text-purple-400 border-purple-500/20"},copilot:{id:"copilot",label:"GitHub Copilot",badgeClasses:"bg-blue-500/10 text-blue-400 border-blue-500/20"},cursor:{id:"cursor",label:"Cursor Agent",badgeClasses:"bg-emerald-500/10 text-emerald-400 border-emerald-500/20"},opencode:{id:"opencode",label:"OpenCode",badgeClasses:"bg-amber-500/10 text-amber-400 border-amber-500/20"},pi:{id:"pi",label:"Pi",badgeClasses:"bg-pink-500/10 text-pink-400 border-pink-500/20"},hermes:{id:"hermes",label:"Hermes",badgeClasses:"bg-indigo-500/10 text-indigo-400 border-indigo-500/20"},openclaw:{id:"openclaw",label:"OpenClaw",badgeClasses:"bg-teal-500/10 text-teal-400 border-teal-500/20"},factory:{id:"factory",label:"Factory Droid",badgeClasses:"bg-rose-500/10 text-rose-400 border-rose-500/20"},devin:{id:"devin",label:"Devin CLI",badgeClasses:"bg-violet-500/10 text-violet-400 border-violet-500/20"},antigravity:{id:"antigravity",label:"Antigravity CLI",badgeClasses:"bg-cyan-500/10 text-cyan-400 border-cyan-500/20"},goose:{id:"goose",label:"Goose",badgeClasses:"bg-lime-500/10 text-lime-400 border-lime-500/20"}};e.s(["KNOWN_CLI_IDS",0,["claude","codex","copilot","cursor","opencode","pi","hermes","openclaw","factory","devin","antigravity","goose"],"getCliBadgeClasses",0,function(e){return t[e]?.badgeClasses??t.claude.badgeClasses},"getCliLabel",0,function(e){return t[e]?.label??e},"isKnownCli",0,function(e){return"string"==typeof e&&Object.prototype.hasOwnProperty.call(t,e)}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let a=Math.round(e/1e3);if(a<3600){let e=Math.floor(a/60);return`${e}m ${a%60}s`}let r=Math.round(e/6e4),l=Math.floor(r/60);return`${l}h ${r%60}m`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])},80060,2711,e=>{"use strict";var t=e.i(18566),a=e.i(71645);function r(e){let t=e.getFullYear(),a=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0");return`${t}-${a}-${r}`}function l(e){var t;if(!e||(t=e,!/^\d{4}-\d{2}-\d{2}$/.test(t)||isNaN(Date.parse(t))))return null;let[a,r,l]=e.split("-").map(Number);return new Date(a,r-1,l,12,0,0)}e.s(["useUrlParams",0,function(){let e=(0,t.useSearchParams)(),r=(0,t.useRouter)(),l=(0,t.usePathname)(),o=(0,a.useRef)(null),s=(0,a.useCallback)(t=>e.get(t),[e]),i=(0,a.useCallback)(()=>new URLSearchParams(e.toString()),[e]),n=(0,a.useCallback)(t=>{o.current&&clearTimeout(o.current),o.current=setTimeout(()=>{let a=new URLSearchParams(e.toString());for(let[e,r]of Object.entries(t))void 0===r||""===r?a.delete(e):a.set(e,r);let o=a.toString(),s=o?`${l}?${o}`:l;r.replace(s,{scroll:!1})},150)},[e,l,r]);return(0,a.useEffect)(()=>()=>{o.current&&clearTimeout(o.current)},[]),{get:s,getAll:i,setAll:n}}],80060),e.s(["dateRangeToParams",0,function(e){let t={};return e.from&&(t.from=r(e.from)),e.to&&(t.to=r(e.to)),t},"keywordsToParam",0,function(e){if(0!==e.length)return e.map(e=>encodeURIComponent(e)).join(",")},"pageToParam",0,function(e){return e<=1?void 0:String(e)},"paramToKeywords",0,function(e){return e?e.split(",").map(e=>decodeURIComponent(e.trim())).filter(e=>e.length>0):[]},"paramToPage",0,function(e){if(!e)return 1;let t=parseInt(e,10);return Number.isFinite(t)&&t>=1?t:1},"paramToPreset",0,function(e){return e&&["all","last-hour","today","last-7-days","last-30-days","custom"].includes(e)?e:"all"},"paramsToDateRange",0,function(e,t){return{from:l(e),to:l(t)}},"presetToParam",0,function(e){return"all"===e?void 0:e}],2711)},89664,e=>{"use strict";var t=e.i(56420);let a={name:"check",size:24,node:[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]};a.node;let r=(0,t.default)(a);e.s(["Check",0,r],89664)},24071,e=>{"use strict";var t=e.i(56420);let a={name:"chevron-left",size:24,node:[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]};a.node;let r=(0,t.default)(a);e.s(["ChevronLeft",0,r],24071)},67927,e=>{"use strict";var t=e.i(56420);let a={name:"chevron-right",size:24,node:[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]};a.node;let r=(0,t.default)(a);e.s(["ChevronRight",0,r],67927)},8734,e=>{"use strict";var t=e.i(56420);let a={name:"copy",size:24,node:[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]};a.node;let r=(0,t.default)(a);e.s(["Copy",0,r],8734)},16306,e=>{"use strict";var t=e.i(56420);let a={name:"shield-alert",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]]};a.node;let r=(0,t.default)(a);e.s(["ShieldAlert",0,r],16306)},84026,29407,e=>{"use strict";var t=e.i(56420);let a={name:"shield-check",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]};a.node;let r=(0,t.default)(a);e.s(["ShieldCheck",0,r],84026);let l={name:"shield-x",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m14.5 9.5-5 5",key:"17q4r4"}],["path",{d:"m9.5 9.5 5 5",key:"18nt4w"}]],aliases:["shield-close"]};l.node;let o=(0,t.default)(l);e.s(["ShieldX",0,o],29407)},82954,e=>{"use strict";var t=e.i(56420);let a={name:"shield",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]};a.node;let r=(0,t.default)(a);e.s(["Shield",0,r],82954)},63676,e=>{"use strict";var t=e.i(56420);let a={name:"x",size:24,node:[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]};a.node;let r=(0,t.default)(a);e.s(["X",0,r],63676)}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,34578,e=>{"use strict";var t=e.i(95187);let a=(0,t.createServerReference)("60b2a3a075bc71a6052007070cfceb60df6ec81cc5",t.callServer,void 0,t.findSourceMapURL,"searchHookActivityAction");e.s(["searchHookActivityAction",0,a])},73520,e=>{"use strict";var t=e.i(43476),a=e.i(24071),r=e.i(67927);e.s(["default",0,function({currentPage:e,totalPages:s,onPageChange:i}){if(s<=1)return null;let l=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>i(e-1),disabled:1===e,className:l(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(s<=7)return Array.from({length:s},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(s-1,e+1);e<=3&&(r=Math.min(5,s-1)),e>=s-2&&(a=Math.max(2,s-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<s-1&&t.push("ellipsis-end"),t.push(s),t})().map(a=>"string"==typeof a?(0,t.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},a):(0,t.jsx)("button",{onClick:()=>i(a),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${e===a?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${a}`,"aria-current":e===a?"page":void 0,children:a},a))}),(0,t.jsxs)("button",{onClick:()=>i(e+1),disabled:e===s,className:l(e===s),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let a=Math.round(e/1e3);if(a<3600){let e=Math.floor(a/60);return`${e}m ${a%60}s`}let r=Math.round(e/6e4),s=Math.floor(r/60);return`${s}h ${r%60}m`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])},24071,e=>{"use strict";var t=e.i(56420);let a={name:"chevron-left",size:24,node:[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]};a.node;let r=(0,t.default)(a);e.s(["ChevronLeft",0,r],24071)},67927,e=>{"use strict";var t=e.i(56420);let a={name:"chevron-right",size:24,node:[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]};a.node;let r=(0,t.default)(a);e.s(["ChevronRight",0,r],67927)},16306,e=>{"use strict";var t=e.i(56420);let a={name:"shield-alert",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]]};a.node;let r=(0,t.default)(a);e.s(["ShieldAlert",0,r],16306)},84026,29407,e=>{"use strict";var t=e.i(56420);let a={name:"shield-check",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]};a.node;let r=(0,t.default)(a);e.s(["ShieldCheck",0,r],84026);let s={name:"shield-x",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m14.5 9.5-5 5",key:"17q4r4"}],["path",{d:"m9.5 9.5 5 5",key:"18nt4w"}]],aliases:["shield-close"]};s.node;let i=(0,t.default)(s);e.s(["ShieldX",0,i],29407)},82954,e=>{"use strict";var t=e.i(56420);let a={name:"shield",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]};a.node;let r=(0,t.default)(a);e.s(["Shield",0,r],82954)}]);
|