failproofai 0.0.13 → 0.0.14-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/.codex/hooks.json +6 -6
- package/.next/standalone/.cursor/hooks.json +6 -6
- package/.next/standalone/.factory/hooks.json +112 -0
- 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 +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/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 +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- 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 +10 -10
- 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 +2 -2
- 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 +3 -3
- package/.next/standalone/.next/server/app/api/audit/run/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/reminder/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 +2 -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 +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- 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 +8 -8
- 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 +3 -2
- 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 +5 -3
- 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 +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]__0--lkk6._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08w4wmd._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g6bbuw._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jllheg._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d9qen3._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ffkmds._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qb590j._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qxztj-._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1shcqgr._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xuedea._.js +3 -0
- package/.next/standalone/.next/server/chunks/{_1z062h5._.js → _1vslkoe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +24 -12
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.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]__00cz9as._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__015_i4t._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__042d3cd._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__05r_17v._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09q1168._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__108slqn._.js → [root-of-the-server]__0brjczq._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e2l3c1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hci7t3._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hdsupo._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p84xee._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07iom4f._.js → [root-of-the-server]__0wp9w6i._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ynf7tx._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +12 -9
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1d8omgc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0_9_ytf._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0ul5rf_._.js → _1_ozgif._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1p0-leb._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_087bt9w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_1fvisnp._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_209u41o._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- 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/lib_0xkhw_v._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_antigravity-projects_ts_0_6zf11._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_devin-projects_ts_0a64qdy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_factory-projects_ts_1uyaahq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_goose-projects_ts_0zkscri._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_13p7zob.js → node_modules_html-to-image_es_index_12rjfea.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_1ezd2jf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.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 +10 -10
- package/.next/standalone/.next/static/chunks/{13f1kmjea-0md.js → 02jcotghcxrbu.js} +1 -1
- package/.next/standalone/.next/static/chunks/{30ysv-_8jj052.js → 0d5lypu1zs21r.js} +1 -1
- package/.next/standalone/.next/static/chunks/0emg30_6svycu.js +1 -0
- package/.next/standalone/.next/static/chunks/0o9wpnze1ey8y.js +6 -0
- package/.next/standalone/.next/static/chunks/{05apzuand0c22.js → 1359o5785rmve.js} +2 -2
- package/.next/standalone/.next/static/chunks/20tuctw38jbgw.js +1 -0
- package/.next/standalone/.next/static/chunks/{0vc0eak148vo6.js → 2ldhnaweu9ew3.js} +1 -1
- package/.next/standalone/.next/static/chunks/{23c7v2svx90dp.js → 2vav_x0_336ds.js} +1 -1
- package/.next/standalone/.next/static/chunks/3-ogr79xqbw0h.js +1 -0
- package/.next/standalone/.next/static/chunks/36j05kb2x323i.css +2 -0
- package/.next/standalone/.next/static/chunks/3cg7x0joi2b98.js +1 -0
- package/.next/standalone/.next/static/chunks/{36wdtkgdf0zrd.js → 3k87p-tijud-4.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2cejn75gug3j8.js → 3kmaqcp7rldad.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-0vavqo2d-tlt3.js → turbopack-10hj8t5pf33tu.js} +1 -1
- package/.next/standalone/.opencode/plugins/failproofai.mjs +30 -11
- package/.next/standalone/app/api/audit/run/route.ts +2 -0
- package/.next/standalone/app/audit/_components/empty-state.tsx +1 -1
- package/.next/standalone/app/components/global-error-listeners.tsx +27 -0
- package/.next/standalone/app/globals.css +25 -1
- package/.next/standalone/app/policies/hooks-client.tsx +2 -4
- package/.next/standalone/app/project/[name]/page.tsx +30 -10
- package/.next/standalone/app/project/[name]/session/[sessionId]/page.tsx +71 -21
- package/.next/standalone/assets/logos/antigravity.svg +1 -0
- package/.next/standalone/assets/logos/devin.svg +1 -0
- package/.next/standalone/assets/logos/factory-dark.png +0 -0
- package/.next/standalone/assets/logos/factory-light.png +0 -0
- package/.next/standalone/assets/logos/goose-dark.svg +1 -0
- package/.next/standalone/assets/logos/goose-light.svg +1 -0
- package/.next/standalone/assets/logos/openclaw.svg +22 -0
- package/.next/standalone/ci/cli-integration/Dockerfile +47 -0
- package/.next/standalone/ci/cli-integration/README.md +69 -0
- package/.next/standalone/ci/cli-integration/canary-policies.mjs +45 -0
- package/.next/standalone/ci/cli-integration/capture-tokens.sh +48 -0
- package/.next/standalone/ci/cli-integration/inject-tokens.sh +44 -0
- package/.next/standalone/ci/cli-integration/install-clis.sh +91 -0
- package/.next/standalone/ci/cli-integration/probe-cli.sh +208 -0
- package/.next/standalone/ci/cli-integration/report.js +74 -0
- package/.next/standalone/ci/cli-integration/run.sh +107 -0
- package/.next/standalone/components/navbar.tsx +5 -11
- package/.next/standalone/components/reach-developers.tsx +2 -2
- package/.next/standalone/docker-hook-sync/Dockerfile +8 -4
- package/.next/standalone/lib/antigravity-projects.ts +224 -0
- package/.next/standalone/lib/antigravity-sessions.ts +283 -0
- package/.next/standalone/lib/cli-registry.ts +26 -6
- package/.next/standalone/lib/devin-projects.ts +141 -0
- package/.next/standalone/lib/devin-sessions.ts +343 -0
- package/.next/standalone/lib/download-session.ts +56 -5
- package/.next/standalone/lib/error-origin.ts +67 -0
- package/.next/standalone/lib/factory-projects.ts +109 -0
- package/.next/standalone/lib/factory-sessions.ts +312 -0
- package/.next/standalone/lib/goose-projects.ts +148 -0
- package/.next/standalone/lib/goose-sessions.ts +296 -0
- package/.next/standalone/lib/install-check.ts +231 -0
- package/.next/standalone/lib/openclaw-projects.ts +182 -0
- package/.next/standalone/lib/openclaw-sessions.ts +284 -0
- package/.next/standalone/lib/projects.ts +33 -9
- package/.next/standalone/lib/telemetry-sanitize.ts +43 -0
- package/.next/standalone/openclaw-plugin/index.js +223 -0
- package/.next/standalone/openclaw-plugin/openclaw.plugin.json +6 -0
- package/.next/standalone/openclaw-plugin/package.json +12 -0
- package/.next/standalone/package.json +2 -3
- package/.next/standalone/server.js +1 -1
- package/README.md +94 -62
- package/bin/failproofai.mjs +98 -30
- package/dist/cli.mjs +12499 -9243
- package/lib/antigravity-projects.ts +224 -0
- package/lib/antigravity-sessions.ts +283 -0
- package/lib/cli-registry.ts +26 -6
- package/lib/devin-projects.ts +141 -0
- package/lib/devin-sessions.ts +343 -0
- package/lib/download-session.ts +56 -5
- package/lib/error-origin.ts +67 -0
- package/lib/factory-projects.ts +109 -0
- package/lib/factory-sessions.ts +312 -0
- package/lib/goose-projects.ts +148 -0
- package/lib/goose-sessions.ts +296 -0
- package/lib/install-check.ts +231 -0
- package/lib/openclaw-projects.ts +182 -0
- package/lib/openclaw-sessions.ts +284 -0
- package/lib/projects.ts +33 -9
- package/lib/telemetry-sanitize.ts +43 -0
- package/openclaw-plugin/index.js +223 -0
- package/openclaw-plugin/openclaw.plugin.json +6 -0
- package/openclaw-plugin/package.json +12 -0
- package/package.json +2 -3
- package/scripts/dev-hook.mjs +292 -0
- package/scripts/install-diagnosis.mjs +5 -8
- package/scripts/install-telemetry.mjs +12 -4
- package/scripts/launch.ts +4 -8
- package/scripts/sync-agent-cli-harnesses-prompt.md +7 -10
- package/scripts/translate-docs/cli.ts +120 -53
- package/scripts/translate-docs/config.ts +57 -13
- package/scripts/translate-docs/mdx-translator.ts +177 -28
- package/scripts/translate-docs/mintlify-nav.ts +91 -10
- package/scripts/translate-docs/readme-translator.ts +13 -5
- package/scripts/translate-docs/translator.ts +38 -2
- package/scripts/translate-docs/types.ts +3 -0
- package/scripts/validate-mdx.ts +12 -4
- package/src/audit/cli-adapters/antigravity.ts +87 -0
- package/src/audit/cli-adapters/devin.ts +53 -0
- package/src/audit/cli-adapters/factory.ts +75 -0
- package/src/audit/cli-adapters/goose.ts +53 -0
- package/src/audit/cli-adapters/index.ts +30 -6
- package/src/audit/cli-adapters/openclaw.ts +56 -0
- package/src/audit/cli.ts +83 -8
- package/src/audit/report.ts +1 -1
- package/src/audit/types.ts +1 -1
- package/src/hooks/builtin-policies.ts +5 -5
- package/src/hooks/configure-wizard.ts +356 -0
- package/src/hooks/handler.ts +283 -218
- package/src/hooks/hook-telemetry.ts +43 -3
- package/src/hooks/install-prompt.ts +92 -173
- package/src/hooks/integrations.ts +739 -138
- package/src/hooks/manager.ts +46 -2
- package/src/hooks/policy-evaluator.ts +301 -121
- package/src/hooks/policy-presets.ts +71 -0
- package/src/hooks/resolve-cwd.ts +1 -1
- package/src/hooks/resolve-permission-mode.ts +1 -5
- package/src/hooks/resolve-transcript-path.ts +25 -7
- package/src/hooks/tool-name-canonicalize.ts +36 -3
- package/src/hooks/tui.ts +578 -0
- package/src/hooks/types.ts +447 -109
- package/.next/standalone/.gemini/settings.json +0 -147
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cag8qd._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cefjd9._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ekj92q._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12v-lma._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__136y39g._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13h8pzr._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1hgv_75._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04bqvos._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dqgs96._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0q5rfxg._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qukut7._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ql-mch._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_11v0g3z._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_gemini-projects_ts_1c7bgx-._.js +0 -3
- package/.next/standalone/.next/static/chunks/0fa7j157kg9_d.js +0 -1
- package/.next/standalone/.next/static/chunks/0nrah-qv6smeg.js +0 -6
- package/.next/standalone/.next/static/chunks/0wtagd8a5gwq-.js +0 -1
- package/.next/standalone/.next/static/chunks/1c8i78p7t1i-5.js +0 -1
- package/.next/standalone/.next/static/chunks/25qr0e6ez0ei0.js +0 -1
- package/.next/standalone/.next/static/chunks/25s52un3k_wht.css +0 -2
- package/.next/standalone/assets/logos/gemini-dark.svg +0 -13
- package/.next/standalone/assets/logos/gemini-light.svg +0 -13
- package/.next/standalone/lib/gemini-projects.ts +0 -243
- package/.next/standalone/lib/gemini-sessions.ts +0 -365
- package/lib/gemini-projects.ts +0 -243
- package/lib/gemini-sessions.ts +0 -365
- package/scripts/postinstall.mjs +0 -243
- package/scripts/preuninstall.mjs +0 -131
- package/src/audit/cli-adapters/gemini.ts +0 -51
- package/src/hooks/first-run-nudge.ts +0 -146
- /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → LkN-JjmhKGmL-WklH7WVi}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → LkN-JjmhKGmL-WklH7WVi}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → LkN-JjmhKGmL-WklH7WVi}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Goose (codename goose, Block) session transcript loader + parser.
|
|
3
|
+
*
|
|
4
|
+
* AUDIT-ONLY (Pillar 2). Goose (≥ v1.10.0) stores every session in one SQLite DB
|
|
5
|
+
* at `~/.local/share/goose/sessions/sessions.db`. We read it DIRECTLY via the
|
|
6
|
+
* bundled sql.js driver (lib/sqlite-reader.ts) — the same reusable path
|
|
7
|
+
* Devin/Hermes/OpenClaw use. Like Devin (and unlike the cwd-less Hermes gateway),
|
|
8
|
+
* each Goose `sessions` row carries a real `working_dir`, so Goose sessions group
|
|
9
|
+
* by project cwd like Claude/Factory/Devin.
|
|
10
|
+
*
|
|
11
|
+
* Schema (verified live against goose v1.43.0, schema_version 15):
|
|
12
|
+
* sessions(id TEXT `YYYYMMDD_N`, name, description, session_type
|
|
13
|
+
* (user|hidden|subagent|scheduled|terminal|acp), working_dir,
|
|
14
|
+
* created_at TIMESTAMP, updated_at TIMESTAMP, total_tokens, …)
|
|
15
|
+
* messages(id INTEGER PK, session_id, role (user|assistant),
|
|
16
|
+
* content_json TEXT=JSON-array-of-blocks, created_timestamp INT, …)
|
|
17
|
+
*
|
|
18
|
+
* `content_json` is a Claude-style typed-block array (NOT OpenAI-style like
|
|
19
|
+
* Devin): `{type:"text",text}`, `{type:"toolRequest",id,toolCall:{value:{name,
|
|
20
|
+
* arguments}},_meta:{goose_extension}}`, and `{type:"toolResponse",id,toolResult:
|
|
21
|
+
* {value:{content:[{type:"text",text}],isError}}}`. Tool RESULTS are stored in
|
|
22
|
+
* `role:"user"` rows (Claude convention). `gooseRowsToLogEntries` pairs each
|
|
23
|
+
* toolRequest with its later toolResponse by id and is a PURE function of the
|
|
24
|
+
* parsed rows, so it is unit-testable without a DB.
|
|
25
|
+
*
|
|
26
|
+
* `session_type = 'hidden'` rows are `goose run --no-session` scratch runs — the
|
|
27
|
+
* enumerator (lib/goose-projects.ts) filters them out.
|
|
28
|
+
*
|
|
29
|
+
* Home override: set `GOOSE_HOME` (the data dir that contains `sessions/`, used
|
|
30
|
+
* by tests) or `GOOSE_DB_PATH` (points directly at a sessions.db).
|
|
31
|
+
*/
|
|
32
|
+
import { homedir } from "node:os";
|
|
33
|
+
import { join } from "node:path";
|
|
34
|
+
import { openSqliteReadonly } from "./sqlite-reader";
|
|
35
|
+
import { runtimeCache } from "./runtime-cache";
|
|
36
|
+
import {
|
|
37
|
+
baseEntry,
|
|
38
|
+
formatTimestamp,
|
|
39
|
+
type LogEntry,
|
|
40
|
+
type UserEntry,
|
|
41
|
+
type AssistantEntry,
|
|
42
|
+
type GenericEntry,
|
|
43
|
+
type ContentBlock,
|
|
44
|
+
type ToolUseBlock,
|
|
45
|
+
type LogSource,
|
|
46
|
+
} from "./log-entries";
|
|
47
|
+
import { formatDuration } from "./format-duration";
|
|
48
|
+
|
|
49
|
+
/** Goose session IDs are date-prefixed counters, e.g. `20260714_3`. */
|
|
50
|
+
export const GOOSE_SESSION_ID_RE = /^\d{8}_\d+$/;
|
|
51
|
+
|
|
52
|
+
/** Absolute path to Goose's data home (override with GOOSE_HOME; respects
|
|
53
|
+
* XDG_DATA_HOME otherwise). */
|
|
54
|
+
export function gooseHome(): string {
|
|
55
|
+
if (process.env.GOOSE_HOME) return process.env.GOOSE_HOME;
|
|
56
|
+
const xdg = process.env.XDG_DATA_HOME;
|
|
57
|
+
return join(xdg && xdg.length > 0 ? xdg : join(homedir(), ".local", "share"), "goose");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Absolute path to Goose's SQLite DB (override with GOOSE_DB_PATH). */
|
|
61
|
+
export function gooseDbPath(): string {
|
|
62
|
+
return process.env.GOOSE_DB_PATH || join(gooseHome(), "sessions", "sessions.db");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Coerce a Goose timestamp to epoch ms. `messages.created_timestamp` is an
|
|
66
|
+
* INTEGER epoch (seconds or ms); `sessions.created_at`/`updated_at` are SQLite
|
|
67
|
+
* `CURRENT_TIMESTAMP` strings ("YYYY-MM-DD HH:MM:SS", UTC). */
|
|
68
|
+
export function gooseTimestampToMs(value: unknown): number {
|
|
69
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
70
|
+
if (value > 1e12) return value; // already ms
|
|
71
|
+
if (value > 1e9) return value * 1000; // seconds
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === "string" && value.length > 0) {
|
|
74
|
+
// SQLite CURRENT_TIMESTAMP has no zone and is UTC — normalize to ISO-Z so
|
|
75
|
+
// Date.parse doesn't treat it as local time.
|
|
76
|
+
const iso = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(value)
|
|
77
|
+
? value.replace(" ", "T") + "Z"
|
|
78
|
+
: value;
|
|
79
|
+
const ms = Date.parse(iso);
|
|
80
|
+
if (!Number.isNaN(ms)) return ms;
|
|
81
|
+
}
|
|
82
|
+
return Date.now();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Parsing helpers ──
|
|
86
|
+
|
|
87
|
+
function safeJsonParse(s: unknown): unknown {
|
|
88
|
+
if (typeof s !== "string" || s.length === 0) return undefined;
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(s);
|
|
91
|
+
} catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
97
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Text from a `content` field that may be a string or an array of
|
|
101
|
+
* `{ type:"text", text }` blocks. */
|
|
102
|
+
function extractText(content: unknown): string {
|
|
103
|
+
if (typeof content === "string") return content;
|
|
104
|
+
if (Array.isArray(content)) {
|
|
105
|
+
return content
|
|
106
|
+
.map((c) => (isPlainObject(c) && typeof c.text === "string" ? (c.text as string) : ""))
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
.join("\n");
|
|
109
|
+
}
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function toDate(value: unknown, fallbackMs: number): Date {
|
|
114
|
+
const ms = typeof value === "number" || typeof value === "string" ? gooseTimestampToMs(value) : NaN;
|
|
115
|
+
return Number.isNaN(ms) ? new Date(fallbackMs) : new Date(ms);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Pull `{ id, name, input }` from a Goose `toolRequest` block:
|
|
119
|
+
* `{id, toolCall:{status, value:{name, arguments}}}`. */
|
|
120
|
+
function extractToolRequest(
|
|
121
|
+
block: Record<string, unknown>,
|
|
122
|
+
idx: number,
|
|
123
|
+
): { id: string; name: string; input: Record<string, unknown> } {
|
|
124
|
+
const id = typeof block.id === "string" && block.id ? block.id : `goose-tool-${idx}`;
|
|
125
|
+
const toolCall = isPlainObject(block.toolCall) ? block.toolCall : {};
|
|
126
|
+
const value = isPlainObject(toolCall.value) ? toolCall.value : {};
|
|
127
|
+
const name = typeof value.name === "string" ? value.name : "tool";
|
|
128
|
+
const input = isPlainObject(value.arguments) ? value.arguments : {};
|
|
129
|
+
return { id, name, input };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Text of a Goose `toolResponse` block:
|
|
133
|
+
* `{id, toolResult:{status, value:{content:[{type:"text",text}], …}}}`. */
|
|
134
|
+
function extractToolResult(block: Record<string, unknown>): string {
|
|
135
|
+
const toolResult = isPlainObject(block.toolResult) ? block.toolResult : {};
|
|
136
|
+
const value = toolResult.value;
|
|
137
|
+
if (isPlainObject(value) && Array.isArray(value.content)) return extractText(value.content);
|
|
138
|
+
if (typeof value === "string") return value;
|
|
139
|
+
if (Array.isArray(value)) return extractText(value);
|
|
140
|
+
return "";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── Pure parser: message rows → LogEntry[] ──
|
|
144
|
+
|
|
145
|
+
export interface GooseMessageRow {
|
|
146
|
+
role: string | null;
|
|
147
|
+
content_json: string | null;
|
|
148
|
+
created_timestamp: number | null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Convert Goose `messages` rows (in insertion order) into `LogEntry[]`. Each
|
|
153
|
+
* row's `content_json` is a typed-block array; `toolRequest` blocks become
|
|
154
|
+
* `tool_use` blocks on an assistant entry, and later `toolResponse` blocks
|
|
155
|
+
* (which arrive in `role:"user"` rows) attach as the matching tool_use's result
|
|
156
|
+
* by id. Pure — unit-testable with plain rows.
|
|
157
|
+
*/
|
|
158
|
+
export function gooseRowsToLogEntries(
|
|
159
|
+
rows: GooseMessageRow[],
|
|
160
|
+
source: LogSource = "session",
|
|
161
|
+
): LogEntry[] {
|
|
162
|
+
const entries: LogEntry[] = [];
|
|
163
|
+
const toolUseById = new Map<string, ToolUseBlock>();
|
|
164
|
+
const toolUseStartMs = new Map<string, number>();
|
|
165
|
+
const baseMs = Date.now();
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < rows.length; i++) {
|
|
168
|
+
const r = rows[i];
|
|
169
|
+
const parsed = safeJsonParse(r.content_json);
|
|
170
|
+
if (!Array.isArray(parsed)) continue;
|
|
171
|
+
|
|
172
|
+
const role = typeof r.role === "string" ? r.role : "system";
|
|
173
|
+
const date = toDate(r.created_timestamp, baseMs + i);
|
|
174
|
+
const timestamp = date.toISOString();
|
|
175
|
+
const raw: Record<string, unknown> = { uuid: `goose-${i}`, parentUuid: null };
|
|
176
|
+
const base = baseEntry(raw, timestamp, date, source);
|
|
177
|
+
|
|
178
|
+
const textParts: string[] = [];
|
|
179
|
+
const toolReqs: Record<string, unknown>[] = [];
|
|
180
|
+
const toolResps: Record<string, unknown>[] = [];
|
|
181
|
+
for (const b of parsed) {
|
|
182
|
+
if (!isPlainObject(b)) continue;
|
|
183
|
+
if (b.type === "text" && typeof b.text === "string") textParts.push(b.text);
|
|
184
|
+
else if (b.type === "toolRequest") toolReqs.push(b);
|
|
185
|
+
else if (b.type === "toolResponse") toolResps.push(b);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Attach tool results to their earlier tool_use blocks (results arrive in
|
|
189
|
+
// role:"user" rows, so this runs before we consider the row a user turn).
|
|
190
|
+
for (const tr of toolResps) {
|
|
191
|
+
const callId = typeof tr.id === "string" ? tr.id : undefined;
|
|
192
|
+
const block = callId ? toolUseById.get(callId) : undefined;
|
|
193
|
+
if (!block) continue;
|
|
194
|
+
const startMs = (callId && toolUseStartMs.get(callId)) || date.getTime();
|
|
195
|
+
const durationMs = Math.max(0, date.getTime() - startMs);
|
|
196
|
+
block.result = {
|
|
197
|
+
timestamp,
|
|
198
|
+
timestampFormatted: formatTimestamp(date),
|
|
199
|
+
content: extractToolResult(tr),
|
|
200
|
+
durationMs,
|
|
201
|
+
durationFormatted: formatDuration(durationMs),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const text = textParts.join("\n").trim();
|
|
206
|
+
|
|
207
|
+
if (toolReqs.length > 0 || (role === "assistant" && text)) {
|
|
208
|
+
const blocks: ContentBlock[] = [];
|
|
209
|
+
if (text) blocks.push({ type: "text", text });
|
|
210
|
+
for (const req of toolReqs) {
|
|
211
|
+
const { id, name, input } = extractToolRequest(req, blocks.length);
|
|
212
|
+
const block: ToolUseBlock = { type: "tool_use", id, name, input };
|
|
213
|
+
blocks.push(block);
|
|
214
|
+
toolUseById.set(id, block);
|
|
215
|
+
toolUseStartMs.set(id, date.getTime());
|
|
216
|
+
}
|
|
217
|
+
if (blocks.length === 0) continue;
|
|
218
|
+
entries.push({
|
|
219
|
+
type: "assistant",
|
|
220
|
+
...base,
|
|
221
|
+
message: { role: "assistant", content: blocks },
|
|
222
|
+
} satisfies AssistantEntry);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (role === "user" && text) {
|
|
227
|
+
entries.push({
|
|
228
|
+
type: "user",
|
|
229
|
+
...base,
|
|
230
|
+
message: { role: "user", content: text },
|
|
231
|
+
} satisfies UserEntry);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Pure tool-result rows (no text, no requests) produced no new entry above.
|
|
236
|
+
if (text) {
|
|
237
|
+
entries.push({ type: "system", ...base, raw } satisfies GenericEntry);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
entries.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
242
|
+
return entries;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ── DB loader ──
|
|
246
|
+
|
|
247
|
+
export interface GooseSessionLogData {
|
|
248
|
+
entries: LogEntry[];
|
|
249
|
+
rawLines: Record<string, unknown>[];
|
|
250
|
+
cwd?: string;
|
|
251
|
+
filePath: string; // synthetic — goose keeps sessions in a DB; we use goose-db://<id>
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Load one session by ID from `sessions.db`. Returns `null` when the DB is
|
|
256
|
+
* unavailable or the session doesn't exist.
|
|
257
|
+
*/
|
|
258
|
+
export async function getGooseSessionLog(
|
|
259
|
+
sessionId: string,
|
|
260
|
+
): Promise<GooseSessionLogData | null> {
|
|
261
|
+
if (!sessionId || !GOOSE_SESSION_ID_RE.test(sessionId)) return null;
|
|
262
|
+
const db = await openSqliteReadonly(gooseDbPath());
|
|
263
|
+
if (!db) return null;
|
|
264
|
+
try {
|
|
265
|
+
const sessionRows = db.query<{ working_dir: string | null }>(
|
|
266
|
+
"SELECT working_dir FROM sessions WHERE id = ?",
|
|
267
|
+
[sessionId],
|
|
268
|
+
);
|
|
269
|
+
if (sessionRows.length === 0) return null;
|
|
270
|
+
const realCwd = sessionRows[0].working_dir;
|
|
271
|
+
|
|
272
|
+
const msgRows = db.query<GooseMessageRow>(
|
|
273
|
+
"SELECT role, content_json, created_timestamp FROM messages " +
|
|
274
|
+
"WHERE session_id = ? ORDER BY id ASC",
|
|
275
|
+
[sessionId],
|
|
276
|
+
);
|
|
277
|
+
const entries = gooseRowsToLogEntries(msgRows, "session");
|
|
278
|
+
const cwd = realCwd && realCwd.length > 0 ? realCwd : undefined;
|
|
279
|
+
return {
|
|
280
|
+
entries,
|
|
281
|
+
rawLines: msgRows as unknown as Record<string, unknown>[],
|
|
282
|
+
cwd,
|
|
283
|
+
filePath: `goose-db://${sessionId}`,
|
|
284
|
+
};
|
|
285
|
+
} catch {
|
|
286
|
+
return null;
|
|
287
|
+
} finally {
|
|
288
|
+
db.close();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export const getCachedGooseSessionLog = runtimeCache(
|
|
293
|
+
(sessionId: string) => getGooseSessionLog(sessionId),
|
|
294
|
+
2,
|
|
295
|
+
{ maxSize: 50 },
|
|
296
|
+
);
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install reporting, driven from the CLI instead of an npm lifecycle script.
|
|
3
|
+
*
|
|
4
|
+
* This used to live in `scripts/postinstall.mjs`. Every package manager now
|
|
5
|
+
* blocks dependency install scripts by default — npm 12 (`allowScripts`), pnpm,
|
|
6
|
+
* bun, and Yarn ≥4.14 — and a publisher cannot opt its own package in, so a
|
|
7
|
+
* postinstall hook is no longer a channel that reaches users. The first non-hook
|
|
8
|
+
* CLI invocation is.
|
|
9
|
+
*
|
|
10
|
+
* Two constraints shape this module:
|
|
11
|
+
*
|
|
12
|
+
* 1. It must never run on the `--hook` fast path in bin/failproofai.mjs. That
|
|
13
|
+
* path fires on every tool call of every agent session; the check belongs
|
|
14
|
+
* in runCli() only.
|
|
15
|
+
* 2. It must be free on the steady-state path. When the recorded version
|
|
16
|
+
* matches, this returns before touching anything else.
|
|
17
|
+
*
|
|
18
|
+
* Everything here is best-effort: telemetry must never break, delay, or fail a
|
|
19
|
+
* real command.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
22
|
+
import { resolve } from "node:path";
|
|
23
|
+
import { platform, arch, release, homedir, hostname } from "node:os";
|
|
24
|
+
import { createHmac } from "node:crypto";
|
|
25
|
+
import { trackInstallEvent } from "../scripts/install-telemetry.mjs";
|
|
26
|
+
|
|
27
|
+
const FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
|
|
28
|
+
const NAMESPACE = "failproofai-telemetry-v1";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Cap on the once-per-version report so a hanging network never stalls a command.
|
|
32
|
+
*
|
|
33
|
+
* A ceiling, not a cost: delivery resolves as soon as PostHog answers, measured
|
|
34
|
+
* at ~1.1s from a cold process (these events are the first thing a fresh process
|
|
35
|
+
* sends, so they always pay the DNS + TLS handshake). Raising this only matters
|
|
36
|
+
* when the network is slow — precisely when a tight budget would silently drop
|
|
37
|
+
* the report instead.
|
|
38
|
+
*
|
|
39
|
+
* 5s matches hook-telemetry and the postinstall script this replaces. An earlier
|
|
40
|
+
* 2s left barely any headroom over a 1.85s worst case seen in testing, for a
|
|
41
|
+
* saving nobody experiences on a healthy network.
|
|
42
|
+
*/
|
|
43
|
+
const REPORT_TIMEOUT_MS = 5000;
|
|
44
|
+
|
|
45
|
+
function hashToId(raw: string): string {
|
|
46
|
+
return createHmac("sha256", NAMESPACE).update(raw).digest("hex");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function lastVersionPath(): string {
|
|
50
|
+
return resolve(homedir(), ".failproofai", "last-version");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Semver ordering. A release outranks the same version carrying a prerelease
|
|
55
|
+
* tag (semver §11); within a prerelease, numeric identifiers rank below
|
|
56
|
+
* non-numeric ones.
|
|
57
|
+
*
|
|
58
|
+
* Prerelease identifiers are separated by dots only (semver §9). A hyphen is a
|
|
59
|
+
* legal character *inside* an identifier, so `beta-2` is one non-numeric
|
|
60
|
+
* identifier compared lexically — not `beta` and `2`.
|
|
61
|
+
*/
|
|
62
|
+
export function compareSemver(a: string, b: string): number {
|
|
63
|
+
const parse = (v: string) => {
|
|
64
|
+
const m = /^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/.exec(v);
|
|
65
|
+
if (!m) return null;
|
|
66
|
+
return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
|
|
67
|
+
};
|
|
68
|
+
const pa = parse(a);
|
|
69
|
+
const pb = parse(b);
|
|
70
|
+
if (!pa || !pb) return a < b ? -1 : a > b ? 1 : 0;
|
|
71
|
+
for (let i = 0; i < 3; i++) {
|
|
72
|
+
if (pa.nums[i] !== pb.nums[i]) return pa.nums[i] < pb.nums[i] ? -1 : 1;
|
|
73
|
+
}
|
|
74
|
+
if (pa.pre === null && pb.pre === null) return 0;
|
|
75
|
+
if (pa.pre === null) return 1;
|
|
76
|
+
if (pb.pre === null) return -1;
|
|
77
|
+
const ax = pa.pre.split(".");
|
|
78
|
+
const bx = pb.pre.split(".");
|
|
79
|
+
for (let i = 0; i < Math.max(ax.length, bx.length); i++) {
|
|
80
|
+
const ai = ax[i], bi = bx[i];
|
|
81
|
+
if (ai === undefined) return -1;
|
|
82
|
+
if (bi === undefined) return 1;
|
|
83
|
+
const aNum = /^\d+$/.test(ai), bNum = /^\d+$/.test(bi);
|
|
84
|
+
if (aNum && bNum) {
|
|
85
|
+
const d = Number(ai) - Number(bi);
|
|
86
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
87
|
+
} else if (aNum) {
|
|
88
|
+
return -1;
|
|
89
|
+
} else if (bNum) {
|
|
90
|
+
return 1;
|
|
91
|
+
} else if (ai !== bi) {
|
|
92
|
+
return ai < bi ? -1 : 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface HooksState {
|
|
99
|
+
configured: boolean;
|
|
100
|
+
registered: boolean;
|
|
101
|
+
policyCount: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Snapshot of the user's hook configuration, carried as properties on
|
|
106
|
+
* package_installed.
|
|
107
|
+
*
|
|
108
|
+
* Only inspects Claude's settings, which understates `registered` for someone
|
|
109
|
+
* running failproofai against another CLI. Preserved as-is from the postinstall
|
|
110
|
+
* script so the event keeps its historical meaning.
|
|
111
|
+
*/
|
|
112
|
+
export function checkHooks(): HooksState {
|
|
113
|
+
const hooksConfigPath = resolve(homedir(), ".failproofai", "policies-config.json");
|
|
114
|
+
if (!existsSync(hooksConfigPath)) {
|
|
115
|
+
return { configured: false, registered: false, policyCount: 0 };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let config;
|
|
119
|
+
try {
|
|
120
|
+
config = JSON.parse(readFileSync(hooksConfigPath, "utf8"));
|
|
121
|
+
} catch {
|
|
122
|
+
return { configured: false, registered: false, policyCount: 0 };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!Array.isArray(config.enabledPolicies) || config.enabledPolicies.length === 0) {
|
|
126
|
+
return { configured: false, registered: false, policyCount: 0 };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const policyCount = config.enabledPolicies.length;
|
|
130
|
+
const settingsPath = resolve(homedir(), ".claude", "settings.json");
|
|
131
|
+
if (!existsSync(settingsPath)) return { configured: true, registered: false, policyCount };
|
|
132
|
+
|
|
133
|
+
let settings;
|
|
134
|
+
try {
|
|
135
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
136
|
+
} catch {
|
|
137
|
+
return { configured: true, registered: false, policyCount };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!settings?.hooks) return { configured: true, registered: false, policyCount };
|
|
141
|
+
|
|
142
|
+
for (const matchers of Object.values(settings.hooks)) {
|
|
143
|
+
if (!Array.isArray(matchers)) continue;
|
|
144
|
+
for (const matcher of matchers) {
|
|
145
|
+
if (!matcher?.hooks) continue;
|
|
146
|
+
if (matcher.hooks.some((h: Record<string, unknown>) => h[FAILPROOFAI_HOOK_MARKER] === true)) {
|
|
147
|
+
return { configured: true, registered: true, policyCount };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { configured: true, registered: false, policyCount };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Report an install or upgrade, at most once per version.
|
|
157
|
+
*
|
|
158
|
+
* Fires first_install (nothing recorded yet) or version_changed (recorded
|
|
159
|
+
* version differs), then package_installed, then records `version`. A same-version
|
|
160
|
+
* run is a no-op — unlike the postinstall script this replaces, the CLI has no
|
|
161
|
+
* signal that would distinguish a reinstall from an ordinary invocation, so
|
|
162
|
+
* `direction: "reinstall"` is no longer reported.
|
|
163
|
+
*/
|
|
164
|
+
export async function maybeReportInstall(version: string): Promise<void> {
|
|
165
|
+
try {
|
|
166
|
+
const versionFile = lastVersionPath();
|
|
167
|
+
|
|
168
|
+
let previousVersion: string | null = null;
|
|
169
|
+
try {
|
|
170
|
+
if (existsSync(versionFile)) {
|
|
171
|
+
previousVersion = readFileSync(versionFile, "utf8").trim() || null;
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
// Unreadable marker — treat as first run rather than give up.
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Steady state: same version already reported. Costs one stat() per command.
|
|
178
|
+
if (previousVersion === version) return;
|
|
179
|
+
|
|
180
|
+
const hooks = checkHooks();
|
|
181
|
+
|
|
182
|
+
const events: Promise<void>[] = [];
|
|
183
|
+
if (previousVersion === null) {
|
|
184
|
+
events.push(
|
|
185
|
+
trackInstallEvent("first_install", {
|
|
186
|
+
platform: platform(),
|
|
187
|
+
arch: arch(),
|
|
188
|
+
os_release: release(),
|
|
189
|
+
node_version: process.versions.node,
|
|
190
|
+
}, { version, timeoutMs: REPORT_TIMEOUT_MS }),
|
|
191
|
+
);
|
|
192
|
+
} else {
|
|
193
|
+
const cmp = compareSemver(previousVersion, version);
|
|
194
|
+
events.push(
|
|
195
|
+
trackInstallEvent("version_changed", {
|
|
196
|
+
from_version: previousVersion,
|
|
197
|
+
to_version: version,
|
|
198
|
+
direction: cmp < 0 ? "upgrade" : "downgrade",
|
|
199
|
+
platform: platform(),
|
|
200
|
+
arch: arch(),
|
|
201
|
+
}, { version, timeoutMs: REPORT_TIMEOUT_MS }),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
events.push(
|
|
206
|
+
trackInstallEvent("package_installed", {
|
|
207
|
+
platform: platform(),
|
|
208
|
+
arch: arch(),
|
|
209
|
+
os_release: release(),
|
|
210
|
+
node_version: process.versions.node,
|
|
211
|
+
hostname_hash: hashToId(hostname()),
|
|
212
|
+
hooks_configured: hooks.configured,
|
|
213
|
+
hooks_registered: hooks.registered,
|
|
214
|
+
enabled_policy_count: hooks.policyCount,
|
|
215
|
+
}, { version, timeoutMs: REPORT_TIMEOUT_MS }),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Record before awaiting: a dropped event is cheaper than re-reporting the
|
|
219
|
+
// same version on every command because delivery kept failing.
|
|
220
|
+
try {
|
|
221
|
+
mkdirSync(resolve(homedir(), ".failproofai"), { recursive: true });
|
|
222
|
+
writeFileSync(versionFile, version, "utf8");
|
|
223
|
+
} catch {
|
|
224
|
+
// best-effort
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
await Promise.all(events.map((p) => p.catch(() => {})));
|
|
228
|
+
} catch {
|
|
229
|
+
// Reporting must never break a command.
|
|
230
|
+
}
|
|
231
|
+
}
|