failproofai 0.0.1-beta.9 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.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 +2 -2
- 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 +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 +1 -1
- 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.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.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.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.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]__02nt~6d._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0su5_t~._.js → [root-of-the-server]__0jvf9jj._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0osi8nq._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0yhzo9v._.js → [root-of-the-server]__0~7bzp~._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- 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 +9 -9
- package/.next/standalone/.next/static/chunks/{0qjjki0j187__.js → 056c865hodfe7.js} +1 -1
- package/.next/standalone/.next/static/chunks/{124wu0bxsexm6.js → 066e8ajzl234v.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0frzv~pmu0hsf.js → 0eh2hq9~6bf53.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0nygnut7i45jn.js → 0nwr.y4dwla00.js} +1 -1
- package/.next/standalone/.next/static/chunks/{14xe0g0rgwk18.js → 0o04-obbhh9.s.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0.aencsvb-yev.js → 0wmsi.tszy~9y.js} +1 -1
- package/.next/standalone/.next/static/chunks/{12olt9p45z-lq.js → 15lp0u9f5fwae.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0pc9pc_pilpi9.js → 17q_c2.bbcoh1.js} +1 -1
- package/.next/standalone/AGENTS.md +80 -0
- package/.next/standalone/CLAUDE.md +137 -0
- package/.next/standalone/bin/failproofai.mjs +12 -1
- package/.next/standalone/dist/index.js +80 -0
- package/.next/standalone/docs/package-aliases.md +25 -25
- package/.next/standalone/examples/policies-notification.js +77 -32
- package/.next/standalone/package.json +3 -2
- package/.next/standalone/scripts/sync-hook-events-prompt.md +60 -0
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/hooks/types.ts +11 -2
- package/bin/failproofai.mjs +12 -1
- package/dist/index.js +80 -0
- package/package.json +3 -2
- package/scripts/sync-hook-events-prompt.md +60 -0
- package/src/hooks/types.ts +11 -2
- /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → odPaRK23IkTvoPxqrn_8P}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → odPaRK23IkTvoPxqrn_8P}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → odPaRK23IkTvoPxqrn_8P}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,63059,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["ChevronRight",0,t],63059)},73375,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);e.s(["ChevronLeft",0,t],73375)},73520,e=>{"use strict";var t=e.i(43476),a=e.i(73375),r=e.i(63059);e.s(["default",0,function({currentPage:e,totalPages:i,onPageChange:s}){if(i<=1)return null;let l=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>s(e-1),disabled:1===e,className:l(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(i<=7)return Array.from({length:i},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(i-1,e+1);e<=3&&(r=Math.min(5,i-1)),e>=i-2&&(a=Math.max(2,i-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<i-1&&t.push("ellipsis-end"),t.push(i),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===i,className:l(e===i),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=e/1e3;if(t<60)return`${t.toFixed(1)}s`;let a=Math.floor(t/60);if(a>=60){let e=Math.floor(a/60);return`${e}h ${a%60}m`}let r=(t%60).toFixed(0);return`${a}m ${r}s`},"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`}])},39616,
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,63059,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["ChevronRight",0,t],63059)},73375,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);e.s(["ChevronLeft",0,t],73375)},73520,e=>{"use strict";var t=e.i(43476),a=e.i(73375),r=e.i(63059);e.s(["default",0,function({currentPage:e,totalPages:i,onPageChange:s}){if(i<=1)return null;let l=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>s(e-1),disabled:1===e,className:l(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(i<=7)return Array.from({length:i},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(i-1,e+1);e<=3&&(r=Math.min(5,i-1)),e>=i-2&&(a=Math.max(2,i-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<i-1&&t.push("ellipsis-end"),t.push(i),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===i,className:l(e===i),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=e/1e3;if(t<60)return`${t.toFixed(1)}s`;let a=Math.floor(t/60);if(a>=60){let e=Math.floor(a/60);return`${e}h ${a%60}m`}let r=(t%60).toFixed(0);return`${a}m ${r}s`},"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`}])},39616,14627,e=>{"use strict";let t=(0,e.i(75254).default)("settings",[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["Settings",0,t],39616);var a=e.i(95187);let r=(0,a.createServerReference)("6041b7d2c91a163d94f0cd988378f19d39b4a5cd65",a.callServer,void 0,a.findSourceMapURL,"searchHookActivityAction");e.s(["searchHookActivityAction",0,r],14627)},81418,88092,51737,e=>{"use strict";var t=e.i(75254);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],81418);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],88092);let i=(0,t.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,i],51737)}]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,33525,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"warnOnce",{enumerable:!0,get:function(){return n}});let n=e=>{}},18967,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={DecodeError:function(){return b},MiddlewareNotFoundError:function(){return P},MissingStaticPage:function(){return S},NormalizeError:function(){return h},PageNotFoundError:function(){return v},SP:function(){return m},ST:function(){return y},WEB_VITALS:function(){return i},execOnce:function(){return a},getDisplayName:function(){return f},getLocationOrigin:function(){return u},getURL:function(){return l},isAbsoluteUrl:function(){return c},isResSent:function(){return d},loadGetInitialProps:function(){return g},normalizeRepeatedSlashes:function(){return p},stringifyError:function(){return E}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=["CLS","FCP","FID","INP","LCP","TTFB"];function a(e){let r,t=!1;return(...n)=>(t||(t=!0,r=e(...n)),r)}let s=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/,c=e=>s.test(e);function u(){let{protocol:e,hostname:r,port:t}=window.location;return`${e}//${r}${t?":"+t:""}`}function l(){let{href:e}=window.location,r=u();return e.substring(r.length)}function f(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function d(e){return e.finished||e.headersSent}function p(e){let r=e.split("?");return r[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(r[1]?`?${r.slice(1).join("?")}`:"")}async function g(e,r){let t=r.res||r.ctx&&r.ctx.res;if(!e.getInitialProps)return r.ctx&&r.Component?{pageProps:await g(r.Component,r.ctx)}:{};let n=await e.getInitialProps(r);if(t&&d(t))return n;if(!n)throw Object.defineProperty(Error(`"${f(e)}.getInitialProps()" should resolve to an object. But found "${n}" instead.`),"__NEXT_ERROR_CODE",{value:"E1025",enumerable:!1,configurable:!0});return n}let m="u">typeof performance,y=m&&["mark","measure","getEntriesByName"].every(e=>"function"==typeof performance[e]);class b extends Error{}class h extends Error{}class v extends Error{constructor(e){super(),this.code="ENOENT",this.name="PageNotFoundError",this.message=`Cannot find module for page: ${e}`}}class S extends Error{constructor(e,r){super(),this.message=`Failed to load static file for page: ${e} ${r}`}}class P extends Error{constructor(){super(),this.code="ENOENT",this.message="Cannot find the middleware module"}}function E(e){return JSON.stringify({message:e.message,stack:e.stack})}},98183,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={assign:function(){return c},searchParamsToUrlQuery:function(){return i},urlQueryToSearchParams:function(){return s}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});function i(e){let r={};for(let[t,n]of e.entries()){let e=r[t];void 0===e?r[t]=n:Array.isArray(e)?e.push(n):r[t]=[e,n]}return r}function a(e){return"string"==typeof e?e:("number"!=typeof e||isNaN(e))&&"boolean"!=typeof e?"":String(e)}function s(e){let r=new URLSearchParams;for(let[t,n]of Object.entries(e))if(Array.isArray(n))for(let e of n)r.append(t,a(e));else r.set(t,a(n));return r}function c(e,...r){for(let t of r){for(let r of t.keys())e.delete(r);for(let[r,n]of t.entries())e.append(r,n)}return e}},9969,e=>{"use strict";let r=null;e.s(["captureClientEvent",0,function(e,t){if(!r||!r.enabled)return;let n=JSON.stringify({api_key:r.apiKey,event:e,distinct_id:r.distinctId,properties:{...t,$lib:"failproofai-web",failproofai_version:r.version,$current_url:window.location.href,$pathname:window.location.pathname}});fetch(r.host.replace(/\/+$/,"")+"/capture/",{method:"POST",headers:{"Content-Type":"application/json"},body:n,signal:AbortSignal.timeout(5e3)}).catch(()=>{})},"setClientTelemetryConfig",0,function(e){r=e}])},95187,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={callServer:function(){return i.callServer},createServerReference:function(){return s.createServerReference},findSourceMapURL:function(){return a.findSourceMapURL}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=e.r(32120),a=e.r(92245),s=e.r(35326)},
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,33525,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"warnOnce",{enumerable:!0,get:function(){return n}});let n=e=>{}},18967,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={DecodeError:function(){return b},MiddlewareNotFoundError:function(){return P},MissingStaticPage:function(){return S},NormalizeError:function(){return h},PageNotFoundError:function(){return v},SP:function(){return m},ST:function(){return y},WEB_VITALS:function(){return i},execOnce:function(){return a},getDisplayName:function(){return f},getLocationOrigin:function(){return u},getURL:function(){return l},isAbsoluteUrl:function(){return c},isResSent:function(){return d},loadGetInitialProps:function(){return g},normalizeRepeatedSlashes:function(){return p},stringifyError:function(){return E}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=["CLS","FCP","FID","INP","LCP","TTFB"];function a(e){let r,t=!1;return(...n)=>(t||(t=!0,r=e(...n)),r)}let s=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/,c=e=>s.test(e);function u(){let{protocol:e,hostname:r,port:t}=window.location;return`${e}//${r}${t?":"+t:""}`}function l(){let{href:e}=window.location,r=u();return e.substring(r.length)}function f(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function d(e){return e.finished||e.headersSent}function p(e){let r=e.split("?");return r[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(r[1]?`?${r.slice(1).join("?")}`:"")}async function g(e,r){let t=r.res||r.ctx&&r.ctx.res;if(!e.getInitialProps)return r.ctx&&r.Component?{pageProps:await g(r.Component,r.ctx)}:{};let n=await e.getInitialProps(r);if(t&&d(t))return n;if(!n)throw Object.defineProperty(Error(`"${f(e)}.getInitialProps()" should resolve to an object. But found "${n}" instead.`),"__NEXT_ERROR_CODE",{value:"E1025",enumerable:!1,configurable:!0});return n}let m="u">typeof performance,y=m&&["mark","measure","getEntriesByName"].every(e=>"function"==typeof performance[e]);class b extends Error{}class h extends Error{}class v extends Error{constructor(e){super(),this.code="ENOENT",this.name="PageNotFoundError",this.message=`Cannot find module for page: ${e}`}}class S extends Error{constructor(e,r){super(),this.message=`Failed to load static file for page: ${e} ${r}`}}class P extends Error{constructor(){super(),this.code="ENOENT",this.message="Cannot find the middleware module"}}function E(e){return JSON.stringify({message:e.message,stack:e.stack})}},98183,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={assign:function(){return c},searchParamsToUrlQuery:function(){return i},urlQueryToSearchParams:function(){return s}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});function i(e){let r={};for(let[t,n]of e.entries()){let e=r[t];void 0===e?r[t]=n:Array.isArray(e)?e.push(n):r[t]=[e,n]}return r}function a(e){return"string"==typeof e?e:("number"!=typeof e||isNaN(e))&&"boolean"!=typeof e?"":String(e)}function s(e){let r=new URLSearchParams;for(let[t,n]of Object.entries(e))if(Array.isArray(n))for(let e of n)r.append(t,a(e));else r.set(t,a(n));return r}function c(e,...r){for(let t of r){for(let r of t.keys())e.delete(r);for(let[r,n]of t.entries())e.append(r,n)}return e}},9969,e=>{"use strict";let r=null;e.s(["captureClientEvent",0,function(e,t){if(!r||!r.enabled)return;let n=JSON.stringify({api_key:r.apiKey,event:e,distinct_id:r.distinctId,properties:{...t,$lib:"failproofai-web",failproofai_version:r.version,$current_url:window.location.href,$pathname:window.location.pathname}});fetch(r.host.replace(/\/+$/,"")+"/capture/",{method:"POST",headers:{"Content-Type":"application/json"},body:n,signal:AbortSignal.timeout(5e3)}).catch(()=>{})},"setClientTelemetryConfig",0,function(e){r=e}])},95187,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={callServer:function(){return i.callServer},createServerReference:function(){return s.createServerReference},findSourceMapURL:function(){return a.findSourceMapURL}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=e.r(32120),a=e.r(92245),s=e.r(35326)},32167,e=>{"use strict";var r=e.i(95187);let t=(0,r.createServerReference)("00da780697b4e8e1582c5a997fdeb82db1d8130bfc",r.callServer,void 0,r.findSourceMapURL,"getTelemetryConfig");e.s(["getTelemetryConfig",0,t])},53348,e=>{"use strict";var r=e.i(43476),t=e.i(71645),n=e.i(32167),o=e.i(9969);e.s(["default",0,function({error:e,reset:i}){return(0,t.useEffect)(()=>{(0,n.getTelemetryConfig)().then(r=>{(0,o.setClientTelemetryConfig)(r),(0,o.captureClientEvent)("client_error",{error_message:e.message,error_name:e.name,error_digest:e.digest,boundary:"global"})}).catch(()=>{})},[e]),(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)("main",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#031035",color:"#f8fafc",fontFamily:"system-ui, sans-serif"},children:(0,r.jsxs)("div",{style:{textAlign:"center",padding:"2rem",border:"1px solid rgba(239,68,68,0.4)",borderRadius:"0.5rem",maxWidth:"500px"},children:[(0,r.jsx)("h2",{style:{color:"#ef4444",marginBottom:"0.5rem",fontSize:"1.25rem"},children:"Something went wrong"}),(0,r.jsx)("p",{style:{color:"#94a3b8",marginBottom:"1.5rem"},children:e.message||"An unexpected error occurred."}),(0,r.jsx)("button",{onClick:i,style:{padding:"0.5rem 1.25rem",background:"#3b82f6",color:"white",border:"none",borderRadius:"0.375rem",cursor:"pointer",fontSize:"0.875rem"},children:"Try again"})]})})})})}])}]);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# AGENTS.md — Codex / agent guidance for this repo
|
|
2
|
+
|
|
3
|
+
This file is read by AI coding agents (Codex, Claude Code, etc.). It duplicates the most
|
|
4
|
+
critical rules from CLAUDE.md in a format optimised for agent consumption.
|
|
5
|
+
|
|
6
|
+
## Non-negotiable rules
|
|
7
|
+
|
|
8
|
+
1. **Branch must contain all commits from main.** Before every push, run:
|
|
9
|
+
```bash
|
|
10
|
+
git fetch origin && git log --oneline origin/main ^HEAD
|
|
11
|
+
```
|
|
12
|
+
If that prints anything, rebase first: `git rebase origin/main`. Never push a branch
|
|
13
|
+
that is missing commits from `main`.
|
|
14
|
+
|
|
15
|
+
3. **One PR per branch.** Check `gh pr list --head <current-branch>` before creating a PR.
|
|
16
|
+
Push to the existing PR if one already exists.
|
|
17
|
+
|
|
18
|
+
4. **CI must be green before you stop.** After every push, poll `gh run list --limit 3`
|
|
19
|
+
until all checks complete. Fix failures before proceeding or declaring done.
|
|
20
|
+
|
|
21
|
+
5. **Never edit a test to make it pass.** Fix the code. Tests may only be changed when the
|
|
22
|
+
test itself is wrong or when the feature under test was intentionally changed.
|
|
23
|
+
|
|
24
|
+
6. **Always add unit tests for new behaviour.** Place tests in `__tests__/`. Unit tests live
|
|
25
|
+
in `__tests__/hooks/`, e2e tests in `__tests__/e2e/hooks/`.
|
|
26
|
+
|
|
27
|
+
7. **Docker is available.** Use `oven/bun:latest` with `--network=host` to do clean-install
|
|
28
|
+
end-to-end testing after every non-trivial implementation. See CLAUDE.md for the exact
|
|
29
|
+
Docker test recipe.
|
|
30
|
+
|
|
31
|
+
## Test commands
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bun run test:run # unit tests (fast, run first)
|
|
35
|
+
bun run test:e2e # end-to-end hook tests (slower, run before push)
|
|
36
|
+
bun run lint # eslint
|
|
37
|
+
bunx tsc --noEmit # type check
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Docker smoke test (run after every change to src/hooks/ or package.json)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm pack --ignore-scripts
|
|
44
|
+
docker run --rm --network=host \
|
|
45
|
+
-v $(pwd)/failproofai-*.tgz:/pkg.tgz \
|
|
46
|
+
oven/bun:latest bash -c "
|
|
47
|
+
apt-get update -qq && apt-get install -y -qq nodejs npm 2>&1 | tail -2
|
|
48
|
+
npm install -g /pkg.tgz --ignore-scripts 2>&1 | tail -3
|
|
49
|
+
cat > /tmp/tp.mjs << 'EOF'
|
|
50
|
+
import { customPolicies, allow } from 'failproofai';
|
|
51
|
+
customPolicies.add({ name: 't', description: 't', match: { events: ['PreToolUse'] }, fn: async () => allow() });
|
|
52
|
+
EOF
|
|
53
|
+
failproofai p -i -c /tmp/tp.mjs
|
|
54
|
+
"
|
|
55
|
+
rm failproofai-*.tgz
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Expected: `Validated 1 custom hook(s): t`, exit 0.
|
|
59
|
+
|
|
60
|
+
## Regression checklist
|
|
61
|
+
|
|
62
|
+
Run through this mentally after any change to `src/hooks/` or `dist/` build:
|
|
63
|
+
|
|
64
|
+
- [ ] ESM import (`from 'failproofai'`) in custom policy resolves
|
|
65
|
+
- [ ] CJS require (`require('failproofai')`) in custom policy resolves
|
|
66
|
+
- [ ] Transitive local imports inside custom policy file work
|
|
67
|
+
- [ ] Builtin policies run when no custom file is configured
|
|
68
|
+
- [ ] `failproofai p -i -c <nonexistent>` fails gracefully (no crash)
|
|
69
|
+
- [ ] `bun run test:run` — all unit tests pass
|
|
70
|
+
- [ ] `bun run test:e2e` — all e2e tests pass
|
|
71
|
+
- [ ] CI is green
|
|
72
|
+
|
|
73
|
+
## Key files
|
|
74
|
+
|
|
75
|
+
| File | Purpose |
|
|
76
|
+
|------|---------|
|
|
77
|
+
| `src/hooks/loader-utils.ts` | `findDistIndex()`, ESM shim, import rewriting |
|
|
78
|
+
| `src/hooks/custom-hooks-loader.ts` | Top-level custom hook loading orchestrator |
|
|
79
|
+
| `src/index.ts` | Public API → `dist/index.js` bundle entry |
|
|
80
|
+
| `package.json` | `files` must include `dist/`; `build` must build `dist/index.js` |
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# CLAUDE.md — Agent guidance for this repo
|
|
2
|
+
|
|
3
|
+
## Environment
|
|
4
|
+
|
|
5
|
+
- **Runtime:** bun (≥1.3.0) and Node.js (≥20.9.0) are both present.
|
|
6
|
+
- **Docker CLI** is available. Use it to spin up clean containers that mimic real user
|
|
7
|
+
installs and validate every non-trivial change end-to-end before pushing.
|
|
8
|
+
- **Package manager:** bun (`bun install`, `bun run <script>`). Do not use npm/yarn to
|
|
9
|
+
install deps locally.
|
|
10
|
+
|
|
11
|
+
## Workflow rules
|
|
12
|
+
|
|
13
|
+
### One PR per branch
|
|
14
|
+
Each local branch maps to exactly one PR. Before opening a PR, check with
|
|
15
|
+
`gh pr list --head <branch>`. If one exists, push new commits to the same branch — never
|
|
16
|
+
open a second PR for the same branch.
|
|
17
|
+
|
|
18
|
+
### Branch must contain all commits from main
|
|
19
|
+
Before pushing, verify your branch is up to date with `main`:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git fetch origin
|
|
23
|
+
git log --oneline origin/main ^HEAD # should print nothing
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If it prints commits, rebase before pushing:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
git rebase origin/main
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Resolve any conflicts, then continue. Never push a branch that is missing commits from
|
|
33
|
+
`main` — the PR diff will be polluted and CI may test against a stale base.
|
|
34
|
+
|
|
35
|
+
### CI must be green after every commit you push
|
|
36
|
+
After every `git push`, run `gh run watch` or poll `gh run list --limit 3` until all checks
|
|
37
|
+
finish. If any job fails, **stop and fix it before continuing**. Never leave a red CI.
|
|
38
|
+
|
|
39
|
+
The CI runs four jobs — all must pass:
|
|
40
|
+
| Job | Command |
|
|
41
|
+
|-----|---------|
|
|
42
|
+
| quality | lint + tsc + version-consistency check |
|
|
43
|
+
| test | `bun run test:run` (unit, 4 env configs) |
|
|
44
|
+
| build | `bun run build` (Next.js + dist/index.js) |
|
|
45
|
+
| test-e2e | `bun run test:e2e` |
|
|
46
|
+
|
|
47
|
+
### Always add unit tests for new behaviour
|
|
48
|
+
When you add or change logic, add a corresponding test in `__tests__/`. Never modify
|
|
49
|
+
existing tests just to make them pass — if a test breaks, fix the code, not the test.
|
|
50
|
+
Exception: updating a test that explicitly tests the value you're changing (e.g. a version
|
|
51
|
+
string or an error message you intentionally changed).
|
|
52
|
+
|
|
53
|
+
## Testing protocol
|
|
54
|
+
|
|
55
|
+
### After every implementation change
|
|
56
|
+
|
|
57
|
+
1. **Unit tests first** — fast, in-process:
|
|
58
|
+
```bash
|
|
59
|
+
bun run test:run
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
2. **Local smoke test** — use the dev dist directly:
|
|
63
|
+
```bash
|
|
64
|
+
bun build --target=node --format=cjs --outfile=dist/index.js src/index.ts
|
|
65
|
+
FAILPROOFAI_DIST_PATH=$(pwd)/dist failproofai p -i -c <policy-file>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
3. **Docker clean-install test** — mimics a real `npm install -g` from scratch.
|
|
69
|
+
Use the `oven/bun:latest` image (bun pre-installed) with `--network=host`:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Pack without running the full build
|
|
73
|
+
npm pack --ignore-scripts
|
|
74
|
+
|
|
75
|
+
docker run --rm --network=host \
|
|
76
|
+
-v $(pwd)/failproofai-*.tgz:/pkg.tgz \
|
|
77
|
+
oven/bun:latest bash -c "
|
|
78
|
+
apt-get update -qq && apt-get install -y -qq nodejs npm 2>&1 | tail -2
|
|
79
|
+
npm install -g /pkg.tgz --ignore-scripts 2>&1 | tail -3
|
|
80
|
+
cat > /tmp/test-policy.mjs << 'EOF'
|
|
81
|
+
import { customPolicies, allow } from 'failproofai';
|
|
82
|
+
customPolicies.add({
|
|
83
|
+
name: 'smoke-test',
|
|
84
|
+
description: 'Smoke test',
|
|
85
|
+
match: { events: ['PreToolUse'] },
|
|
86
|
+
fn: async (ctx) => allow(),
|
|
87
|
+
});
|
|
88
|
+
EOF
|
|
89
|
+
failproofai --version
|
|
90
|
+
failproofai p -i -c /tmp/test-policy.mjs
|
|
91
|
+
"
|
|
92
|
+
|
|
93
|
+
rm failproofai-*.tgz
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Expected output includes `Validated 1 custom hook(s): smoke-test` and exit 0.
|
|
97
|
+
|
|
98
|
+
4. **E2E tests** (before pushing):
|
|
99
|
+
```bash
|
|
100
|
+
bun run test:e2e
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Regression areas to always check
|
|
104
|
+
|
|
105
|
+
After any change to `src/hooks/`, verify these scenarios don't regress:
|
|
106
|
+
|
|
107
|
+
| Scenario | How to check |
|
|
108
|
+
|----------|-------------|
|
|
109
|
+
| Custom policy with `from 'failproofai'` ESM import | Docker clean-install test above |
|
|
110
|
+
| Custom policy with `require('failproofai')` CJS | Write a `.js` test file with `require` and run `p -i -c` |
|
|
111
|
+
| Transitive local imports in custom policy | Use `examples/policies-advanced/index.js` |
|
|
112
|
+
| Builtin policies still fire (no custom file) | `failproofai p -i` without `-c` |
|
|
113
|
+
| `findDistIndex()` fallback when `FAILPROOFAI_DIST_PATH` unset | Unset the var and test |
|
|
114
|
+
| `loadCustomHooks` fail-open (bad file path) | Pass a nonexistent file without `--strict` |
|
|
115
|
+
|
|
116
|
+
## Project structure cheatsheet
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
bin/failproofai.mjs Entry point (bun shebang); sets FAILPROOFAI_DIST_PATH
|
|
120
|
+
src/hooks/
|
|
121
|
+
custom-hooks-loader.ts Orchestrates temp-file creation + dynamic import
|
|
122
|
+
loader-utils.ts findDistIndex(), createEsmShim(), rewriteFileTree()
|
|
123
|
+
custom-hooks-registry.ts globalThis registry shared between loader and handler
|
|
124
|
+
policy-helpers.ts allow() / deny() / instruct()
|
|
125
|
+
handler.ts Called by Claude Code --hook events
|
|
126
|
+
manager.ts policies --install / --uninstall / list
|
|
127
|
+
src/index.ts Public API entry point → compiled to dist/index.js
|
|
128
|
+
dist/index.js CJS bundle (built by `bun run build`; shipped in npm pkg)
|
|
129
|
+
__tests__/ Unit + e2e tests (vitest)
|
|
130
|
+
examples/ Sample custom policy files
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Version bumps
|
|
134
|
+
|
|
135
|
+
When bumping the version, update **only** `package.json` (root). The CI version-consistency
|
|
136
|
+
check compares `packages/*/package.json` against root — that directory does not currently
|
|
137
|
+
exist, so no other files need updating.
|
|
@@ -24,8 +24,19 @@ if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
if (!process.env.FAILPROOFAI_DIST_PATH) {
|
|
28
|
+
process.env.FAILPROOFAI_DIST_PATH = resolve(
|
|
29
|
+
dirname(realpathSync(fileURLToPath(import.meta.url))),
|
|
30
|
+
"..",
|
|
31
|
+
"dist"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
const args = process.argv.slice(2);
|
|
28
36
|
|
|
37
|
+
// Normalize 'p' → 'policies' (shorthand alias)
|
|
38
|
+
if (args[0] === "p") args[0] = "policies";
|
|
39
|
+
|
|
29
40
|
// --help / -h (only when not inside a subcommand that handles its own --help)
|
|
30
41
|
const SUBCOMMANDS = ["policies"];
|
|
31
42
|
if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
|
|
@@ -38,7 +49,7 @@ USAGE
|
|
|
38
49
|
COMMANDS
|
|
39
50
|
(no args) Launch the policy dashboard
|
|
40
51
|
|
|
41
|
-
policies
|
|
52
|
+
policies, p List all available policies and their status
|
|
42
53
|
policies --install, -i Enable policies in Claude Code settings
|
|
43
54
|
[names...] Specific policy names to enable
|
|
44
55
|
--scope user|project|local Config scope to write to (default: user)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/index.ts
|
|
40
|
+
var exports_src = {};
|
|
41
|
+
__export(exports_src, {
|
|
42
|
+
instruct: () => instruct,
|
|
43
|
+
getCustomHooks: () => getCustomHooks,
|
|
44
|
+
deny: () => deny,
|
|
45
|
+
customPolicies: () => customPolicies,
|
|
46
|
+
clearCustomHooks: () => clearCustomHooks,
|
|
47
|
+
allow: () => allow
|
|
48
|
+
});
|
|
49
|
+
module.exports = __toCommonJS(exports_src);
|
|
50
|
+
|
|
51
|
+
// src/hooks/custom-hooks-registry.ts
|
|
52
|
+
var REGISTRY_KEY = "__failproofai_custom_hooks__";
|
|
53
|
+
function getRegistry() {
|
|
54
|
+
const g = globalThis;
|
|
55
|
+
if (!Array.isArray(g[REGISTRY_KEY]))
|
|
56
|
+
g[REGISTRY_KEY] = [];
|
|
57
|
+
return g[REGISTRY_KEY];
|
|
58
|
+
}
|
|
59
|
+
var customPolicies = {
|
|
60
|
+
add(hook) {
|
|
61
|
+
getRegistry().push(hook);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function getCustomHooks() {
|
|
65
|
+
return getRegistry();
|
|
66
|
+
}
|
|
67
|
+
function clearCustomHooks() {
|
|
68
|
+
const g = globalThis;
|
|
69
|
+
g[REGISTRY_KEY] = [];
|
|
70
|
+
}
|
|
71
|
+
// src/hooks/policy-helpers.ts
|
|
72
|
+
function allow() {
|
|
73
|
+
return { decision: "allow" };
|
|
74
|
+
}
|
|
75
|
+
function deny(reason) {
|
|
76
|
+
return { decision: "deny", reason };
|
|
77
|
+
}
|
|
78
|
+
function instruct(reason) {
|
|
79
|
+
return { decision: "instruct", reason };
|
|
80
|
+
}
|
|
@@ -22,39 +22,39 @@ To eliminate this surface, **we pre-emptively own all common misspellings and fo
|
|
|
22
22
|
|
|
23
23
|
**Formatting variants** — different ways to write "failproof ai":
|
|
24
24
|
|
|
25
|
-
| Package |
|
|
26
|
-
|
|
27
|
-
| `failproof` |
|
|
28
|
-
| `failproof-ai` |
|
|
29
|
-
| `fail-proof-ai` |
|
|
30
|
-
| `failproof_ai` |
|
|
31
|
-
| `fail_proof_ai` |
|
|
32
|
-
| `fail-proofai` |
|
|
33
|
-
|
|
34
|
-
> **Why pending?** npm's spam-prevention policy blocks registration of names that normalize to the same string as an existing package after stripping punctuation (e.g. `failproof-ai` → `failproofai`). We have contacted npm support to reserve these names for anti-squatting purposes. They will be activated once approved.
|
|
25
|
+
| Package | Status |
|
|
26
|
+
|---------|--------|
|
|
27
|
+
| `failproof` | ✅ Published |
|
|
28
|
+
| `failproof-ai` | ⏳ Pending npm support |
|
|
29
|
+
| `fail-proof-ai` | ⏳ Pending npm support |
|
|
30
|
+
| `failproof_ai` | ⏳ Pending npm support |
|
|
31
|
+
| `fail_proof_ai` | ⏳ Pending npm support |
|
|
32
|
+
| `fail-proofai` | ⏳ Pending npm support |
|
|
35
33
|
|
|
36
34
|
**`failprof*` typos** — missing one `o` from "proof":
|
|
37
35
|
|
|
38
|
-
| Package |
|
|
39
|
-
|
|
40
|
-
| `failprof` |
|
|
41
|
-
| `failprof-ai` |
|
|
42
|
-
| `failprofai` |
|
|
43
|
-
| `fail-prof-ai` |
|
|
44
|
-
| `failprof_ai` |
|
|
36
|
+
| Package | Status |
|
|
37
|
+
|---------|--------|
|
|
38
|
+
| `failprof` | ✅ Published |
|
|
39
|
+
| `failprof-ai` | ✅ Published |
|
|
40
|
+
| `failprofai` | ⏳ Pending npm support |
|
|
41
|
+
| `fail-prof-ai` | ⏳ Pending npm support |
|
|
42
|
+
| `failprof_ai` | ⏳ Pending npm support |
|
|
45
43
|
|
|
46
44
|
**`faliproof*` typos** — transposed `a` and `i`:
|
|
47
45
|
|
|
48
|
-
| Package |
|
|
49
|
-
|
|
50
|
-
| `faliproof` |
|
|
51
|
-
| `faliproof-ai` |
|
|
52
|
-
| `faliproofai` |
|
|
46
|
+
| Package | Status |
|
|
47
|
+
|---------|--------|
|
|
48
|
+
| `faliproof` | ✅ Published |
|
|
49
|
+
| `faliproof-ai` | ✅ Published |
|
|
50
|
+
| `faliproofai` | ⏳ Pending npm support |
|
|
51
|
+
|
|
52
|
+
> **Why pending?** npm's spam-prevention policy blocks names that normalize to the same string as an existing package after stripping punctuation and running similarity checks. We have contacted npm support to reserve these names for anti-squatting purposes. They will be activated once approved.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
You can verify any published alias is owned by us:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
npm info failproof
|
|
57
|
+
npm info failproof
|
|
58
58
|
# Look for: "ExosphereHost Inc." in the maintainers field
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -65,7 +65,7 @@ npm info failproof-ai
|
|
|
65
65
|
Each alias package:
|
|
66
66
|
|
|
67
67
|
1. Lists `failproofai` as a dependency — so the real package (including its `postinstall` hook setup) runs on install
|
|
68
|
-
2. Exposes a binary matching its own name (e.g. `
|
|
68
|
+
2. Exposes a binary matching its own name (e.g. `failprof-ai`) that proxies all arguments to the `failproofai` binary
|
|
69
69
|
|
|
70
70
|
The proxy is a two-line Node script; there is no logic, no network calls, and no data collection beyond what `failproofai` itself does.
|
|
71
71
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* policies-notification.js — Notification event
|
|
2
|
+
* policies-notification.js — Notification and SessionEnd event examples
|
|
3
3
|
*
|
|
4
|
-
* Forwards Claude's idle notifications to Slack.
|
|
4
|
+
* Forwards Claude's idle notifications and session-end events to Slack.
|
|
5
5
|
*
|
|
6
6
|
* Prerequisites:
|
|
7
7
|
* Set the SLACK_WEBHOOK_URL environment variable to your Slack incoming webhook URL.
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* failproofai --install-hooks custom ./examples/policies-notification.js
|
|
11
11
|
*
|
|
12
12
|
* Test by letting Claude finish a task and go idle — you should receive a Slack message.
|
|
13
|
+
* Test session end by exiting Claude — you should receive a session summary message.
|
|
13
14
|
*/
|
|
14
|
-
import { customPolicies, allow } from "failproofai";
|
|
15
|
+
import { customPolicies, allow, instruct } from "failproofai";
|
|
15
16
|
|
|
16
17
|
// Forward Claude idle notifications to Slack
|
|
17
18
|
customPolicies.add({
|
|
@@ -22,38 +23,82 @@ customPolicies.add({
|
|
|
22
23
|
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
|
|
23
24
|
if (!webhookUrl) return allow(); // skip if not configured
|
|
24
25
|
|
|
25
|
-
const type = String(ctx.payload?.notification_type ?? "");
|
|
26
|
-
if (type !== "idle") return allow(); // only forward idle notifications
|
|
27
|
-
|
|
28
26
|
const message = String(ctx.payload?.message ?? "Claude is waiting for input");
|
|
29
27
|
const cwd = ctx.session?.cwd ?? "unknown";
|
|
30
28
|
const sessionId = ctx.session?.sessionId ?? "unknown";
|
|
31
29
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
30
|
+
// Await so the request completes before process.exit() is called by the CLI
|
|
31
|
+
try {
|
|
32
|
+
await fetch(webhookUrl, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "Content-Type": "application/json" },
|
|
35
|
+
body: JSON.stringify({
|
|
36
|
+
blocks: [
|
|
37
|
+
{
|
|
38
|
+
type: "header",
|
|
39
|
+
text: { type: "plain_text", text: "💬 Claude is waiting for you", emoji: true },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: "section",
|
|
43
|
+
text: { type: "mrkdwn", text: message },
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: "section",
|
|
47
|
+
fields: [
|
|
48
|
+
{ type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
|
|
49
|
+
{ type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
}),
|
|
54
|
+
signal: AbortSignal.timeout(5000),
|
|
55
|
+
});
|
|
56
|
+
} catch {
|
|
57
|
+
// Never block Claude if Slack is unreachable
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return instruct(`We have sent the notification to the user on Slack about: ${message}`);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Notify Slack when a Claude session ends
|
|
65
|
+
customPolicies.add({
|
|
66
|
+
name: "slack-on-session-end",
|
|
67
|
+
description: "Notify Slack when a Claude session ends (set SLACK_WEBHOOK_URL env var)",
|
|
68
|
+
match: { events: ["SessionEnd"] },
|
|
69
|
+
fn: async (ctx) => {
|
|
70
|
+
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
|
|
71
|
+
if (!webhookUrl) return allow(); // skip if not configured
|
|
72
|
+
|
|
73
|
+
const cwd = ctx.session?.cwd ?? "unknown";
|
|
74
|
+
const sessionId = ctx.session?.sessionId ?? "unknown";
|
|
75
|
+
|
|
76
|
+
// Await so the request completes before process.exit() is called by the CLI
|
|
77
|
+
try {
|
|
78
|
+
await fetch(webhookUrl, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: { "Content-Type": "application/json" },
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
blocks: [
|
|
83
|
+
{
|
|
84
|
+
type: "header",
|
|
85
|
+
text: { type: "plain_text", text: "✅ Claude session ended", emoji: true },
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: "section",
|
|
89
|
+
fields: [
|
|
90
|
+
{ type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
|
|
91
|
+
{ type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
}),
|
|
96
|
+
signal: AbortSignal.timeout(5000),
|
|
97
|
+
});
|
|
98
|
+
} catch {
|
|
99
|
+
// Never block Claude if Slack is unreachable
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return instruct(`We have sent the notification to the user on Slack about: Claude session ended (project: ${cwd}, session: ${sessionId})`);
|
|
58
103
|
},
|
|
59
104
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "0.0.1
|
|
3
|
+
"version": "0.0.1",
|
|
4
4
|
"description": "Open-source hooks, policies, and project visualization for Claude Code & Agents SDK",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./bin/failproofai.mjs"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"scripts/",
|
|
12
12
|
"lib/",
|
|
13
13
|
".next/standalone/",
|
|
14
|
+
"dist/",
|
|
14
15
|
"README.md"
|
|
15
16
|
],
|
|
16
17
|
"engines": {
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
"scripts": {
|
|
21
22
|
"predev": "bun link",
|
|
22
23
|
"dev": "FAILPROOFAI_TELEMETRY_DISABLED=1 bun scripts/dev.ts --port 8020",
|
|
23
|
-
"build": "bun --bun next build && node -e \"const {cpSync}=require('fs');cpSync('.next/static','.next/standalone/.next/static',{recursive:true});\"",
|
|
24
|
+
"build": "bun build --target=node --format=cjs --outfile=dist/index.js src/index.ts && bun --bun next build && node -e \"const {cpSync}=require('fs');cpSync('.next/static','.next/standalone/.next/static',{recursive:true});\"",
|
|
24
25
|
"prestart": "bun link",
|
|
25
26
|
"start": "FAILPROOFAI_TELEMETRY_DISABLED=1 bun scripts/start.ts",
|
|
26
27
|
"test": "vitest",
|