failproofai 0.0.13 → 0.0.14-beta.0
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/.factory/hooks.json +112 -0
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +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/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/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +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/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/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/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]__0xuaoik._.js +3 -3
- 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/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]__07iom4f._.js → [root-of-the-server]__07qfsfa._.js} +2 -2
- 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]__0e2l3c1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ge8e7w._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h12me5._.js +1 -1
- 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]__0ynf7tx._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11rtg6s._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +1 -1
- 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]__1dky4g0._.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/[root-of-the-server]__212nf49._.js +1 -1
- 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_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_0g5r64e.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0-uvagv.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_03c7gi5.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_09p-8om.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0v-kfiu.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0xrgzyz.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1806lsc.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1j6dd-e.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1sa65r-.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 +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/static/chunks/{23c7v2svx90dp.js → 003uz088wbmz7.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0vc0eak148vo6.js → 0405htbxay9vd.js} +1 -1
- package/.next/standalone/.next/static/chunks/0iinhn4417nk1.js +1 -0
- package/.next/standalone/.next/static/chunks/1374x9bv_s_vg.js +6 -0
- package/.next/standalone/.next/static/chunks/{30ysv-_8jj052.js → 17sdztgbcc0fi.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2cejn75gug3j8.js → 2m_tbl39fcedn.js} +1 -1
- package/.next/standalone/.next/static/chunks/{36wdtkgdf0zrd.js → 2o3ql0n5owqsw.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0wtagd8a5gwq-.js → 2ufuwhv03yvtb.js} +1 -1
- package/.next/standalone/.next/static/chunks/35-90m9py64d4.js +1 -0
- package/.next/standalone/.next/static/chunks/3r--yyjkp9_7b.css +2 -0
- package/.next/standalone/.next/static/chunks/43weu8f59tlix.js +1 -0
- package/.next/standalone/.next/static/chunks/{05apzuand0c22.js → 454dyizqik6t9.js} +2 -2
- 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/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/components/navbar.tsx +5 -11
- package/.next/standalone/components/reach-developers.tsx +1 -1
- 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/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/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 -1
- package/.next/standalone/server.js +1 -1
- package/README.md +105 -57
- package/bin/failproofai.mjs +87 -29
- package/dist/cli.mjs +12803 -9809
- 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/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/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 -1
- package/scripts/launch.ts +4 -8
- package/scripts/postinstall.mjs +3 -4
- package/scripts/sync-agent-cli-harnesses-prompt.md +7 -10
- package/scripts/translate-docs/cli.ts +73 -52
- package/scripts/translate-docs/config.ts +57 -13
- package/scripts/translate-docs/mdx-translator.ts +51 -31
- package/scripts/translate-docs/mintlify-nav.ts +91 -10
- package/scripts/translate-docs/types.ts +3 -0
- 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 +44 -1
- 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]__108slqn._.js +0 -3
- 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/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/src/audit/cli-adapters/gemini.ts +0 -51
- package/src/hooks/first-run-nudge.ts +0 -146
- /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → MNJWFb2jLRrxRKGFNK6Hy}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → MNJWFb2jLRrxRKGFNK6Hy}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → MNJWFb2jLRrxRKGFNK6Hy}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory (droid) session transcript loader + parser.
|
|
3
|
+
*
|
|
4
|
+
* AUDIT-ONLY (Pillar 2). droid writes one JSONL transcript per session at
|
|
5
|
+
* `~/.factory/sessions/<encoded-cwd>/<sessionId>.jsonl` (Claude-style
|
|
6
|
+
* encoded-cwd folders — e.g. `-home-chetan-project`), alongside a
|
|
7
|
+
* `<sessionId>.settings.json` sibling we IGNORE. Verified live against droid
|
|
8
|
+
* v0.171.0.
|
|
9
|
+
*
|
|
10
|
+
* The transcript is type-discriminated JSONL:
|
|
11
|
+
* {type:"session_start", id, cwd, …} — header (carries cwd)
|
|
12
|
+
* {type:"message", timestamp, message:{role, content, visibility}} — the turns
|
|
13
|
+
* {type:"compaction_state", …} — metadata (skipped)
|
|
14
|
+
*
|
|
15
|
+
* Message content is Claude-style: a string (user text) or an array of
|
|
16
|
+
* {type:"text",text} / {type:"tool_use",id,name,input} / {type:"tool_result",
|
|
17
|
+
* tool_use_id,content} blocks. `factoryLinesToLogEntries` pairs each assistant
|
|
18
|
+
* `tool_use` with its later `tool_result` by id (mirrors lib/openclaw-sessions.ts)
|
|
19
|
+
* and is PURE, so it is unit-testable with plain line objects.
|
|
20
|
+
*
|
|
21
|
+
* Home override: set `FACTORY_HOME` (used by tests / to point at a copied
|
|
22
|
+
* sessions 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 { decodeFolderName } from "./paths";
|
|
29
|
+
import { runtimeCache } from "./runtime-cache";
|
|
30
|
+
import {
|
|
31
|
+
baseEntry,
|
|
32
|
+
formatTimestamp,
|
|
33
|
+
parseRawLines,
|
|
34
|
+
type LogEntry,
|
|
35
|
+
type UserEntry,
|
|
36
|
+
type AssistantEntry,
|
|
37
|
+
type GenericEntry,
|
|
38
|
+
type ContentBlock,
|
|
39
|
+
type ToolUseBlock,
|
|
40
|
+
type LogSource,
|
|
41
|
+
} from "./log-entries";
|
|
42
|
+
import { formatDuration } from "./format-duration";
|
|
43
|
+
|
|
44
|
+
/** Factory sessions are stored under UUID filenames. */
|
|
45
|
+
export const FACTORY_SESSION_ID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
46
|
+
|
|
47
|
+
/** Absolute path to Factory's config home (override with FACTORY_HOME). */
|
|
48
|
+
export function factoryHome(): string {
|
|
49
|
+
return process.env.FACTORY_HOME || join(homedir(), ".factory");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Absolute path to the Factory sessions root. */
|
|
53
|
+
export function factorySessionsRoot(): string {
|
|
54
|
+
return join(factoryHome(), "sessions");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── Parsing helpers ──
|
|
58
|
+
|
|
59
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
60
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Text from a `content` field that may be a string or an array of
|
|
64
|
+
* `{ type:"text", text }` blocks. */
|
|
65
|
+
function extractText(content: unknown): string {
|
|
66
|
+
if (typeof content === "string") return content;
|
|
67
|
+
if (Array.isArray(content)) {
|
|
68
|
+
return content
|
|
69
|
+
.map((c) => (isPlainObject(c) && typeof c.text === "string" ? (c.text as string) : ""))
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
.join("\n");
|
|
72
|
+
}
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function toDate(value: unknown, fallbackMs: number): Date {
|
|
77
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
78
|
+
if (value > 1e12) return new Date(value);
|
|
79
|
+
if (value > 1e9) return new Date(value * 1000);
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === "string") {
|
|
82
|
+
const ms = Date.parse(value);
|
|
83
|
+
if (!Number.isNaN(ms)) return new Date(ms);
|
|
84
|
+
}
|
|
85
|
+
return new Date(fallbackMs);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── Pure parser: transcript lines → LogEntry[] ──
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Convert Factory transcript JSONL lines (parsed objects, in file order) into
|
|
92
|
+
* `LogEntry[]`. Only `type:"message"` lines carry turns; `session_start` /
|
|
93
|
+
* `compaction_state` / other metadata lines are skipped. Assistant `tool_use`
|
|
94
|
+
* blocks are paired with their later `tool_result` (delivered on a user/tool
|
|
95
|
+
* message) by tool_use id. Pure — unit-testable with plain line objects.
|
|
96
|
+
*/
|
|
97
|
+
export function factoryLinesToLogEntries(
|
|
98
|
+
lines: Record<string, unknown>[],
|
|
99
|
+
source: LogSource = "session",
|
|
100
|
+
): LogEntry[] {
|
|
101
|
+
const entries: LogEntry[] = [];
|
|
102
|
+
const toolUseById = new Map<string, ToolUseBlock>();
|
|
103
|
+
const toolUseStartMs = new Map<string, number>();
|
|
104
|
+
const baseMs = Date.now();
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < lines.length; i++) {
|
|
107
|
+
const line = lines[i];
|
|
108
|
+
if (!isPlainObject(line)) continue;
|
|
109
|
+
if (line.type !== "message") continue; // skip session_start / compaction_state / …
|
|
110
|
+
|
|
111
|
+
const m = isPlainObject(line.message) ? line.message : undefined;
|
|
112
|
+
if (!m) continue;
|
|
113
|
+
const role = typeof m.role === "string" ? m.role : "system";
|
|
114
|
+
|
|
115
|
+
const date = toDate(line.timestamp ?? m.timestamp, baseMs + i);
|
|
116
|
+
const timestamp = date.toISOString();
|
|
117
|
+
const raw: Record<string, unknown> = {
|
|
118
|
+
uuid: line.id != null ? String(line.id) : `factory-${i}`,
|
|
119
|
+
parentUuid: line.parentId != null ? String(line.parentId) : null,
|
|
120
|
+
};
|
|
121
|
+
const base = baseEntry(raw, timestamp, date, source);
|
|
122
|
+
const content = m.content;
|
|
123
|
+
|
|
124
|
+
if (role === "assistant") {
|
|
125
|
+
const blocks: ContentBlock[] = [];
|
|
126
|
+
if (Array.isArray(content)) {
|
|
127
|
+
for (const b of content) {
|
|
128
|
+
if (!isPlainObject(b)) continue;
|
|
129
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
130
|
+
blocks.push({ type: "text", text: b.text });
|
|
131
|
+
} else if (b.type === "tool_use") {
|
|
132
|
+
const id = typeof b.id === "string" ? b.id : `${String(b.name ?? "tool")}-${blocks.length}`;
|
|
133
|
+
const name = typeof b.name === "string" ? b.name : "tool";
|
|
134
|
+
const input = isPlainObject(b.input) ? b.input : {};
|
|
135
|
+
const block: ToolUseBlock = { type: "tool_use", id, name, input };
|
|
136
|
+
blocks.push(block);
|
|
137
|
+
toolUseById.set(id, block);
|
|
138
|
+
toolUseStartMs.set(id, date.getTime());
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
const text = extractText(content);
|
|
143
|
+
if (text) blocks.push({ type: "text", text });
|
|
144
|
+
}
|
|
145
|
+
if (blocks.length === 0) continue; // empty / failed assistant turn
|
|
146
|
+
entries.push({
|
|
147
|
+
type: "assistant",
|
|
148
|
+
...base,
|
|
149
|
+
message: { role: "assistant", content: blocks, model: typeof m.model === "string" ? m.model : undefined },
|
|
150
|
+
} satisfies AssistantEntry);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// user / tool role: may carry tool_result blocks and/or text.
|
|
155
|
+
if (Array.isArray(content)) {
|
|
156
|
+
let attachedAny = false;
|
|
157
|
+
const textParts: string[] = [];
|
|
158
|
+
for (const b of content) {
|
|
159
|
+
if (!isPlainObject(b)) continue;
|
|
160
|
+
if (b.type === "tool_result") {
|
|
161
|
+
const callId =
|
|
162
|
+
typeof b.tool_use_id === "string"
|
|
163
|
+
? (b.tool_use_id as string)
|
|
164
|
+
: typeof b.toolUseId === "string"
|
|
165
|
+
? (b.toolUseId as string)
|
|
166
|
+
: undefined;
|
|
167
|
+
const block = callId ? toolUseById.get(callId) : undefined;
|
|
168
|
+
if (block) {
|
|
169
|
+
const startMs = (callId && toolUseStartMs.get(callId)) || date.getTime();
|
|
170
|
+
const durationMs = Math.max(0, date.getTime() - startMs);
|
|
171
|
+
block.result = {
|
|
172
|
+
timestamp,
|
|
173
|
+
timestampFormatted: formatTimestamp(date),
|
|
174
|
+
content: extractText(b.content),
|
|
175
|
+
durationMs,
|
|
176
|
+
durationFormatted: formatDuration(durationMs),
|
|
177
|
+
};
|
|
178
|
+
attachedAny = true;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (b.type === "text" && typeof b.text === "string") textParts.push(b.text);
|
|
183
|
+
}
|
|
184
|
+
if (textParts.length > 0) {
|
|
185
|
+
entries.push({
|
|
186
|
+
type: "user",
|
|
187
|
+
...base,
|
|
188
|
+
message: { role: "user", content: textParts.join("\n") },
|
|
189
|
+
} satisfies UserEntry);
|
|
190
|
+
} else if (!attachedAny) {
|
|
191
|
+
entries.push({ type: "system", ...base, raw } satisfies GenericEntry);
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (role === "user") {
|
|
197
|
+
entries.push({
|
|
198
|
+
type: "user",
|
|
199
|
+
...base,
|
|
200
|
+
message: { role: "user", content: extractText(content) },
|
|
201
|
+
} satisfies UserEntry);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
entries.push({ type: "system", ...base, raw } satisfies GenericEntry);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
entries.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
209
|
+
return entries;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Discovery + file loader ──
|
|
213
|
+
|
|
214
|
+
export interface FactoryTranscriptFile {
|
|
215
|
+
/** Encoded folder name on disk (e.g. "-home-user-project"). */
|
|
216
|
+
projectName: string;
|
|
217
|
+
/** Decoded cwd of the project (lossy; canonical cwd lives in session_start). */
|
|
218
|
+
cwd: string;
|
|
219
|
+
sessionId: string;
|
|
220
|
+
transcriptPath: string;
|
|
221
|
+
mtimeMs: number;
|
|
222
|
+
sizeBytes: number;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Enumerate `sessions/<encoded-cwd>/<uuid>.jsonl` transcripts, skipping the
|
|
226
|
+
* `<uuid>.settings.json` sibling files. */
|
|
227
|
+
export function listFactoryTranscripts(): FactoryTranscriptFile[] {
|
|
228
|
+
const root = factorySessionsRoot();
|
|
229
|
+
const out: FactoryTranscriptFile[] = [];
|
|
230
|
+
let projectDirs: import("node:fs").Dirent[];
|
|
231
|
+
try {
|
|
232
|
+
projectDirs = readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
233
|
+
} catch {
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
for (const dir of projectDirs) {
|
|
237
|
+
const projectName = dir.name;
|
|
238
|
+
const cwd = decodeFolderName(projectName);
|
|
239
|
+
const projectPath = join(root, projectName);
|
|
240
|
+
let files: string[];
|
|
241
|
+
try {
|
|
242
|
+
files = readdirSync(projectPath);
|
|
243
|
+
} catch {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
for (const file of files) {
|
|
247
|
+
// Only `<uuid>.jsonl` — not `<uuid>.settings.json`.
|
|
248
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
249
|
+
const sessionId = file.slice(0, -".jsonl".length);
|
|
250
|
+
if (!FACTORY_SESSION_ID_RE.test(sessionId)) continue;
|
|
251
|
+
const transcriptPath = join(projectPath, file);
|
|
252
|
+
try {
|
|
253
|
+
const st = statSync(transcriptPath);
|
|
254
|
+
out.push({ projectName, cwd, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
255
|
+
} catch {
|
|
256
|
+
// skip unreadable
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return out;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Resolve a session UUID to its on-disk transcript path (host-side). Guards
|
|
264
|
+
* against traversal by requiring a UUID filename. Shared by the audit adapter,
|
|
265
|
+
* the hook transcript resolver, and download-session. Synchronous so the hook
|
|
266
|
+
* hot path can call it without awaits. */
|
|
267
|
+
export function findFactoryTranscript(sessionId: string): string | null {
|
|
268
|
+
if (!FACTORY_SESSION_ID_RE.test(sessionId)) return null;
|
|
269
|
+
for (const t of listFactoryTranscripts()) {
|
|
270
|
+
if (t.sessionId === sessionId) return t.transcriptPath;
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface FactorySessionLogData {
|
|
276
|
+
entries: LogEntry[];
|
|
277
|
+
rawLines: Record<string, unknown>[];
|
|
278
|
+
cwd?: string;
|
|
279
|
+
filePath: string;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Load and parse one session transcript by UUID. Returns `null` when the file
|
|
283
|
+
* is missing/unreadable or the id fails validation. */
|
|
284
|
+
export async function getFactorySessionLog(
|
|
285
|
+
sessionId: string,
|
|
286
|
+
): Promise<FactorySessionLogData | null> {
|
|
287
|
+
const filePath = findFactoryTranscript(sessionId);
|
|
288
|
+
if (!filePath) return null;
|
|
289
|
+
let content: string;
|
|
290
|
+
try {
|
|
291
|
+
content = await readFile(filePath, "utf-8");
|
|
292
|
+
} catch {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
const rawLines = parseRawLines(content, "session");
|
|
296
|
+
const entries = factoryLinesToLogEntries(rawLines, "session");
|
|
297
|
+
// cwd lives on the `type:"session_start"` header line.
|
|
298
|
+
let cwd: string | undefined;
|
|
299
|
+
for (const line of rawLines) {
|
|
300
|
+
if (isPlainObject(line) && line.type === "session_start" && typeof line.cwd === "string" && line.cwd.length > 0) {
|
|
301
|
+
cwd = line.cwd;
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { entries, rawLines, cwd, filePath };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export const getCachedFactorySessionLog = runtimeCache(
|
|
309
|
+
(sessionId: string) => getFactorySessionLog(sessionId),
|
|
310
|
+
60,
|
|
311
|
+
{ maxSize: 50 },
|
|
312
|
+
);
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Goose (codename goose, Block) session enumeration — AUDIT-ONLY.
|
|
3
|
+
*
|
|
4
|
+
* Reads the `sessions` table directly from
|
|
5
|
+
* `~/.local/share/goose/sessions/sessions.db` via the bundled sql.js reader.
|
|
6
|
+
* Like Devin (and unlike the cwd-less Hermes gateway), each Goose session
|
|
7
|
+
* carries a real `working_dir`, so Goose sessions group by project cwd like
|
|
8
|
+
* Claude/Factory/Devin — one `ProjectFolder` per encoded cwd, and a cwd present
|
|
9
|
+
* in multiple stores merges on the shared encoded slug (see `mergeProjectFolders`
|
|
10
|
+
* in lib/projects.ts).
|
|
11
|
+
*
|
|
12
|
+
* `session_type = 'hidden'` rows are `goose run --no-session` scratch runs and
|
|
13
|
+
* are excluded (mirrors Devin's `hidden` filter).
|
|
14
|
+
*/
|
|
15
|
+
import { openSqliteReadonly } from "./sqlite-reader";
|
|
16
|
+
import { gooseDbPath, gooseTimestampToMs } from "./goose-sessions";
|
|
17
|
+
import { encodeFolderName } from "./paths";
|
|
18
|
+
import { runtimeCache } from "./runtime-cache";
|
|
19
|
+
import type { ProjectFolder, SessionFile } from "./projects";
|
|
20
|
+
import { formatDate } from "./format-date";
|
|
21
|
+
|
|
22
|
+
export interface GooseSessionRef {
|
|
23
|
+
sessionId: string;
|
|
24
|
+
cwd?: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
/** Encoded-cwd folder slug (matches Claude's `-home-user-project` scheme). */
|
|
27
|
+
projectName: string;
|
|
28
|
+
/** From `updated_at` (falls back to `created_at`) — epoch ms. */
|
|
29
|
+
mtimeMs: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface SessionRow {
|
|
33
|
+
id: string;
|
|
34
|
+
working_dir: string | null;
|
|
35
|
+
description: string | null;
|
|
36
|
+
name: string | null;
|
|
37
|
+
session_type: string | null;
|
|
38
|
+
created_at: string | null;
|
|
39
|
+
updated_at: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* List every Goose session. Returns `[]` when the DB is missing or unreadable
|
|
44
|
+
* (fail-open — the audit just skips Goose). `hidden` (no-session) rows are
|
|
45
|
+
* excluded.
|
|
46
|
+
*/
|
|
47
|
+
export async function getGooseSessions(): Promise<GooseSessionRef[]> {
|
|
48
|
+
const db = await openSqliteReadonly(gooseDbPath());
|
|
49
|
+
if (!db) return [];
|
|
50
|
+
try {
|
|
51
|
+
const rows = db.query<SessionRow>(
|
|
52
|
+
"SELECT id, working_dir, description, name, session_type, created_at, updated_at " +
|
|
53
|
+
"FROM sessions ORDER BY updated_at DESC",
|
|
54
|
+
);
|
|
55
|
+
return rows
|
|
56
|
+
.filter((r) => r.session_type !== "hidden")
|
|
57
|
+
.map((r) => {
|
|
58
|
+
const cwd = r.working_dir ?? undefined;
|
|
59
|
+
const title = (r.description && r.description.length > 0 ? r.description : r.name) ?? undefined;
|
|
60
|
+
return {
|
|
61
|
+
sessionId: r.id,
|
|
62
|
+
cwd,
|
|
63
|
+
title: title && title.length > 0 ? title : undefined,
|
|
64
|
+
projectName: cwd ? encodeFolderName(cwd) : "goose",
|
|
65
|
+
mtimeMs: gooseTimestampToMs(r.updated_at ?? r.created_at),
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
} catch {
|
|
69
|
+
return [];
|
|
70
|
+
} finally {
|
|
71
|
+
db.close();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const getCachedGooseSessions = runtimeCache(getGooseSessions, 2);
|
|
76
|
+
|
|
77
|
+
// ── Dashboard history browser (projects list + project-detail sessions) ──
|
|
78
|
+
|
|
79
|
+
/** One `ProjectFolder` per encoded-cwd discovered in the Goose DB. */
|
|
80
|
+
export async function getGooseProjects(): Promise<ProjectFolder[]> {
|
|
81
|
+
const sessions = await getGooseSessions();
|
|
82
|
+
const byName = new Map<string, { latest: number; cwd: string; name: string }>();
|
|
83
|
+
for (const s of sessions) {
|
|
84
|
+
if (!s.cwd) continue;
|
|
85
|
+
const existing = byName.get(s.projectName);
|
|
86
|
+
if (!existing || s.mtimeMs > existing.latest) {
|
|
87
|
+
byName.set(s.projectName, { latest: s.mtimeMs, cwd: s.cwd, name: s.projectName });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const folders: ProjectFolder[] = [];
|
|
91
|
+
for (const { name, cwd, latest } of byName.values()) {
|
|
92
|
+
const lastModified = new Date(latest);
|
|
93
|
+
folders.push({
|
|
94
|
+
name,
|
|
95
|
+
path: cwd,
|
|
96
|
+
isDirectory: true,
|
|
97
|
+
lastModified,
|
|
98
|
+
lastModifiedFormatted: formatDate(lastModified),
|
|
99
|
+
cli: ["goose"],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
folders.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
103
|
+
return folders;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface GooseProjectByName {
|
|
107
|
+
/** Canonical cwd recovered from the session's `working_dir`. */
|
|
108
|
+
cwd: string | null;
|
|
109
|
+
sessions: SessionFile[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Resolve the Goose sessions for a project URL slug (the encoded-cwd folder
|
|
114
|
+
* name). Goose's `working_dir` re-encodes to the same slug Claude uses, so the
|
|
115
|
+
* slug matches directly and the canonical cwd is the session's real
|
|
116
|
+
* `working_dir`.
|
|
117
|
+
*/
|
|
118
|
+
export async function getGooseSessionsByEncodedName(
|
|
119
|
+
name: string,
|
|
120
|
+
): Promise<GooseProjectByName> {
|
|
121
|
+
const sessions = await getGooseSessions();
|
|
122
|
+
const matched = sessions.filter((s) => s.cwd && s.projectName === name);
|
|
123
|
+
if (matched.length === 0) return { cwd: null, sessions: [] };
|
|
124
|
+
|
|
125
|
+
const sorted = [...matched].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
126
|
+
const cwd = sorted[0].cwd ?? null;
|
|
127
|
+
return {
|
|
128
|
+
cwd,
|
|
129
|
+
sessions: sorted.map((s) => {
|
|
130
|
+
const lastModified = new Date(s.mtimeMs);
|
|
131
|
+
return {
|
|
132
|
+
name: s.title ?? s.sessionId,
|
|
133
|
+
path: `goose-db://${s.sessionId}`,
|
|
134
|
+
lastModified,
|
|
135
|
+
lastModifiedFormatted: formatDate(lastModified),
|
|
136
|
+
sessionId: s.sessionId,
|
|
137
|
+
cli: "goose" as const,
|
|
138
|
+
};
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const getCachedGooseProjects = runtimeCache(getGooseProjects, 2);
|
|
144
|
+
export const getCachedGooseSessionsByEncodedName = runtimeCache(
|
|
145
|
+
(name: string) => getGooseSessionsByEncodedName(name),
|
|
146
|
+
2,
|
|
147
|
+
{ maxSize: 50 },
|
|
148
|
+
);
|