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,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw (openclaw gateway) session enumeration — AUDIT-ONLY.
|
|
3
|
+
*
|
|
4
|
+
* Surfaces the on-disk transcripts (agents/<agentId>/sessions/<uuid>.jsonl) as
|
|
5
|
+
* synthetic dashboard "projects" grouped by agentId. The per-agent
|
|
6
|
+
* `sessions.json` index maps sessionKey → {sessionId, timestamps}; we read it to
|
|
7
|
+
* recover the sessionKey (which encodes the channel for gateway sessions) and a
|
|
8
|
+
* reliable last-activity time. Verified live against openclaw v2026.7.1.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { runtimeCache } from "./runtime-cache";
|
|
13
|
+
import { listOpenClawTranscripts, openclawHome } from "./openclaw-sessions";
|
|
14
|
+
import type { ProjectFolder, SessionFile } from "./projects";
|
|
15
|
+
import { formatDate } from "./format-date";
|
|
16
|
+
|
|
17
|
+
export interface OpenClawSessionRef {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
agentId: string;
|
|
20
|
+
/** Channel/source the session last ran in (from sessions.json metadata) —
|
|
21
|
+
* e.g. "telegram", "slack", or "local" for a CLI session. Drives grouping. */
|
|
22
|
+
channel: string;
|
|
23
|
+
/** Human-readable label from `origin.label` (e.g. "Chetan (@chhhee10) id:…"). */
|
|
24
|
+
label?: string;
|
|
25
|
+
/** Chat id (e.g. "telegram:8674922496") + type ("direct"/"group") for the
|
|
26
|
+
* gateway-metadata columns. */
|
|
27
|
+
chatId?: string;
|
|
28
|
+
chatType?: string;
|
|
29
|
+
mtimeMs: number;
|
|
30
|
+
sizeBytes: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface SessionIndexMeta {
|
|
34
|
+
lastMs?: number;
|
|
35
|
+
channel?: string;
|
|
36
|
+
chatType?: string;
|
|
37
|
+
label?: string;
|
|
38
|
+
chatId?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function str(v: unknown): string | undefined {
|
|
42
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Read the per-agent sessions.json index → sessionId → routing metadata.
|
|
46
|
+
* OpenClaw routes gateway sessions through the agent's default key
|
|
47
|
+
* (`agent:<id>:main`) and records the channel in metadata fields
|
|
48
|
+
* (`lastChannel`, `chatType`, `origin.{label,provider,from}`, `lastTo`) rather
|
|
49
|
+
* than in the key — verified live against v2026.7.1. */
|
|
50
|
+
function readSessionsIndex(agentId: string): Map<string, SessionIndexMeta> {
|
|
51
|
+
const out = new Map<string, SessionIndexMeta>();
|
|
52
|
+
const indexPath = join(openclawHome(), "agents", agentId, "sessions", "sessions.json");
|
|
53
|
+
let raw: unknown;
|
|
54
|
+
try {
|
|
55
|
+
raw = JSON.parse(readFileSync(indexPath, "utf-8"));
|
|
56
|
+
} catch {
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
if (!raw || typeof raw !== "object") return out;
|
|
60
|
+
for (const v of Object.values(raw as Record<string, unknown>)) {
|
|
61
|
+
if (!v || typeof v !== "object") continue;
|
|
62
|
+
const e = v as Record<string, unknown>;
|
|
63
|
+
const sessionId = str(e.sessionId);
|
|
64
|
+
if (!sessionId) continue;
|
|
65
|
+
const origin = (e.origin && typeof e.origin === "object" ? e.origin : {}) as Record<string, unknown>;
|
|
66
|
+
const lastMs =
|
|
67
|
+
typeof e.lastInteractionAt === "number"
|
|
68
|
+
? e.lastInteractionAt
|
|
69
|
+
: typeof e.updatedAt === "number"
|
|
70
|
+
? (e.updatedAt as number)
|
|
71
|
+
: undefined;
|
|
72
|
+
out.set(sessionId, {
|
|
73
|
+
lastMs,
|
|
74
|
+
channel: str(e.lastChannel) ?? str(origin.provider) ?? str(origin.surface),
|
|
75
|
+
chatType: str(e.chatType) ?? str(origin.chatType),
|
|
76
|
+
label: str(origin.label),
|
|
77
|
+
chatId: str(e.lastTo) ?? str(origin.from),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** List every OpenClaw session across all agents. Fail-open ([] on any error). */
|
|
84
|
+
export async function getOpenClawSessions(): Promise<OpenClawSessionRef[]> {
|
|
85
|
+
const transcripts = listOpenClawTranscripts();
|
|
86
|
+
const indexByAgent = new Map<string, Map<string, SessionIndexMeta>>();
|
|
87
|
+
const refs: OpenClawSessionRef[] = [];
|
|
88
|
+
for (const t of transcripts) {
|
|
89
|
+
let idx = indexByAgent.get(t.agentId);
|
|
90
|
+
if (!idx) {
|
|
91
|
+
idx = readSessionsIndex(t.agentId);
|
|
92
|
+
indexByAgent.set(t.agentId, idx);
|
|
93
|
+
}
|
|
94
|
+
const meta = idx.get(t.sessionId);
|
|
95
|
+
refs.push({
|
|
96
|
+
sessionId: t.sessionId,
|
|
97
|
+
agentId: t.agentId,
|
|
98
|
+
// Gateway sessions group by channel; CLI/local runs have none.
|
|
99
|
+
channel: meta?.channel ?? "local",
|
|
100
|
+
label: meta?.label,
|
|
101
|
+
chatType: meta?.chatType,
|
|
102
|
+
chatId: meta?.chatId,
|
|
103
|
+
mtimeMs: meta?.lastMs ?? t.mtimeMs,
|
|
104
|
+
sizeBytes: t.sizeBytes,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
108
|
+
return refs;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const getCachedOpenClawSessions = runtimeCache(getOpenClawSessions, 2);
|
|
112
|
+
|
|
113
|
+
// ── Dashboard history browser (projects list + project-detail sessions) ──
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Surface OpenClaw sessions as synthetic "projects" grouped by **channel**
|
|
117
|
+
* (telegram/slack/…/local) — gateway sessions run per channel, not in a host
|
|
118
|
+
* repo. Mirrors Hermes's group-by-source. One ProjectFolder per channel; its
|
|
119
|
+
* `name` is `openclaw-<channel>`, reversed in `getOpenClawSessionsByEncodedName`.
|
|
120
|
+
*/
|
|
121
|
+
export async function getOpenClawProjects(): Promise<ProjectFolder[]> {
|
|
122
|
+
const sessions = await getOpenClawSessions();
|
|
123
|
+
const latestByChannel = new Map<string, number>();
|
|
124
|
+
for (const s of sessions) {
|
|
125
|
+
latestByChannel.set(s.channel, Math.max(latestByChannel.get(s.channel) ?? 0, s.mtimeMs));
|
|
126
|
+
}
|
|
127
|
+
const out: ProjectFolder[] = [];
|
|
128
|
+
for (const [channel, latest] of latestByChannel) {
|
|
129
|
+
const lastModified = new Date(latest);
|
|
130
|
+
out.push({
|
|
131
|
+
name: `openclaw-${channel}`,
|
|
132
|
+
path: `openclaw:${channel}`,
|
|
133
|
+
isDirectory: true,
|
|
134
|
+
lastModified,
|
|
135
|
+
lastModifiedFormatted: formatDate(lastModified),
|
|
136
|
+
cli: ["openclaw"],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
out.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface OpenClawProjectByName {
|
|
144
|
+
cwd: string | null;
|
|
145
|
+
sessions: SessionFile[];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Resolve the OpenClaw sessions for a synthetic project name
|
|
149
|
+
* (`openclaw-<channel>`), for the project-detail page. Session names use the
|
|
150
|
+
* human-readable `origin.label` (e.g. "Chetan (@chhhee10) id:…") rather than
|
|
151
|
+
* the raw session key. */
|
|
152
|
+
export async function getOpenClawSessionsByEncodedName(
|
|
153
|
+
name: string,
|
|
154
|
+
): Promise<OpenClawProjectByName> {
|
|
155
|
+
if (!name.startsWith("openclaw-")) return { cwd: null, sessions: [] };
|
|
156
|
+
const channel = name.slice("openclaw-".length);
|
|
157
|
+
const sessions = await getOpenClawSessions();
|
|
158
|
+
const matched = sessions.filter((s) => s.channel === channel);
|
|
159
|
+
return {
|
|
160
|
+
cwd: `openclaw:${channel}`,
|
|
161
|
+
sessions: matched.map((s) => {
|
|
162
|
+
const lastModified = new Date(s.mtimeMs);
|
|
163
|
+
return {
|
|
164
|
+
name: s.label ?? s.chatId ?? s.sessionId,
|
|
165
|
+
path: s.sessionId,
|
|
166
|
+
lastModified,
|
|
167
|
+
lastModifiedFormatted: formatDate(lastModified),
|
|
168
|
+
sessionId: s.sessionId,
|
|
169
|
+
cli: "openclaw" as const,
|
|
170
|
+
channelId: s.chatId,
|
|
171
|
+
channelType: s.chatType,
|
|
172
|
+
};
|
|
173
|
+
}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export const getCachedOpenClawProjects = runtimeCache(getOpenClawProjects, 2);
|
|
178
|
+
export const getCachedOpenClawSessionsByEncodedName = runtimeCache(
|
|
179
|
+
(name: string) => getOpenClawSessionsByEncodedName(name),
|
|
180
|
+
2,
|
|
181
|
+
{ maxSize: 50 },
|
|
182
|
+
);
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw (openclaw gateway) session transcript loader + parser.
|
|
3
|
+
*
|
|
4
|
+
* AUDIT-ONLY (Pillar 2). OpenClaw writes one JSONL transcript per session at
|
|
5
|
+
* `~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl` (sessionId is a
|
|
6
|
+
* UUID), alongside a much larger `<sessionId>.trajectory.jsonl` OTel trace we
|
|
7
|
+
* IGNORE, and a `sessions.json` index keyed by sessionKey. Verified live
|
|
8
|
+
* against openclaw v2026.7.1.
|
|
9
|
+
*
|
|
10
|
+
* The transcript is type-discriminated JSONL:
|
|
11
|
+
* {type:"session", cwd, …} — header (carries cwd)
|
|
12
|
+
* {type:"model_change" | "thinking_level_change" | "custom", …} — metadata
|
|
13
|
+
* {type:"message", timestamp, message:{role, content, …}} — the turns, where
|
|
14
|
+
* role ∈ "user" (content string) | "assistant" (content = list of
|
|
15
|
+
* {type:"text",text} / {type:"toolCall", id, name, arguments}) |
|
|
16
|
+
* "toolResult" ({toolCallId, toolName, content, details, isError}).
|
|
17
|
+
* `openclawLinesToLogEntries` pairs each assistant `toolCall` with its later
|
|
18
|
+
* `toolResult` by `toolCallId` (mirrors lib/hermes-sessions.ts) and is PURE, so
|
|
19
|
+
* it is unit-testable with plain line objects.
|
|
20
|
+
*
|
|
21
|
+
* Home override: set `OPENCLAW_HOME` (used by tests / to point at a copied
|
|
22
|
+
* gateway config dir).
|
|
23
|
+
*/
|
|
24
|
+
import { readFile } from "node:fs/promises";
|
|
25
|
+
import { readdirSync, statSync } from "node:fs";
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
import { runtimeCache } from "./runtime-cache";
|
|
29
|
+
import {
|
|
30
|
+
baseEntry,
|
|
31
|
+
formatTimestamp,
|
|
32
|
+
parseRawLines,
|
|
33
|
+
type LogEntry,
|
|
34
|
+
type UserEntry,
|
|
35
|
+
type AssistantEntry,
|
|
36
|
+
type GenericEntry,
|
|
37
|
+
type ContentBlock,
|
|
38
|
+
type ToolUseBlock,
|
|
39
|
+
type LogSource,
|
|
40
|
+
} from "./log-entries";
|
|
41
|
+
import { formatDuration } from "./format-duration";
|
|
42
|
+
|
|
43
|
+
/** OpenClaw sessions are stored under UUID filenames. */
|
|
44
|
+
export const OPENCLAW_SESSION_ID_RE = /^[0-9a-fA-F-]{36}$/;
|
|
45
|
+
|
|
46
|
+
/** Absolute path to OpenClaw's config home (override with OPENCLAW_HOME). */
|
|
47
|
+
export function openclawHome(): string {
|
|
48
|
+
return process.env.OPENCLAW_HOME || join(homedir(), ".openclaw");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Parsing helpers ──
|
|
52
|
+
|
|
53
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
54
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Text from a `content` field that may be a string or an array of
|
|
58
|
+
* `{ type:"text", text }` blocks. */
|
|
59
|
+
function extractText(content: unknown): string {
|
|
60
|
+
if (typeof content === "string") return content;
|
|
61
|
+
if (Array.isArray(content)) {
|
|
62
|
+
return content
|
|
63
|
+
.map((c) => (isPlainObject(c) && typeof c.text === "string" ? (c.text as string) : ""))
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join("\n");
|
|
66
|
+
}
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toDate(value: unknown, fallbackMs: number): Date {
|
|
71
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
72
|
+
if (value > 1e12) return new Date(value);
|
|
73
|
+
if (value > 1e9) return new Date(value * 1000);
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
const ms = Date.parse(value);
|
|
77
|
+
if (!Number.isNaN(ms)) return new Date(ms);
|
|
78
|
+
}
|
|
79
|
+
return new Date(fallbackMs);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Pure parser: transcript lines → LogEntry[] ──
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Convert OpenClaw transcript JSONL lines (parsed objects, in file order) into
|
|
86
|
+
* `LogEntry[]`. Only `type:"message"` lines carry turns; `session` /
|
|
87
|
+
* `model_change` / `thinking_level_change` / `custom` lines are metadata and
|
|
88
|
+
* are skipped (they'd otherwise clutter the audit with empty system entries).
|
|
89
|
+
* Assistant `toolCall` blocks are paired with their later `toolResult` message
|
|
90
|
+
* by `toolCallId`. Pure — unit-testable with plain line objects.
|
|
91
|
+
*/
|
|
92
|
+
export function openclawLinesToLogEntries(
|
|
93
|
+
lines: Record<string, unknown>[],
|
|
94
|
+
source: LogSource = "session",
|
|
95
|
+
): LogEntry[] {
|
|
96
|
+
const entries: LogEntry[] = [];
|
|
97
|
+
const toolUseById = new Map<string, ToolUseBlock>();
|
|
98
|
+
const toolUseStartMs = new Map<string, number>();
|
|
99
|
+
const baseMs = Date.now();
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < lines.length; i++) {
|
|
102
|
+
const line = lines[i];
|
|
103
|
+
if (!isPlainObject(line)) continue;
|
|
104
|
+
if (line.type !== "message") continue; // skip session/model_change/custom/…
|
|
105
|
+
|
|
106
|
+
const m = isPlainObject(line.message) ? line.message : undefined;
|
|
107
|
+
if (!m) continue;
|
|
108
|
+
const role = typeof m.role === "string" ? m.role : "system";
|
|
109
|
+
|
|
110
|
+
// Outer line `timestamp` is an ISO string; message.timestamp is epoch ms.
|
|
111
|
+
const date = toDate(line.timestamp ?? m.timestamp, baseMs + i);
|
|
112
|
+
const timestamp = date.toISOString();
|
|
113
|
+
const raw: Record<string, unknown> = {
|
|
114
|
+
uuid: line.id != null ? String(line.id) : `openclaw-${i}`,
|
|
115
|
+
parentUuid: line.parentId != null ? String(line.parentId) : null,
|
|
116
|
+
};
|
|
117
|
+
const base = baseEntry(raw, timestamp, date, source);
|
|
118
|
+
|
|
119
|
+
if (role === "user") {
|
|
120
|
+
entries.push({
|
|
121
|
+
type: "user",
|
|
122
|
+
...base,
|
|
123
|
+
message: { role: "user", content: extractText(m.content) },
|
|
124
|
+
} satisfies UserEntry);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (role === "assistant") {
|
|
129
|
+
const blocks: ContentBlock[] = [];
|
|
130
|
+
const content = m.content;
|
|
131
|
+
if (Array.isArray(content)) {
|
|
132
|
+
for (const b of content) {
|
|
133
|
+
if (!isPlainObject(b)) continue;
|
|
134
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
135
|
+
blocks.push({ type: "text", text: b.text });
|
|
136
|
+
} else if (b.type === "toolCall") {
|
|
137
|
+
const id = typeof b.id === "string" ? b.id : `${String(b.name ?? "tool")}-${blocks.length}`;
|
|
138
|
+
const name = typeof b.name === "string" ? b.name : "tool";
|
|
139
|
+
const input = isPlainObject(b.arguments) ? b.arguments : {};
|
|
140
|
+
const block: ToolUseBlock = { type: "tool_use", id, name, input };
|
|
141
|
+
blocks.push(block);
|
|
142
|
+
toolUseById.set(id, block);
|
|
143
|
+
toolUseStartMs.set(id, date.getTime());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
const text = extractText(content);
|
|
148
|
+
if (text) blocks.push({ type: "text", text });
|
|
149
|
+
}
|
|
150
|
+
if (blocks.length === 0) continue; // empty / failed assistant turn
|
|
151
|
+
entries.push({
|
|
152
|
+
type: "assistant",
|
|
153
|
+
...base,
|
|
154
|
+
message: { role: "assistant", content: blocks, model: typeof m.model === "string" ? m.model : undefined },
|
|
155
|
+
} satisfies AssistantEntry);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (role === "toolResult") {
|
|
160
|
+
const callId = typeof m.toolCallId === "string" ? m.toolCallId : undefined;
|
|
161
|
+
const block = callId ? toolUseById.get(callId) : undefined;
|
|
162
|
+
if (block) {
|
|
163
|
+
const details = isPlainObject(m.details) ? m.details : undefined;
|
|
164
|
+
const startMs = (callId && toolUseStartMs.get(callId)) || date.getTime();
|
|
165
|
+
const durationMs =
|
|
166
|
+
details && typeof details.durationMs === "number"
|
|
167
|
+
? details.durationMs
|
|
168
|
+
: Math.max(0, date.getTime() - startMs);
|
|
169
|
+
block.result = {
|
|
170
|
+
timestamp,
|
|
171
|
+
timestampFormatted: formatTimestamp(date),
|
|
172
|
+
content: extractText(m.content),
|
|
173
|
+
durationMs,
|
|
174
|
+
durationFormatted: formatDuration(durationMs),
|
|
175
|
+
};
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
// Orphan tool result — fall through to a system entry.
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
entries.push({ type: "system", ...base, raw } satisfies GenericEntry);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
entries.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
185
|
+
return entries;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ── Discovery + file loader ──
|
|
189
|
+
|
|
190
|
+
export interface OpenClawTranscriptFile {
|
|
191
|
+
agentId: string;
|
|
192
|
+
sessionId: string;
|
|
193
|
+
transcriptPath: string;
|
|
194
|
+
mtimeMs: number;
|
|
195
|
+
sizeBytes: number;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Enumerate `agents/<agentId>/sessions/<uuid>.jsonl` transcripts, skipping the
|
|
199
|
+
* heavy `.trajectory.jsonl` OTel traces and pointer files. */
|
|
200
|
+
export function listOpenClawTranscripts(): OpenClawTranscriptFile[] {
|
|
201
|
+
const agentsDir = join(openclawHome(), "agents");
|
|
202
|
+
const out: OpenClawTranscriptFile[] = [];
|
|
203
|
+
let agentIds: string[];
|
|
204
|
+
try {
|
|
205
|
+
agentIds = readdirSync(agentsDir, { withFileTypes: true })
|
|
206
|
+
.filter((d) => d.isDirectory())
|
|
207
|
+
.map((d) => d.name);
|
|
208
|
+
} catch {
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
for (const agentId of agentIds) {
|
|
212
|
+
const sessionsDir = join(agentsDir, agentId, "sessions");
|
|
213
|
+
let files: string[];
|
|
214
|
+
try {
|
|
215
|
+
files = readdirSync(sessionsDir);
|
|
216
|
+
} catch {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
for (const file of files) {
|
|
220
|
+
// Only `<uuid>.jsonl` — not `<uuid>.trajectory.jsonl` / `.trajectory-path.json`.
|
|
221
|
+
if (!file.endsWith(".jsonl") || file.endsWith(".trajectory.jsonl")) continue;
|
|
222
|
+
const sessionId = file.slice(0, -".jsonl".length);
|
|
223
|
+
if (!OPENCLAW_SESSION_ID_RE.test(sessionId)) continue;
|
|
224
|
+
const transcriptPath = join(sessionsDir, file);
|
|
225
|
+
try {
|
|
226
|
+
const st = statSync(transcriptPath);
|
|
227
|
+
out.push({ agentId, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
228
|
+
} catch {
|
|
229
|
+
// skip unreadable
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Resolve a session UUID to its on-disk transcript path (host-side). Guards
|
|
237
|
+
* against traversal by requiring a UUID filename. Shared by the audit adapter
|
|
238
|
+
* and download-session. */
|
|
239
|
+
export function findOpenClawTranscript(sessionId: string): string | null {
|
|
240
|
+
if (!OPENCLAW_SESSION_ID_RE.test(sessionId)) return null;
|
|
241
|
+
for (const t of listOpenClawTranscripts()) {
|
|
242
|
+
if (t.sessionId === sessionId) return t.transcriptPath;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface OpenClawSessionLogData {
|
|
248
|
+
entries: LogEntry[];
|
|
249
|
+
rawLines: Record<string, unknown>[];
|
|
250
|
+
cwd?: string;
|
|
251
|
+
filePath: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Load and parse one session transcript by UUID. Returns `null` when the file
|
|
255
|
+
* is missing/unreadable or the id fails validation. */
|
|
256
|
+
export async function getOpenClawSessionLog(
|
|
257
|
+
sessionId: string,
|
|
258
|
+
): Promise<OpenClawSessionLogData | null> {
|
|
259
|
+
const filePath = findOpenClawTranscript(sessionId);
|
|
260
|
+
if (!filePath) return null;
|
|
261
|
+
let content: string;
|
|
262
|
+
try {
|
|
263
|
+
content = await readFile(filePath, "utf-8");
|
|
264
|
+
} catch {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const rawLines = parseRawLines(content, "session");
|
|
268
|
+
const entries = openclawLinesToLogEntries(rawLines, "session");
|
|
269
|
+
// cwd lives on the `type:"session"` header line.
|
|
270
|
+
let cwd: string | undefined;
|
|
271
|
+
for (const line of rawLines) {
|
|
272
|
+
if (isPlainObject(line) && line.type === "session" && typeof line.cwd === "string" && line.cwd.length > 0) {
|
|
273
|
+
cwd = line.cwd;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return { entries, rawLines, cwd, filePath };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export const getCachedOpenClawSessionLog = runtimeCache(
|
|
281
|
+
(sessionId: string) => getOpenClawSessionLog(sessionId),
|
|
282
|
+
2,
|
|
283
|
+
{ maxSize: 50 },
|
|
284
|
+
);
|
|
@@ -16,7 +16,7 @@ import { formatDate } from "./format-date";
|
|
|
16
16
|
export const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
17
17
|
export const PATH_TRAVERSAL_RE = /(^|[\\/])\.\.($|[\\/])/;
|
|
18
18
|
|
|
19
|
-
export type ProjectCli = "claude" | "codex" | "copilot" | "cursor" | "opencode" | "pi" | "
|
|
19
|
+
export type ProjectCli = "claude" | "codex" | "copilot" | "cursor" | "opencode" | "pi" | "hermes" | "openclaw" | "factory" | "devin" | "antigravity" | "goose";
|
|
20
20
|
|
|
21
21
|
export interface ProjectFolder {
|
|
22
22
|
name: string;
|
|
@@ -143,18 +143,26 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
|
|
|
143
143
|
{ getCursorProjects },
|
|
144
144
|
{ getOpenCodeProjects },
|
|
145
145
|
{ getPiProjects },
|
|
146
|
-
{ getGeminiProjects },
|
|
147
146
|
{ getHermesProjects },
|
|
147
|
+
{ getOpenClawProjects },
|
|
148
|
+
{ getFactoryProjects },
|
|
149
|
+
{ getDevinProjects },
|
|
150
|
+
{ getAntigravityProjects },
|
|
151
|
+
{ getGooseProjects },
|
|
148
152
|
] = await Promise.all([
|
|
149
153
|
import("./codex-projects"),
|
|
150
154
|
import("./copilot-projects"),
|
|
151
155
|
import("./cursor-projects"),
|
|
152
156
|
import("./opencode-projects"),
|
|
153
157
|
import("./pi-projects"),
|
|
154
|
-
import("./gemini-projects"),
|
|
155
158
|
import("./hermes-projects"),
|
|
159
|
+
import("./openclaw-projects"),
|
|
160
|
+
import("./factory-projects"),
|
|
161
|
+
import("./devin-projects"),
|
|
162
|
+
import("./antigravity-projects"),
|
|
163
|
+
import("./goose-projects"),
|
|
156
164
|
]);
|
|
157
|
-
const [claude, codex, copilot, cursor, opencode, pi,
|
|
165
|
+
const [claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose] = await Promise.all([
|
|
158
166
|
getClaudeProjectFolders(),
|
|
159
167
|
getCodexProjects().catch((error) => {
|
|
160
168
|
logError("Error reading Codex projects:", error);
|
|
@@ -176,16 +184,32 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
|
|
|
176
184
|
logError("Error reading Pi projects:", error);
|
|
177
185
|
return [] as ProjectFolder[];
|
|
178
186
|
}),
|
|
179
|
-
getGeminiProjects().catch((error) => {
|
|
180
|
-
logError("Error reading Gemini projects:", error);
|
|
181
|
-
return [] as ProjectFolder[];
|
|
182
|
-
}),
|
|
183
187
|
getHermesProjects().catch((error) => {
|
|
184
188
|
logError("Error reading Hermes projects:", error);
|
|
185
189
|
return [] as ProjectFolder[];
|
|
186
190
|
}),
|
|
191
|
+
getOpenClawProjects().catch((error) => {
|
|
192
|
+
logError("Error reading OpenClaw projects:", error);
|
|
193
|
+
return [] as ProjectFolder[];
|
|
194
|
+
}),
|
|
195
|
+
getFactoryProjects().catch((error) => {
|
|
196
|
+
logError("Error reading Factory projects:", error);
|
|
197
|
+
return [] as ProjectFolder[];
|
|
198
|
+
}),
|
|
199
|
+
getDevinProjects().catch((error) => {
|
|
200
|
+
logError("Error reading Devin projects:", error);
|
|
201
|
+
return [] as ProjectFolder[];
|
|
202
|
+
}),
|
|
203
|
+
getAntigravityProjects().catch((error) => {
|
|
204
|
+
logError("Error reading Antigravity projects:", error);
|
|
205
|
+
return [] as ProjectFolder[];
|
|
206
|
+
}),
|
|
207
|
+
getGooseProjects().catch((error) => {
|
|
208
|
+
logError("Error reading Goose projects:", error);
|
|
209
|
+
return [] as ProjectFolder[];
|
|
210
|
+
}),
|
|
187
211
|
]);
|
|
188
|
-
return mergeProjectFolders(claude, codex, copilot, cursor, opencode, pi,
|
|
212
|
+
return mergeProjectFolders(claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose);
|
|
189
213
|
}
|
|
190
214
|
|
|
191
215
|
/**
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
|
|
3
|
+
/** Max length of a telemetry error message; longer strings are truncated. */
|
|
4
|
+
const MAX_ERROR_MESSAGE_LEN = 300;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Turn an error into a telemetry-safe message string for the `error_message`
|
|
8
|
+
* property on failure events (e.g. `cli_audit_failed`, `audit_run_failed`).
|
|
9
|
+
*
|
|
10
|
+
* Two protections keep this consistent with failproofai's telemetry philosophy
|
|
11
|
+
* (no raw local paths / PII on the wire):
|
|
12
|
+
* 1. The user's home directory is collapsed to `~`, so an error that embeds a
|
|
13
|
+
* path like `/home/alice/.codex/…` is reported as `~/.codex/…`.
|
|
14
|
+
* 2. The message is truncated to a bounded length (with an ellipsis marker).
|
|
15
|
+
*
|
|
16
|
+
* Never throws — falls back to `"unknown"` if the error can't be stringified.
|
|
17
|
+
*/
|
|
18
|
+
export function sanitizeErrorMessage(err: unknown): string {
|
|
19
|
+
let msg: string;
|
|
20
|
+
try {
|
|
21
|
+
msg = err instanceof Error ? err.message : String(err);
|
|
22
|
+
} catch {
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
if (!msg) return "";
|
|
26
|
+
|
|
27
|
+
let home = "";
|
|
28
|
+
try {
|
|
29
|
+
home = os.homedir();
|
|
30
|
+
} catch {
|
|
31
|
+
/* homedir unavailable — skip path stripping */
|
|
32
|
+
}
|
|
33
|
+
// Only strip a real, non-root home to avoid mangling messages when homedir is
|
|
34
|
+
// "/" or empty.
|
|
35
|
+
if (home && home.length > 1) {
|
|
36
|
+
msg = msg.split(home).join("~");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (msg.length > MAX_ERROR_MESSAGE_LEN) {
|
|
40
|
+
msg = msg.slice(0, MAX_ERROR_MESSAGE_LEN) + "…";
|
|
41
|
+
}
|
|
42
|
+
return msg;
|
|
43
|
+
}
|