failproofai 1.0.2-beta.4 → 1.0.2-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +5 -5
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js.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 +2 -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 +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__03r-lqq._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12021ot._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0clqfab._.js → [root-of-the-server]__1tg4pu6._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_1lbh81u._.js → _19uwam-._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +7 -7
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1m84s36._.js → [root-of-the-server]__00-s7h8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07gm3zl._.js +6 -6
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__14muoc4._.js → [root-of-the-server]__128se8m._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0jzbk4_._.js → [root-of-the-server]__190won8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rsd3ll._.js → [root-of-the-server]__1sc84bb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0ovx4lz._.js → _0no96tu._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1dkh76z._.js → _11fc-hb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1db3-8n._.js → _1if9ixi._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/lib_1cf5ya2._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_opencode-projects_ts_15bjxkm._.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 +19 -19
- package/.next/standalone/.next/static/chunks/{0lshnlpy1m6bc.js → 0931avh-06lj5.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k_yvjc1yz0pq.js → 0h-3zffoy-_ji.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ds64uculrze8.js → 0p84npzxdgpc5.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0d_re32wscs0b.js → 0sdgnflvw8kta.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2dbi_x0c8n8fi.js → 0z7au3njlak0j.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1o0ahew9568ls.js → 1id7o44jjegf0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{00_t-xvc59tkb.js → 276n-edifojbd.js} +2 -2
- package/.next/standalone/.next/static/chunks/{07j2u70_3qsx8.js → 2jfskwd2u8o5r.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2817_8e94ah9z.js → 2pxr7nr1nyotn.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1oz0svxwt9574.js → 3i8rg2fv9nd6e.js} +2 -2
- package/.next/standalone/.next/static/chunks/{1q76jk_1_wgjq.js → 3vu_7_eor1m-k.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-17utl_jw0wj11.js → turbopack-2x-o099q64i8h.js} +1 -1
- package/.next/standalone/lib/opencode-db.ts +77 -0
- package/.next/standalone/lib/opencode-projects.ts +37 -41
- package/.next/standalone/lib/opencode-sessions.ts +59 -36
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +116 -34
- package/dist/cli.mjs +1732 -1177
- package/dist/worker.mjs +180 -165
- package/lib/opencode-db.ts +77 -0
- package/lib/opencode-projects.ts +37 -41
- package/lib/opencode-sessions.ts +59 -36
- package/package.json +9 -9
- package/src/audit/cache.ts +167 -11
- package/src/audit/cli-adapters/claude.ts +111 -1
- package/src/audit/cli-adapters/index.ts +31 -1
- package/src/audit/cli.ts +12 -0
- package/src/audit/index.ts +164 -13
- package/src/hooks/configure-wizard.ts +112 -18
- package/src/hooks/fp-reset.ts +1 -1
- package/src/hooks/handler.ts +13 -1
- package/src/hooks/manager.ts +69 -4
- package/src/hooks/onboarding-attempt.ts +4 -4
- package/src/hooks/pack-cli.ts +399 -32
- package/src/hooks/pack-store.ts +21 -8
- package/src/hooks/read-stdin.ts +78 -8
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1l-khtb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mhmdzs._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qxztj-._.js +0 -3
- /package/.next/standalone/.next/static/{0iNzkTsP6rh14Rj5WL17o → mAYN9BOxWVFREnVp3yCHp}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{0iNzkTsP6rh14Rj5WL17o → mAYN9BOxWVFREnVp3yCHp}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{0iNzkTsP6rh14Rj5WL17o → mAYN9BOxWVFREnVp3yCHp}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,25524,e=>{"use strict";var t=e.i(95187);let a=(0,t.createServerReference)("60de61ef2672a01929db49ffbce77062a11063e39f",t.callServer,void 0,t.findSourceMapURL,"searchHookActivityAction");e.s(["searchHookActivityAction",0,a])},73520,e=>{"use strict";var t=e.i(43476),a=e.i(24071),r=e.i(67927);e.s(["default",0,function({currentPage:e,totalPages:l,onPageChange:s}){if(l<=1)return null;let o=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>s(e-1),disabled:1===e,className:o(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(l<=7)return Array.from({length:l},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(l-1,e+1);e<=3&&(r=Math.min(5,l-1)),e>=l-2&&(a=Math.max(2,l-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<l-1&&t.push("ellipsis-end"),t.push(l),t})().map(a=>"string"==typeof a?(0,t.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},a):(0,t.jsx)("button",{onClick:()=>s(a),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${e===a?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${a}`,"aria-current":e===a?"page":void 0,children:a},a))}),(0,t.jsxs)("button",{onClick:()=>s(e+1),disabled:e===l,className:o(e===l),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},24578,e=>{"use strict";let t={claude:{id:"claude",label:"Claude Code",badgeClasses:"bg-orange-500/10 text-orange-400 border-orange-500/20"},codex:{id:"codex",label:"OpenAI Codex",badgeClasses:"bg-purple-500/10 text-purple-400 border-purple-500/20"},copilot:{id:"copilot",label:"GitHub Copilot",badgeClasses:"bg-blue-500/10 text-blue-400 border-blue-500/20"},cursor:{id:"cursor",label:"Cursor Agent",badgeClasses:"bg-emerald-500/10 text-emerald-400 border-emerald-500/20"},opencode:{id:"opencode",label:"OpenCode",badgeClasses:"bg-amber-500/10 text-amber-400 border-amber-500/20"},pi:{id:"pi",label:"Pi",badgeClasses:"bg-pink-500/10 text-pink-400 border-pink-500/20"},hermes:{id:"hermes",label:"Hermes",badgeClasses:"bg-indigo-500/10 text-indigo-400 border-indigo-500/20"},openclaw:{id:"openclaw",label:"OpenClaw",badgeClasses:"bg-teal-500/10 text-teal-400 border-teal-500/20"},factory:{id:"factory",label:"Factory Droid",badgeClasses:"bg-rose-500/10 text-rose-400 border-rose-500/20"},devin:{id:"devin",label:"Devin CLI",badgeClasses:"bg-violet-500/10 text-violet-400 border-violet-500/20"},antigravity:{id:"antigravity",label:"Antigravity CLI",badgeClasses:"bg-cyan-500/10 text-cyan-400 border-cyan-500/20"},goose:{id:"goose",label:"Goose",badgeClasses:"bg-lime-500/10 text-lime-400 border-lime-500/20"}};e.s(["KNOWN_CLI_IDS",0,["claude","codex","copilot","cursor","opencode","pi","hermes","openclaw","factory","devin","antigravity","goose"],"getCliBadgeClasses",0,function(e){return t[e]?.badgeClasses??t.claude.badgeClasses},"getCliLabel",0,function(e){return t[e]?.label??e},"isKnownCli",0,function(e){return"string"==typeof e&&Object.prototype.hasOwnProperty.call(t,e)}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let a=Math.round(e/1e3);if(a<3600){let e=Math.floor(a/60);return`${e}m ${a%60}s`}let r=Math.round(e/6e4),l=Math.floor(r/60);return`${l}h ${r%60}m`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])},80060,2711,e=>{"use strict";var t=e.i(18566),a=e.i(71645);function r(e){let t=e.getFullYear(),a=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0");return`${t}-${a}-${r}`}function l(e){var t;if(!e||(t=e,!/^\d{4}-\d{2}-\d{2}$/.test(t)||isNaN(Date.parse(t))))return null;let[a,r,l]=e.split("-").map(Number);return new Date(a,r-1,l,12,0,0)}e.s(["useUrlParams",0,function(){let e=(0,t.useSearchParams)(),r=(0,t.useRouter)(),l=(0,t.usePathname)(),s=(0,a.useRef)(null),o=(0,a.useCallback)(t=>e.get(t),[e]),i=(0,a.useCallback)(()=>new URLSearchParams(e.toString()),[e]),n=(0,a.useCallback)(t=>{s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{let a=new URLSearchParams(e.toString());for(let[e,r]of Object.entries(t))void 0===r||""===r?a.delete(e):a.set(e,r);let s=a.toString(),o=s?`${l}?${s}`:l;r.replace(o,{scroll:!1})},150)},[e,l,r]);return(0,a.useEffect)(()=>()=>{s.current&&clearTimeout(s.current)},[]),{get:o,getAll:i,setAll:n}}],80060),e.s(["dateRangeToParams",0,function(e){let t={};return e.from&&(t.from=r(e.from)),e.to&&(t.to=r(e.to)),t},"keywordsToParam",0,function(e){if(0!==e.length)return e.map(e=>encodeURIComponent(e)).join(",")},"pageToParam",0,function(e){return e<=1?void 0:String(e)},"paramToKeywords",0,function(e){return e?e.split(",").map(e=>decodeURIComponent(e.trim())).filter(e=>e.length>0):[]},"paramToPage",0,function(e){if(!e)return 1;let t=parseInt(e,10);return Number.isFinite(t)&&t>=1?t:1},"paramToPreset",0,function(e){return e&&["all","last-hour","today","last-7-days","last-30-days","custom"].includes(e)?e:"all"},"paramsToDateRange",0,function(e,t){return{from:l(e),to:l(t)}},"presetToParam",0,function(e){return"all"===e?void 0:e}],2711)},89664,e=>{"use strict";let t=(0,e.i(56420).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["Check",0,t],89664)},24071,e=>{"use strict";let t=(0,e.i(56420).default)("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);e.s(["ChevronLeft",0,t],24071)},67927,e=>{"use strict";let t=(0,e.i(56420).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["ChevronRight",0,t],67927)},8734,e=>{"use strict";let t=(0,e.i(56420).default)("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);e.s(["Copy",0,t],8734)},16306,e=>{"use strict";let t=(0,e.i(56420).default)("shield-alert",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]]);e.s(["ShieldAlert",0,t],16306)},84026,29407,e=>{"use strict";var t=e.i(56420);let a=(0,t.default)("shield-check",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);e.s(["ShieldCheck",0,a],84026);let r=(0,t.default)("shield-x",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m14.5 9.5-5 5",key:"17q4r4"}],["path",{d:"m9.5 9.5 5 5",key:"18nt4w"}]]);e.s(["ShieldX",0,r],29407)},82954,e=>{"use strict";let t=(0,e.i(56420).default)("shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);e.s(["Shield",0,t],82954)},63676,e=>{"use strict";let t=(0,e.i(56420).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["X",0,t],63676)}]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,{otherChunks:["static/chunks/1c6aptmytnu6q.js","static/chunks/2yuzx8716maf0.js","static/chunks/
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,{otherChunks:["static/chunks/1c6aptmytnu6q.js","static/chunks/2yuzx8716maf0.js","static/chunks/3i8rg2fv9nd6e.js","static/chunks/354-n7p7labpn.js"],runtimeModuleIds:[94553]}]),(()=>{let e;if(!Array.isArray(globalThis.TURBOPACK))return;var t,r=function(){if(null!=self.TURBOPACK_ASSET_SUFFIX)return self.TURBOPACK_ASSET_SUFFIX;let e=document?.currentScript?.getAttribute?.("src")??"",t=e.indexOf("?");return t>=0?e.slice(t):""}(),n=((t=n||{})[t.Runtime=0]="Runtime",t[t.Parent=1]="Parent",t[t.Update=2]="Update",t);let o=new WeakMap;function l(e,t){this.m=e,this.e=t}let i=l.prototype,u=Object.prototype.hasOwnProperty,s="u">typeof Symbol&&Symbol.toStringTag;function c(e,t,r){u.call(e,t)||Object.defineProperty(e,t,r)}function a(e,t){let r=e[t];return r||(r=f(t),e[t]=r),r}function f(e){return{exports:{},error:void 0,id:e,namespaceObject:void 0}}function p(e,t,r){c(e,"__esModule",{value:!0}),s&&c(e,s,{value:"Module"});let n=0;for(;n<t.length;){let r=t[n++],o=t[n++];if("number"==typeof o)if(0===o)c(e,r,{value:t[n++],enumerable:!0,writable:!1});else throw Error(`unexpected tag: ${o}`);else"function"==typeof t[n]?c(e,r,{get:o,set:t[n++],enumerable:!0}):c(e,r,{get:o,enumerable:!0})}r||Object.seal(e)}function h(e,t){(null!=t?a(this.c,t):this.m).exports=e}i.s=function(e,t,r){let n,o;null!=t?o=(n=a(this.c,t)).exports:(n=this.m,o=this.e),n.namespaceObject=o,p(o,e,r)},i.j=function(e,t){let r,n;null!=t?n=(r=a(this.c,t)).exports:(r=this.m,n=this.e);let l=function(e,t){let r=o.get(e);if(!r){o.set(e,r=[]);let n=e=>{if("default"!==e){for(let t of r)if(u.call(t,e))return t}};e.exports=e.namespaceObject=new Proxy(t,{get(e,t){if(u.call(e,t)||"default"===t||"__esModule"===t)return Reflect.get(e,t);let r=n(t);return r&&Reflect.get(r,t)},set:()=>!1,defineProperty:()=>!1,deleteProperty:()=>!1,has:(e,t)=>!!Reflect.has(e,t)||"default"!==t&&"__esModule"!==t&&void 0!==n(t),ownKeys(e){let t=Reflect.ownKeys(e);for(let e of r)for(let r of Reflect.ownKeys(e))"default"===r||t.includes(r)||t.push(r);return t},getOwnPropertyDescriptor(e,t){let r=Reflect.getOwnPropertyDescriptor(e,t);if(r||"default"===t||"__esModule"===t)return r;let o=n(t);if(o)return{enumerable:!0,configurable:!0,get:()=>Reflect.get(o,t)}}})}return r}(r,n);"object"==typeof e&&null!==e&&l.push(e)},i.v=h,i.n=function(e,t){let r;(r=null!=t?a(this.c,t):this.m).exports=r.namespaceObject=e};let d=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,m=[null,d({}),d([]),d(d)];function y(e,t,r){let n=[],o=-1;for(let t=e;("object"==typeof t||"function"==typeof t)&&!m.includes(t);t=d(t))for(let r of Object.getOwnPropertyNames(t))n.push(r,function(e,t){return()=>e[t]}(e,r)),-1===o&&"default"===r&&(o=n.length-1);return r&&o>=0||(o>=0?n.splice(o,1,0,e):n.push("default",0,e)),p(t,n),t}function g(e){let t=K(e,this.m);if(t.namespaceObject)return t.namespaceObject;let r=t.exports;return t.namespaceObject=y(r,"function"==typeof r?function(...e){return r.apply(this,e)}:Object.create(null),r&&r.__esModule)}function b(e){let t=e.indexOf("#");-1!==t&&(e=e.substring(0,t));let r=e.indexOf("?");return -1!==r&&(e=e.substring(0,r)),e}i.i=g,i.A=function(e){return this.r(e)(g.bind(this))},i.t="function"==typeof require?require:function(){throw Error("Unexpected use of runtime require")},i.r=function(e){return K(e,this.m).exports},i.f=function(e){function t(t){if(t=b(t),u.call(e,t))return e[t].module();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r}return t.keys=()=>Object.keys(e),t.resolve=t=>{if(t=b(t),u.call(e,t))return e[t].id();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r},t.import=async e=>await t(e),t};let O=function(e){let t=new URL(e,"x:/"),r={};for(let e in t)r[e]=t[e];for(let t in r.href=e,r.pathname=e.replace(/[?#].*/,""),r.origin=r.protocol="",r.toString=r.toJSON=(...t)=>e,r)Object.defineProperty(this,t,{enumerable:!0,configurable:!0,value:r[t]})};function w(e,t){throw Error(`Invariant: ${t(e)}`)}O.prototype=URL.prototype,i.U=O,i.z=function(e){throw Error("dynamic usage of require is not supported")},i.g=globalThis;let R=l.prototype,k="string"==typeof TURBOPACK_CHUNK_BASE_PATH?TURBOPACK_CHUNK_BASE_PATH:"/_next/",U=new Map;i.M=U;let v=new Map,_=new Map,P=new Map;async function C(e,t,r){let n;if("string"==typeof r)return function(e,t,r){return A(e,t,r)}(e,t,E(r));let o=r.included||[],l=o.map(e=>!!U.has(e)||v.get(e));if(l.length>0&&l.every(e=>e))return void await Promise.all(l);for(let l of(n=A(e,t,E(r.path)),o))v.has(l)||v.set(l,n);await n}R.l=function(e){return C(n.Parent,this.m.id,e)};let j=Promise.resolve(void 0),$=new WeakMap;function A(t,r,o){let l=e.loadChunkCached(t,o),i=$.get(l);if(void 0===i){let e=$.set.bind($,l,j);i=l.then(e).catch(e=>{let l;switch(t){case n.Runtime:l=`as a runtime dependency of chunk ${r}`;break;case n.Parent:l=`from module ${r}`;break;case n.Update:l="from an HMR update";break;default:w(t,e=>`Unknown source type: ${e}`)}let i=Error(`Failed to load chunk ${o} ${l}${e?`: ${e}`:""}`,e?{cause:e}:void 0);throw i.name="ChunkLoadError",i}),$.set(l,i)}return i}R.L=function(e){var t,r;return t=n.Parent,r=this.m.id,A(t,r,e)};R.R=function(e){let t=this.r(e);return t?.default??t},R.P=function(e){return`/ROOT/${e??""}`},R.F=function(e){return e?`file:///ROOT/${e.split("/").map(encodeURIComponent).join("/")}`:"file:///ROOT/"},R.q=function(e,t){h.call(this,`${e}${r}`,t)};let T=/[^A-Za-z0-9\-_.!~*'()/]/;function E(e,t=k){let n=T.test(e)?e.split("/").map(encodeURIComponent).join("/"):e;return`${t}${n}${r}`}function S(e,t){let r,n=e.indexOf("?");if(-1!==n)r=n;else{let t=e.indexOf("#");r=-1!==t?t:e.length}return r>=t.length&&e.startsWith(t,r-t.length)}R.b=k,R.X=r,R.h=E;function M(e){return S(e,".css")}let x={};i.c=x;let K=(e,t)=>{let r=x[e];if(r){if(r.error)throw r.error;return r}return N(e,n.Parent,t.id)};function N(e,t,r){let n=U.get(e);if("function"!=typeof n)throw Error(function(e,t,r){let n;switch(t){case 0:n=`as a runtime entry of chunk ${r}`;break;case 1:n=`because it was required from module ${r}`;break;case 2:n="because of an HMR update";break;default:w(t,e=>`Unknown source type: ${e}`)}return`Module ${e} was instantiated ${n}, but the module factory is not available.`}(e,t,r));let o=f(e),i=o.exports;x[e]=o;let u=new l(o,i);try{n(u,o,i)}catch(e){throw o.error=e,e}return o.namespaceObject&&o.exports!==o.namespaceObject&&y(o.exports,o.namespaceObject),o}function B(t){let r;if(!Array.isArray(t))return e.registerChunk(void 0,t);let n=function(e){if("string"==typeof e)return e;if(e)return{src:e.getAttribute("src")};if("u">typeof TURBOPACK_NEXT_CHUNK_URLS)return{src:TURBOPACK_NEXT_CHUNK_URLS.pop()};throw Error("chunk path empty but not in a worker")}(t[0]);return 2===t.length?r=t[1]:(r=void 0,!function(e,t){let r=1;for(;r<e.length;){let n,o=r+1;for(;o<e.length&&"function"!=typeof e[o];)o++;if(o===e.length)throw Error("malformed chunk format, expected a factory function");let l=e[o];for(let l=r;l<o;l++){let r=e[l],o=t.get(r);if(o){n=o;break}}let i=n??l,u=!1;for(let n=r;n<o;n++){let r=e[n];t.has(r)||(u||(i===l&&Object.defineProperty(l,"name",{value:"module evaluation"}),u=!0),t.set(r,i))}r=o+1}}(t,U)),e.registerChunk(n,r)}let L=new Map;function I(e){let t=L.get(e);if(!t){let r,n;t={resolved:!1,loadingStarted:!1,retryAttempts:0,promise:new Promise((e,t)=>{r=e,n=t}),resolve:()=>{t.resolved=!0,r()},reject:n},L.set(e,t)}return t}function q(e,t,r,n,o){!(null==n||n instanceof DOMException&&"NetworkError"===n.name)||r.retryAttempts>=1||L.get(t)!==r?(L.get(t)===r&&L.delete(t),r.reject(n)):(r.retryAttempts++,setTimeout(()=>{r.resolved||L.get(t)!==r||(o?o():(r.loadingStarted=!1,H(e,t)))},200+Math.floor(401*Math.random())))}function H(e,t){let r=I(t);if(r.loadingStarted)return r.promise;if(e===n.Runtime)return r.loadingStarted=!0,M(t)&&r.resolve(),r.promise;if("function"==typeof importScripts)if(M(t));else if(S(t,".js")){self.TURBOPACK_NEXT_CHUNK_URLS.push(t);try{importScripts(t)}catch(n){q(e,t,r,n)}}else throw Error(`can't infer type of chunk from URL ${t} in worker`);else{let n=decodeURI(t);if(M(t))if(document.querySelectorAll(`link[rel=stylesheet][href="${t}"],link[rel=stylesheet][href^="${t}?"],link[rel=stylesheet][href="${n}"],link[rel=stylesheet][href^="${n}?"]`).length>0)r.resolve();else{let n=()=>{let o=document.createElement("link");return o.rel="stylesheet",o.crossOrigin=null,o.href=t,o.onerror=()=>{let l=document.createComment("");o.replaceWith(l),q(e,t,r,void 0,()=>l.replaceWith(n()))},o.onload=()=>{r.resolve()},o};document.head.appendChild(n())}else if(S(t,".js")){let o=document.querySelectorAll(`script[src="${t}"],script[src^="${t}?"],script[src="${n}"],script[src^="${n}?"]`);if(o.length>0)for(let n of Array.from(o))n.addEventListener("error",()=>{n.remove(),q(e,t,r)},{once:!0});else{let n=document.createElement("script");n.crossOrigin=null,n.src=t,n.onerror=()=>{n.remove(),q(e,t,r)},document.head.appendChild(n)}}else throw Error(`can't infer type of chunk from URL ${t}`)}return r.loadingStarted=!0,r.promise}e={async registerChunk(e,t){let r;if(null!=e&&(r=function(e){if("string"==typeof e)return e;let t=decodeURIComponent(e.src.replace(/[?#].*$/,""));return t.startsWith(k)?t.slice(k.length):t}(e),I("string"==typeof e?E(e):e.src).resolve()),null!=t){for(let e of t.otherChunks)I(E("string"==typeof e?e:e.path));if(await Promise.all(t.otherChunks.map(e=>{var t;return t=r,C(n.Runtime,t,e)})),t.runtimeModuleIds.length>0)for(let e of t.runtimeModuleIds)!function(e,t){let r=x[t];if(r){if(r.error)throw r.error;return}N(t,n.Runtime,e)}(r,e)}},loadChunkCached:(e,t)=>H(e,t)};var F=globalThis.TURBOPACK;globalThis.TURBOPACK={push:B},F.forEach(B)})();
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only access to OpenCode's SQLite database.
|
|
3
|
+
*
|
|
4
|
+
* This used to shell out: `opencode db --format json "<sql>"`, on the reasoning
|
|
5
|
+
* that going through the CLI avoided coupling us to opencode's internal schema.
|
|
6
|
+
* It did not — the SQL was ours either way, naming opencode's own tables and
|
|
7
|
+
* columns, so the schema coupling was identical and the subprocess bought
|
|
8
|
+
* nothing but its own cost.
|
|
9
|
+
*
|
|
10
|
+
* And that cost was the single largest component of `failproofai audit` on any
|
|
11
|
+
* machine with opencode installed. Each spawn is a full CLI start: ~1.5s
|
|
12
|
+
* measured. `getOpenCodeSessionExport` issues three queries, so a 30-session
|
|
13
|
+
* history spent ~135 seconds launching 90 processes — against a total audit
|
|
14
|
+
* time of ~140s. The audit sat at ~0% CPU throughout, which is why it read as
|
|
15
|
+
* "slow scanning" rather than as what it was: waiting on `execFileSync`.
|
|
16
|
+
*
|
|
17
|
+
* Reading the file directly is the same thing every other SQLite-backed
|
|
18
|
+
* integration already does through `openSqliteReadonly` — goose, devin, hermes,
|
|
19
|
+
* openclaw and antigravity. Opening the database costs microseconds, and one
|
|
20
|
+
* open now serves every query a caller needs instead of one process per query.
|
|
21
|
+
*
|
|
22
|
+
* Fail-open contract is unchanged: a missing file, an unreadable database or a
|
|
23
|
+
* query error yields `null`, and callers degrade to an empty result exactly as
|
|
24
|
+
* they did when the binary was absent from PATH.
|
|
25
|
+
*/
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
import { openSqliteReadonly, type SqliteReader } from "./sqlite-reader";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Where the database lives.
|
|
32
|
+
*
|
|
33
|
+
* `OPENCODE_DB_PATH` names the file outright and `OPENCODE_HOME` the directory
|
|
34
|
+
* holding it — the same override shape `GOOSE_DB_PATH`/`GOOSE_HOME` and
|
|
35
|
+
* `DEVIN_DB_PATH`/`DEVIN_HOME` use, so tests point at a fixture the same way
|
|
36
|
+
* for every one of them.
|
|
37
|
+
*/
|
|
38
|
+
export function opencodeDbPath(): string {
|
|
39
|
+
if (process.env.OPENCODE_DB_PATH) return process.env.OPENCODE_DB_PATH;
|
|
40
|
+
const home = process.env.OPENCODE_HOME ?? join(homedir(), ".local", "share", "opencode");
|
|
41
|
+
return join(home, "opencode.db");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Open the database, hand it to `fn`, and always close it.
|
|
46
|
+
*
|
|
47
|
+
* Callers that need several queries take ONE of these and run them all inside
|
|
48
|
+
* it. That is the whole point: the shape being replaced ran a separate process
|
|
49
|
+
* per query, so three related reads cost three CLI starts.
|
|
50
|
+
*/
|
|
51
|
+
export async function withOpenCodeDb<T>(
|
|
52
|
+
fn: (db: SqliteReader) => T,
|
|
53
|
+
): Promise<T | null> {
|
|
54
|
+
const db = await openSqliteReadonly(opencodeDbPath());
|
|
55
|
+
if (!db) return null;
|
|
56
|
+
try {
|
|
57
|
+
return fn(db);
|
|
58
|
+
} catch {
|
|
59
|
+
// A query error is indistinguishable from a missing database to every
|
|
60
|
+
// caller, and both mean "no opencode data" rather than "audit failed".
|
|
61
|
+
return null;
|
|
62
|
+
} finally {
|
|
63
|
+
try {
|
|
64
|
+
db.close();
|
|
65
|
+
} catch {
|
|
66
|
+
/* closing a read-only handle cannot fail in a way that matters here */
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** One query, for callers that genuinely need only one. */
|
|
72
|
+
export async function queryOpenCodeDb<T = Record<string, unknown>>(
|
|
73
|
+
sql: string,
|
|
74
|
+
params: unknown[] = [],
|
|
75
|
+
): Promise<T[] | null> {
|
|
76
|
+
return withOpenCodeDb((db) => db.query<T>(sql, params));
|
|
77
|
+
}
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Unlike the other agent CLIs that store sessions as filesystem JSONL trees,
|
|
5
5
|
* OpenCode stores everything in a single SQLite database (managed by
|
|
6
|
-
* Drizzle ORM) at `~/.local/share/opencode/opencode.db
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Drizzle ORM) at `~/.local/share/opencode/opencode.db`, read directly
|
|
7
|
+
* through `lib/opencode-db.ts`.
|
|
8
|
+
*
|
|
9
|
+
* This used to shell out to `opencode db --format json "<sql>"`, to avoid
|
|
10
|
+
* coupling to opencode's internal schema. That reasoning did not survive
|
|
11
|
+
* contact with the SQL: the queries name opencode's own tables and columns
|
|
12
|
+
* either way, so the coupling was identical and the subprocess bought only
|
|
13
|
+
* its own cost — see `lib/opencode-db.ts` for what that cost turned out to
|
|
14
|
+
* be.
|
|
10
15
|
*
|
|
11
16
|
* Verified live against opencode v1.14.31:
|
|
12
17
|
* • `session` columns: id, project_id, parent_id, slug, directory, title,
|
|
@@ -21,7 +26,7 @@
|
|
|
21
26
|
* Refs: https://opencode.ai/docs/ (CLI reference)
|
|
22
27
|
* https://opencode.ai/docs/plugins/ (plugin model context)
|
|
23
28
|
*/
|
|
24
|
-
import {
|
|
29
|
+
import { queryOpenCodeDb, withOpenCodeDb } from "./opencode-db";
|
|
25
30
|
import { encodeFolderName } from "./paths";
|
|
26
31
|
import type { ProjectFolder, SessionFile } from "./projects";
|
|
27
32
|
import { runtimeCache } from "./runtime-cache";
|
|
@@ -51,41 +56,34 @@ interface OpenCodeProjectRow {
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const stdout = execFileSync("opencode", ["db", "--format", "json", sql], {
|
|
61
|
-
encoding: "utf8",
|
|
62
|
-
timeout: 5_000,
|
|
63
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
64
|
-
});
|
|
65
|
-
if (!stdout.trim()) return [];
|
|
66
|
-
const parsed = JSON.parse(stdout) as unknown;
|
|
67
|
-
if (!Array.isArray(parsed)) return null;
|
|
68
|
-
return parsed as T[];
|
|
69
|
-
} catch {
|
|
70
|
-
// Binary missing, db locked, malformed JSON, or SQL error — fail open.
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Pull session rows directly from opencode's SQLite. Selects the columns the
|
|
77
|
-
* dashboard needs and orders newest-first.
|
|
59
|
+
* Session rows the dashboard needs, newest first.
|
|
60
|
+
*
|
|
61
|
+
* One caller needs sessions alone; the other two need sessions and projects
|
|
62
|
+
* together and take `readBothRows`, which serves both from a single open
|
|
63
|
+
* rather than two.
|
|
78
64
|
*/
|
|
79
|
-
function readSessionRows(): OpenCodeSessionRow[] | null {
|
|
80
|
-
return
|
|
81
|
-
|
|
65
|
+
async function readSessionRows(): Promise<OpenCodeSessionRow[] | null> {
|
|
66
|
+
return queryOpenCodeDb<OpenCodeSessionRow>(
|
|
67
|
+
"SELECT id, project_id, slug, directory, title, time_created, time_updated FROM session ORDER BY time_updated DESC LIMIT ?",
|
|
68
|
+
[SESSION_LIMIT],
|
|
82
69
|
);
|
|
83
70
|
}
|
|
84
71
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
/** Both tables from one open, for the callers that use them together. */
|
|
73
|
+
async function readBothRows(): Promise<{
|
|
74
|
+
sessions: OpenCodeSessionRow[] | null;
|
|
75
|
+
projects: OpenCodeProjectRow[] | null;
|
|
76
|
+
}> {
|
|
77
|
+
const rows = await withOpenCodeDb((db) => ({
|
|
78
|
+
sessions: db.query<OpenCodeSessionRow>(
|
|
79
|
+
"SELECT id, project_id, slug, directory, title, time_created, time_updated FROM session ORDER BY time_updated DESC LIMIT ?",
|
|
80
|
+
[SESSION_LIMIT],
|
|
81
|
+
),
|
|
82
|
+
projects: db.query<OpenCodeProjectRow>(
|
|
83
|
+
"SELECT id, worktree, vcs, name, time_created, time_updated FROM project",
|
|
84
|
+
),
|
|
85
|
+
}));
|
|
86
|
+
return { sessions: rows?.sessions ?? null, projects: rows?.projects ?? null };
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
/**
|
|
@@ -97,8 +95,7 @@ function readProjectRows(): OpenCodeProjectRow[] | null {
|
|
|
97
95
|
* if no sessions exist yet).
|
|
98
96
|
*/
|
|
99
97
|
export async function getOpenCodeProjects(): Promise<ProjectFolder[]> {
|
|
100
|
-
const sessions =
|
|
101
|
-
const projects = readProjectRows();
|
|
98
|
+
const { sessions, projects } = await readBothRows();
|
|
102
99
|
if (sessions === null && projects === null) {
|
|
103
100
|
// Binary missing or query failed — silent degrade (no log spam).
|
|
104
101
|
return [];
|
|
@@ -163,7 +160,7 @@ export async function getOpenCodeProjects(): Promise<ProjectFolder[]> {
|
|
|
163
160
|
|
|
164
161
|
/** Sessions under a given absolute cwd. Used by the project detail page. */
|
|
165
162
|
export async function getOpenCodeSessionsForCwd(cwd: string): Promise<SessionFile[]> {
|
|
166
|
-
const sessions = readSessionRows();
|
|
163
|
+
const sessions = await readSessionRows();
|
|
167
164
|
if (!sessions) return [];
|
|
168
165
|
const matches = sessions.filter((s) => s.directory === cwd);
|
|
169
166
|
return matches.map((s) => {
|
|
@@ -193,8 +190,7 @@ export async function getOpenCodeSessionsByEncodedName(name: string): Promise<Op
|
|
|
193
190
|
let projects: OpenCodeProjectRow[] | null;
|
|
194
191
|
let sessions: OpenCodeSessionRow[] | null;
|
|
195
192
|
try {
|
|
196
|
-
projects =
|
|
197
|
-
sessions = readSessionRows();
|
|
193
|
+
({ projects, sessions } = await readBothRows());
|
|
198
194
|
} catch (error) {
|
|
199
195
|
logWarn("Failed to read OpenCode DB:", error);
|
|
200
196
|
return { cwd: null, sessions: [] };
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* OpenCode (sst/opencode) session transcript loader.
|
|
3
3
|
*
|
|
4
4
|
* Sessions live in opencode's SQLite DB (`~/.local/share/opencode/opencode.db`),
|
|
5
|
-
* not on disk as JSONL like the other CLIs.
|
|
6
|
-
* `opencode
|
|
7
|
-
*
|
|
5
|
+
* not on disk as JSONL like the other CLIs. Read directly through
|
|
6
|
+
* `lib/opencode-db.ts` — see there for why this stopped shelling out to
|
|
7
|
+
* `opencode db` — with the same fail-open contract as every other provider
|
|
8
|
+
* (database unreadable → return null).
|
|
8
9
|
*
|
|
9
10
|
* Schema verified live on opencode v1.14.31:
|
|
10
11
|
* • `session(id, project_id, parent_id, slug, directory, title, time_*, …)`
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
*
|
|
18
19
|
* Refs: https://opencode.ai/docs/ (CLI reference)
|
|
19
20
|
*/
|
|
20
|
-
import {
|
|
21
|
+
import { withOpenCodeDb } from "./opencode-db";
|
|
21
22
|
import { runtimeCache } from "./runtime-cache";
|
|
22
23
|
import {
|
|
23
24
|
baseEntry,
|
|
@@ -59,19 +60,19 @@ interface OpenCodePartRow {
|
|
|
59
60
|
data: string; // JSON-encoded
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
/**
|
|
63
|
-
*
|
|
64
|
-
|
|
63
|
+
/**
|
|
64
|
+
* One query, with its failure isolated to itself.
|
|
65
|
+
*
|
|
66
|
+
* The three reads below used to be three separate processes, so one of them
|
|
67
|
+
* failing left the others' results intact — and the callers depend on that: a
|
|
68
|
+
* session row that loads while its messages do not yields an EMPTY log rather
|
|
69
|
+
* than a missing one. Running all three inside a single open would otherwise
|
|
70
|
+
* turn any one failure into a null for the whole session, which reads to the
|
|
71
|
+
* dashboard as "no such session" rather than "no messages".
|
|
72
|
+
*/
|
|
73
|
+
function safeQuery<T>(run: () => T[]): T[] | null {
|
|
65
74
|
try {
|
|
66
|
-
|
|
67
|
-
encoding: "utf8",
|
|
68
|
-
timeout: 5_000,
|
|
69
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
70
|
-
});
|
|
71
|
-
if (!stdout.trim()) return [];
|
|
72
|
-
const parsed = JSON.parse(stdout) as unknown;
|
|
73
|
-
if (!Array.isArray(parsed)) return null;
|
|
74
|
-
return parsed as T[];
|
|
75
|
+
return run();
|
|
75
76
|
} catch {
|
|
76
77
|
return null;
|
|
77
78
|
}
|
|
@@ -215,19 +216,33 @@ export interface OpenCodeSessionLogData {
|
|
|
215
216
|
* or the binary is unavailable.
|
|
216
217
|
*/
|
|
217
218
|
export async function getOpenCodeSessionLog(sessionId: string): Promise<OpenCodeSessionLogData | null> {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
// Parameter binding already makes injection impossible, so this guard is no
|
|
220
|
+
// longer load-bearing for that — but an id that cannot match anything is
|
|
221
|
+
// still not worth a database open, and weakening an existing check while
|
|
222
|
+
// changing transport is how a regression gets in unnoticed.
|
|
223
|
+
if (!sessionId || !/^[A-Za-z0-9_-]+$/.test(sessionId)) return null;
|
|
224
|
+
// One open, three queries. Each of these was a separate `opencode db`
|
|
225
|
+
// process — and this function is called once per session by the audit, so
|
|
226
|
+
// the spawns were the audit.
|
|
227
|
+
const rows = await withOpenCodeDb((db) => ({
|
|
228
|
+
sessions: safeQuery(() => db.query<OpenCodeSessionRow>(
|
|
229
|
+
"SELECT id, project_id, slug, directory, title, time_created, time_updated FROM session WHERE id = ?",
|
|
230
|
+
[sessionId],
|
|
231
|
+
)),
|
|
232
|
+
messages: safeQuery(() => db.query<OpenCodeMessageRow>(
|
|
233
|
+
"SELECT id, session_id, time_created, time_updated, data FROM message WHERE session_id = ? ORDER BY time_created ASC",
|
|
234
|
+
[sessionId],
|
|
235
|
+
)),
|
|
236
|
+
parts: safeQuery(() => db.query<OpenCodePartRow>(
|
|
237
|
+
"SELECT id, message_id, session_id, time_created, time_updated, data FROM part WHERE session_id = ? ORDER BY time_created ASC",
|
|
238
|
+
[sessionId],
|
|
239
|
+
)),
|
|
240
|
+
}));
|
|
241
|
+
const sessions = rows?.sessions ?? null;
|
|
222
242
|
if (!sessions || sessions.length === 0) return null;
|
|
223
243
|
const session = sessions[0];
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
`SELECT id, session_id, time_created, time_updated, data FROM message WHERE session_id = '${sessionId}' ORDER BY time_created ASC`,
|
|
227
|
-
);
|
|
228
|
-
const parts = runOpenCodeDb<OpenCodePartRow>(
|
|
229
|
-
`SELECT id, message_id, session_id, time_created, time_updated, data FROM part WHERE session_id = '${sessionId}' ORDER BY time_created ASC`,
|
|
230
|
-
);
|
|
244
|
+
const messages = rows?.messages ?? null;
|
|
245
|
+
const parts = rows?.parts ?? null;
|
|
231
246
|
if (!messages) return { entries: [], rawLines: [], cwd: session.directory ?? undefined, filePath: `opencode://${sessionId}` };
|
|
232
247
|
|
|
233
248
|
// Group parts by message_id for O(1) lookup.
|
|
@@ -289,20 +304,28 @@ export interface OpenCodeSessionExportData {
|
|
|
289
304
|
|
|
290
305
|
export async function getOpenCodeSessionExport(sessionId: string): Promise<OpenCodeSessionExportData | null> {
|
|
291
306
|
if (!sessionId || !/^[A-Za-z0-9_-]+$/.test(sessionId)) return null;
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
307
|
+
const rows = await withOpenCodeDb((db) => ({
|
|
308
|
+
sessions: safeQuery(() => db.query<OpenCodeSessionRow>(
|
|
309
|
+
"SELECT id, project_id, slug, directory, title, time_created, time_updated FROM session WHERE id = ?",
|
|
310
|
+
[sessionId],
|
|
311
|
+
)),
|
|
312
|
+
messages: safeQuery(() => db.query<OpenCodeMessageRow>(
|
|
313
|
+
"SELECT id, session_id, time_created, time_updated, data FROM message WHERE session_id = ? ORDER BY time_created ASC",
|
|
314
|
+
[sessionId],
|
|
315
|
+
)),
|
|
316
|
+
parts: safeQuery(() => db.query<OpenCodePartRow>(
|
|
317
|
+
"SELECT id, message_id, session_id, time_created, time_updated, data FROM part WHERE session_id = ? ORDER BY time_created ASC",
|
|
318
|
+
[sessionId],
|
|
319
|
+
)),
|
|
320
|
+
}));
|
|
321
|
+
const sessions = rows?.sessions ?? null;
|
|
295
322
|
if (!sessions || sessions.length === 0) return null;
|
|
296
323
|
// Don't coalesce a `null` return (query failure) into `[]` — that would
|
|
297
324
|
// silently serve an "empty session" export. A genuinely empty session has
|
|
298
325
|
// an `[]` from the DB; a query failure has `null`. Treat the latter as
|
|
299
326
|
// not-found so the route returns 404 rather than a misleading 200.
|
|
300
|
-
const messages =
|
|
301
|
-
|
|
302
|
-
);
|
|
303
|
-
const parts = runOpenCodeDb<OpenCodePartRow>(
|
|
304
|
-
`SELECT id, message_id, session_id, time_created, time_updated, data FROM part WHERE session_id = '${sessionId}' ORDER BY time_created ASC`,
|
|
305
|
-
);
|
|
327
|
+
const messages = rows?.messages ?? null;
|
|
328
|
+
const parts = rows?.parts ?? null;
|
|
306
329
|
if (messages === null || parts === null) return null;
|
|
307
330
|
return {
|
|
308
331
|
session: sessions[0],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "1.0.2-beta.
|
|
3
|
+
"version": "1.0.2-beta.6",
|
|
4
4
|
"description": "Observability and enforcement for AI agent harnesses. 39 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs",
|
|
@@ -120,15 +120,15 @@
|
|
|
120
120
|
"sharp": "0.35.0"
|
|
121
121
|
},
|
|
122
122
|
"optionalDependencies": {
|
|
123
|
-
"@failproofai/failproofaid-linux-x64": "1.0.2-beta.
|
|
124
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.2-beta.
|
|
125
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.2-beta.
|
|
126
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.
|
|
123
|
+
"@failproofai/failproofaid-linux-x64": "1.0.2-beta.6",
|
|
124
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.2-beta.6",
|
|
125
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.2-beta.6",
|
|
126
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.6"
|
|
127
127
|
},
|
|
128
128
|
"failproofaidBinaries": {
|
|
129
|
-
"linux-x64": "
|
|
130
|
-
"linux-arm64": "
|
|
131
|
-
"darwin-x64": "
|
|
132
|
-
"darwin-arm64": "
|
|
129
|
+
"linux-x64": "1e883d50ea40dad46394a137fab9b8ecddb7b37064312af9ddf89cb3de5eb7b8",
|
|
130
|
+
"linux-arm64": "47a51a75a01c021417a4d67fb7eb5caa8fa9c46abca5ec85d23e8ddead1ebfdd",
|
|
131
|
+
"darwin-x64": "bbc4e714c1e961d53d7dd6c65b84dcc74b4e24439349ebdef3f7d8681ece1776",
|
|
132
|
+
"darwin-arm64": "ca6f6e688070a2d2a63fd333f7c3ac7c7d2bd991ca499cfced23b249e7ca684b"
|
|
133
133
|
}
|
|
134
134
|
}
|
|
@@ -9,7 +9,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
|
|
|
9
9
|
const hostname = process.env.HOSTNAME || '0.0.0.0'
|
|
10
10
|
|
|
11
11
|
let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
|
|
12
|
-
const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"1.0.2-beta.
|
|
12
|
+
const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"1.0.2-beta.6"},"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"instrumentationClientInject":[],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":true,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/failproofai/failproofai","enablePrerenderSourceMaps":true,"cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":true,"coldCacheBadge":false,"devValidationWorker":true,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"dynamicOnHover":false,"useOffline":false,"varyParams":true,"optimisticRouting":true,"instrumentationClientRouterTransitionEvents":false,"prefetchInlining":{"maxSize":2048,"maxBundleSize":10240},"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptOperationCache":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"devMemoryThresholdRestart":true,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"requestInsights":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"useTypeScriptCli":true,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":true,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"serverComponentsHmrCancellation":false,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":true,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","turbopackMemoryEvictionMode":"auto","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"useCacheTimeout":54,"instantInsights":{"validationLevel":"warning"},"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","outputFileTracingExcludes":{"*":["node_modules/@img/**","node_modules/sharp/**"]},"turbopack":{"root":"/home/runner/work/failproofai/failproofai"},"repoRoot":"/home/runner/work/failproofai/failproofai","distDirRoot":".next"}
|
|
13
13
|
|
|
14
14
|
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
|
|
15
15
|
|