af360 0.1.13 → 0.1.15
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/bin/af360.mjs +38 -5
- package/dist/.next/BUILD_ID +1 -1
- package/dist/.next/app-build-manifest.json +136 -138
- package/dist/.next/app-path-routes-manifest.json +20 -20
- package/dist/.next/build-manifest.json +5 -5
- package/dist/.next/prerender-manifest.json +46 -46
- package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/_not-found.html +1 -1
- package/dist/.next/server/app/_not-found.rsc +3 -3
- package/dist/.next/server/app/ahead/page.js +1 -1
- package/dist/.next/server/app/ahead/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/ahead.html +1 -1
- package/dist/.next/server/app/ahead.rsc +4 -4
- package/dist/.next/server/app/api/browse/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/card/route.js +1 -1
- package/dist/.next/server/app/api/card/route.js.nft.json +1 -1
- package/dist/.next/server/app/api/card/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/check/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/config/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/connections/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/connectors/github/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/consent/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/context/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/discover/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/expiry/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/first-light/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/handoff/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/health/route.js +1 -1
- package/dist/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/incidents/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/insights/route.js +1 -1
- package/dist/.next/server/app/api/insights/route.js.nft.json +1 -1
- package/dist/.next/server/app/api/insights/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/log/route.js +1 -1
- package/dist/.next/server/app/api/log/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pulse/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/radar/route.js +1 -1
- package/dist/.next/server/app/api/radar/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/report/route.js +1 -1
- package/dist/.next/server/app/api/report/route.js.nft.json +1 -1
- package/dist/.next/server/app/api/report/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/services/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/status/route.js +1 -1
- package/dist/.next/server/app/api/status/route.js.nft.json +1 -1
- package/dist/.next/server/app/api/status/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/connections/page.js +2 -2
- package/dist/.next/server/app/connections/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/connections.html +1 -1
- package/dist/.next/server/app/connections.rsc +4 -4
- package/dist/.next/server/app/dashboard/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/dashboard.html +1 -1
- package/dist/.next/server/app/dashboard.rsc +3 -3
- package/dist/.next/server/app/discoveries/page.js +1 -1
- package/dist/.next/server/app/discoveries/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/discoveries.html +1 -1
- package/dist/.next/server/app/discoveries.rsc +4 -4
- package/dist/.next/server/app/expiry/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/expiry.html +1 -1
- package/dist/.next/server/app/expiry.rsc +3 -3
- package/dist/.next/server/app/fleet/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/fleet.html +1 -1
- package/dist/.next/server/app/fleet.rsc +3 -3
- package/dist/.next/server/app/home/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/home.html +1 -1
- package/dist/.next/server/app/home.rsc +3 -3
- package/dist/.next/server/app/legal/privacy/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/legal/terms/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/log/page.js +1 -1
- package/dist/.next/server/app/log/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/log.html +1 -1
- package/dist/.next/server/app/log.rsc +4 -4
- package/dist/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/projects/page.js +1 -1
- package/dist/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/projects.html +1 -1
- package/dist/.next/server/app/projects.rsc +4 -4
- package/dist/.next/server/app/radar/page.js +1 -64
- package/dist/.next/server/app/radar/page.js.nft.json +1 -1
- package/dist/.next/server/app/radar/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/radar.html +1 -1
- package/dist/.next/server/app/radar.rsc +4 -4
- package/dist/.next/server/app/report/page.js +2 -2
- package/dist/.next/server/app/report/page.js.nft.json +1 -1
- package/dist/.next/server/app/report/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/report.html +1 -1
- package/dist/.next/server/app/report.rsc +4 -4
- package/dist/.next/server/app/settings/page.js +1 -1
- package/dist/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/settings.html +1 -1
- package/dist/.next/server/app/settings.rsc +4 -4
- package/dist/.next/server/app/sites/page.js +1 -1
- package/dist/.next/server/app/sites/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/sites.html +1 -1
- package/dist/.next/server/app/sites.rsc +4 -4
- package/dist/.next/server/app/welcome/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/welcome.html +1 -1
- package/dist/.next/server/app/welcome.rsc +4 -4
- package/dist/.next/server/app-paths-manifest.json +20 -20
- package/dist/.next/server/chunks/2346.js +1 -1
- package/dist/.next/server/chunks/2465.js +64 -1
- package/dist/.next/server/chunks/2817.js +1 -1
- package/dist/.next/server/chunks/4825.js +1 -0
- package/dist/.next/server/chunks/572.js +1 -1
- package/dist/.next/server/chunks/6992.js +1 -1
- package/dist/.next/server/chunks/7511.js +1 -1
- package/dist/.next/server/chunks/7719.js +1 -1
- package/dist/.next/server/chunks/8134.js +1 -1
- package/dist/.next/server/chunks/8150.js +1 -1
- package/dist/.next/server/chunks/876.js +1 -1
- package/dist/.next/server/chunks/9170.js +1 -1
- package/dist/.next/server/middleware-build-manifest.js +1 -1
- package/dist/.next/server/middleware-manifest.json +5 -5
- package/dist/.next/server/pages/404.html +1 -1
- package/dist/.next/server/pages/500.html +1 -1
- package/dist/.next/server/server-reference-manifest.json +1 -1
- package/dist/.next/static/chunks/{1255-612df69cb02f2021.js → 1255-fc6c00ab2f765339.js} +1 -1
- package/dist/.next/static/chunks/3192-40ade9877c89ec45.js +1 -0
- package/dist/.next/static/chunks/5623-53fb7cea5de0afef.js +1 -0
- package/dist/.next/static/chunks/7897-b9c15e8805df61ef.js +1 -0
- package/dist/.next/static/chunks/8050-a75457e83cd8be0f.js +1 -0
- package/dist/.next/static/chunks/app/ahead/page-75602559f92be6c4.js +1 -0
- package/dist/.next/static/chunks/app/connections/page-ff9d0b4e4fcf5ebe.js +1 -0
- package/dist/.next/static/chunks/app/discoveries/{page-52696e6ee0c8d3d5.js → page-6b06c0c2a94f3b94.js} +1 -1
- package/dist/.next/static/chunks/app/{error-37987420de14c10c.js → error-c67c297ef2dd70de.js} +1 -1
- package/dist/.next/static/chunks/app/log/page-842048c36f071dd6.js +1 -0
- package/dist/.next/static/chunks/app/projects/{page-75a41d4a90675d93.js → page-1d6b330842a2ff80.js} +1 -1
- package/dist/.next/static/chunks/app/radar/page-584b42327baac21b.js +1 -0
- package/dist/.next/static/chunks/app/report/page-9dc52d7f76569e42.js +1 -0
- package/dist/.next/static/chunks/app/settings/{page-a38c457b7c57567b.js → page-582279bf3fe509d4.js} +1 -1
- package/dist/.next/static/chunks/app/sites/page-3dcc5a0990fae677.js +1 -0
- package/dist/.next/static/chunks/{main-6828c74376f2b965.js → main-574ae5bf2f2165c4.js} +1 -1
- package/dist/.next/static/css/fbb4cf81dde34731.css +1 -0
- package/dist/build-id.json +145 -147
- package/dist/node_modules/@next/env/package.json +1 -1
- package/dist/node_modules/next/dist/build/swc/index.js +1 -1
- package/dist/node_modules/next/dist/build/webpack-config.js +2 -2
- package/dist/node_modules/next/dist/compiled/next-server/app-page-experimental.runtime.prod.js +2 -2
- package/dist/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +2 -2
- package/dist/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +2 -2
- package/dist/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js +2 -2
- package/dist/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js +1 -1
- package/dist/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +7 -7
- package/dist/node_modules/next/dist/compiled/next-server/pages.runtime.prod.js +2 -2
- package/dist/node_modules/next/dist/lib/has-necessary-dependencies.js +2 -0
- package/dist/node_modules/next/dist/lib/verify-typescript-setup.js +19 -0
- package/dist/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +1 -1
- package/dist/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/dist/node_modules/next/dist/server/image-optimizer.js +22 -2
- package/dist/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/dist/node_modules/next/dist/server/lib/incremental-cache/file-system-cache.js +17 -4
- package/dist/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/dist/node_modules/next/dist/shared/lib/canary-only.js +1 -1
- package/dist/node_modules/next/dist/shared/lib/router/utils/escape-path-delimiters.js +1 -1
- package/dist/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/dist/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/dist/node_modules/next/package.json +11 -11
- package/dist/package.json +2 -2
- package/dist/shell/bin/af360-bridge.exe +0 -0
- package/lib/mcpActivity.mjs +1 -0
- package/package.json +2 -2
- package/scripts/mcp-server.mjs +174 -2
- package/shell/bin/af360-bridge.exe +0 -0
- package/dist/.next/server/chunks/1155.js +0 -1
- package/dist/.next/server/chunks/6584.js +0 -1
- package/dist/.next/static/chunks/1681-b233404f9ec2fc2e.js +0 -1
- package/dist/.next/static/chunks/2036-e29441d605c493a4.js +0 -1
- package/dist/.next/static/chunks/3192-6b341d547a49fa85.js +0 -1
- package/dist/.next/static/chunks/5623-ec4f1a6f7695d8d1.js +0 -1
- package/dist/.next/static/chunks/7897-6e6c509aea495560.js +0 -1
- package/dist/.next/static/chunks/app/ahead/page-e72bed448dd40bd5.js +0 -1
- package/dist/.next/static/chunks/app/connections/page-5d6cae02e6f2b49a.js +0 -1
- package/dist/.next/static/chunks/app/log/page-45b4cfd8f1a29d09.js +0 -1
- package/dist/.next/static/chunks/app/radar/page-4b4aad549dffcd3a.js +0 -1
- package/dist/.next/static/chunks/app/report/page-145496e8f0d5bc86.js +0 -1
- package/dist/.next/static/chunks/app/sites/page-96a7dcf3c1e9aa19.js +0 -1
- package/dist/.next/static/css/94d302c686de7e23.css +0 -1
- /package/dist/.next/static/{da1c2e66999c334d135d9 → 4602553707d90d0fadeba}/_buildManifest.js +0 -0
- /package/dist/.next/static/{da1c2e66999c334d135d9 → 4602553707d90d0fadeba}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";exports.id=2817,exports.ids=[2817],exports.modules={52817:(a,b,c)=>{c.d(b,{GG:()=>o,_d:()=>p,xF:()=>q});var d=c(29021),e=c(33873),f=c.n(e),g=c(71011),h=c(1232),i=c(86587);function j(a){return"unsaved"===a.losable?0:a.losable?1:"expiry"===a.kind?a.entrusted&&!a.cost?1:2:"site"===a.kind?2:3}function k(a){return a?a.items.map((a,b)=>({it:a,i:b})).sort((a,b)=>Number(!!a.it.handled)-Number(!!b.it.handled)||j(a.it)-j(b.it)||Number("red"===b.it.tone)-Number("red"===a.it.tone)||(a.it.since??1/0)-(b.it.since??1/0)||a.i-b.i).map(a=>a.it):[]}var l=c(88804);function m(a){return`${a} project${1===a?"":"s"}`}function n(a=(0,g.yF)()){return f().join(a,"last-look.json")}function o(a){let b=a.now??Date.now(),c=a.attention,d=function(a,b){if(!a)return null;let c=(0,i.yy)(a);if(c)return{commits:0,files:0,projects:0,components:{unpushed:{commits:0,projects:0},unsaved:{files:0,projects:0},unbacked:{projects:0,commits:0}},unmeasured:[],empty:c,zero:!c};let d=new Map(b.map(a=>[a.path.toLowerCase(),a])),e=0,f=0,g=0,h=0,j=0,m=0,n=0,o=0,p=0,q=[];for(let b of a?k(a).filter(a=>!a.handled&&a.losable):[]){let a="project"===b.kind?d.get(b.id):void 0;if(!a)continue;let c=!1;a.statusKnown?a.uncommitted>0&&(m+=a.uncommitted,n++,f+=a.uncommitted,c=!0):q.push({name:a.name,why:`af360 could not read ${a.name}'s working tree, so its unsaved changes are not in this number`,whyP:(0,l.ph)("radius.unmeasured.status","af360 could not read {0}'s working tree, so its unsaved changes are not in this number",a.name)}),(0,i.ze)(a)?(o++,null!==a.commitCount?(p+=a.commitCount,e+=a.commitCount,c=!0):q.push({name:a.name,why:`af360 could not count ${a.name}'s commits, so its history is not in this number`,whyP:(0,l.ph)("radius.unmeasured.commits","af360 could not count {0}'s commits, so its history is not in this number",a.name)})):null!==a.ahead?a.ahead>0&&(h+=a.ahead,j++,e+=a.ahead,c=!0):a.isGit&&a.hasRemote&&q.push({name:a.name,why:`${a.name} has no upstream branch set, so af360 cannot tell which of its commits are only here`,whyP:(0,l.ph)("radius.unmeasured.upstream","{0} has no upstream branch set, so af360 cannot tell which of its commits are only here",a.name)}),c&&g++}for(let a of b)a.isGit||a.looksDownloaded||q.some(b=>b.name===a.name)||q.push({name:a.name,why:`${a.name} is not in git, so af360 has no way to tell whether a copy of it exists anywhere else`,whyP:(0,l.ph)("radius.unmeasured.noGit","{0} is not in git, so af360 has no way to tell whether a copy of it exists anywhere else",a.name)});return{commits:e,files:f,projects:g,components:{unpushed:{commits:h,projects:j},unsaved:{files:m,projects:n},unbacked:{projects:o,commits:p}},unmeasured:q,empty:!1,zero:0===e&&0===f}}(c,a.projects)??{commits:0,files:0,projects:0,components:{unpushed:{commits:0,projects:0},unsaved:{files:0,projects:0},unbacked:{projects:0,commits:0}},unmeasured:[],empty:!0,zero:!1},e=function(a){var b,c;if(a.empty)return{tone:"clean",headline:(0,l.pl)("radius.empty.head","Nothing to lose yet"),under:(0,l.pl)("radius.empty.under","Point af360 at a folder and it will tell you what only exists here.")};if(a.zero){let b=a.unmeasured.length;return{tone:"clean",headline:0===b?(0,l.pl)("radius.zero.head","If this laptop died right now: nothing would be lost."):(0,l.pl)("radius.zero.headPartial","Everything af360 could check has a copy elsewhere"),under:0===b?(0,l.pl)("radius.zero.under","Every project af360 watches also exists somewhere off this machine."):(0,l.ph)("radius.zero.underPartial","Nothing af360 could measure is only on this machine - and it could not measure {0}, so this does not speak for that work.",m(b))}}let d=[];a.commits>0&&d.push((b=a.commits,`${b} commit${1===b?"":"s"}`)),a.files>0&&d.push((c=a.files,`${c} file${1===c?"":"s"}`));let e=d.join(", ");return{tone:"amber",headline:(0,l.ph)("radius.head","If this laptop died right now: {0} across {1} - gone forever.",e,m(a.projects)),under:(0,l.pl)("radius.under","That work exists nowhere but this machine.")}}(d),f=function(a){if(0===a.unmeasured.length)return null;let b=a.unmeasured.length;return(0,l.ph)("radius.excluded","{0} not included: af360 could not measure {1}.",m(b),1===b?a.unmeasured[0].name:"them this time")}(d),g=c?(0,i.Pl)(c):null,h=k(c).map(a=>({id:a.id,kind:a.kind,tone:a.tone,label:a.label,what:a.what,why:a.why,consequence:function(a){let b=j(a);return b<=1?"losable":2===b?"money":"hygiene"}(a),losable:a.losable,handled:a.handled,since:a.since,steps:a.steps,devSteps:a.devSteps,path:"project"===a.kind?a.id:void 0})),n=c?.tones??{},o=a.projects.map(a=>({name:a.name,path:a.path,isGit:a.isGit,branch:a.branch,hasRemote:a.hasRemote,remoteKnown:a.remoteKnown,statusKnown:a.statusKnown,uncommitted:a.uncommitted,deleted:a.deleted,ahead:a.ahead,behind:a.behind,commitCount:a.commitCount,lastCommit:a.lastCommit,lastActivity:a.lastActivity,tone:n[a.path.toLowerCase()]??null})),p=a.sites.map(a=>({label:a.entry.label??a.entry.url,url:a.entry.url,ok:a.current?a.current.ok:null,status:a.current?.status,checkedAt:a.current?.ts??null,since:a.sinceText,tone:n[a.entry.id]??null})),q=a.expiry.map(a=>({id:a.id,name:a.name,date:a.date,daysLeft:a.daysLeft,source:a.manual?"entrusted":"observed",kind:a.kind,cost:a.cost,done:a.done,note:a.note})),r={scannedAt:a.scannedAt,projects:a.projects.slice(0,250).map(a=>({path:a.path,name:a.name,uncommitted:a.uncommitted,statusKnown:a.statusKnown,ahead:a.ahead,commitCount:a.commitCount,hasRemote:a.hasRemote,lastCommit:a.lastCommit})),capped:a.projects.length>250||void 0,sites:a.sites.map(a=>({url:a.entry.url,ok:a.current?a.current.ok:null})),radius:{commits:d.commits,files:d.files,projects:d.projects}},s=a.previous.filter(b=>b.scannedAt!==a.scannedAt);return s.push(r),s.sort((a,b)=>a.scannedAt-b.scannedAt),{v:2,scannedAt:a.scannedAt,ts:b,configFault:a.configFault,empty:!c||(0,i.yy)(c),estate:{state:c?.state??"QUIET",open:c?.open??0,line:g?.line??"",under:g?.under??"",notChecked:a.notChecked,counts:{projects:a.projects.length,sites:a.sites.length,findings:h.length},findings:h},projects:o,sites:p,ahead:q,radius:{...d,headline:e.headline.en,under:e.under.en,tone:e.tone,excluded:f?f.en:null},looks:s.slice(-40)}}async function p(a=(0,g.yF)()){try{let b=JSON.parse(await d.promises.readFile(n(a),"utf8"));if(!b||"object"!=typeof b||"number"!=typeof b.scannedAt)return null;return b}catch{return null}}async function q(a,b=(0,g.yF)()){try{await d.promises.mkdir(b,{recursive:!0});let c=(0,h.fI)(a),e=f().join(b,`.last-look.${process.pid}.tmp`);await d.promises.writeFile(e,JSON.stringify(c)+"\n","utf8"),await d.promises.rename(e,n(b))}catch{}}},86587:(a,b,c)=>{c.d(b,{Pl:()=>n,W8:()=>k,yy:()=>m,ze:()=>l});var d=c(72346),e=c(88804),f=c(87078),g=c(83720);function h(a){return{what:a.what.en,whatP:a.what,why:a.why.en,whyP:a.why,evidence:a.evidence.map(a=>a.en),evidenceP:a.evidence,steps:a.steps.map(a=>a.en),stepsP:a.steps}}function i(a){return{action:a.en,actionP:a}}function j(a){return a?{handledNote:a.en,handledNoteP:a}:{}}function k(a){let b=a=>Array.isArray(a)?a:[],c=b(a.projects),k=b(a.urls),m=b(a.expiryItems),n=b(a.incidents),r=b(a.missingRoots),s=b(a.ignored),{snoozes:t,isAcked:u,isDupe:v,offline:w}=a,x=Date.now(),y=a=>{let b=t?.[a];return!!b&&new Date(b).getTime()>x},z=[],A={};for(let a of c){let b=a.path.toLowerCase(),h=function(a,b,c){var h,i;let j=[],k=[],m=[],n=[];a.changedTotal>0&&(j.push((0,e.ph)("attn.reason.unsaved",`{0} unsaved change${1===a.changedTotal?"":"s"}`,a.changedTotal)),k.push((0,e.ph)("attn.ev.changed",`changed: {0}${a.changedTotal>a.dirtyFiles.length?" and {1} more":""}`,a.dirtyFiles.join(", "),a.changedTotal-a.dirtyFiles.length))),a.deleted>0&&(j.push((0,e.ph)("attn.reason.deleted",`{0} file${1===a.deleted?"":"s"} deleted`,a.deleted)),k.push((0,e.ph)("attn.ev.deleted",`deleted: {0}${a.deleted>a.deletedFiles.length?" and {1} more":""}`,a.deletedFiles.join(", "),a.deleted-a.deletedFiles.length))),a.uncommitted>0&&(m.push((0,e.pl)("attn.step.commit","Commit the work - a commit makes it permanent history.")),n.push('git add -A && git commit -m "wip"')),a.deleted>0&&(m.push((0,e.pl)("attn.step.deleted","If a deletion was not meant, git still holds the file - restore it before committing.")),n.push("git restore --")),(a.ahead??0)>0&&(j.push((0,e.ph)("attn.reason.unpushed",`{0} commit${1===a.ahead?"":"s"} not pushed`,a.ahead??0)),k.push((0,e.ph)("attn.ev.onlyHere",`{0} commit${1===a.ahead?"":"s"} exist only on this machine`,a.ahead??0)),m.push((0,e.pl)("attn.step.push","Push once - the remote gets a full copy and the risk is gone.")),n.push("git push")),l(a)&&(j.push((0,e.pl)("attn.reason.noBackup","no backup anywhere")),k.push((0,e.pl)("attn.ev.noRemote","no remote is configured - this disk holds the only copy")),m.push((0,e.pl)("attn.step.createRepo","Create an empty repository on your code host, connect it, and push.")),n.push("git remote add origin <url>","git push -u origin main"));let o=a.isGit&&!a.statusKnown;o&&(j.push((0,e.pl)("unread.reason",d.UI.unread.reason)),k.push((0,e.pl)("unread.evidence",d.UI.unread.evidence)),m.push((0,e.pl)("unread.step",d.UI.unread.step)));let p=b?function(a,b){let c=b.filter(b=>b.name===a.name&&b.path!==a.path);if(0===c.length)return null;let d=a.remoteKnown?(0,f.p8)(a.remoteUrl):null,e=a=>a.remoteKnown?(0,f.p8)(a.remoteUrl):null;if(d){let a=c.filter(a=>e(a)===d);if(a.length>0)return{kind:"duplicate",others:a}}return!d||c.some(a=>null===e(a))?{kind:"unproven",others:c}:{kind:"sharesName",others:c}}(a,c):null,q=p?.kind==="duplicate"?p:null,r=!!q&&("dormant"===a.staleness||"forgotten"===a.staleness);if(0===j.length&&r&&q){for(let a of(j.push((0,e.pl)("attn.reason.dupe","a dormant duplicate")),q.others))k.push((0,e.ph)("attn.ev.livingCopy","the living copy: {0}",a.path));let b=(0,f.p8)(a.remoteUrl);b&&k.push((0,e.ph)("attn.ev.sameRemote","both point at the same repository: {0}",b)),a.lastActivity&&(0,g.nW)(a.lastActivity)?k.push((0,e.pl)("attn.ev.untouchedSkew",`this copy untouched ${g.lt.since}`)):k.push((0,e.ph)("attn.ev.untouched","this copy untouched {0}",a.lastActivity?(h=a.lastActivity,(0,g.I)(h)):"for a long time")),m.push((0,e.pl)("attn.step.keepOne","Keep one copy - archive or delete the other so future-you isn't guessing."))}if(0===j.length)return{tone:"clean",what:"",why:"",evidence:[],steps:[]};let s=l(a),t=a.uncommitted>0?"unsaved":(a.ahead??0)>0?"unpushed":s?"unbacked":void 0,u=o&&1===j.length,v="unsaved"===t?(0,e.ph)("act.save","Save {0}",a.name):"unpushed"===t?(0,e.ph)("act.push","Push {0}",a.name):"unbacked"===t?(0,e.ph)("act.backUp","Back up {0}",a.name):u?(0,e.ph)("unread.what","af360 could not finish reading {0}.",a.name):(0,e.ph)("act.duplicate","Decide on {0}'s duplicate",a.name),w=(i=j.map(a=>a.en),(0,e.nn)(i,e.v1.and,e.v1.comma)),x=u?(0,e.ph)("unread.what","af360 could not finish reading {0}.",a.name):{...(0,e.ph)("attn.project.what","{0} has {1}.",a.name,w),list:{slot:1,items:j}},y=u?(0,e.pl)("unread.why",d.UI.unread.why):s?(0,e.pl)("attn.why.noBackup","One disk failure and it's gone - a remote copy fixes that in a minute."):(a.ahead??0)>0&&0===a.uncommitted?(0,e.pl)("attn.why.unpushed","A push costs a minute; losing them costs the work."):r&&0===a.uncommitted?(0,e.pl)("attn.why.dupe","Two copies of one project drift apart quietly."):(0,e.pl)("attn.why.unsaved","Unsaved work survives nowhere - one bad day and it's gone.");return k.push((0,e.ph)("attn.ev.path","{0}",`path: ${a.path}`)),{tone:"amber",what:x.en,why:y.en,whatP:x,whyP:y,since:a.lastActivity?new Date(a.lastActivity).getTime():void 0,evidence:k.map(a=>a.en),evidenceP:k,steps:m.map(a=>a.en),stepsP:m,devSteps:n.length>0?n:void 0,losable:t,action:v.en,actionP:v}}(a,v(a.name),c);A[b]=h.tone,"clean"!==h.tone&&z.push({...h,id:b,kind:"project",label:a.name,handled:y(b)||void 0,handledNote:y(b)?"quiet until tomorrow":void 0,openPath:a.path,laterId:b,ignore:{id:b,kind:"project",label:a.name,atRisk:l(a)}})}let B=a.github;if(B)for(let a of c){let b=B.byPath[a.path.toLowerCase()];if(!b?.ok)continue;let c=b.value;if(c.ci.ok&&c.ci.value&&"failing"===c.ci.value.state){let b=c.ci.value,d=`ci:${a.path.toLowerCase()}`;z.push({id:d,kind:"project",tone:"amber",label:a.name,since:b.at,...h({what:(0,e.ph)("attn.gh.ci.what","{0}'s build failed on {1}.",a.name,b.branch),why:(0,e.pl)("attn.gh.ci.why","GitHub Actions went red - the last push didn't survive CI."),evidence:[(0,e.ph)("attn.gh.ci.ev1","the {0} workflow reported {1}",b.workflow,b.conclusion??"a failure"),(0,e.ph)("attn.gh.ci.ev2","on branch {0}",b.branch),(0,e.ph)("attn.gh.ci.ev3","GitHub last moved this run at {0}",new Date(b.at).toISOString())],steps:[(0,e.pl)("attn.gh.ci.step1","Open the run on GitHub and read the first failing step - the rest usually follow from it."),(0,e.pl)("attn.gh.ci.step2","Nothing here is lost: the commits are pushed. This is about the checks, not the work.")]}),handled:y(d)||void 0,...j(y(d)?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),openPath:a.path,laterId:d,...i((0,e.ph)("act.gh.ci","Read {0}'s failing run on GitHub",a.name))})}if("refuted"===c.backup.verdict){let b=c.backup,d=`backup:${a.path.toLowerCase()}`;z.push({id:d,kind:"project",tone:"amber",label:a.name,losable:"unpushed",...h({what:(0,e.ph)("attn.gh.backup.what","{0} says everything is pushed, and GitHub doesn't have it.",a.name),why:(0,e.pl)("attn.gh.backup.why","Local git reports nothing to push and nothing to pull, so this machine and GitHub should be on the same commit. They aren't."),evidence:[(0,e.ph)("attn.gh.backup.ev1","this machine is on {0} at branch {1}",b.localSha.slice(0,12),b.branch),(0,e.ph)("attn.gh.backup.ev2","GitHub's {0} is at {1}",b.branch,b.remoteSha.slice(0,12)),(0,e.pl)("attn.gh.backup.ev3","af360 asked both ends this scan - this is not read from local git's own record of the remote")],steps:[(0,e.pl)("attn.gh.backup.step1","Fetch from the remote and look again - the usual cause is that this machine's record of the remote is out of date."),(0,e.pl)("attn.gh.backup.step2","If the commits really are only here, push them. Until then treat this project as not backed up.")]}),devSteps:["git fetch --all","git status -sb","git log --oneline -1 HEAD"],handled:y(d)||void 0,...j(y(d)?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),openPath:a.path,laterId:d,...i((0,e.ph)("act.gh.backup","Check where {0}'s commits actually are",a.name))})}}if(w&&k.length>0){for(let a of k)A[a.entry.id]="clean";z.push({id:"offline",kind:"site",tone:"amber",label:"Internet connection",...h({what:(0,e.pl)("attn.offline.what","This computer seems to be offline."),why:(0,e.pl)("attn.offline.why","Your sites may be perfectly fine - af360 can't reach anything right now, so it won't guess."),evidence:[(0,e.ph)("attn.offline.ev1","none of the {0} watched sites answered",k.length),(0,e.pl)("attn.offline.ev2","the domain lookup didn't get through either"),(0,e.pl)("attn.offline.ev3","checks keep running while af360 is open; this clears itself the moment something answers")],steps:[(0,e.pl)("attn.offline.step1","Check this machine's internet connection - wifi, cable, or a VPN that dropped."),(0,e.pl)("attn.offline.step2","Nothing to do here once it's back: af360 rechecks every minute while it is open, and clears this on its own.")]}),devSteps:["ping 1.1.1.1","nslookup example.com"],...i((0,e.pl)("act.offline","Check this machine's connection"))})}for(let a of w?[]:k){let b=a.entry.id,c=a.current,d=a.entry.label||function(a){try{return new URL(a).hostname}catch{return a}}(a.entry.url);if(!c||c.ok){A[b]="clean";continue}A[b]="red";let f=n.find(a=>a.subjectId===b&&!a.endedTs),g=u(b),k=y(b);z.push({id:b,kind:"site",tone:"red",label:d,...h({what:c.status?(0,e.ph)("attn.down.whatStatus","{0} is down (HTTP {1}).",d,c.status):c.error?(0,e.ph)("attn.down.whatError","{0} is down - {1}.",d,c.error):(0,e.ph)("attn.down.what","{0} is down.",d),why:(0,e.pl)("attn.down.why","af360 asked for your site from this machine and got an error back instead of the page."),evidence:[(0,e.ph)("attn.down.evUrl","{0}",a.entry.url),c.status?(0,e.ph)("attn.down.evAnswer","last answer: HTTP {0}",c.status):c.error?(0,e.ph)("attn.down.evAnswerText","last answer: {0}",c.error):(0,e.pl)("attn.down.evNoAnswer","no answer"),f?(0,e.ph)("attn.down.evSince","down since {0}",new Date(f.startedTs).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"})):(0,e.pl)("attn.down.evJust","just went quiet"),(0,e.pl)("attn.down.evCadence","checked every minute while af360 is open; recovery is logged automatically")],steps:[(0,e.pl)("attn.down.step1","Open your host's dashboard - most outages are a crashed service or a failed deploy."),(0,e.pl)("attn.down.step2","Press Check once you're on it; the row calms down and clears itself when the site answers again.")]}),since:f?.startedTs,handled:g||k||void 0,...j(g?(0,e.pl)("attn.handled.acked","being handled ✓"):k?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),devSteps:[`curl -sI ${a.entry.url}`],...i((0,e.ph)("act.siteDown","{0} is down",d)),checkId:b,laterId:b,ignore:{id:b,kind:"url",label:d,atRisk:!0}})}for(let a of s){if(!a.resurface)continue;let b=`resurface:${a.id}`;A[b]="amber",z.push({id:b,kind:"resurface",tone:"amber",label:a.label,...h({what:(0,e.ph)("attn.resurface.what","{0}",a.resurface.text),why:(0,e.pl)("attn.resurface.why","You silenced this earlier - the situation moved, so it gets one more look."),evidence:[(0,e.ph)("attn.resurface.evThen","ignored at: {0}",o(a.snapshot)),(0,e.ph)("attn.resurface.evNow","now: {0}",o(a.resurface.current))],steps:[(0,e.pl)("attn.resurface.step","Watch again to bring it back, or Still ignore to re-silence it at today's numbers.")]}),...i((0,e.ph)("act.decide","Decide on {0}",a.label)),resurfaceId:a.id,ignore:{id:a.id,kind:a.kind,label:a.label,atRisk:!1}})}for(let a of m){if(a.manual){if(a.done||null===a.rung||void 0===a.rung){A[a.id]="clean";continue}A[a.id]="amber";let b=y(a.id);z.push({id:a.id,kind:"expiry",tone:"amber",label:a.name,entrusted:!0,cost:a.cost,passed:a.passed,entrustedId:a.id,...h({what:a.passed?(0,e.ph)("attn.entrusted.passed","{0} was due {1}.",a.name,q(a.daysLeft)):(0,e.ph)("attn.entrusted.what","{0} is due {1}.",a.name,q(a.daysLeft)),why:a.note?(0,e.ph)("attn.entrusted.userNote","{0}",a.note):(0,e.pl)("attn.entrusted.why","You asked af360 to tell you about this one."),evidence:[(0,e.pl)("attn.entrusted.evAdded","you added this date"),(0,e.ph)("attn.expiry.evDate","date: {0}",new Date(a.date).toLocaleDateString()),a.note?(0,e.ph)("attn.entrusted.evNote",'what you wrote: "{0}"',a.note):null].filter(a=>null!==a),steps:[(0,e.pl)("attn.entrusted.step","Deal with it, or open Ahead to change the date, mark it done or remove it.")]}),handled:b||void 0,...j(b?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),...i((0,e.ph)("act.entrusted","Deal with {0}",a.name)),laterId:a.id});continue}let b="red"===a.chip||"amber"===a.chip&&"ssl"===a.kind&&a.managed;if(A[a.id]=b?"amber":"clean",!b)continue;let c=y(a.id);z.push({id:a.id,kind:"expiry",tone:"amber",label:a.name,...h({what:"ssl"===a.kind&&a.managed&&"red"!==a.chip?(0,e.ph)("attn.expiry.certLate","{0}'s certificate should have auto-renewed by now - it hasn't.",a.name):a.daysLeft<0?(0,e.ph)("attn.expiry.expired","{0} expired {1}.",a.name,q(a.daysLeft)):(0,e.ph)("attn.expiry.expires","{0} expires {1}.",a.name,q(a.daysLeft)),why:a.note?(0,e.ph)("attn.expiry.userNote","{0}",a.note):"ssl"===a.kind?(0,e.pl)("attn.expiry.whySsl","When it lapses, browsers show a scary warning instead of your site."):(0,e.pl)("attn.expiry.whyDomain","After the date it stops working - renewing early is painless."),evidence:[(0,e.ph)("attn.expiry.evDate","date: {0}",new Date(a.date).toLocaleDateString()),a.judgment?(0,e.ph)("attn.expiry.evJudgment","{0}",a.judgment):a.note?(0,e.ph)("attn.expiry.evNote",'your note: "{0}"',a.note):null,a.issuer?(0,e.ph)("attn.expiry.evIssuer","issuer: {0}",a.issuer):null].filter(a=>null!==a),steps:"ssl"===a.kind?[(0,e.pl)("attn.expiry.stepSsl","Ask your host why the renewal hasn't landed - managed certificates renew on their own, so a late one means something is stuck.")]:[(0,e.pl)("attn.expiry.stepDomain","Renew it (or let it lapse on purpose), then remove or update the entry on Ahead.")]}),handled:c||void 0,...j(c?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),...i("ssl"===a.kind&&a.managed&&"red"!==a.chip?(0,e.ph)("act.certLate","Chase {0}'s certificate",a.name):(0,e.ph)("act.renew","Renew {0}",a.name)),laterId:a.id})}for(let a of r){let b=`missing:${a}`;A[b]="amber",z.push({id:b,kind:"missing",tone:"amber",label:p(a),...h({what:(0,e.ph)("attn.missing.what","The folder {0} can't be found",a),why:(0,e.pl)("attn.missing.why","It moved or was renamed - point me at its new home, or stop watching it."),evidence:[(0,e.ph)("attn.missing.evPath","watched path: {0}",a),(0,e.pl)("attn.missing.evScan","the folder stopped answering on the last scan")],steps:[(0,e.pl)("attn.missing.step1","Press Find it and click through to where the folder lives now - the old entry is replaced."),(0,e.pl)("attn.missing.step2","If it's gone on purpose, Stop watching removes it.")]}),...i((0,e.ph)("act.find","Find {0}",p(a))),findPath:a})}z.sort((a,b)=>Number(a.handled??!1)-Number(b.handled??!1)||Number("red"===b.tone)-Number("red"===a.tone));let C=z.filter(a=>!a.handled).length,D=z.some(a=>"red"===a.tone&&!a.handled),E=b(a.findings).filter(a=>a?.cat==="safety").length,F=0;if(B){for(let a of c){let b=B.byPath[a.path.toLowerCase()];b&&!b.ok&&F++}B.outage&&F++}return F+=b(a.unreachedChecks).length,{state:D?"INCIDENT":C>0?"NEEDS_YOU":E>0?"SAFETY":"QUIET",open:C,items:z,tones:A,unreached:F+=new Set(b(a.unscannedSubjects)).size,safety:E}}function l(a){return a.isGit&&a.remoteKnown&&!a.hasRemote&&!a.looksDownloaded}function m(a){return 0===a.items.length&&0===Object.keys(a.tones).length}function n(a){let b=(a,b)=>({line:a.en,under:b.en,lineP:a,underP:b});if(m(a))return b((0,e.pl)("head.emptyLine","Nothing watched yet"),(0,e.pl)("head.emptyUnder","Point me at a folder and I'll show you the projects inside it."));if("INCIDENT"===a.state){let c=a.items.filter(a=>"red"===a.tone&&!a.handled);return b(1===c.length?(0,e.ph)("head.downOne","{0} is down",c[0].label):(0,e.ph)("head.downMany","{0} down",c.length),1===c.length?(0,e.pl)("head.incidentUnder","everything else can wait - this is the one to look at"):(0,e.pl)("head.incidentUnderMany","everything else can wait - these are the ones to look at"))}if("NEEDS_YOU"===a.state)return a.safety>0?b((0,e.ph)("head.needsLine",`{0} need${1===a.open?"s":""} you`,a.open),(0,e.ph)("head.needsUnderSafety",`none of it is on fire - each has a reason, and {0} safety finding${1===a.safety?" is":"s are"} waiting in Discoveries`,a.safety)):b((0,e.ph)("head.needsLine",`{0} need${1===a.open?"s":""} you`,a.open),(0,e.pl)("head.needsUnder","none of it is on fire - each has a reason"));let c=a.items.filter(a=>a.handled).length,d=a.unreached??0,f=a=>`thing${1===a?"":"s"}`;if("SAFETY"===a.state){let c=a.safety;return b((0,e.ph)("head.safetyLine",`{0} safety finding${1===c?"":"s"}`,c),d>0?(0,e.ph)("head.safetyMissed",`${1===c?"it is":"they are"} waiting in Discoveries - and af360 couldn't check {0} ${f(d)} this time`,d):(0,e.pl)("head.safetyUnder",`${1===c?"it is":"they are"} waiting in Discoveries`))}return b((0,e.pl)("head.quietLine","All quiet"),c>0?d>0?(0,e.ph)("head.quietHandledMissed",`nothing new needs you - {0} item${1===c?" is":"s are"} being handled, and af360 couldn't check {1} ${f(d)} this time`,c,d):(0,e.ph)("head.quietHandled",`nothing new needs you - {0} item${1===c?" is":"s are"} being handled`,c):d>0?(0,e.ph)("head.quietMissed",`nothing needs you - and af360 couldn't check {0} ${f(d)} this time`,d):(0,e.pl)("head.quietUnder","nothing needs you - go build something"))}function o(a){if(!a)return"nothing recorded";let b=[];return"number"==typeof a.uncommitted&&b.push(`${a.uncommitted} unsaved`),!1===a.hasRemote&&b.push("no backup"),a.down&&b.push("down"),b.length>0?b.join(", "):"all fine"}function p(a){let b=a.split(/[\\/]/).filter(Boolean);return b[b.length-1]??a}function q(a){return a<0?`${-a} day${-1===a?"":"s"} ago`:0===a?"today":1===a?"tomorrow":`in ${a} days`}}};
|
|
1
|
+
"use strict";exports.id=2817,exports.ids=[2817],exports.modules={52817:(a,b,c)=>{c.d(b,{GG:()=>o,_d:()=>p,eN:()=>r,xF:()=>q});var d=c(29021),e=c(33873),f=c.n(e),g=c(71011),h=c(1232),i=c(86587);function j(a){return"unsaved"===a.losable?0:a.losable?1:"expiry"===a.kind?a.entrusted&&!a.cost?1:2:"site"===a.kind?2:3}function k(a){return a?a.items.map((a,b)=>({it:a,i:b})).sort((a,b)=>Number(!!a.it.handled)-Number(!!b.it.handled)||j(a.it)-j(b.it)||Number("red"===b.it.tone)-Number("red"===a.it.tone)||(a.it.since??1/0)-(b.it.since??1/0)||a.i-b.i).map(a=>a.it):[]}var l=c(88804);function m(a){return`${a} project${1===a?"":"s"}`}function n(a=(0,g.yF)()){return f().join(a,"last-look.json")}function o(a){let b=a.now??Date.now(),c=a.attention,d=function(a,b){if(!a)return null;let c=(0,i.yy)(a);if(c)return{commits:0,files:0,projects:0,components:{unpushed:{commits:0,projects:0},unsaved:{files:0,projects:0},unbacked:{projects:0,commits:0}},unmeasured:[],empty:c,zero:!c};let d=new Map(b.map(a=>[a.path.toLowerCase(),a])),e=0,f=0,g=0,h=0,j=0,m=0,n=0,o=0,p=0,q=[];for(let b of a?k(a).filter(a=>!a.handled&&a.losable):[]){let a="project"===b.kind?d.get(b.id):void 0;if(!a)continue;let c=!1;a.statusKnown?a.uncommitted>0&&(m+=a.uncommitted,n++,f+=a.uncommitted,c=!0):q.push({name:a.name,why:`af360 could not read ${a.name}'s working tree, so its unsaved changes are not in this number`,whyP:(0,l.ph)("radius.unmeasured.status","af360 could not read {0}'s working tree, so its unsaved changes are not in this number",a.name)}),(0,i.ze)(a)?(o++,null!==a.commitCount?(p+=a.commitCount,e+=a.commitCount,c=!0):q.push({name:a.name,why:`af360 could not count ${a.name}'s commits, so its history is not in this number`,whyP:(0,l.ph)("radius.unmeasured.commits","af360 could not count {0}'s commits, so its history is not in this number",a.name)})):null!==a.ahead?a.ahead>0&&(h+=a.ahead,j++,e+=a.ahead,c=!0):a.isGit&&a.hasRemote&&q.push({name:a.name,why:`${a.name} has no upstream branch set, so af360 cannot tell which of its commits are only here`,whyP:(0,l.ph)("radius.unmeasured.upstream","{0} has no upstream branch set, so af360 cannot tell which of its commits are only here",a.name)}),c&&g++}for(let a of b)a.isGit||a.looksDownloaded||q.some(b=>b.name===a.name)||q.push({name:a.name,why:`${a.name} is not in git, so af360 has no way to tell whether a copy of it exists anywhere else`,whyP:(0,l.ph)("radius.unmeasured.noGit","{0} is not in git, so af360 has no way to tell whether a copy of it exists anywhere else",a.name)});return{commits:e,files:f,projects:g,components:{unpushed:{commits:h,projects:j},unsaved:{files:m,projects:n},unbacked:{projects:o,commits:p}},unmeasured:q,empty:!1,zero:0===e&&0===f}}(c,a.projects)??{commits:0,files:0,projects:0,components:{unpushed:{commits:0,projects:0},unsaved:{files:0,projects:0},unbacked:{projects:0,commits:0}},unmeasured:[],empty:!0,zero:!1},e=function(a){var b,c;if(a.empty)return{tone:"clean",headline:(0,l.pl)("radius.empty.head","Nothing to lose yet"),under:(0,l.pl)("radius.empty.under","Point af360 at a folder and it will tell you what only exists here.")};if(a.zero){let b=a.unmeasured.length;return{tone:"clean",headline:0===b?(0,l.pl)("radius.zero.head","Work that exists only on this machine: none."):(0,l.pl)("radius.zero.headPartial","Everything af360 could check has a copy elsewhere"),under:0===b?(0,l.pl)("radius.zero.under","Every project af360 watches also exists somewhere off this machine."):(0,l.ph)("radius.zero.underPartial","Nothing af360 could measure is only on this machine - and it could not measure {0}, so this does not speak for that work.",m(b))}}let d=[];a.commits>0&&d.push((b=a.commits,`${b} commit${1===b?"":"s"}`)),a.files>0&&d.push((c=a.files,`${c} file${1===c?"":"s"}`));let e=d.join(", ");return{tone:"amber",headline:(0,l.ph)("radius.head","Work that exists only on this machine: {0} across {1}.",e,m(a.projects)),under:(0,l.pl)("radius.under","af360 found no copy of it anywhere off this machine.")}}(d),f=function(a){if(0===a.unmeasured.length)return null;let b=a.unmeasured.length;return(0,l.ph)("radius.excluded","{0} not included: af360 could not measure {1}.",m(b),1===b?a.unmeasured[0].name:"them this time")}(d),g=c?(0,i.Pl)(c):null,h=k(c).map(a=>({id:a.id,kind:a.kind,tone:a.tone,label:a.label,what:a.what,why:a.why,consequence:function(a){let b=j(a);return b<=1?"losable":2===b?"money":"hygiene"}(a),losable:a.losable,handled:a.handled,since:a.since,steps:a.steps,devSteps:a.devSteps,path:"project"===a.kind?a.id:void 0})),n=c?.tones??{},o=a.projects.map(a=>({name:a.name,path:a.path,isGit:a.isGit,branch:a.branch,hasRemote:a.hasRemote,remoteKnown:a.remoteKnown,statusKnown:a.statusKnown,uncommitted:a.uncommitted,deleted:a.deleted,ahead:a.ahead,behind:a.behind,commitCount:a.commitCount,lastCommit:a.lastCommit,lastActivity:a.lastActivity,tone:n[a.path.toLowerCase()]??null})),p=a.sites.map(a=>({label:a.entry.label??a.entry.url,url:a.entry.url,ok:a.current?a.current.ok:null,status:a.current?.status,checkedAt:a.current?.ts??null,since:a.sinceText,tone:n[a.entry.id]??null})),q=a.expiry.map(a=>({id:a.id,name:a.name,date:a.date,daysLeft:a.daysLeft,source:a.manual?"entrusted":"observed",kind:a.kind,cost:a.cost,done:a.done,note:a.note})),r={scannedAt:a.scannedAt,projects:a.projects.slice(0,250).map(a=>({path:a.path,name:a.name,uncommitted:a.uncommitted,statusKnown:a.statusKnown,ahead:a.ahead,commitCount:a.commitCount,hasRemote:a.hasRemote,lastCommit:a.lastCommit})),capped:a.projects.length>250||void 0,sites:a.sites.map(a=>({url:a.entry.url,ok:a.current?a.current.ok:null})),radius:{commits:d.commits,files:d.files,projects:d.projects}},s=a.previous.filter(b=>b.scannedAt!==a.scannedAt);return s.push(r),s.sort((a,b)=>a.scannedAt-b.scannedAt),{v:2,scannedAt:a.scannedAt,ts:b,configFault:a.configFault,empty:!c||(0,i.yy)(c),estate:{state:c?.state??"QUIET",open:c?.open??0,line:g?.line??"",under:g?.under??"",notChecked:a.notChecked,counts:{projects:a.projects.length,sites:a.sites.length,findings:h.length},findings:h},projects:o,sites:p,ahead:q,radius:{...d,headline:e.headline.en,under:e.under.en,tone:e.tone,excluded:f?f.en:null},looks:s.slice(-40),discoveries:a.discoveries}}async function p(a=(0,g.yF)()){try{let b=JSON.parse(await d.promises.readFile(n(a),"utf8"));if(!b||"object"!=typeof b||"number"!=typeof b.scannedAt)return null;return b}catch{return null}}async function q(a,b=(0,g.yF)()){try{await d.promises.mkdir(b,{recursive:!0});let c=(0,h.fI)(a),e=f().join(b,`.last-look.${process.pid}.tmp`);await d.promises.writeFile(e,JSON.stringify(c)+"\n","utf8"),await d.promises.rename(e,n(b))}catch{}}async function r(a,b=(0,g.yF)()){try{let c=await p(b);if(!c)return;let e={...c,discoveries:(0,h.fI)(a)};await d.promises.mkdir(b,{recursive:!0});let g=f().join(b,`.last-look.${process.pid}.tmp`);await d.promises.writeFile(g,JSON.stringify(e)+"\n","utf8"),await d.promises.rename(g,n(b))}catch{}}},86587:(a,b,c)=>{c.d(b,{Pl:()=>n,W8:()=>k,yy:()=>m,ze:()=>l});var d=c(72346),e=c(88804),f=c(87078),g=c(83720);function h(a){return{what:a.what.en,whatP:a.what,why:a.why.en,whyP:a.why,evidence:a.evidence.map(a=>a.en),evidenceP:a.evidence,steps:a.steps.map(a=>a.en),stepsP:a.steps}}function i(a){return{action:a.en,actionP:a}}function j(a){return a?{handledNote:a.en,handledNoteP:a}:{}}function k(a){let b=a=>Array.isArray(a)?a:[],c=b(a.projects),k=b(a.urls),m=b(a.expiryItems),n=b(a.incidents),r=b(a.missingRoots),s=b(a.ignored),{snoozes:t,isAcked:u,isDupe:v,offline:w}=a,x=Date.now(),y=a=>{let b=t?.[a];return!!b&&new Date(b).getTime()>x},z=[],A={};for(let a of c){let b=a.path.toLowerCase(),h=function(a,b,c){var h,i;let j=[],k=[],m=[],n=[];a.changedTotal>0&&(j.push((0,e.ph)("attn.reason.unsaved",`{0} unsaved change${1===a.changedTotal?"":"s"}`,a.changedTotal)),k.push((0,e.ph)("attn.ev.changed",`changed: {0}${a.changedTotal>a.dirtyFiles.length?" and {1} more":""}`,a.dirtyFiles.join(", "),a.changedTotal-a.dirtyFiles.length))),a.deleted>0&&(j.push((0,e.ph)("attn.reason.deleted",`{0} file${1===a.deleted?"":"s"} deleted`,a.deleted)),k.push((0,e.ph)("attn.ev.deleted",`deleted: {0}${a.deleted>a.deletedFiles.length?" and {1} more":""}`,a.deletedFiles.join(", "),a.deleted-a.deletedFiles.length))),a.uncommitted>0&&(m.push((0,e.pl)("attn.step.commit","Commit the work - a commit makes it permanent history.")),n.push('git add -A && git commit -m "wip"')),a.deleted>0&&(m.push((0,e.pl)("attn.step.deleted","If a deletion was not meant, git still holds the file - restore it before committing.")),n.push("git restore --")),(a.ahead??0)>0&&(j.push((0,e.ph)("attn.reason.unpushed",`{0} commit${1===a.ahead?"":"s"} not pushed`,a.ahead??0)),k.push((0,e.ph)("attn.ev.onlyHere",`{0} commit${1===a.ahead?"":"s"} exist only on this machine`,a.ahead??0)),m.push((0,e.pl)("attn.step.push","Push once - the remote gets a full copy and the risk is gone.")),n.push("git push")),l(a)&&(j.push((0,e.pl)("attn.reason.noBackup","no backup anywhere")),k.push((0,e.pl)("attn.ev.noRemote","no remote is configured - this disk holds the only copy")),m.push((0,e.pl)("attn.step.createRepo","Create an empty repository on your code host, connect it, and push.")),n.push("git remote add origin <url>","git push -u origin main"));let o=a.isGit&&!a.statusKnown;o&&(j.push((0,e.pl)("unread.reason",d.UI.unread.reason)),k.push((0,e.pl)("unread.evidence",d.UI.unread.evidence)),m.push((0,e.pl)("unread.step",d.UI.unread.step)));let p=b?function(a,b){let c=b.filter(b=>b.name===a.name&&b.path!==a.path);if(0===c.length)return null;let d=a.remoteKnown?(0,f.p8)(a.remoteUrl):null,e=a=>a.remoteKnown?(0,f.p8)(a.remoteUrl):null;if(d){let a=c.filter(a=>e(a)===d);if(a.length>0)return{kind:"duplicate",others:a}}return!d||c.some(a=>null===e(a))?{kind:"unproven",others:c}:{kind:"sharesName",others:c}}(a,c):null,q=p?.kind==="duplicate"?p:null,r=!!q&&("dormant"===a.staleness||"forgotten"===a.staleness);if(0===j.length&&r&&q){for(let a of(j.push((0,e.pl)("attn.reason.dupe","a dormant duplicate")),q.others))k.push((0,e.ph)("attn.ev.livingCopy","the living copy: {0}",a.path));let b=(0,f.p8)(a.remoteUrl);b&&k.push((0,e.ph)("attn.ev.sameRemote","both point at the same repository: {0}",b)),a.lastActivity&&(0,g.nW)(a.lastActivity)?k.push((0,e.pl)("attn.ev.untouchedSkew",`this copy untouched ${g.lt.since}`)):k.push((0,e.ph)("attn.ev.untouched","this copy untouched {0}",a.lastActivity?(h=a.lastActivity,(0,g.I)(h)):"for a long time")),m.push((0,e.pl)("attn.step.keepOne","Keep one copy - archive or delete the other so future-you isn't guessing."))}if(0===j.length)return{tone:"clean",what:"",why:"",evidence:[],steps:[]};let s=l(a),t=a.uncommitted>0?"unsaved":(a.ahead??0)>0?"unpushed":s?"unbacked":void 0,u=o&&1===j.length,v="unsaved"===t?(0,e.ph)("act.save","Save {0}",a.name):"unpushed"===t?(0,e.ph)("act.push","Push {0}",a.name):"unbacked"===t?(0,e.ph)("act.backUp","Back up {0}",a.name):u?(0,e.ph)("unread.what","af360 could not finish reading {0}.",a.name):(0,e.ph)("act.duplicate","Decide on {0}'s duplicate",a.name),w=(i=j.map(a=>a.en),(0,e.nn)(i,e.v1.and,e.v1.comma)),x=u?(0,e.ph)("unread.what","af360 could not finish reading {0}.",a.name):{...(0,e.ph)("attn.project.what","{0} has {1}.",a.name,w),list:{slot:1,items:j}},y=u?(0,e.pl)("unread.why",d.UI.unread.why):s?(0,e.pl)("attn.why.noBackup","One disk failure and it's gone - a remote copy fixes that in a minute."):(a.ahead??0)>0&&0===a.uncommitted?(0,e.pl)("attn.why.unpushed","A push costs a minute; losing them costs the work."):r&&0===a.uncommitted?(0,e.pl)("attn.why.dupe","Two copies of one project drift apart quietly."):(0,e.pl)("attn.why.unsaved","Unsaved work survives nowhere - one bad day and it's gone.");return k.push((0,e.ph)("attn.ev.path","{0}",`path: ${a.path}`)),{tone:"amber",what:x.en,why:y.en,whatP:x,whyP:y,since:a.lastActivity?new Date(a.lastActivity).getTime():void 0,evidence:k.map(a=>a.en),evidenceP:k,steps:m.map(a=>a.en),stepsP:m,devSteps:n.length>0?n:void 0,losable:t,action:v.en,actionP:v}}(a,v(a.name),c);A[b]=h.tone,"clean"!==h.tone&&z.push({...h,id:b,kind:"project",label:a.name,handled:y(b)||void 0,handledNote:y(b)?"quiet until tomorrow":void 0,openPath:a.path,laterId:b,ignore:{id:b,kind:"project",label:a.name,atRisk:l(a)}})}let B=a.github;if(B)for(let a of c){let b=B.byPath[a.path.toLowerCase()];if(!b?.ok)continue;let c=b.value;if(c.ci.ok&&c.ci.value&&"failing"===c.ci.value.state){let b=c.ci.value,d=`ci:${a.path.toLowerCase()}`;z.push({id:d,kind:"project",tone:"amber",label:a.name,since:b.at,...h({what:(0,e.ph)("attn.gh.ci.what","{0}'s build failed on {1}.",a.name,b.branch),why:(0,e.pl)("attn.gh.ci.why","GitHub Actions went red - the last push didn't survive CI."),evidence:[(0,e.ph)("attn.gh.ci.ev1","the {0} workflow reported {1}",b.workflow,b.conclusion??"a failure"),(0,e.ph)("attn.gh.ci.ev2","on branch {0}",b.branch),(0,e.ph)("attn.gh.ci.ev3","GitHub last moved this run at {0}",new Date(b.at).toISOString())],steps:[(0,e.pl)("attn.gh.ci.step1","Open the run on GitHub and read the first failing step - the rest usually follow from it."),(0,e.pl)("attn.gh.ci.step2","Nothing here is lost: the commits are pushed. This is about the checks, not the work.")]}),handled:y(d)||void 0,...j(y(d)?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),openPath:a.path,laterId:d,...i((0,e.ph)("act.gh.ci","Read {0}'s failing run on GitHub",a.name))})}if("refuted"===c.backup.verdict){let b=c.backup,d=`backup:${a.path.toLowerCase()}`;z.push({id:d,kind:"project",tone:"amber",label:a.name,losable:"unpushed",...h({what:(0,e.ph)("attn.gh.backup.what","{0} says everything is pushed, and GitHub doesn't have it.",a.name),why:(0,e.pl)("attn.gh.backup.why","Local git reports nothing to push and nothing to pull, so this machine and GitHub should be on the same commit. They aren't."),evidence:[(0,e.ph)("attn.gh.backup.ev1","this machine is on {0} at branch {1}",b.localSha.slice(0,12),b.branch),(0,e.ph)("attn.gh.backup.ev2","GitHub's {0} is at {1}",b.branch,b.remoteSha.slice(0,12)),(0,e.pl)("attn.gh.backup.ev3","af360 asked both ends this scan - this is not read from local git's own record of the remote")],steps:[(0,e.pl)("attn.gh.backup.step1","Fetch from the remote and look again - the usual cause is that this machine's record of the remote is out of date."),(0,e.pl)("attn.gh.backup.step2","If the commits really are only here, push them. Until then treat this project as not backed up.")]}),devSteps:["git fetch --all","git status -sb","git log --oneline -1 HEAD"],handled:y(d)||void 0,...j(y(d)?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),openPath:a.path,laterId:d,...i((0,e.ph)("act.gh.backup","Check where {0}'s commits actually are",a.name))})}}if(w&&k.length>0){for(let a of k)A[a.entry.id]="clean";z.push({id:"offline",kind:"site",tone:"amber",label:"Internet connection",...h({what:(0,e.pl)("attn.offline.what","This computer seems to be offline."),why:(0,e.pl)("attn.offline.why","Your sites may be perfectly fine - af360 can't reach anything right now, so it won't guess."),evidence:[(0,e.ph)("attn.offline.ev1","none of the {0} watched sites answered",k.length),(0,e.pl)("attn.offline.ev2","the domain lookup didn't get through either"),(0,e.pl)("attn.offline.ev3","checks keep running while af360 is open; this clears itself the moment something answers")],steps:[(0,e.pl)("attn.offline.step1","Check this machine's internet connection - wifi, cable, or a VPN that dropped."),(0,e.pl)("attn.offline.step2","Nothing to do here once it's back: af360 rechecks every minute while it is open, and clears this on its own.")]}),devSteps:["ping 1.1.1.1","nslookup example.com"],...i((0,e.pl)("act.offline","Check this machine's connection"))})}for(let a of w?[]:k){let b=a.entry.id,c=a.current,d=a.entry.label||function(a){try{return new URL(a).hostname}catch{return a}}(a.entry.url);if(!c||c.ok){A[b]="clean";continue}A[b]="red";let f=n.find(a=>a.subjectId===b&&!a.endedTs),g=u(b),k=y(b);z.push({id:b,kind:"site",tone:"red",label:d,...h({what:c.status?(0,e.ph)("attn.down.whatStatus","{0} is down (HTTP {1}).",d,c.status):c.error?(0,e.ph)("attn.down.whatError","{0} is down - {1}.",d,c.error):(0,e.ph)("attn.down.what","{0} is down.",d),why:(0,e.pl)("attn.down.why","af360 asked for your site from this machine and got an error back instead of the page."),evidence:[(0,e.ph)("attn.down.evUrl","{0}",a.entry.url),c.status?(0,e.ph)("attn.down.evAnswer","last answer: HTTP {0}",c.status):c.error?(0,e.ph)("attn.down.evAnswerText","last answer: {0}",c.error):(0,e.pl)("attn.down.evNoAnswer","no answer"),f?(0,e.ph)("attn.down.evSince","down since {0}",new Date(f.startedTs).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"})):(0,e.pl)("attn.down.evJust","just went quiet"),(0,e.pl)("attn.down.evCadence","checked every minute while af360 is open; recovery is logged automatically")],steps:[(0,e.pl)("attn.down.step1","Open your host's dashboard - most outages are a crashed service or a failed deploy."),(0,e.pl)("attn.down.step2","Press Check once you're on it; the row calms down and clears itself when the site answers again.")]}),since:f?.startedTs,handled:g||k||void 0,...j(g?(0,e.pl)("attn.handled.acked","being handled ✓"):k?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),devSteps:[`curl -sI ${a.entry.url}`],...i((0,e.ph)("act.siteDown","{0} is down",d)),checkId:b,laterId:b,ignore:{id:b,kind:"url",label:d,atRisk:!0}})}for(let a of s){if(!a.resurface)continue;let b=`resurface:${a.id}`;A[b]="amber",z.push({id:b,kind:"resurface",tone:"amber",label:a.label,...h({what:(0,e.ph)("attn.resurface.what","{0}",a.resurface.text),why:(0,e.pl)("attn.resurface.why","You silenced this earlier - the situation moved, so it gets one more look."),evidence:[(0,e.ph)("attn.resurface.evThen","ignored at: {0}",o(a.snapshot)),(0,e.ph)("attn.resurface.evNow","now: {0}",o(a.resurface.current))],steps:[(0,e.pl)("attn.resurface.step","Watch again to bring it back, or Still ignore to re-silence it at today's numbers.")]}),...i((0,e.ph)("act.decide","Decide on {0}",a.label)),resurfaceId:a.id,ignore:{id:a.id,kind:a.kind,label:a.label,atRisk:!1}})}for(let a of m){if(a.manual){if(a.done||null===a.rung||void 0===a.rung){A[a.id]="clean";continue}A[a.id]="amber";let b=y(a.id);z.push({id:a.id,kind:"expiry",tone:"amber",label:a.name,entrusted:!0,cost:a.cost,passed:a.passed,entrustedId:a.id,...h({what:a.passed?(0,e.ph)("attn.entrusted.passed","{0} was due {1}.",a.name,q(a.daysLeft)):(0,e.ph)("attn.entrusted.what","{0} is due {1}.",a.name,q(a.daysLeft)),why:a.note?(0,e.ph)("attn.entrusted.userNote","{0}",a.note):(0,e.pl)("attn.entrusted.why","You asked af360 to tell you about this one."),evidence:[(0,e.pl)("attn.entrusted.evAdded","you added this date"),(0,e.ph)("attn.expiry.evDate","date: {0}",new Date(a.date).toLocaleDateString()),a.note?(0,e.ph)("attn.entrusted.evNote",'what you wrote: "{0}"',a.note):null].filter(a=>null!==a),steps:[(0,e.pl)("attn.entrusted.step","Deal with it, or open Ahead to change the date, mark it done or remove it.")]}),handled:b||void 0,...j(b?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),...i((0,e.ph)("act.entrusted","Deal with {0}",a.name)),laterId:a.id});continue}let b="red"===a.chip||"amber"===a.chip&&"ssl"===a.kind&&a.managed;if(A[a.id]=b?"amber":"clean",!b)continue;let c=y(a.id);z.push({id:a.id,kind:"expiry",tone:"amber",label:a.name,...h({what:"ssl"===a.kind&&a.managed&&"red"!==a.chip?(0,e.ph)("attn.expiry.certLate","{0}'s certificate should have auto-renewed by now - it hasn't.",a.name):a.daysLeft<0?(0,e.ph)("attn.expiry.expired","{0} expired {1}.",a.name,q(a.daysLeft)):(0,e.ph)("attn.expiry.expires","{0} expires {1}.",a.name,q(a.daysLeft)),why:a.note?(0,e.ph)("attn.expiry.userNote","{0}",a.note):"ssl"===a.kind?(0,e.pl)("attn.expiry.whySsl","When it lapses, browsers show a scary warning instead of your site."):(0,e.pl)("attn.expiry.whyDomain","After the date it stops working - renewing early is painless."),evidence:[(0,e.ph)("attn.expiry.evDate","date: {0}",new Date(a.date).toLocaleDateString()),a.judgment?(0,e.ph)("attn.expiry.evJudgment","{0}",a.judgment):a.note?(0,e.ph)("attn.expiry.evNote",'your note: "{0}"',a.note):null,a.issuer?(0,e.ph)("attn.expiry.evIssuer","issuer: {0}",a.issuer):null].filter(a=>null!==a),steps:"ssl"===a.kind?[(0,e.pl)("attn.expiry.stepSsl","Ask your host why the renewal hasn't landed - managed certificates renew on their own, so a late one means something is stuck.")]:[(0,e.pl)("attn.expiry.stepDomain","Renew it (or let it lapse on purpose), then remove or update the entry on Ahead.")]}),handled:c||void 0,...j(c?(0,e.pl)("attn.handled.snoozed","quiet until tomorrow"):void 0),...i("ssl"===a.kind&&a.managed&&"red"!==a.chip?(0,e.ph)("act.certLate","Chase {0}'s certificate",a.name):(0,e.ph)("act.renew","Renew {0}",a.name)),laterId:a.id})}for(let a of r){let b=`missing:${a}`;A[b]="amber",z.push({id:b,kind:"missing",tone:"amber",label:p(a),...h({what:(0,e.ph)("attn.missing.what","The folder {0} can't be found",a),why:(0,e.pl)("attn.missing.why","It moved or was renamed - point me at its new home, or stop watching it."),evidence:[(0,e.ph)("attn.missing.evPath","watched path: {0}",a),(0,e.pl)("attn.missing.evScan","the folder stopped answering on the last scan")],steps:[(0,e.pl)("attn.missing.step1","Press Find it and click through to where the folder lives now - the old entry is replaced."),(0,e.pl)("attn.missing.step2","If it's gone on purpose, Stop watching removes it.")]}),...i((0,e.ph)("act.find","Find {0}",p(a))),findPath:a})}z.sort((a,b)=>Number(a.handled??!1)-Number(b.handled??!1)||Number("red"===b.tone)-Number("red"===a.tone));let C=z.filter(a=>!a.handled).length,D=z.some(a=>"red"===a.tone&&!a.handled),E=b(a.findings).filter(a=>a?.cat==="safety").length,F=0;if(B){for(let a of c){let b=B.byPath[a.path.toLowerCase()];b&&!b.ok&&F++}B.outage&&F++}return F+=b(a.unreachedChecks).length,{state:D?"INCIDENT":C>0?"NEEDS_YOU":E>0?"SAFETY":"QUIET",open:C,items:z,tones:A,unreached:F+=new Set(b(a.unscannedSubjects)).size,safety:E}}function l(a){return a.isGit&&a.remoteKnown&&!a.hasRemote&&!a.looksDownloaded}function m(a){return 0===a.items.length&&0===Object.keys(a.tones).length}function n(a){let b=(a,b)=>({line:a.en,under:b.en,lineP:a,underP:b});if(m(a))return b((0,e.pl)("head.emptyLine","Nothing watched yet"),(0,e.pl)("head.emptyUnder","Point me at a folder and I'll show you the projects inside it."));if("INCIDENT"===a.state){let c=a.items.filter(a=>"red"===a.tone&&!a.handled);return b(1===c.length?(0,e.ph)("head.downOne","{0} is down",c[0].label):(0,e.ph)("head.downMany","{0} down",c.length),1===c.length?(0,e.pl)("head.incidentUnder","everything else can wait - this is the one to look at"):(0,e.pl)("head.incidentUnderMany","everything else can wait - these are the ones to look at"))}if("NEEDS_YOU"===a.state)return a.safety>0?b((0,e.ph)("head.needsLine",`{0} need${1===a.open?"s":""} you`,a.open),(0,e.ph)("head.needsUnderSafety",`none of it is on fire - each has a reason, and {0} safety finding${1===a.safety?" is":"s are"} waiting in Discoveries`,a.safety)):b((0,e.ph)("head.needsLine",`{0} need${1===a.open?"s":""} you`,a.open),(0,e.pl)("head.needsUnder","none of it is on fire - each has a reason"));let c=a.items.filter(a=>a.handled).length,d=a.unreached??0,f=a=>`thing${1===a?"":"s"}`;if("SAFETY"===a.state){let c=a.safety;return b((0,e.ph)("head.safetyLine",`{0} safety finding${1===c?"":"s"}`,c),d>0?(0,e.ph)("head.safetyMissed",`${1===c?"it is":"they are"} waiting in Discoveries - and af360 couldn't check {0} ${f(d)} this time`,d):(0,e.pl)("head.safetyUnder",`${1===c?"it is":"they are"} waiting in Discoveries`))}return b((0,e.pl)("head.quietLine","All quiet"),c>0?d>0?(0,e.ph)("head.quietHandledMissed",`nothing new needs you - {0} item${1===c?" is":"s are"} being handled, and af360 couldn't check {1} ${f(d)} this time`,c,d):(0,e.ph)("head.quietHandled",`nothing new needs you - {0} item${1===c?" is":"s are"} being handled`,c):d>0?(0,e.ph)("head.quietMissed",`nothing needs you - and af360 couldn't check {0} ${f(d)} this time`,d):(0,e.pl)("head.quietUnder","nothing needs you - go build something"))}function o(a){if(!a)return"nothing recorded";let b=[];return"number"==typeof a.uncommitted&&b.push(`${a.uncommitted} unsaved`),!1===a.hasRemote&&b.push("no backup"),a.down&&b.push("down"),b.length>0?b.join(", "):"all fine"}function p(a){let b=a.split(/[\\/]/).filter(Boolean);return b[b.length-1]??a}function q(a){return a<0?`${-a} day${-1===a?"":"s"} ago`:0===a?"today":1===a?"tomorrow":`in ${a} days`}}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";exports.id=4825,exports.ids=[4825,6988],exports.modules={14825:(a,b,c)=>{c.d(b,{r_:()=>aB,od:()=>aM,ag:()=>aL,w7:()=>aC,t7:()=>aN,Nb:()=>aE});var d=c(29021),e=c(35060),f=c(21820),g=c.n(f),h=c(33873),i=c.n(h),j=c(35672),k=c.n(j),l=c(71011);let m=[{major:14,eol:"2023-04-30",ltsName:"Fermium"},{major:16,eol:"2023-09-11",ltsName:"Gallium"},{major:17,eol:"2022-06-01"},{major:18,eol:"2025-04-30",ltsName:"Hydrogen"},{major:19,eol:"2023-06-01"},{major:20,eol:"2026-04-30",ltsName:"Iron"},{major:21,eol:"2024-06-01"},{major:22,eol:"2027-04-30",ltsName:"Jod"},{major:23,eol:"2025-06-01"},{major:24,eol:"2028-04-30",ltsName:"Krypton"},{major:25,eol:"2026-06-01"}],n=[{id:"paid-sdk",cat:"money",name:"Paid services in dormant projects",blurb:"Projects you haven't opened in a month or more that still reference paid services"},{id:"cloud-config-stale",cat:"money",name:"Deploy configs with no recent work",blurb:"Deploy files (Vercel, Railway, Docker) in projects that stopped moving"},{id:"identity",cat:"identity",name:"Commit identity mismatches",blurb:"Projects committing as a different email than your global identity"},{id:"ssh-keys",cat:"identity",name:"SSH key inventory",blurb:"The keys living in your .ssh folder and how old they are"},{id:"unpushed",cat:"time-capsule",name:"Unpushed commits",blurb:"Commits that exist only on this machine"},{id:"no-remote",cat:"time-capsule",name:"Projects with no remote",blurb:"Projects whose only home is this disk"},{id:"no-upstream",cat:"time-capsule",name:"Branches that push nowhere",blurb:"Local branches with no upstream, so their commits stay on this machine"},{id:"no-git-folder",cat:"time-capsule",name:"Folders with no git at all",blurb:"Watched folders holding work that has no history and no copy elsewhere"},{id:"readme-stub",cat:"time-capsule",name:"README still a placeholder",blurb:"READMEs that still say TODO, with age"},{id:"todo-census",cat:"time-capsule",name:"TODO census",blurb:"TODOs across the estate, eldest quoted"},{id:"abandoned-branches",cat:"time-capsule",name:"Abandoned branches",blurb:"Branches untouched for six months or more"},{id:"anniversary",cat:"time-capsule",name:"Project anniversaries",blurb:"First-commit birthdays coming up"},{id:"inventory-burst",cat:"time-capsule",name:"Large deletions and change bursts",blurb:"Watched folders that lost a lot of files, or had a lot of them change, since the last look"},{id:"file-gone",cat:"time-capsule",name:"Files that disappeared",blurb:"Files that were there last time and are not now, with the command to bring back the ones git still holds"},{id:"agent-effects",cat:"time-capsule",name:"Changes while an agent was connected",blurb:"Files that changed or went missing in a window that overlapped an agent's questions - af360 cannot tell whose edits they were"},{id:"sync-folder",cat:"safety",name:"Repos inside sync folders",blurb:"Git repos living inside OneDrive, Dropbox or Google Drive"},{id:"history-secrets",cat:"safety",name:"Key-shaped strings in git history",blurb:"Access-key-shaped strings in the history of publicly-remoted repos"},{id:"gitignore-gaps",cat:"safety",name:".gitignore gaps",blurb:"Repos tracking node_modules, .env or build output"},{id:"dev-server-exposed",cat:"safety",name:"Dev server open to the network",blurb:"Dev scripts binding 0.0.0.0 instead of localhost"},{id:"install-scripts",cat:"safety",name:"Dependencies with install scripts",blurb:"Dependencies that run their own code at install time, named"},{id:"dep-uncommitted",cat:"safety",name:"Installs git has not recorded",blurb:"Dependency lists that changed on disk and have not been committed"},{id:"unbacked-ignored",cat:"safety",name:"Ignored files in a project with no remote",blurb:"Environment files, databases and upload folders git ignores, in projects with nowhere to push"},{id:"node-modules",cat:"health",name:"node_modules weight",blurb:"What your node_modules folders weigh and what dormancy would give back"},{id:"build-artifacts",cat:"health",name:"Build-artifact graveyards",blurb:"Old .next, dist and build folders with sizes"},{id:"runtime-eol",cat:"health",name:"Runtime end of life",blurb:"Your Node.js version against its published support dates"},{id:"disk-weight",cat:"health",name:"Tool caches",blurb:"What the caches other tools leave behind are weighing, not counting node_modules or build folders"},{id:"runtime-census",cat:"health",name:"Runtime census",blurb:"Projects asking for a Node version this machine is not running"},{id:"repo-front-door",cat:"health",name:"README and licence",blurb:"Repositories with a remote that carry neither"},{id:"dep-versions",cat:"health",name:"Same dependency, many versions",blurb:"One dependency installed at several versions across the estate, with sizes"},{id:"dep-arrivals",cat:"health",name:"Dependency arrivals",blurb:"What joined your dependency lists, and the date the project recorded it"},{id:"estate-stats",cat:"health",name:"Estate line count",blurb:"Lines of code and language map - the pride stat"},{id:"duplicate-downloads",cat:"health",name:"Downloads sharing a project's name",blurb:"Downloaded folders whose name matches a project you already have, with sizes"},{id:"deploy-sites",cat:"site",name:"Addresses your projects deploy to",blurb:"Hostnames named by deploy configs in your own repos, offered for watching"},{id:"og-preview",cat:"site",name:"Social share preview",blurb:"The card WhatsApp and LinkedIn render when your link is shared"},{id:"security-headers",cat:"site",name:"Security headers",blurb:"Protective headers your site does or doesn't send"},{id:"mixed-content",cat:"site",name:"Mixed content",blurb:"Secure pages loading pieces over plain http"},{id:"redirect-chain",cat:"site",name:"Redirect chain and www",blurb:"How many hops a visit takes, and whether www agrees with the bare domain"},{id:"robots-blocking",cat:"site",name:"robots.txt blocking all",blurb:"A robots.txt turning every search engine away"},{id:"sitemap-missing",cat:"site",name:"Sitemap missing",blurb:"No sitemap for search engines to read"},{id:"dns-watch",cat:"site",name:"DNS snapshot and changes",blurb:"Your domain's DNS records, and any change since last look"}];var o=c(83720);let p=e.EO,q="package-lock.json",r=[{file:"yarn.lock",tool:"Yarn"},{file:"pnpm-lock.yaml",tool:"pnpm"},{file:"bun.lockb",tool:"Bun"},{file:"poetry.lock",tool:"Poetry"},{file:"Pipfile.lock",tool:"Pipenv"},{file:"Cargo.lock",tool:"Cargo"},{file:"go.sum",tool:"Go"},{file:"composer.lock",tool:"Composer"},{file:"Gemfile.lock",tool:"Bundler"}],s=[q,...r.map(a=>a.file)];async function t(a,b){try{let{stdout:c}=await p("git",(0,e.tQ)(b,a),{cwd:a,timeout:8e3,windowsHide:!0,maxBuffer:3e7,env:(0,e.tl)()});return c}catch{return null}}function u(a){let b;try{b=JSON.parse(a)}catch{return null}if(!b||"object"!=typeof b||!b.packages)return null;let c={},d=new Set;for(let[a,e]of Object.entries(b.packages)){if(!e?.version)continue;let b=a.match(/^node_modules\/((?:@[^/]+\/)?[^/]+)$/);b&&(c[b[1]]=e.version,e.hasInstallScript&&d.add(b[1]))}return{set:c,scripts:d}}let v=()=>{};async function w(a,b){let c=await t(a,["ls-files","-z","--",...s.map(a=>`*${a}`)]);if(null===c)return null;let d=c.split("\0").filter(Boolean).filter(a=>s.includes(a.split("/").pop()??""));for(let c of d.slice(8))b(`${a}/${c}`.toLowerCase());return d.slice(0,8)}async function x(a,b){let c=await t(a,["log","-40","--format=%H%x09%aI","--",b]);return c?c.split("\n").filter(Boolean).map(a=>{let[b,c]=a.split(" ");return{commit:b,date:c}}).filter(a=>a.commit&&a.date):[]}async function y(a,b,c){return t(a,["show",`${b}:${c}`])}async function z(a,b){if(0===b.length)return{known:!0};let c=await t(a,["status","--porcelain","--",...b]);if(null===c)return{known:!1};let d=c.split("\n").map(a=>a.replace(/\r$/,"")).find(a=>a.trim().length>0);if(!d)return{known:!0};let e=d.slice(3).trim(),f=(e.includes(" -> ")?e.split(" -> ")[1]:e).replace(/^"|"$/g,""),g=d.slice(0,2);return{known:!0,file:f,kind:g.includes("D")?"deleted":g.includes("R")?"renamed":g.includes("A")?"added":g.includes("M")?"modified":void 0}}async function A(a){let b=await t(a,["status","--porcelain","--untracked-files=all","--",...s]);if(null===b)return{known:!1};let c=b.split("\n").map(a=>a.replace(/\r$/,"")).find(a=>a.startsWith("??")&&a.trim().length>0);return c?{known:!0,file:c.slice(3).trim().replace(/^"|"$/g,""),kind:"added"}:{known:!0}}async function B(a,b=v){let c={name:a.name,path:a.path,read:!1,unread:[],otherNpm:[],arrivals:[],removed:[],uncommitted:!1,uncommittedKnown:!1};if(!a.isGit)return{...c,reason:"this project is not in git, and the arrival record lives in git"};let d=await w(a.path,b);if(null===d)return{...c,reason:"git did not answer when af360 asked which files it is tracking"};let e=[];for(let a of d){let b=a.split("/").pop()??"",c=r.find(a=>a.file===b);c&&e.push({file:a,tool:c.tool})}let f=d.filter(a=>(a.split("/").pop()??"")===q),g=await z(a.path,d);if(g.known&&!g.file){let b=await A(a.path);b.file&&(g=b)}if(0===f.length)return{...c,read:0===e.length,reason:e.length>0?"af360 does not read this lockfile format yet":void 0,unread:e,uncommitted:!!g.file,uncommittedKnown:g.known,uncommittedFile:g.file,uncommittedKind:g.kind};let h=function(a){let b=a=>a.split("/").length-1;return[...a].sort((a,c)=>b(a)-b(c))[0]}(f),i=f.filter(a=>a!==h),j=await x(a.path,h);if(0===j.length)return{...c,unread:e,otherNpm:i,readFile:h,reason:"git has no record of this dependency list changing",uncommitted:!!g.file,uncommittedKnown:g.known,uncommittedFile:g.file,uncommittedKind:g.kind};let k=j[0];if(1===j.length)return{...c,read:!0,unread:e,otherNpm:i,readFile:h,firstRecord:!0,commit:k.commit,recordedAt:k.date,uncommitted:!!g.file,uncommittedKnown:g.known,uncommittedFile:g.file,uncommittedKind:g.kind};let l=j[1],[m,n]=await Promise.all([y(a.path,k.commit,h),y(a.path,l.commit,h)]);if(null===m||null===n)return{...c,unread:e,otherNpm:i,readFile:h,reason:"af360 could not read this dependency list out of git",uncommitted:!!g.file,uncommittedKnown:g.known,uncommittedFile:g.file,uncommittedKind:g.kind};let o=u(m),p=u(n);if(!o||!p)return{...c,unread:e,otherNpm:i,readFile:h,reason:"this dependency list is not in a shape af360 can read",uncommitted:!!g.file,uncommittedKnown:g.known,uncommittedFile:g.file,uncommittedKind:g.kind};let{arrivals:s,removed:t}=function(a,b,c){let d=[];for(let[e,f]of Object.entries(b)){let b=a[e];b!==f&&d.push({name:e,version:f,from:b,installScript:c.has(e)})}let e=Object.keys(a).filter(a=>!(a in b));return d.sort((a,b)=>a.name.localeCompare(b.name)),e.sort(),{arrivals:d,removed:e}}(p.set,o.set,o.scripts);return{name:a.name,path:a.path,read:!0,unread:e,otherNpm:i,readFile:h,arrivals:s,removed:t,commit:k.commit,recordedAt:k.date,uncommitted:!!g.file,uncommittedKnown:g.known,uncommittedFile:g.file}}async function C(a,b=v,c=40){let d=[];for(let d of a.slice(c))b(d.path.toLowerCase());for(let e of a.slice(0,c))d.push(await B(e,b));return d}var D=c(23854),E=c(1232);let F=Object.freeze(["unidentified","af360","node"]),G=Object.freeze({"disc.agentWindowBurstOne":"1 file changed in {0}, in a window that overlapped an agent session.","disc.agentWindowBurst":"{0} files changed in {1}, in a window that overlapped an agent session.","disc.agentWindowGoneOne":"1 file went missing from {0} while an agent was connected.","disc.agentWindowGone":"{0} files went missing from {1} while an agent was connected.","disc.agentUnnamed":"af360 could not identify which agent.","disc.agentNamed":"The agent connected was {0}.","disc.agentGrace":"af360 counts an agent session as open until {0} minutes after its last question.","disc.agentAlsoYou":"af360 cannot tell your own edits from an agent's. If you were working in this folder too, some of these changes are yours.","disc.agentOnlyWhenOpen":"af360 only sees an agent when it is running. If af360 was closed, an agent could have been connected and af360 would not know.","disc.agentUnread":"af360 could not finish reading {0}, so this window is short by whatever is in there.","disc.agentWindowIs":"af360 measures change between its own looks. This window opened when af360 last looked at {0}, not when the agent started.","disc.agentFirstLook":"This is af360's first look at {0}, so there is nothing to compare against yet."});function H(a,...b){return b.reduce((a,b,c)=>a.split(`{${c}}`).join(String(b)),String(a))}var I=c(79748),J=c(5731);let K=h.join(J.lF,"agent-shadow.jsonl"),L=h.join(J.lF,"agent-shadow.1.jsonl"),M=a=>{try{return JSON.parse(a)}catch{return null}};async function N(a=Date.now()){let b="";for(let a of[L,K])try{b+=await (0,I.readFile)(a,"utf8")}catch{}let c=b.split("\n").filter(a=>""!==a.trim()),d=[],e=0;for(let b of c){let c=M(b);if(!c||!Number.isFinite(c.ts)){e++;continue}a-c.ts>2592e6||d.push(c)}d.sort((a,b)=>a.ts-b.ts);let f=d.length?d[0].ts:null;return{entries:d,unreadable:e,oldestTs:f,spansWindow:null!==f&&a-f>=25056e5}}async function O(a,b=Date.now()){if(!Array.isArray(a)||0===a.length)return{written:0,skipped:0};let c=new Set;try{let{entries:a}=await N(b);c=new Set(a.map(a=>a.key))}catch{}let d=[],e=0;for(let b of a){if(!b||!b.key||c.has(b.key)){e++;continue}c.add(b.key),d.push(b)}if(0===d.length)return{written:0,skipped:e};let f=d.map(a=>JSON.stringify({ts:b,key:a.key,kind:a.kind,project:(0,E.dK)(String(a.project??"")),n:a.n,window:a.window,session:a.session,named:a.named,wouldFire:a.wouldFire,counts:a.counts,headline:(0,E.dK)(String(a.headline??"")),who:(0,E.dK)(String(a.who??"")),honesty:(a.honesty??[]).map(a=>(0,E.dK)(String(a))),grace:(0,E.dK)(String(a.grace??"")),shadow:!0})).join("\n")+"\n";try{let a=await (0,I.stat)(K).catch(()=>null);a&&a.size+f.length>262144&&await (0,I.rename)(K,L)}catch{}try{await (0,I.appendFile)(K,f,"utf8")}catch{return{written:0,skipped:e}}return{written:d.length,skipped:e}}var P=c(84848);let Q=h.join(J.lF,"held-look.json");async function R(a=Q){try{let c;var b=await (0,I.readFile)(a,"utf8");try{c=JSON.parse(b)}catch{return null}if(!c||"object"!=typeof c||1!==c.v||"number"!=typeof c.at||!Number.isFinite(c.at)||c.at<=0||!Array.isArray(c.insights)||!Array.isArray(c.failed)||!Array.isArray(c.unscanned))return null;for(let a of c.insights)if(!a||"object"!=typeof a||"string"!=typeof a.id||""===a.id||"string"!=typeof a.verdict||""===a.verdict||"string"!=typeof a.cat||""===a.cat)return null;for(let a of c.failed)if("string"!=typeof a)return null;for(let a of c.unscanned)if("string"!=typeof a)return null;return{insights:c.insights,failed:c.failed,unscanned:c.unscanned,at:c.at}}catch{return null}}async function S(a,b=Q){if(a&&"number"==typeof a.at)try{await (0,I.writeFile)(b,JSON.stringify({v:1,at:a.at,insights:a.insights,failed:a.failed,unscanned:a.unscanned})+"\n","utf8")}catch{}}var T=c(22191),U=c(88804),V=c(87078);let W=new Set(["co","com","org","net","ac","gov","edu","ltd","plc"]);function X(a){let b=a.toLowerCase().split(".").filter(Boolean);return b.length<=2?b.join("."):W.has(b[b.length-2])&&b.length>=3?b.slice(-3).join("."):b.slice(-2).join(".")}let Y=[{file:"CNAME",extract:a=>ab(a)},{file:"public/CNAME",extract:a=>ab(a)},{file:"docs/CNAME",extract:a=>ab(a)},{file:"railway.toml",extract:a=>ac(a,/^\s*(?:domain|domains|url|custom_domain)\s*=/i)},{file:"railway.json",extract:a=>ac(a,/"(?:domain|domains|url|customDomain)"\s*:/i)},{file:"wrangler.toml",extract:a=>ac(a,/^\s*(?:route|routes|pattern|custom_domain|zone_name)\s*=/i)},{file:"vercel.json",extract:a=>ac(a,/"(?:alias|domains?|host)"\s*:/i)},{file:"netlify.toml",extract:a=>ac(a,/^\s*(?:from|to|url|base_url)\s*=/i)},{file:"next.config.js",extract:a=>ac(a,/(?:domains|assetPrefix|hostname|destination)\s*[:=]/i)},{file:"next.config.mjs",extract:a=>ac(a,/(?:domains|assetPrefix|hostname|destination)\s*[:=]/i)},{file:"next.config.ts",extract:a=>ac(a,/(?:domains|assetPrefix|hostname|destination)\s*[:=]/i)},{file:"package.json",extract:a=>ac(a,/"homepage"\s*:/i)},{file:"docker-compose.yml",extract:a=>ad(a)},{file:"docker-compose.yaml",extract:a=>ad(a)},{file:"compose.yaml",extract:a=>ad(a)},{file:"Dockerfile",extract:a=>ad(a)},{file:"README.md",prose:!0,extract:a=>ae(a)},{file:"docs/README.md",prose:!0,extract:a=>ae(a)}],Z=/\b((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z-]{1,23})\b/gi,$=new Set(["example.com","example.org","example.net","localhost.com","github.com","www.github.com","raw.githubusercontent.com","gitlab.com","bitbucket.org","npmjs.com","www.npmjs.com","registry.npmjs.org","yarnpkg.com","unpkg.com","jsdelivr.net","cdn.jsdelivr.net","cdnjs.cloudflare.com","shields.io","img.shields.io","badgen.net","schema.org","json-schema.org","www.w3.org","w3.org","spdx.org","opensource.org","docker.io","hub.docker.com","docker.com","ghcr.io","quay.io","gcr.io","nodejs.org","reactjs.org","react.dev","nextjs.org","vitejs.dev","python.org","fonts.googleapis.com","fonts.gstatic.com","googleapis.com","gstatic.com","google.com","www.google.com","letsencrypt.org","mozilla.org","developer.mozilla.org","vercel.com","www.vercel.com","vercel.app","railway.app","up.railway.app","netlify.com","www.netlify.com","netlify.app","cloudflare.com","www.cloudflare.com","pages.dev","workers.dev","github.io","fly.io","render.com","heroku.com","herokuapp.com"]),_=new Set(["local","localhost","internal","invalid","test","example","arpa","onion","js","mjs","cjs","ts","tsx","jsx","json","md","css","scss","sass","less","html","htm","xml","yml","yaml","toml","lock","txt","log","env","sh","ps","py","go","rs","rb","java","kt","swift","php","sql","png","jpg","jpeg","gif","svg","ico","webp","woff","woff2","ttf","map","min","d","node","gitignore","npmrc","nvmrc","config","spec","example","sample","bak","tmp"]),aa=["your-","your.","yourdomain","example","sample","placeholder","localhost","<",">","mydomain","changeme"];function ab(a){let b=[];return a.split(/\r?\n/).forEach((a,c)=>{let d=a.trim();!(!d||d.startsWith("#"))&&/^[a-z0-9.-]+$/i.test(d)&&d.includes(".")&&b.push({host:d.toLowerCase(),line:c+1})}),b}function ac(a,b){let c=[],d=a.split(/\r?\n/);return d.forEach((a,e)=>{if(!b.test(a))return;let f=[a,d[e+1]??"",d[e+2]??""];for(let[d,g]of(b.test(a)&&/[[{]\s*$/.test(a.trim())?f.join("\n"):a).split("\n").entries())for(let a of g.matchAll(Z))c.push({host:a[1].toLowerCase(),line:e+1+d})}),c}function ad(a){let b=[];return a.split(/\r?\n/).forEach((a,c)=>{if(/Host\(`[^`]+`\)/.test(a)||/\b(?:VIRTUAL_HOST|LETSENCRYPT_HOST|DOMAIN|DOMAIN_NAME|APP_URL|PUBLIC_URL|SITE_URL|HOSTNAME)\b\s*[:=]/i.test(a))for(let d of a.matchAll(Z))b.push({host:d[1].toLowerCase(),line:c+1})}),b}function ae(a){let b=[];return a.split(/\r?\n/).forEach((a,c)=>{for(let d of a.matchAll(/https?:\/\/([a-z0-9.-]+)/gi)){let a=d[1].toLowerCase().replace(/\.$/,"");a.includes(".")&&b.push({host:a,line:c+1})}}),b}async function af(a,b,c=3){let e=[],f=[],g=new Set;for(let h of Y){let j,k,l=i().join(a.path,...h.file.split("/"));try{j=await d.promises.stat(l)}catch{continue}if(j.isFile()){if(j.size>262144){f.push({file:h.file,project:a.name});continue}try{k=await d.promises.readFile(l,"utf8")}catch{f.push({file:h.file,project:a.name});continue}for(let d of h.extract(k))if(function(a){let b=a.toLowerCase();if(aa.some(a=>b.includes(a))||$.has(b))return!1;let c=b.split(".");if(c.length<2)return!1;let d=c[c.length-1];return!(_.has(d)||/^\d+$/.test(d)||b.endsWith(".localhost"))}(d.host)&&(!h.prose||function(a,b){if(b.apexes.has(X(a)))return"apex";let c=function(a){let b=new Set;for(let c of a.toLowerCase().split("."))for(let a of(b.add(c),c.split("-")))b.add(a);return b}(a);for(let a of b.projectNames)if(a.length>=4&&c.has(a))return"name";for(let a of b.gitIdentities)if(a.length>=4&&c.has(a))return"identity";return null}(d.host,b))&&!g.has(d.host)){if(e.length>=c)break;g.add(d.host),e.push({host:d.host,file:h.file,line:d.line,project:a.name,projectPath:a.path,fromProse:!!h.prose})}}}return{found:e,unread:f}}async function ag(a,b=[],c=()=>{}){let d=function(a,b){let c=new Set;for(let a of b){let b=(0,V.Pm)(a);b&&c.add(X(b))}let d=new Set,e=new Set;for(let b of a)if(b.name&&d.add(b.name.toLowerCase()),b.remoteUrl){let a=function(a){let b=/^(?:[a-z+]+:\/\/)?(?:[^@/]*@)?[^/:]+[/:]+([^/]+)\//i.exec(a.trim()),c=b?.[1]?.toLowerCase();return c&&/^[a-z0-9][a-z0-9._-]*$/i.test(c)?c:null}(b.remoteUrl);a&&e.add(a)}return{apexes:c,projectNames:d,gitIdentities:e}}(a,b),e=[],f=[];for(let b of a.slice(40))c(b.path.toLowerCase());for(let b of a.slice(0,40)){if(b.looksDownloaded)continue;let a=await af(b,d);e.push(...a.found),f.push(...a.unread)}return{found:e,unread:f}}function ah(a){return`${a.file}:${a.line}`}var ai=c(26988);let aj=e.EO,ak=new Set(["node_modules",".git",".next","dist","build","out",".venv","venv","__pycache__","coverage",".turbo",".cache","target","vendor",".pytest_cache"]);async function al(a){let b,c={},e=Date.now(),f=!0,g=async(h,j)=>{let k;if(f){if(j>12){f=!1,b="the folder is nested deeper than af360 looks";return}if(Date.now()-e>5e3){f=!1,b="the folder took longer to read than af360 allows";return}try{k=await d.promises.readdir(h,{withFileTypes:!0})}catch{f=!1,b=0===j?"af360 could not open the folder":"af360 could not open a folder inside it";return}for(let e of k){let k;if(!f)return;if(e.isSymbolicLink())continue;if(e.isDirectory()){if(ak.has(e.name)||e.name.startsWith("."))continue;await g(i().join(h,e.name),j+1);continue}if(!e.isFile())continue;if(Object.keys(c).length>=2e4){f=!1,b="the folder holds more files than af360 counts";return}let l=i().join(h,e.name);try{k=await d.promises.stat(l)}catch{continue}c[i().relative(a,l).split(i().sep).join("/")]=`${Math.round(k.mtimeMs)}:${k.size}`}}};return await g(a,0),f?{ts:Date.now(),files:c,complete:!0}:{ts:Date.now(),files:{},complete:!1,reason:b}}let am={measured:!1,since:0,before:0,after:0,gone:[],added:[],edited:[],standing:[]};function an(a){if(!a.measured)return!1;let b=a.gone.length;return b>=200||!(b<6)&&!(b<.1*a.before)&&(b>=20||b>=.5*a.before)}function ao(a){return!!a.measured&&a.added.length+a.edited.length>=30}function ap(a=(0,l.yF)()){return i().join(a,"inventory.json")}async function aq(a=(0,l.yF)()){try{let b=JSON.parse(await d.promises.readFile(ap(a),"utf8"));if(!b||"object"!=typeof b||Array.isArray(b))return{};let c={};for(let[a,d]of Object.entries(b)){if(!d||"object"!=typeof d||"number"!=typeof d.ts||"boolean"!=typeof d.complete||!d.files||"object"!=typeof d.files)continue;let b={};for(let[a,c]of Object.entries(d.missing??{}))c&&"object"==typeof c&&"number"==typeof c.since&&(b[a]={since:c.since,held:!0===c.held});c[a]={ts:d.ts,complete:d.complete,files:d.files,reason:"string"==typeof d.reason?d.reason:void 0,missing:Object.keys(b).length>0?b:void 0}}return c}catch{return{}}}async function ar(a,b=(0,l.yF)()){try{await d.promises.mkdir(b,{recursive:!0}),await d.promises.writeFile(ap(b),JSON.stringify(a),"utf8")}catch{}}async function as(a){try{let{stdout:b}=await aj("git",(0,e.tQ)(["ls-files","--deleted"],a),{cwd:a,timeout:8e3,windowsHide:!0,env:(0,e.tl)()});return{answered:!0,files:new Set(b.split(/\r?\n/).map(a=>a.trim()).filter(Boolean))}}catch{return{answered:!1,files:new Set}}}let at={answered:!1,files:new Set};function au(a){return`git restore -- "${a}"`}let av=e.EO;function aw(a){return{verdict:a.en,verdictP:a}}function ax(a,b){return{list:{slot:a,items:[b]}}}function ay(a){null!==a.settledAt||a.pending.size>0||az.__af360Engine===a&&(a.settledAt=Date.now(),S({insights:a.order,failed:[...a.failed],unscanned:[...a.unscanned],at:a.settledAt}).catch(()=>{}))}let az=globalThis,aA=az.__af360NmSizes??(az.__af360NmSizes=new Map);async function aB(a,b,c={}){let d=new Set(b.rules.muted),e=a.map(a=>a.path).join("|")+"::"+b.urls.map(a=>a.url).join("|")+"::m:"+[...d].sort().join(",")+"::d:"+b.declinedSites.map(a=>`${a.host}@${a.citation}`).sort().join(","),f=az.__af360Engine;if(!c.fresh&&f&&f.key===e&&Date.now()-f.ts<36e5)return aD(f,d);let g=f?null!==f.settledAt?{insights:[...f.order],failed:[...f.failed],unscanned:[...f.unscanned],at:f.settledAt}:f.held:await R(),h={key:e,ts:Date.now(),order:[],pending:new Set,deepTotal:0,failed:new Set,unscanned:new Set,settledAt:null,held:g};az.__af360Engine=h,aY.clear(),a0=null,aZ.clear();let i=aI.filter(a=>!d.has(a.id)),j={projects:a,cfg:b,skip:a=>{az.__af360Engine===h&&h.unscanned.add(a)}};for(let a of(await Promise.all(i.filter(a=>"fast"===a.speed).map(a=>a.run(j).catch(()=>[])))))h.order.push(...a);let k=i.filter(a=>"slow"===a.speed),l=function(){let a=Number(process.env.AF360_DEEP_DELAY_MS);return Number.isFinite(a)&&a>0?Math.min(a,6e4):0}();for(let[a,b]of(h.deepTotal=k.length,k.entries()))h.pending.add(b.id),(l>0?new Promise(b=>setTimeout(b,l*(a+1))):Promise.resolve()).then(()=>b.run(j)).then(a=>{az.__af360Engine===h&&h.order.push(...a)}).catch(()=>{if(az.__af360Engine!==h)return;h.failed.add(b.id);let a=n.find(a=>a.id===b.id)?.name??b.id;(0,T.logEvent)(`${a} didn't finish this time`,"look")}).finally(()=>{h.pending.delete(b.id),ay(h)});return ay(h),aD(h,d)}async function aC(a){if(az.__af360Engine)return null;let b=await R();if(!b)return null;let c=new Set(a.rules.muted),d=aI.filter(a=>"slow"===a.speed&&!c.has(a.id)).length;return{insights:b.insights,looking:!0,lookAt:b.at,muted:n.filter(a=>c.has(a.id)).map(a=>({id:a.id,name:a.name})),pending:d,deepTotal:d,failed:b.failed.map(a=>({id:a,name:n.find(b=>b.id===a)?.name??a})),unscanned:b.unscanned}}function aD(a,b){let c=null===a.settledAt?a.held:{insights:a.order,failed:[...a.failed],unscanned:[...a.unscanned],at:a.settledAt};return{insights:c?c.insights:[],looking:null===a.settledAt,lookAt:c?c.at:null,muted:n.filter(a=>b.has(a.id)).map(a=>({id:a.id,name:a.name})),pending:a.pending.size,deepTotal:a.deepTotal,failed:(c?c.failed:[]).map(a=>({id:a,name:n.find(b=>b.id===a)?.name??a})),unscanned:c?c.unscanned:[]}}function aE(){let a=az.__af360Engine;if(!a||Date.now()-a.ts>=36e5)return null;let b=null===a.settledAt?a.held:{insights:a.order};return b?b.insights.map(a=>({id:a.id,cat:a.cat})):null}let aF=globalThis;function aG(a){let b=az.__af360Engine,c=aF.__af360InvPass;if(c&&c.state===b)return c.p;let d=aH(a);return aF.__af360InvPass={state:b,p:d},d}async function aH({projects:a,skip:b}){let c=await aq(),d={...c},e=[],f=[],g=new Map;for(let h of function(a,b=()=>{}){let c=a.filter(a=>!a.looksDownloaded);for(let a of c.slice(40))b(a.path.toLowerCase());return c.slice(0,40)}(a,b)){let a=h.path.toLowerCase(),b=await al(h.path),i=function(a,b){if(!a)return{...am,first:!0,reason:"af360 has not looked inside this folder before"};if(!a.complete)return{...am,since:a.ts,reason:a.reason??"af360 could not read this folder last time"};if(!b.complete)return{...am,since:a.ts,reason:b.reason??"af360 could not read this folder this time"};let c=[],d=[],e=[];for(let[d,f]of Object.entries(a.files)){let a=b.files[d];void 0===a?c.push(d):a!==f&&e.push(d)}for(let c of Object.keys(b.files))void 0===a.files[c]&&d.push(c);let f=[];for(let[c,d]of Object.entries(a.missing??{}))void 0===b.files[c]&&f.push({rel:c,since:d.since,held:d.held});for(let b of c)void 0===(a.missing??{})[b]&&f.push({rel:b,since:a.ts,held:!1});return{measured:!0,since:a.ts,before:Object.keys(a.files).length,after:Object.keys(b.files).length,gone:c,added:d,edited:e,standing:f}}(c[a],b);i.measured||i.first||f.push({name:h.name,reason:i.reason??"af360 could not read it"});let j=i.measured&&i.standing.length>0&&h.isGit?await as(h.path):at,k=i.measured?function(a,b){return a.filter(a=>!(a.held&&b.answered&&!b.files.has(a.rel))).map(a=>({...a,held:a.held||b.files.has(a.rel)}))}(i.standing,j):[];g.set(h.path,j),e.push({p:h,d:{...i,standing:k}}),b.complete&&(d[a]={...b,missing:k.length>0?function(a){let b={};for(let c of a)b[c.rel]={since:c.since,held:c.held};return b}(k):void 0})}return await ar(d),{diffs:e,unreadable:f,gitHeld:g}}let aI=[{id:"unpushed",speed:"fast",async run({projects:a}){let b=a.filter(a=>a.isGit&&(a.ahead??0)>0);if(0===b.length)return[];let c=b.reduce((a,b)=>a+(b.ahead??0),0);return[{id:"unpushed",det:"unpushed",cat:"time-capsule",...aw((0,U.ph)("disc.unpushed",`{0} commit${1===c?"":"s"} exist${1===c?"s":""} only on this laptop, across {1} project${1===b.length?"":"s"}.`,c,b.length)),why:"A push costs a minute; losing them costs the work itself.",detail:b.map(a=>`${a.name} - ${a.ahead} unpushed`),steps:["Open each project and push once - the remote gets a full copy."],devDetail:b.map(a=>a.path)}]}},{id:"no-remote",speed:"fast",async run({projects:a,cfg:b}){let c=a.filter(a=>a.isGit&&a.remoteKnown&&!a.hasRemote&&!a.looksDownloaded);if(0===c.length)return[];let d=new Set(b.ignored.map(a=>a.id)),e=c.some(a=>d.has(a.path.toLowerCase()));return[{id:"no-remote",det:"no-remote",cat:"time-capsule",...aw((0,U.ph)("disc.noRemote",`{0} project${1===c.length?" has":"s have"} no home but this machine.`,c.length)),why:e?"Counting the ones you've silenced too - a remote copy is the whole insurance policy.":"A remote copy is the whole insurance policy.",detail:c.map(a=>a.name),steps:["Create an empty repository on your code host, connect it, and push - a minute per project."],devDetail:c.map(a=>a.path)}]}},{id:"no-upstream",speed:"slow",async run({projects:a,skip:b}){let c=[];for(let d of a_(a,40,b,a=>a.path.toLowerCase()))c.push(await (0,D.Lr)(d));let d=[],e=c.filter(a=>a.read&&a.noUpstream.length>0).sort((a,b)=>b.noUpstream.length-a.noUpstream.length);if(e.length>0){let a=e[0];d.push({id:"no-upstream",det:"no-upstream",cat:"time-capsule",...aw(1===e.length?(0,U.ph)("disc.noUpstream","{0} has {1} local branches that push nowhere.",a.name,a.noUpstream.length):(0,U.ph)("disc.noUpstreamMany","{0} projects have local branches that push nowhere - {1} branches in total.",e.length,e.reduce((a,b)=>a+b.noUpstream.length,0))),why:"A branch with no upstream is not sent anywhere by git push on its own. The commits on it are only on this machine.",detail:e.flatMap(a=>[`${a.name} - ${a.noUpstream.length} of ${a.total} local branches`,...a.noUpstream.map(b=>`${a.name}: ${b}`)]),steps:["If a branch is meant to be shared, one command sets it: git push -u origin <branch>.","If it is finished or abandoned, deleting it locally is the other honest answer."],dev:"git for-each-ref refs/heads, comparing %(refname:short) against %(upstream) - an empty upstream field is the finding",devDetail:e.map(a=>`${a.path}: ${a.noUpstream.join(", ")}`)})}let f=c.filter(a=>!a.read);return f.length>0&&d.push({id:"no-upstream-unread",det:"no-upstream",cat:"time-capsule",...aw((0,U.ph)("disc.noUpstreamUnread","af360 could not list the branches in {0} of your projects.",f.length)),why:"git did not answer for these. af360 is not saying their branches are fine, it is saying it has not read them.",detail:f.map(a=>a.name),steps:["Nothing to do. This names what af360 could not read, not a problem in the project."],devDetail:f.map(a=>a.path)}),d}},{id:"no-git-folder",speed:"slow",async run({cfg:a,skip:b}){let c=await (0,D.XK)(a.roots??[],b);if(0===c.length)return[];let d=[];for(let a of c)d.push(await (0,D.aI)(a));let e=[],f=d.filter(a=>a.complete&&!a.hasRepoBeneath&&a.files>=D.EZ).sort((a,b)=>b.files-a.files);if(f.length>0){let a=f[0];e.push({id:"no-git-folder",det:"no-git-folder",cat:"time-capsule",...aw(1===f.length?(0,U.ph)("disc.noGitFolder","{0} is not in git, and it holds {1} files.",a.name,a.files):(0,U.ph)("disc.noGitFolderMany","{0} folders inside the places you watch are not in git - the largest is {1}, holding {2} files.",f.length,a.name,a.files)),why:"Nothing in these has a history, so nothing here records what changed or lets you go back. af360 only looked inside the folder - it cannot see whether a copy exists somewhere else. If it is work you would miss, git init is the first step.",detail:[...f.map(a=>`${a.name} - ${a.files} files, and no git anywhere inside it`),`af360 names folders holding ${D.EZ} files or more; smaller ones are left alone.`],steps:["git init inside the folder, then commit - that gives it a history on this machine.","Adding a remote and pushing is what gives it a copy somewhere else."],dev:`walked to depth ${D.wq}, file cap ${D.EH}, time cap ${D.Vk}ms, symlinks never followed; a folder with a repository anywhere beneath it is a container and is excluded`,devDetail:f.map(a=>`${a.path}: ${a.files} files`)})}let g=d.filter(a=>!a.complete);return g.length>0&&e.push({id:"no-git-folder-unread",det:"no-git-folder",cat:"time-capsule",...aw((0,U.ph)("disc.noGitFolderUnread","af360 could not look all the way inside {0} folders in the places you watch.",g.length)),why:"af360 is not saying these have no backup. It did not finish looking, and a folder it did not finish reading could hold a repository it never reached.",detail:g.map(a=>`${a.name} - ${a.reason??"af360 could not finish the walk"}`),steps:["Nothing to do. This names where af360 stopped, not a problem in the folder."],devDetail:g.map(a=>`${a.path}: ${a.reason}`)}),e}},{id:"readme-stub",speed:"fast",async run({projects:a,skip:b}){for(let c of a_(a,60,b,a=>a.path.toLowerCase())){let a=await aV(c.path);if(a)return[{id:`readme:${c.path.toLowerCase()}`,det:"readme-stub",cat:"time-capsule",...aw(c.lastActivity?(0,U.ph)("disc.readmeStubSince",'{0}\'s README has said "{1}" since about {2}.',c.name,a,(0,o.KN)(c.lastActivity).replace(" ago"," back")):(0,U.ph)("disc.readmeStubWhile",'{0}\'s README has said "{1}" for a while.',c.name,a)),why:"Future-you will wonder what this was. Two honest sentences fix it.",steps:["Open the README and write what it is and how to run it - nothing fancier."]}]}return[]}},{id:"anniversary",speed:"fast",async run({projects:a}){let b=new Date;for(let c of a){if(!c.firstCommit)continue;let a=new Date(c.firstCommit);if(isNaN(a.getTime()))continue;let d=new Date(a);d.setFullYear(b.getFullYear()),d.getTime()<b.getTime()-864e5&&d.setFullYear(b.getFullYear()+1);let e=d.getFullYear()-a.getFullYear(),f=Math.round((d.getTime()-b.getTime())/864e5);if(e<1||f>14)continue;let g=f<=0?(0,U.pl)("disc.whenToday","today"):1===f?(0,U.pl)("disc.whenTomorrow","tomorrow"):(0,U.ph)("disc.whenOn","on {0}",d.toLocaleDateString([],{month:"short",day:"numeric"})),h=g.en;return[{id:`anniversary:${c.path.toLowerCase()}`,det:"anniversary",cat:"time-capsule",...aw(null!=c.commitCount?{...(0,U.ph)("disc.anniversaryCommits","{0} turns {1} {2} - {3} commits since the first.",c.name,e,h,c.commitCount),...ax(2,g)}:{...(0,U.ph)("disc.anniversary","{0} turns {1} {2}.",c.name,e,h),...ax(2,g)}),why:"Estates have birthdays too. The full list lives on Ahead.",link:{href:"/ahead",label:"See Ahead →"},dev:`first commit ${a.toLocaleDateString()}`}]}return[]}},{id:"paid-sdk",speed:"fast",async run({projects:a,skip:b}){let c=["stripe","twilio","@sendgrid/","openai","@anthropic-ai/","aws-sdk","@aws-sdk/","@azure/","@google-cloud/","plaid","algoliasearch","pusher","mailgun","firebase-admin"],d=[];for(let e of a_(a.filter(a=>("dormant"===a.staleness||"forgotten"===a.staleness)&&!a.looksDownloaded),40,b,a=>a.path.toLowerCase())){let a=(await aR(e.path)).filter(a=>c.some(b=>b.endsWith("/")?a.startsWith(b):a===b));a.length>0&&d.push({p:e,deps:a})}return 0===d.length?[]:[{id:"paid-sdk",det:"paid-sdk",cat:"money",...aw((0,U.ph)("disc.paidSdk",`{0} project${1===d.length?" you haven't opened in a month or more still references":"s you haven't opened in a month or more still reference"} paid services.`,d.length)),why:"Dormant code doesn't bill; the accounts behind it might. Worth one look.",detail:d.map(a=>`${a.p.name} - ${a.deps.join(", ")}`),steps:["Check each service's dashboard for an active subscription or usage billing tied to these projects."],devDetail:d.map(a=>a.p.path)}]}},{id:"cloud-config-stale",speed:"fast",async run({projects:a,skip:b}){let c=["vercel.json","railway.toml","railway.json","netlify.toml","fly.toml","render.yaml","Procfile","Dockerfile"],d=[];for(let e of a_(a.filter(a=>("dormant"===a.staleness||"forgotten"===a.staleness)&&!a.looksDownloaded),40,b,a=>a.path.toLowerCase())){let a=[];for(let b of c)await aQ(i().join(e.path,b))&&a.push(b);a.length>0&&d.push({p:e,files:a})}return 0===d.length?[]:[{id:"cloud-config-stale",det:"cloud-config-stale",cat:"money",...aw((0,U.ph)("disc.cloudConfigStale",`{0} dormant project${1===d.length?" still carries":"s still carry"} deploy configs - anything still running for them may still be billing.`,d.length)),why:"A deploy that outlives its project is the quietest subscription there is.",detail:d.map(a=>`${a.p.name} - ${a.files.join(", ")}${a.p.lastActivity?` \xb7 untouched since ${(0,o.KN)(a.p.lastActivity).replace(" ago"," back")}`:""}`),steps:["Open your host's dashboard and check whether these projects still have live deployments."],devDetail:d.map(a=>a.p.path)}]}},{id:"ssh-keys",speed:"fast",async run(){let a,b=i().join(g().homedir(),".ssh");try{a=await d.promises.readdir(b,{withFileTypes:!0})}catch{return[]}let c=[];for(let e of a){if(!e.isFile())continue;let a=e.name;if((/^id_[a-z0-9_]+$/i.test(a)||a.endsWith(".pem"))&&!a.endsWith(".pub"))try{let e=await d.promises.stat(i().join(b,a));c.push({name:a,ageDays:Math.floor((Date.now()-e.mtimeMs)/864e5)})}catch{}}if(0===c.length)return[];c.sort((a,b)=>b.ageDays-a.ageDays);let e=c[0],f=Math.floor(e.ageDays/365),h=e.ageDays>=365?(0,U.ph)("disc.ageYears",`{0} year${1===f?"":"s"}`,f):(0,U.ph)("disc.ageDays","{0} days",e.ageDays),j=h.en;return[{id:"ssh-keys",det:"ssh-keys",cat:"identity",...aw({...(0,U.ph)("disc.sshKeys",`{0} SSH key${1===c.length?" lives":"s live"} on this machine - the eldest, {1}, was last changed {2} ago.`,c.length,e.name,j),...ax(2,h)}),why:"Keys don't expire on their own; the ones you forgot still open doors.",detail:c.map(a=>`${a.name} - last changed ${a.ageDays>=365?`${Math.floor(a.ageDays/365)}y`:`${a.ageDays}d`} ago`),steps:["Check which of these still match keys registered with your code host or your servers; retire the ones nothing uses."],dev:`read from ${b} - names and dates only, key contents never read`}]}},{id:"identity",speed:"slow",async run({projects:a}){let b=a.filter(a=>a.isGit),c=await aO(process.cwd(),["config","--global","user.email"])??"";if(!c)return[];let d=(await Promise.all(b.map(async a=>({p:a,email:await aO(a.path,["config","user.email"])??""})))).filter(a=>a.email&&a.email.toLowerCase()!==c.toLowerCase());if(0===d.length)return[];let e=new Map;for(let a of d)e.set(a.email,[...e.get(a.email)??[],a.p.name]);let[f,g]=[...e.entries()].sort((a,b)=>b[1].length-a[1].length)[0];return[{id:"identity",det:"identity",cat:"identity",...aw((0,U.ph)("disc.identity",`{0} project${1===g.length?" commits":"s commit"} as {1} - your global identity is {2}.`,g.length,f,c)),why:"Mixed identities scatter your contribution history across strangers.",detail:[...e.entries()].map(([a,b])=>`${a}: ${b.join(", ")}`),steps:["In each project, set the identity you want its history to carry."],dev:`global user.email = ${c}`,devDetail:['git config user.email "you@example.com" (run inside the project)']}]}},{id:"sync-folder",speed:"fast",run:async({projects:a})=>a.filter(a=>a.isGit&&/OneDrive|Dropbox|Google Drive/i.test(a.path)).map(a=>({id:`sync:${a.path.toLowerCase()}`,det:"sync-folder",cat:"safety",...aw((0,U.ph)("disc.syncFolder","{0} lives inside a sync folder - git histories and sync services corrupt each other.",a.name)),why:"Sync tools rewrite .git internals mid-operation. Moving it out takes one drag.",detail:[a.path],steps:["Move the folder somewhere the sync tool doesn't watch (like C:\\dev), then keep using it as before."]}))},{id:"dev-server-exposed",speed:"fast",async run({projects:a,skip:b}){let c=[];for(let e of a_(a,60,b,a=>a.path.toLowerCase()))try{let a=i().join(e.path,"package.json");if((await d.promises.stat(a)).size>2e6)continue;let b=JSON.parse(await d.promises.readFile(a,"utf8")),f=b?.scripts&&"object"==typeof b.scripts?b.scripts:{},g=Object.entries(f).find(([,a])=>"string"==typeof a&&a.includes("0.0.0.0"));g&&c.push({p:e,script:`${g[0]}: ${g[1]}`})}catch{}return 0===c.length?[]:[{id:"dev-server-exposed",det:"dev-server-exposed",cat:"safety",...aw((0,U.ph)("disc.devServerExposed",`{0} project${1===c.length?" has a dev script that binds":"s have dev scripts that bind"} 0.0.0.0 - while that server is running it accepts connections from the whole network, not just this machine.`,c.length)),why:"Fine when you're testing from a phone on purpose; worth knowing when you're not.",detail:c.map(a=>`${a.p.name} - ${a.script}`),steps:["Remove the 0.0.0.0 binding unless you need other devices to reach the dev server; localhost is the default for a reason."],devDetail:c.map(a=>a.p.path)}]}},{id:"gitignore-gaps",speed:"slow",async run({projects:a,skip:b}){let c=[];for(let d of a_(a.filter(a=>a.isGit&&!a.looksDownloaded),40,b,a=>a.path.toLowerCase())){let a=await aO(d.path,["ls-files","--","node_modules",".env",".env.*","dist",".next","build"]);if(!a)continue;let b=a.split("\n").filter(Boolean).filter(a=>!/^\.env\.(example|sample|template|dist)$/i.test(a));if(0===b.length)continue;let e=new Set;for(let a of b)a.startsWith("node_modules")?e.add("node_modules"):(a.startsWith(".env"),e.add(a.split("/")[0]));c.push({p:d,what:[...e]})}return 0===c.length?[]:[{id:"gitignore-gaps",det:"gitignore-gaps",cat:"safety",...aw((0,U.ph)("disc.gitignoreGaps",`{0} repo${1===c.length?" is":"s are"} tracking things .gitignore usually keeps out.`,c.length)),why:"node_modules bloats every clone; a tracked .env puts its contents in history for good.",detail:c.map(a=>`${a.p.name} - tracking ${a.what.join(", ")}`),steps:["Add the folder or file to .gitignore, then untrack it (the file stays on disk, git just stops carrying it).","If a tracked .env held real secrets, rotate them - removing the file doesn't remove it from history."],devDetail:c.map(a=>`${a.p.path}: git rm -r --cached ${a.what.join(" ")}`)}]}},{id:"history-secrets",speed:"slow",async run({projects:a,skip:b}){let c=/github\.com|gitlab\.com|bitbucket\.org/i,d=[[/AKIA[0-9A-Z]{16,}(?![0-9A-Z])/,"AWS key"],[/sk_live_[0-9a-zA-Z]{16,}(?![0-9a-zA-Z])/,"Stripe live key"],[/ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/,"GitHub token"],[/github_pat_[A-Za-z0-9_]{22,}(?![A-Za-z0-9_])/,"GitHub token"],[/AIza[0-9A-Za-z_-]{35,}(?![0-9A-Za-z_-])/,"Google API key"],[/xox[baprs]-[0-9A-Za-z-]{10,}(?![0-9A-Za-z-])/,"Slack token"],[/-----BEGIN [A-Z ]*PRIVATE KEY-----/,"private key block"]],f=RegExp(d.map(([a])=>a.source).join("|"),"g"),g=a=>{for(let[b,c]of d)if(RegExp(`^(?:${b.source})`).test(a))return c;return"key"},h=a.filter(a=>a.isGit&&!a.looksDownloaded&&a.remoteUrl&&c.test(a.remoteUrl)),i=h.slice(0,8);for(let a of h.slice(8))b(a.path.toLowerCase());let j=[];for(let a of i)try{let{stdout:b}=await av("git",(0,e.tQ)(["log","--all","--format=","-p","-G","AKIA[0-9A-Z]{16}|sk_live_|ghp_|github_pat_|AIza[0-9A-Za-z_-]{20}|xox[baprs]-|BEGIN [A-Z ]*PRIVATE KEY"]),{cwd:a.path,timeout:3e4,windowsHide:!0,maxBuffer:16e6,env:(0,e.tl)()}),c=new Set,d=new Set;for(let a of b.matchAll(f)){c.add(a[0]);let b=g(a[0]);d.add("private key block"===b?"a private key block":`${b} ...${a[0].slice(-4)}`)}if(d.size>0){let b=[...d].sort();j.push({p:a,samples:b.slice(0,4),count:c.size,forms:d.size})}}catch{b(a.path.toLowerCase())}return 0===j.length?[]:[{id:"history-secrets",det:"history-secrets",cat:"safety",...aw(1===j.length?(0,U.ph)("disc.historySecretsOne","{0}'s git history holds strings shaped like access keys - and the repo has a remote - history travels with every clone.",j[0].p.name):(0,U.ph)("disc.historySecretsMany","{0} repos' git history holds strings shaped like access keys - and the repos have a remote - history travels with every clone.",j.length)),why:"If any of these are real, deleting the file didn't unpublish them: every copy of the repo still carries the commit that held them.",detail:[...h.length>8?[(0,U.ph)("disc.historySecretsCapped","af360 read the history of {0} of {1} projects this scan - the rest were not looked at",8,h.length).en]:[],...j.map(a=>{let b=a=>1===a?"":"s",c=a.count===a.forms?`${a.count} key-shaped string${b(a.count)}`:`${a.count} key-shaped string${b(a.count)} in ${a.forms} distinct masked form${b(a.forms)}`,d=a.samples.length<a.forms?`, ${a.samples.length} shown`:"";return`${a.p.name} - ${c}${d}: ${a.samples.join(", ")}`})],steps:["Check whether any are real and still active - if so, rotate them first; that alone ends the risk.","Then, if you want history clean too, rewrite it (git filter-repo) and force-push - optional once keys are rotated."],dev:"matched shapes: AWS, Stripe live, GitHub tokens, Google API, Slack, PEM blocks - values masked at capture (kind + last four, no prefix and no length), raw strings never kept",devDetail:j.map(a=>a.p.path)}]}},{id:"stale-remote",speed:"slow",async run({projects:a}){let b=await (0,ai.cachedGithubScan)();if(!b)return[];let c=[];for(let d of a){let a=b.byPath[d.path.toLowerCase()];if(!a||a.ok||"moved"!==a.kind)continue;let e=/GitHub now calls this repository (\S+)$/.exec(a.reason);c.push({p:d,to:e?e[1]:null})}return 0===c.length?[]:[{id:"stale-remote",det:"stale-remote",cat:"health",...aw(1===c.length?(0,U.ph)("disc.staleRemoteOne","{0}'s remote points at a name GitHub has moved.",c[0].p.name):(0,U.ph)("disc.staleRemoteMany","{0} projects have a remote pointing at a name GitHub has moved.",c.length)),why:"Git still works - GitHub forwards the old name, so `git push` and `git pull` are fine. But af360 asks GitHub about your projects by the name in your remote, and it cannot follow that forward. Until the remote is updated, these projects stay outside every GitHub answer af360 gives you.",detail:c.map(({p:a,to:b})=>b?`${a.name} - your remote: ${a.remoteUrl??"not read"} - GitHub now calls it: ${b}`:`${a.name} - your remote: ${a.remoteUrl??"not read"} - GitHub says it has moved and did not say where`),steps:["`git remote set-url origin <new URL>` in the project updates it.","Nothing is lost either way - this is about what af360 can see, not about your work."],dev:"GitHub answered 301/308 for this repository; the Location header was validated to the API host and the {owner}/{name} shape before any part of it was rendered. Projects af360 did not ask about - past the per-scan ceiling, rate-limited, non-GitHub remotes, or unread .git/config - are NOT candidates and are not counted as current either.",devDetail:c.map(({p:a})=>a.path)}]}},{id:"install-scripts",speed:"slow",async run({projects:a,skip:b}){let c=new Map,d=0;for(let e of a_(a,40,b,a=>a.path.toLowerCase())){let a=await aX(e.path);if(!a)continue;let b=function(a){let b=new Set;for(let[c,d]of Object.entries(a.packages??{})){if(!d?.hasInstallScript)continue;let a=c.split("node_modules/").pop();a&&b.add(a)}return[...b]}(a);for(let a of(b.length>0&&d++,b))c.set(a,(c.get(a)??0)+1)}if(0===c.size)return[];let e=[...c.entries()].sort((a,b)=>b[1]-a[1]);return[{id:"install-scripts",det:"install-scripts",cat:"safety",...aw((0,U.ph)("disc.installScripts",`{0} dependenc${1===c.size?"y runs":"ies run"} their own code at install time, across {1} project${1===d?"":"s"}.`,c.size,d)),why:"Normal for packages that build native code - worth knowing the names, since install scripts run with your full user account.",detail:e.map(([a,b])=>`${a} - in ${b} project${1===b?"":"s"}`),steps:["Nothing to fix if these are names you recognize from your own stack. An unfamiliar name is worth a look before the next install."],dev:"read from package-lock.json hasInstallScript entries - nothing was executed"}]}},{id:"unbacked-ignored",speed:"slow",async run({projects:a,skip:b}){let c=a.filter(a=>a.isGit&&a.remoteKnown&&!a.hasRemote&&!a.looksDownloaded);if(0===c.length)return[];let d=[];for(let a of a_(c,40,b,a=>a.path.toLowerCase()))d.push(await (0,D.iS)(a));let e=[],f=d.filter(a=>a.read&&a.found.length>0);if(f.length>0){let a=f[0];e.push({id:"unbacked-ignored",det:"unbacked-ignored",cat:"safety",...aw(1===f.length?(0,U.ph)("disc.unbackedIgnored","{0} has no remote, and git is ignoring {1} things inside it.",a.name,a.found.length):(0,U.ph)("disc.unbackedIgnoredMany","{0} projects have no remote and hold things git is ignoring.",f.length)),why:"A file git ignores is not in the repository, and a project with no remote is not on a server either. af360 looked at those two places and neither holds this file - it has not looked anywhere else on this machine or off it.",detail:[...f.flatMap(a=>a.found.map(b=>`${a.name}: ${b.rel} - ${b.kind}`)),`af360 looked for ${a.lookedFor}.`],steps:["If these hold anything you would miss, copy them somewhere off this machine.","Keep them out of git if they hold secrets - the answer is a backup, not a commit."],dev:"candidate paths checked for existence, then confirmed ignored by git check-ignore --stdin - af360 does not reimplement .gitignore rules",devDetail:f.map(a=>`${a.path}: ${a.found.map(a=>a.rel).join(", ")}`)})}let g=d.filter(a=>!a.read);return g.length>0&&e.push({id:"unbacked-ignored-unread",det:"unbacked-ignored",cat:"safety",...aw((0,U.ph)("disc.unbackedIgnoredUnread","af360 could not check what git ignores in {0} of your projects.",g.length)),why:"git did not answer for these, so af360 has not looked rather than found nothing.",detail:g.map(a=>a.name),steps:["Nothing to do. This names what af360 could not read."],devDetail:g.map(a=>a.path)}),e}},{id:"dep-uncommitted",speed:"slow",async run({projects:a,skip:b}){let c=(await a$(a,b)).filter(a=>a.uncommittedKnown&&a.uncommitted);if(0===c.length)return[];let d=c[0];return[{id:"dep-uncommitted",det:"dep-uncommitted",cat:"safety",...aw(1===c.length?"deleted"===d.uncommittedKind?(0,U.ph)("disc.depDeleted","{0}'s dependency list was deleted and git has not recorded it yet.",d.name):"added"===d.uncommittedKind?(0,U.ph)("disc.depAdded","{0} has a new dependency list that git has not recorded yet.",d.name):(0,U.ph)("disc.depUncommitted","{0}'s dependency list changed and git has not recorded it yet.",d.name):(0,U.ph)("disc.depUncommittedMany","{0} projects have dependency lists that changed and git has not recorded yet.",c.length)),why:"The dependency list on disk and the one in the project's record no longer match. Committing the change puts it in the record, where the next person to clone it will get the same versions you have.",detail:c.map(a=>`${a.name} - ${a.uncommittedFile??"its dependency list"} is changed and not committed`),steps:["Look at what changed, and commit it if you meant it.","If you did not change it yourself, this is worth understanding before you commit it."],dev:"git status --porcelain over the tracked lockfile paths for a modified, added, deleted or renamed entry, and - only when that finds nothing - a second pass with --untracked-files=all over the lockfile NAMES, which is the only way to see a file git has never been told about (R-32, 2026-08-22). The status code is read rather than discarded, so a deletion is not announced as an install (R-31)",devDetail:c.map(a=>`${a.path}: ${a.uncommittedFile??"(path not reported)"}`)}]}},{id:"runtime-eol",speed:"fast",async run(){let a=process.version,b=function(a){let b=a.match(/^v?(\d+)/);return b?m.find(a=>a.major===parseInt(b[1],10))??null:null}(a);if(!b)return[];let c=new Date(b.eol),d=Math.floor((c.getTime()-Date.now())/864e5);if(d>90)return[];let e=c.toLocaleDateString([],{month:"long",year:"numeric"});return[{id:"runtime-eol",det:"runtime-eol",cat:"health",...aw(d<0?(0,U.ph)("disc.runtimeEolPast","Your Node.js ({0}) reached end of life in {1} - it still runs, it just stopped getting security fixes.",a,e):(0,U.ph)("disc.runtimeEolSoon","Your Node.js ({0}) reaches end of life in {1} days.",a,d)),why:"New releases install alongside your projects; most things just keep working.",steps:["Install the current LTS from nodejs.org (or with your version manager), then re-run your projects once to check."],dev:`support table ships with af360 (static data, nothing fetched) - v${b.major}${b.ltsName?` "${b.ltsName}"`:""} EOL ${b.eol}`}]}},{id:"node-modules",speed:"slow",async run({projects:a,skip:b}){let c=[],d={truncated:!1};for(let e of a){let a=i().join(e.path,"node_modules"),f=await aU(a,b,d);f>0&&c.push({p:e,bytes:f})}if(0===c.length)return[];let e=c.reduce((a,b)=>a+b.bytes,0),f=c.filter(a=>"dormant"===a.p.staleness||"forgotten"===a.p.staleness).reduce((a,b)=>a+b.bytes,0);return[{id:"node-modules",det:"node-modules",cat:"health",...aw(d.truncated?f>1e8?(0,U.ph)("disc.nodeModulesDormantFloor","Your node_modules folders weigh at least {0} - {1} of that belongs to projects you haven't opened in a month or more. af360 stopped measuring some of them before the end.",aP(e),aP(f)):(0,U.ph)("disc.nodeModulesFloor",`Your node_modules folders weigh at least {0} across {1} project${1===c.length?"":"s"} - af360 stopped measuring some of them before the end.`,aP(e),c.length):f>1e8?(0,U.ph)("disc.nodeModulesDormant","Your node_modules folders weigh {0} - {1} belongs to projects you haven't opened in a month or more.",aP(e),aP(f)):(0,U.ph)("disc.nodeModules",`Your node_modules folders weigh {0} across {1} project${1===c.length?"":"s"}.`,aP(e),c.length)),why:"npm install is what brings node_modules back, so it costs a reinstall rather than the work itself - and that needs the network and the registry on the day you do it.",detail:c.sort((a,b)=>b.bytes-a.bytes).map(a=>`${a.p.name} - ${aP(a.bytes)}${"dormant"===a.p.staleness||"forgotten"===a.p.staleness?" \xb7 dormant":""}`),steps:["Delete node_modules in the ones you are not working in; npm install brings it back when you next need it, as long as you are online."],dev:`${e.toLocaleString()} bytes total, ${f.toLocaleString()} reclaimable`,devDetail:c.sort((a,b)=>b.bytes-a.bytes).map(a=>`${i().join(a.p.path,"node_modules")} - ${a.bytes.toLocaleString()} bytes`)}]}},{id:"build-artifacts",speed:"slow",async run({projects:a,skip:b}){let c=[".next","dist","build","out",".turbo","coverage",".nuxt",".svelte-kit"],d=[],e={truncated:!1};for(let f of a){let a=0,g=[];for(let d of c){let c=await aU(i().join(f.path,d),b,e);c>0&&(a+=c,g.push(d))}a>0&&d.push({p:f,bytes:a,dirs:g})}let f=d.reduce((a,b)=>a+b.bytes,0);return f<2e8?[]:[{id:"build-artifacts",det:"build-artifacts",cat:"health",...aw(e.truncated?(0,U.ph)("disc.buildArtifactsFloor","At least {0} sits in folders that build and test tools create - af360 stopped measuring some of them before the end.",aP(f)):(0,U.ph)("disc.buildArtifacts","{0} sits in folders that build and test tools create.",aP(f))),why:"af360 recognised these by their names. Most are remade the next time the tool runs, and a coverage folder comes from a test rather than a build.",detail:d.sort((a,b)=>b.bytes-a.bytes).map(a=>`${a.p.name} - ${aP(a.bytes)} (${a.dirs.join(", ")})`),steps:["Check what made each folder before deleting it - the names are listed above, and the tool that created one is what puts it back."],devDetail:d.sort((a,b)=>b.bytes-a.bytes).map(a=>`${a.p.path}: ${a.dirs.join(", ")} - ${a.bytes.toLocaleString()} bytes`)}]}},{id:"disk-weight",speed:"slow",async run({projects:a,skip:b}){let c={truncated:!1},e=[".cache",".parcel-cache",".vite","target","__pycache__",".pytest_cache",".venv",".gradle",".m2","Pods",".terraform",".mypy_cache",".tox"],f=[],g=[];for(let h of a){let a=0,j=[];for(let f of e){let e=i().join(h.path,f),k=!1;try{k=(await d.promises.stat(e)).isDirectory()}catch{continue}if(!k)continue;let l={truncated:!1,unreadable:!1},m=await aU(e,b,l);l.truncated&&(c.truncated=!0),m>0?(a+=m,j.push(f)):l.unreadable&&g.push(`${h.name}/${f}`)}a>0&&f.push({p:h,bytes:a,dirs:j})}let h=f.reduce((a,b)=>a+b.bytes,0);return h<1e8&&0===g.length?[]:[{id:"disk-weight",det:"disk-weight",cat:"health",...aw(c.truncated?(0,U.ph)("disc.diskWeightFloor",`At least {0} sits in tool caches across {1} project${1===f.length?"":"s"} - af360 stopped measuring some of them before the end${g.length>0?`, and could not size ${g.length} more folder${1===g.length?"":"s"}`:""}.`,aP(h),f.length):g.length>0?(0,U.ph)("disc.diskWeightPartial",`{0} sits in tool caches across {1} project${1===f.length?"":"s"} - and af360 could not size {2} more folder${1===g.length?"":"s"}.`,aP(h),f.length,g.length):(0,U.ph)("disc.diskWeight",`{0} sits in tool caches across {1} project${1===f.length?"":"s"}.`,aP(h),f.length)),why:"af360 recognised these by their names - the caches build tools keep beside a project. Whether a given one comes back on its own depends on the tool that made it.",detail:[...f.sort((a,b)=>b.bytes-a.bytes).map(a=>`${a.p.name} - ${aP(a.bytes)} (${a.dirs.join(", ")})`),...g.map(a=>`${a} - af360 could not read this folder`)],steps:["Check what made each cache before deleting it - the names are listed above, and some (a virtual environment, a dependency folder) need their own command to come back rather than returning on the next run."],dev:`counted: ${e.join(", ")}. NOT counted here, they have their own detectors: node_modules, .next, dist, build, out, .turbo, coverage, .nuxt, .svelte-kit`,devDetail:f.sort((a,b)=>b.bytes-a.bytes).map(a=>`${a.p.path}: ${a.dirs.join(", ")} - ${a.bytes.toLocaleString()} bytes`)}]}},{id:"runtime-census",speed:"slow",async run({projects:a,skip:b}){let c=process.version.replace(/^v/,""),e=parseInt(c.split(".")[0],10),f=[],g=0,h=0,j=[];for(let k of a_(a,60,b,a=>a.path.toLowerCase())){let a,b=null,l=null;try{b=await d.promises.readFile(i().join(k.path,"package.json"),"utf8")}catch{b=null}try{l=(await d.promises.readFile(i().join(k.path,".nvmrc"),"utf8")).trim()}catch{l=null}if(null===b&&null===l)continue;let m=null;if(null!==b)try{m=JSON.parse(b).engines?.node??null}catch{j.push(`${k.name} - package.json could not be read`);continue}let n=m??(l?l.replace(/^v/,""):null);if(!n){h++;continue}g++;let o=n.trim(),p=/^>=?\s*v?(\d+)/.exec(o),q=/^[\^~]\s*v?(\d+)/.exec(o),r=/^v?(\d+)(?:\.\d+)*\s*$/.exec(o);if("unread"===(a=/\|\||\s-\s|<|\*|x/i.test(o)?"unread":p?e>=parseInt(p[1],10)?"ok":"mismatch":q?e===parseInt(q[1],10)?"ok":"mismatch":r?e===parseInt(r[1],10)?"ok":"mismatch":"unread")){j.push(`${k.name} - "${n}" is a constraint af360 does not read`);continue}"mismatch"===a&&f.push(`${k.name} - asks for ${n}, this machine runs ${c}`)}return 0===f.length&&0===j.length?[]:[{id:"runtime-census",det:"runtime-census",cat:"health",...aw(f.length>0?(0,U.ph)("disc.runtimeCensus",`{0} project${1===f.length?" asks":"s ask"} for a Node version this machine is not running.`,f.length):(0,U.ph)("disc.runtimeCensusUnread",`af360 could not read the Node version {0} project${1===j.length?" asks":"s ask"} for.`,j.length)),why:"The mismatch usually surfaces as an install that fails or a build that behaves differently here than it does everywhere else.",detail:[...f,...j],steps:["Run the project once on the version it asks for - a version manager (nvm, fnm, volta) switches per folder without changing your default."],dev:`installed ${process.version}; ${g} declared, ${h} declared nothing, ${j.length} unread`}]}},{id:"repo-front-door",speed:"slow",async run({projects:a}){let b=[],c=[];for(let e of a){if(!e.isGit||e.looksDownloaded)continue;if(!e.remoteKnown){c.push(`${e.name} - af360 could not read this project's remote`);continue}if(!e.hasRemote)continue;let a=async a=>{for(let b of a)try{return await d.promises.stat(i().join(e.path,b)),!0}catch{}return!1},f=[];await a(["README.md","README","readme.md","README.rst","README.txt"])||f.push("README"),await a(["LICENSE","LICENCE","LICENSE.md","LICENCE.md","LICENSE.txt","COPYING"])||f.push("licence"),f.length>0&&b.push({name:e.name,lacks:f})}return 0===b.length&&0===c.length?[]:[{id:"repo-front-door",det:"repo-front-door",cat:"health",...aw(b.length>0?b.every(a=>1===a.lacks.length&&"licence"===a.lacks[0])?(0,U.ph)("disc.frontDoorLicence",`{0} project${1===b.length?" with a remote has":"s with a remote have"} no licence.`,b.length):b.every(a=>1===a.lacks.length&&"README"===a.lacks[0])?(0,U.ph)("disc.frontDoorReadme",`{0} project${1===b.length?" with a remote has":"s with a remote have"} no README.`,b.length):(0,U.ph)("disc.frontDoor",`{0} project${1===b.length?" with a remote is":"s with a remote are"} missing a README or a licence.`,b.length):(0,U.ph)("disc.frontDoorUnread",`af360 could not read the remote on {0} project${1===c.length?"":"s"}, so it has not judged them.`,c.length)),why:"af360 read a remote in each of these, so the code exists somewhere besides this machine - it has not checked whether any of them are public. A licence decides what anyone who can see the code may legally do with it, and its absence is the most restrictive answer rather than the most open one.",detail:[...b.map(a=>`${a.name} - no ${a.lacks.join(", no ")}`),...c],steps:["Add a README saying what the project is, and pick a licence at choosealicense.com - one file each."],dev:`${b.length} with a remote and a gap, ${c.length} unread`}]}},{id:"dep-versions",speed:"slow",async run({projects:a,skip:b}){let c=new Map,d=a_(a,40,b,a=>a.path.toLowerCase());for(let a of d){let b=await aX(a.path);if(b)for(let[d,e]of function(a){let b=[];for(let[c,d]of Object.entries(a.packages??{})){if(!d?.version)continue;let a=c.match(/^node_modules\/((?:@[^/]+\/)?[^/]+)$/);a&&b.push([a[1],d.version])}return b}(b)){let b=c.get(d)??new Map,f=b.get(e)??new Set;f.add(a.name),b.set(e,f),c.set(d,b)}}let e=[...c.entries()].filter(([,a])=>a.size>=3).sort((a,b)=>b[1].size-a[1].size);if(0===e.length)return[];let[f]=e[0],g=0;for(let a of d)g+=await aU(i().join(a.path,"node_modules",f),b);let[,h]=e[0];return[{id:"dep-versions",det:"dep-versions",cat:"health",...aw(g>0?(0,U.ph)("disc.depVersionsSize","{0} exists in {1} different versions across the {3} projects af360 read - {2} of the same library.",f,h.size,aP(g),d.length):(0,U.ph)("disc.depVersions","{0} exists in {1} different versions across the {2} projects af360 read.",f,h.size,d.length)),why:"Nothing is broken; it's just the same code paid for several times over in disk and install time.",detail:e.map(([a,b])=>`${a} - ${b.size} versions: ${[...b.keys()].join(", ")}`),steps:["Aligning versions is optional housekeeping: bump the stragglers next time you're in each project anyway."],devDetail:e.flatMap(([a,b])=>[...b.entries()].map(([b,c])=>`${a}@${b}: ${[...c].join(", ")}`))}]}},{id:"dep-arrivals",speed:"slow",async run({projects:a,skip:b}){let c=await a$(a,b),d=[],e=function(a){let b=null,c="";for(let d of a){if(!d.read||!d.recordedAt||0===d.arrivals.length||d.recordedAt<=c)continue;let a=d.arrivals.find(a=>!a.from)??d.arrivals[0];b={project:d,arrival:a},c=d.recordedAt}return b}(c);if(e){let{project:a,arrival:b}=e,c=(0,o.OL)(a.recordedAt),f=a.arrivals.length,g=a.arrivals.filter(a=>a.installScript);d.push({id:"dep-arrivals",det:"dep-arrivals",cat:"health",...aw(1===f?(0,U.ph)("disc.depArrival","{0} {1} joined {2}'s dependency list, recorded on {3}.",b.name,b.version,a.name,c):(0,U.ph)("disc.depArrivalMany","{0} packages joined or moved in {1}'s dependency list, recorded on {2}.",f,a.name,c)),why:"That date is when the project committed the change. An install often happens earlier than the commit that writes it down, so this is the day the record moved rather than the day the package landed on the disk.",detail:[...a.arrivals.map(a=>`${a.name} ${a.version}${a.from?` - was ${a.from}`:" - new"}${a.installScript?", and it runs its own code at install time":""}`),...a.removed.map(a=>`${a} - left the list`)],steps:g.length>0?["The full list of dependencies that run their own code at install time is on the 'Dependencies with install scripts' discovery - this card marks them, that one counts them."]:["Nothing to do. This is the estate's own record, read back."],dev:`parsed from ${q} at two commits via git show - no diff text was read, because a version-only bump does not carry the package name into the hunk`,devDetail:[`${a.path}: ${a.commit} (${a.recordedAt})`,...a.arrivals.map(a=>`${a.name}: ${a.from??"(absent)"} -> ${a.version}${a.installScript?" [install script]":""}`)]})}let f=c.filter(a=>!a.read),g=c.filter(a=>a.read&&(a.otherNpm.length>0||a.unread.length>0));if(f.length>0&&d.push({id:"dep-arrivals-unread",det:"dep-arrivals",cat:"health",...aw(1===f.length?(0,U.ph)("disc.depUnread","af360 has no dependency answer for {0}.",f[0].name):(0,U.ph)("disc.depUnreadMany","af360 has no dependency answer for {0} projects.",f.length)),why:"Each one is listed above with its own reason - a lockfile format af360 does not read yet, a project with no dependency list, or a history git would not give up. This is af360 saying it has not looked, which is a different thing from saying nothing arrived.",detail:f.map(a=>{let b=a.unread.map(a=>`${a.file} (${a.tool})`).join(", ");return b?`${a.name} - ${b}`:`${a.name} - ${a.reason??"af360 could not read it"}`}),steps:["Nothing to do. This names a gap in what af360 can read, not a problem in your project."],dev:`v1 parses ${q} only; other lockfiles are detected by name through git ls-files and reported unread rather than counted as zero`,devDetail:f.map(a=>`${a.path}: ${a.reason??"unreadable lockfile format"}${a.unread.length?` [${a.unread.map(a=>a.file).join(", ")}]`:""}`)}),g.length>0){let a=g[0];d.push({id:"dep-arrivals-partial",det:"dep-arrivals",cat:"health",...aw(1===g.length?(0,U.ph)("disc.depPartial","af360 read 1 of the {1} dependency lists it found in {0}.",a.name,1+a.otherNpm.length+a.unread.length):(0,U.ph)("disc.depPartialMany","af360 read one dependency list each in {0} projects that have more than one.",g.length)),why:"af360 answers for one dependency list per project in this version, and it says which. The others are not read yet - that is different from there being nothing in them.",detail:g.flatMap(a=>[`${a.name} - read: ${a.readFile??"(not recorded)"}`,...a.otherNpm.map(b=>`${a.name} - not read: ${b}`),...a.unread.map(b=>`${a.name} - not read: ${b.file} (${b.tool})`)]),steps:["Nothing to do. The list af360 answered for is named above, so you can see which part of the project this card is about."],dev:"the SHALLOWEST lockfile is chosen - depth decides, and among equal depths git ls-files' alphabetical order breaks the tie, because the sort is stable and reads depth only",devDetail:g.map(a=>`${a.path}: read ${a.readFile}${a.otherNpm.length?`, unanswered ${a.otherNpm.join(", ")}`:""}${a.unread.length?`, unreadable ${a.unread.map(a=>a.file).join(", ")}`:""}`)})}return d}},{id:"estate-stats",speed:"slow",async run({projects:a,skip:b}){let c=await aL(a,b);if(!c||0===c.lines)return[];let d=Object.entries(c.languages).sort((a,b)=>b[1]-a[1]),e=d[0];return[{id:"estate-stats",det:"estate-stats",cat:"health",...aw(c.bounded?(0,U.ph)("disc.estateStatsFloor",`Your estate is at least {0} lines across {1} project${1===c.projectCount?"":"s"} - mostly {2}. af360 stopped counting some projects before the end.`,c.lines.toLocaleString(),c.projectCount,e[0]):(0,U.ph)("disc.estateStats",`Your estate is {0} lines across {1} project${1===c.projectCount?"":"s"} - mostly {2}.`,c.lines.toLocaleString(),c.projectCount,e[0])),why:"You wrote all of that. The full picture makes a good screenshot.",detail:d.map(([a,b])=>`${a} - ${b.toLocaleString()} lines`),link:{href:"/report",label:"See your Estate Report →"},dev:`${c.files.toLocaleString()} files counted (bounded walk; node_modules and build output skipped)`}]}},{id:"todo-census",speed:"slow",async run({projects:a,skip:b}){let c=await aM(a,b);return 0===c.count?[]:[{id:"todo-census",det:"todo-census",cat:"time-capsule",...aw(c.bounded?(0,U.ph)("disc.todoCensusFloor","af360 found at least {0} TODOs in the files it read - it stopped reading some projects before the end.",c.count):(0,U.ph)("disc.todoCensus","af360 found {0} TODOs in the files it read.",c.count)),why:c.quote?`The elder, from ${c.from}:`:void 0,quoted:c.quote?{text:c.quote,what:"a note written in this project's code"}:void 0,dev:`${c.files} files carried at least one`}]}},{id:"abandoned-branches",speed:"slow",async run({projects:a,skip:b}){let c=[];for(let d of a_(a.filter(a=>a.isGit&&!a.looksDownloaded),40,b,a=>a.path.toLowerCase())){let a=await aO(d.path,["for-each-ref","refs/heads","--format=%(refname:short)|%(committerdate:iso8601)"]);if(!a)continue;let b=a.split("\n").filter(Boolean).map(a=>{let[b,c]=a.split("|");return{name:b,age:Date.now()-new Date(c).getTime()}}).filter(a=>a.name!==d.branch&&"main"!==a.name&&"master"!==a.name&&a.age>158112e5);b.length>0&&c.push({p:d,branches:b.map(a=>({name:a.name,ageDays:Math.floor(a.age/864e5)}))})}if(0===c.length)return[];let d=c.reduce((a,b)=>a+b.branches.length,0),e=c.flatMap(a=>a.branches).sort((a,b)=>b.ageDays-a.ageDays)[0];return[{id:"abandoned-branches",det:"abandoned-branches",cat:"time-capsule",...aw((0,U.ph)("disc.abandonedBranches",`{0} branch${1===d?" sits":"es sit"} abandoned across {1} project${1===c.length?"":"s"} - the eldest untouched {2} months.`,d,c.length,Math.floor(e.ageDays/30))),why:"Old branches are usually finished thoughts or abandoned ones. Either way, the name stops meaning anything.",detail:c.flatMap(a=>a.branches.map(b=>`${a.p.name} - ${b.name} \xb7 ${Math.floor(b.ageDays/30)}mo`)),steps:["Merge or delete the ones you recognize; git branch -d refuses to lose unmerged work, so it's a safe sweep."],devDetail:c.map(a=>a.p.path)}]}},{id:"duplicate-downloads",speed:"slow",async run({projects:a,skip:b}){let c=a=>a.toLowerCase().replace(/-(main|master)$/i,"").replace(/\s*\(\d+\)$/,"").replace(/[-_]v?\d+(\.\d+)+$/i,""),d=new Set(a.filter(a=>!a.looksDownloaded).map(a=>c(a.name))),e=new Map;for(let b of a.filter(a=>a.looksDownloaded)){let a=c(b.name);e.set(a,[...e.get(a)??[],b])}let f=[];for(let[a,c]of e)if(d.has(a)||c.length>1)for(let a of c)f.push({p:a,bytes:await aU(a.path,b)});if(0===f.length)return[];let g=f.reduce((a,b)=>a+b.bytes,0);return[{id:"duplicate-downloads",det:"duplicate-downloads",cat:"health",...aw((0,U.ph)("disc.duplicateDownloads",`{0} downloaded folder${1===f.length?"":"s"} share a name with a project you already have - {1} sitting idle.`,f.length,aP(g))),why:"af360 matched these by folder name only - it has not compared their contents or their history with the projects they resemble.",detail:f.sort((a,b)=>b.bytes-a.bytes).map(a=>`${a.p.name} - ${aP(a.bytes)}`),steps:["Open each one beside the project of the same name and check they really are the same work before you delete anything - af360 matched them by name, not by contents."],devDetail:f.map(a=>a.p.path)}]}},{id:"og-preview",speed:"slow",async run({cfg:a,skip:b}){let c=[];for(let d of(await a2(a.urls,b))){let a=await a3(d.url);if(!a){b(d.url);continue}if(!a.html)continue;let e=function(a,b){let c=(()=>{try{return new URL(a).hostname}catch{return a}})(),d=a=>{let c=RegExp(`<meta[^>]+(?:property|name)=["']${a}["'][^>]*content=["']([^"']*)["']|<meta[^>]+content=["']([^"']*)["'][^>]*(?:property|name)=["']${a}["']`,"i"),d=b.match(c);return(d?.[1]??d?.[2])?.trim()||void 0},e=d("og:title")??b.match(/<title[^>]*>([^<]*)<\/title>/i)?.[1]?.trim(),f=d("og:description")??d("description"),g=d("og:image");if(g&&!/^https?:/i.test(g)){try{g=new URL(g,a).href}catch{g=void 0}g&&!/^https?:/i.test(g)&&(g=void 0)}let h=[];return e||h.push("title"),f||h.push("description"),g||h.push("image"),{url:a,host:c,title:e,description:f,image:g,missing:h}}(d.url,a.html),f=d.label||e.host;c.push({id:`og:${d.id}`,det:"og-preview",cat:"site",...aw(0===e.missing.length?(0,U.ph)("disc.ogSharp","{0} looks sharp when shared - this is the card people see.",f):e.missing.includes("image")?(0,U.ph)("disc.ogSharedBare","{0} shows up bare when shared - no {1}.",f,e.missing.join(", no ")):(0,U.ph)("disc.ogSharedThin","{0} shows up thin when shared - no {1}.",f,e.missing.join(", no "))),why:0===e.missing.length?void 0:"Chat apps and social feeds all render this card. It's the first impression.",steps:0===e.missing.length?void 0:["Add the missing og: meta tags to the page head - title, description and a 1200\xd7630 image cover it."],og:e})}return c}},{id:"security-headers",speed:"slow",async run({cfg:a,skip:b}){let c=["strict-transport-security","content-security-policy","x-content-type-options","referrer-policy"],d=[];for(let e of(await a2(a.urls,b))){let a=await a3(e.url);if(!a){b(e.url);continue}if(!a.ok)continue;let f=c.filter(b=>a.headers[b]),g=c.filter(b=>!a.headers[b]);if(0===g.length)continue;let h=e.label||a.host;d.push({id:`headers:${e.id}`,det:"security-headers",cat:"site",...aw((0,U.ph)("disc.securityHeaders","{0} sends {1} of {2} protective headers.",h,f.length,c.length)),why:"Each one closes a small door - browsers do the enforcing, the server just has to ask.",detail:[...f.map(a=>`${a} ✓`),...g.map(a=>`${a} - not sent`)],steps:["Most hosts set these in one config block, in the headers section of your host's settings. Start with strict-transport-security and x-content-type-options - they're the safe two."],dev:`final URL ${a.finalUrl} \xb7 HTTP ${a.status}`})}return d}},{id:"mixed-content",speed:"slow",async run({cfg:a,skip:b}){let c=[];for(let d of(await a2(a.urls,b))){let a=await a3(d.url);if(!a){b(d.url);continue}if(!a.html||!a.finalUrl.startsWith("https:"))continue;let e=[...a.html.matchAll(/(?:src|href)=["'](http:\/\/[^"']+)["']/gi)].map(a=>a[1]).filter(a=>!/^http:\/\/(localhost|127\.)/.test(a));if(0===e.length)continue;let f=d.label||a.host;c.push({id:`mixed:${d.id}`,det:"mixed-content",cat:"site",...aw(a.html.length>=4e5?(0,U.ph)("disc.mixedContentFloor",`{0} is secure, but af360 found {1} piece${1===e.length?"":"s"} loading over plain http in the part of the page it read.`,f,e.length):(0,U.ph)("disc.mixedContent",`{0} is secure, but {1} piece${1===e.length?"":"s"} of it load over plain http.`,f,e.length)),why:"Browsers block or flag mixed pieces - images vanish, scripts don't run, the padlock loses its meaning.",detail:[...new Set(e)].slice(0,6),steps:["Change the http:// references to https:// (or protocol-relative) - most of these hosts already answer on https."]})}return c}},{id:"redirect-chain",speed:"slow",async run({cfg:a,skip:b}){let c=[];for(let d of(await a2(a.urls,b))){let a=await a3(d.url);if(!a){b(d.url);continue}let e=d.label||a.host;a.chain.length>=3&&c.push({id:`redirect:${d.id}`,det:"redirect-chain",cat:"site",...aw((0,U.ph)("disc.redirectChain","A visit to {0} takes {1} hops before landing.",e,a.chain.length-1)),why:"Each hop is a round trip - on slow connections the chain is the loading time.",detail:a.chain,steps:["Point the first address straight at the final one - one redirect (http → https) is normal, more is detour."]});let f=a.host,g=f.split(".");if(2===g.length||3===g.length&&"www"===g[0]){let a=f.startsWith("www.")?f.slice(4):`www.${f}`,b=await a3(`https://${a}/`);b&&b.ok||c.push({id:`www:${d.id}`,det:"redirect-chain",cat:"site",...aw(b?(0,U.ph)("disc.wwwMismatch","{0} answers but {1} returns an error - links with the other form strand visitors.",f,a):(0,U.ph)("disc.wwwNoAnswer","{0} answers and af360 got no reply at all from {1} - links with the other form may strand visitors.",f,a)),why:"People type both. One should redirect to the other.",detail:[`${f} - answers ✓`,`${a} - ${b?`HTTP ${b.status}`:"no answer"}`],steps:["Add a redirect for the unused form at your DNS host or registrar - usually one toggle."]})}}return c}},{id:"robots-blocking",speed:"slow",async run({cfg:a,skip:b}){let c=[];for(let d of(await a2(a.urls,b))){let a=await a3(d.url);if(!a){b(d.url);continue}if(!a.ok)continue;let e=await a6(new URL("/robots.txt",a.finalUrl).href);if(e&&function(a){let b=!1;for(let c of a.split("\n")){let a=c.trim().toLowerCase();if(a.startsWith("user-agent:"))b="*"===a.slice(11).trim();else if(b&&/^disallow:\s*\/\s*$/.test(a))return!0}return!1}(e)){let b=d.label||a.host;c.push({id:`robots:${d.id}`,det:"robots-blocking",cat:"site",...aw((0,U.ph)("disc.robotsBlocking","{0}'s robots.txt turns every search engine away.",b)),why:"Right for a staging site, surprising for a live one - the site won't appear in search either way.",detail:e.split("\n").slice(0,6).map(a=>a.trim()).filter(Boolean),steps:["If this site should be findable, remove the Disallow: / line. If it is meant to be private, this file is not what makes it private - it asks search engines not to list the site, and anyone who has the address can still open it."]})}}return c}},{id:"sitemap-missing",speed:"slow",async run({cfg:a,skip:b}){let c=[];for(let d of(await a2(a.urls,b))){let a=await a3(d.url);if(!a){b(d.url);continue}if(!a.ok)continue;let e=new URL(a.finalUrl).origin,f=await a6(e+"/robots.txt");if(f&&/^sitemap:/im.test(f))continue;let g=await a7(e+"/sitemap.xml");if(g>=200&&g<400)continue;let h=d.label||a.host;c.push({id:`sitemap:${d.id}`,det:"sitemap-missing",cat:"site",...aw((0,U.ph)("disc.sitemapMissing","af360 did not find a sitemap for {0} in the two usual places - search engines find pages slower without one.",h)),why:"Not urgent; most site generators can emit one with a flag.",detail:[`${e}/sitemap.xml - ${g>0?`HTTP ${g}`:"no answer"}`,"robots.txt lists no Sitemap: line"],steps:["Check whether your site already publishes one at another address - WordPress uses /wp-sitemap.xml and Yoast /sitemap_index.xml - and if not, turn on your framework's sitemap option, then add a Sitemap: line to robots.txt."]})}return c}},{id:"deploy-sites",speed:"slow",async run({projects:a,cfg:b,skip:c}){let d=await ag(a,b.urls.map(a=>a.url),c),e=new Set(b.urls.map(a=>(0,V.Pm)(a.url)).filter(Boolean)),f=new Map(b.declinedSites.map(a=>[a.host,a])),g=[],h=new Set;for(let a of d.found){let b;if(e.has(a.host)||h.has(a.host))continue;let c=ah(a),d=f.get(a.host);if(d){if(d.citation===c)continue;b={before:d.citation,after:c}}h.add(a.host),g.push({id:`propose:${a.host}`,det:"deploy-sites",cat:"site",...aw((0,U.ph)("disc.deploySite","{0} is named in {1} - af360 isn't watching it yet.",a.host,a.project)),why:"af360 can only watch an address it has been told about; this one was already written down in your own project.",detail:[`${a.project} - ${a.file} line ${a.line}`],steps:["Watch it if the address is yours and live; set it aside if it isn't."],devDetail:[`${a.projectPath} - ${a.file}:${a.line}`],proposal:{host:a.host,url:`https://${a.host}/`,project:a.project,file:a.file,line:a.line,citation:c,changed:b}})}let i=new Map(b.declinedDates.map(a=>[a.host,a])),j=function(a,b,c){let d=new Set;for(let b of a)!b.manual&&b.name&&d.add(b.name.trim().toLowerCase());for(let a of b)a.name&&d.add(a.name.trim().toLowerCase());for(let a of c){let b=(0,V.Pm)(a.url);b&&d.add(b)}return d.delete(""),d}([],b.manualExpiries,b.urls),k=new Set;for(let a of d.found){let b;if(a.fromProse||k.has(a.host)||j.has(a.host))continue;let c=ah(a),d=i.get(a.host);if(d){if(d.citation===c)continue;b={before:d.citation,after:c}}k.add(a.host),g.push({id:`datepropose:${a.host}`,det:"deploy-sites",cat:"money",...aw((0,U.ph)("disc.dateSuggestHost","{0} deploys on {1} - track its renewal?",a.project,a.host)),why:"af360 read the address out of the file that deploys this project. It has no way to know when that renews, so it is asking rather than filling anything in.",detail:[`${a.project} - ${a.file} line ${a.line}`],steps:["Track it and give it the date, or set it aside."],devDetail:[`${a.projectPath} - ${a.file}:${a.line}`],dateProposal:{host:a.host,name:a.host,project:a.project,file:a.file,line:a.line,citation:c,changed:b}})}if(d.unread.length>0){let a=[...new Set(d.unread.map(a=>a.file))];g.push({id:"deploy-sites-unread",det:"deploy-sites",cat:"site",...aw((0,U.ph)("disc.deploySiteUnread",`af360 could not look inside {0} deployment file${1===d.unread.length?"":"s"} - any address they name is not in the list below.`,d.unread.length)),why:"A file af360 could not open is not a file with nothing in it, so this says so instead of staying quiet.",detail:d.unread.map(a=>`${a.project} - ${a.file}`),steps:["Check the file is readable if you expect an address to come from it; nothing here needs doing otherwise."],dev:`unreadable: ${a.join(", ")}`})}return g}},{id:"inventory-burst",speed:"slow",async run(a){let b=await aG(a),c=[];for(let{p:a,d}of b.diffs)if(an(d)&&(c.push({id:`gone:${a.path.toLowerCase()}`,det:"inventory-burst",cat:"time-capsule",...aw((0,U.ph)("disc.massDeletion","{0} files are gone from {1} since af360 last looked.",d.gone.length,a.name)),why:"A drop that size is usually deliberate, and it is worth being sure it was.",detail:[`${d.before} files before, ${d.after} now - last looked ${(0,o.KN)(new Date(d.since).toISOString())}`,...d.gone],steps:["If this was you, nothing to do. If it was not, a backup or your git remote is where the files still are."],devDetail:[a.path]}),(0,T.logEvent)(`${d.gone.length} files are gone from ${a.name} since the last look.`,"change")),ao(d)){let b=d.added.length+d.edited.length;c.push({id:`burst:${a.path.toLowerCase()}`,det:"inventory-burst",cat:"time-capsule",...aw((0,U.ph)("disc.changeBurst","{0} files changed in {1} since your last look.",b,a.name)),why:"You'd want to know whether that was you.",detail:[`${d.added.length} new, ${d.edited.length} edited - since ${(0,o.KN)(new Date(d.since).toISOString())}`,...d.added,...d.edited],steps:["Nothing to do here. If the work is not committed yet, that is the thing worth doing."],devDetail:[a.path]})}return b.unreadable.length>0&&c.push({id:"inventory-unread",det:"inventory-burst",cat:"time-capsule",...aw((0,U.ph)("disc.inventoryUnread",`af360 could not look inside {0} watched folder${1===b.unreadable.length?"":"s"} this time - nothing above counts them.`,b.unreadable.length)),why:"A folder af360 could not read is not a folder with nothing in it, so this says so instead of staying quiet.",detail:b.unreadable.map(a=>`${a.name} - ${a.reason}`),steps:["Nothing needs doing unless you expected a report about one of these."]}),c}},{id:"file-gone",speed:"slow",async run(a){let b=await aG(a),c=[];for(let{p:a,d}of b.diffs){if(!d.measured||0===d.standing.length)continue;let e=(b.gitHeld.get(a.path)??at).files;for(let b of d.standing){let d=b.rel,f=e.has(d);c.push({id:`filegone:${a.path.toLowerCase()}:${d.toLowerCase()}`,det:"file-gone",cat:"time-capsule",...aw((0,U.ph)("disc.fileGone","{0} is not in {1} any more.",d,a.name)),why:f?"git still holds it - one command brings it back.":"af360 noticed because it was there the last time it looked.",detail:[`last seen ${(0,o.KN)(new Date(b.since).toISOString())}`,`last known path: ${i().join(a.path,d)}`],steps:f?["Run this in the project folder to bring the file back:",au(d)]:["If you meant to delete it, nothing to do. If not, a backup is where it still is - af360 could not find it in git."],copy:f?{text:au(d)}:void 0,devDetail:[`${a.path} - ${d}`]})}}return c}},{id:"dns-watch",speed:"slow",async run({cfg:a,skip:b}){let c=[],e=i().join((0,l.yF)(),"dns.json"),f={};try{f=JSON.parse(await d.promises.readFile(e,"utf8"))}catch{}let g=!1;for(let d of[...new Set(a_(a.urls,5,b,a=>a.id).map(a=>{try{return new URL(a.url).hostname}catch{return null}}).filter(Boolean))]){let a=await a8(d);if(!a)continue;let b=f[d];if(b){let e=[];for(let c of new Set([...Object.keys(b.records),...Object.keys(a)])){let d=b.records[c]??[],f=a[c]??[],g=d.join(", ")||"(none)",h=f.join(", ")||"(none)";g!==h&&(("A"===c||"AAAA"===c)&&d.some(a=>f.includes(a))||e.push(`${c}: was ${g} → now ${h}`))}e.length>0&&(c.push({id:`dns:${d}`,det:"dns-watch",cat:"site",...aw((0,U.ph)("disc.dnsWatch","{0}'s DNS changed since last look.",d)),why:"You'd want to know whether that was you.",detail:e,steps:["If you made this change, nothing to do. If not, check your registrar account - DNS is the first thing a hijack touches."],dev:`previous snapshot ${new Date(b.ts).toLocaleString()}`}),(0,T.logEvent)(`${d}'s DNS records changed - ${e.length} record type${1===e.length?"":"s"}.`,"change"))}else(0,T.logEvent)(`DNS snapshot taken for ${d} - I'll mention it if the records change.`,"look");f[d]={ts:Date.now(),records:a},g=!0}if(g)try{await d.promises.mkdir((0,l.yF)(),{recursive:!0}),await d.promises.writeFile(e,JSON.stringify(f,null,2),"utf8")}catch{}return c}},{id:"agent-effects",speed:"slow",async run(a){let b=await aG(a),c=function(a,b=12e5){let c=[...a??[]].filter(a=>a&&Number.isFinite(a.ts)).sort((a,b)=>a.ts-b.ts),d=[];for(let a of c){let c=d[d.length-1];c&&a.ts-c.lastAsk<=b?(c.lastAsk=a.ts,c.end=a.ts+b,c.asks+=1,c.callers.add(a.caller??null)):d.push({start:a.ts,lastAsk:a.ts,end:a.ts+b,asks:1,callers:new Set([a.caller??null])})}return d.map(({callers:a,...b})=>{let c=[...a].filter(a=>a&&!F.includes(String(a).toLowerCase()));return{...b,named:1===c.length&&1===a.size?c[0]:null}})}(await (0,P.Wq)().catch(()=>[]));if(0===c.length)return[];let d=Date.now(),e=function(a,b,c=Date.now(),d={}){let e=d.floor??5,f=d.isBurst??(()=>!1),g=d.isMassDeletion??(()=>!1),h=[];for(let{project:d,diff:k}of a??[]){if(!k||!k.measured)continue;let a=(k.added?.length??0)+(k.edited?.length??0),l=k.gone?.length??0;for(let m of function(a,b){return(b??[]).filter(b=>a.start<b.end&&b.start<a.end)}({start:k.since,end:c},b))for(let[b,n]of[["burst",a],["gone",l]])if(0!==n&&("burst"!==b||!(n<e))){var i,j;h.push({key:`${b}:${function(a){let b=5381,c=String(a??"").toLowerCase();for(let a=0;a<c.length;a++)b=((b<<5)+b^c.charCodeAt(a))>>>0;return b.toString(36)}(d.path)}:${k.since}:${m.start}`,kind:b,project:d.name,n,window:{since:k.since,until:c},session:{start:m.start,lastAsk:m.lastAsk,end:m.end,asks:m.asks},named:m.named,wouldFire:{burst:"burst"===b&&!!f(k),massDeletion:"gone"===b&&!!g(k)},counts:{added:k.added?.length??0,edited:k.edited?.length??0,gone:l,before:k.before,after:k.after},headline:(i=d.name,"gone"===b?1===n?H(G["disc.agentWindowGoneOne"],i):H(G["disc.agentWindowGone"],n,i):1===n?H(G["disc.agentWindowBurstOne"],i):H(G["disc.agentWindowBurst"],n,i)),who:(j=m.named)?H(G["disc.agentNamed"],j):G["disc.agentUnnamed"],honesty:function({project:a,unreadable:b=!1,first:c=!1}={}){let d=[G["disc.agentAlsoYou"]];return d.push(H(G["disc.agentWindowIs"],a)),d.push(G["disc.agentOnlyWhenOpen"]),b&&d.push(H(G["disc.agentUnread"],a)),c&&d.push(H(G["disc.agentFirstLook"],a)),d}({project:d.name,unreadable:!1,first:!1}),grace:function(a=12e5){return H(G["disc.agentGrace"],Math.round(a/6e4))}()})}}return h}(b.diffs.map(({p:a,d:b})=>({project:{name:a.name,path:a.path},diff:b})),c,d,{isBurst:ao,isMassDeletion:an});try{await O(e,d)}catch{}return[]}}],aJ={".ts":"TypeScript",".tsx":"TypeScript",".js":"JavaScript",".jsx":"JavaScript",".mjs":"JavaScript",".py":"Python",".css":"CSS",".scss":"CSS",".html":"HTML",".md":"Markdown",".go":"Go",".rs":"Rust",".java":"Java",".rb":"Ruby",".php":"PHP",".c":"C",".h":"C",".cpp":"C++",".cs":"C#",".swift":"Swift",".kt":"Kotlin",".sql":"SQL",".sh":"Shell",".ps1":"PowerShell",".json":"JSON",".yml":"YAML",".yaml":"YAML"},aK=new Set(["node_modules",".git",".next","dist","build","out",".venv","venv","__pycache__","coverage",".turbo"]);async function aL(a,b=()=>{}){let c=az.__af360Estate;if(c&&Date.now()-c.ts<36e5)return c;let e=a.filter(a=>!a.looksDownloaded),f=0,g=0,h=!1,j={};for(let a of e){let c=1500,e=Date.now()+15e3;async function k(a,b){let h;if(!(c<=0||b>6||Date.now()>e)){try{h=await d.promises.readdir(a,{withFileTypes:!0})}catch{return}for(let l of h){if(c<=0||Date.now()>e)return;let h=i().join(a,l.name);if(l.isSymbolicLink())continue;if(l.isDirectory()){aK.has(l.name)||l.name.startsWith(".")||await k(h,b+1);continue}let m=aJ[i().extname(l.name).toLowerCase()];if(m){c--;try{if((await d.promises.stat(h)).size>524288)continue;let a=(await d.promises.readFile(h,"utf8")).split("\n").length;f++,g+=a,j[m]=(j[m]??0)+a}catch{}}}}}await k(a.path,0),(c<=0||Date.now()>e)&&(b(a.path.toLowerCase()),h=!0)}let l={ts:Date.now(),bounded:h,projectCount:e.length,files:f,lines:g,languages:j};return az.__af360Estate=l,l}async function aM(a,b=()=>{}){let c=az.__af360Census;if(c&&Date.now()-c.ts<36e5)return c;let d={ts:Date.now(),...await ba(a,b)};return az.__af360Census=d,d}async function aN(a,b=()=>{}){let c=0;for(let d of a)("dormant"===d.staleness||"forgotten"===d.staleness)&&(c+=await aU(i().join(d.path,"node_modules"),b));return c}async function aO(a,b){try{let{stdout:c}=await av("git",(0,e.tQ)(b,a),{cwd:a,timeout:8e3,windowsHide:!0,env:(0,e.tl)()});return c.trim()||null}catch{return null}}function aP(a){return a>=1e9?`${(a/1e9).toFixed(1)} GB`:a<1e6?"under 1 MB":`${Math.round(a/1e6)} MB`}async function aQ(a){try{return await d.promises.access(a),!0}catch{return!1}}async function aR(a){try{let b=i().join(a,"package.json");if((await d.promises.stat(b)).size>2e6)return[];let c=JSON.parse(await d.promises.readFile(b,"utf8"));return[...Object.keys(c?.dependencies??{}),...Object.keys(c?.devDependencies??{})]}catch{return[]}}let aS=new Set([".git"]);async function aT(a,b=0,c=Date.now()+2e4,e={truncated:!1}){let f;if(b>8||Date.now()>c)return e.truncated=!0,0;let g=0;try{f=await d.promises.readdir(a,{withFileTypes:!0})}catch{return 0===b&&(e.unreadable=!0),0}for(let h of f){if(Date.now()>c){e.truncated=!0;break}let f=i().join(a,h.name);if(!h.isSymbolicLink()){if(h.isDirectory())aS.has(h.name)||(g+=await aT(f,b+1,c,e));else if(h.isFile())try{g+=(await d.promises.stat(f)).size}catch{}}}return g}async function aU(a,b=()=>{},c={truncated:!1}){let d=a.toLowerCase(),e=aA.get(d);if(e&&Date.now()-e.ts<432e5)return e.truncated&&(b(d),c.truncated=!0),e.bytes;let f={truncated:!1,unreadable:!1},g=await aT(a,0,Date.now()+2e4,f);return aA.set(d,{ts:Date.now(),bytes:g,truncated:f.truncated}),f.unreadable&&(c.unreadable=!0),f.truncated&&(b(d),c.truncated=!0),g}async function aV(a){try{let b=i().join(a,"README.md");if((await d.promises.stat(b)).size>1048576)return null;let c=(await d.promises.readFile(b,"utf8")).trim(),e=c.slice(0,200);if(/\bTODO\b|coming soon/i.test(e)){let a=e.match(/\bTODO\b[^\n.]*|coming soon[^\n.]*/i);return(a?.[0]??"TODO").trim().slice(0,60)}if(c.length>0&&c.length<80)return c.split("\n")[0].slice(0,60);return null}catch{return null}}let aW=new Map;async function aX(a){let b=i().join(a,"package-lock.json"),c=b.toLowerCase();if(aW.has(c))return aW.get(c)??null;let e=null;try{(await d.promises.stat(b)).size<=3e7&&(e=JSON.parse(await d.promises.readFile(b,"utf8")))}catch{}return aW.set(c,e),aW.size>100&&aW.clear(),e}let aY=new Map,aZ=new Map;function a$(a,b){let c=aZ.get("estate");if(c)return c;let d=C(a.map(a=>({name:a.name,path:a.path,isGit:a.isGit})),b);return aZ.set("estate",d),d}function a_(a,b,c,d){for(let e of a.slice(b))c(d(e));return a.slice(0,b)}let a0=null;async function a1(a){let b=Date.now(),c=[],d=[],e=0,f=async()=>{for(;;){let f=e++;if(f>=a.length)return;let g=a[f];if(Date.now()-b>=2e4){d.push(g.id);continue}try{await a3(g.url)}catch{}c.push(g)}};return await Promise.all(Array.from({length:Math.min(6,Math.max(1,a.length))},f)),{reached:c,skipped:d}}async function a2(a,b){a0||(a0=a1(a));let c=await a0;for(let a of c.skipped)b(a);return c.reached}function a3(a){let b=a.toLowerCase(),c=aY.get(b);if(c)return c;let d=a4(a);return aY.set(b,d),d}async function a4(a){let b=[],c=a;try{for(let a=0;a<6;a++){b.push(c);let a=new AbortController,d=setTimeout(()=>a.abort(),1e4);try{let d=await fetch(c,{signal:a.signal,redirect:"manual",cache:"no-store"});if(d.status>=300&&d.status<400){let a=d.headers.get("location");if(!a)return a5(c,d,b,null);try{d.body?.cancel()}catch{}c=new URL(a,c).href;continue}let e=d.ok&&(d.headers.get("content-type")??"").includes("text/html")?(await d.text()).slice(0,4e5):null;return a5(c,d,b,e)}finally{clearTimeout(d)}}return null}catch{return null}}function a5(a,b,c,d){let e={};b.headers.forEach((a,b)=>{e[b.toLowerCase()]=a.slice(0,200)});let f=a;try{f=new URL(a).hostname}catch{}return{finalUrl:a,host:f,status:b.status,ok:b.ok,chain:c,headers:e,html:d}}async function a6(a){let b=new AbortController,c=setTimeout(()=>b.abort(),8e3);try{let c=await fetch(a,{signal:b.signal,redirect:"follow",cache:"no-store"});if(!c.ok)return null;return(await c.text()).slice(0,1e5)}catch{return null}finally{clearTimeout(c)}}async function a7(a){let b=new AbortController,c=setTimeout(()=>b.abort(),8e3);try{let c=await fetch(a,{signal:b.signal,redirect:"follow",cache:"no-store"});try{c.body?.cancel()}catch{}return c.status}catch{return 0}finally{clearTimeout(c)}}async function a8(a){let b={},c=async(a,c)=>{try{let d=(await c()).sort();d.length>0&&(b[a]=d)}catch{}};return await Promise.all([c("A",()=>k().resolve4(a)),c("AAAA",()=>k().resolve6(a)),c("CNAME",()=>k().resolveCname(a)),c("NS",()=>k().resolveNs(a)),c("MX",async()=>(await k().resolveMx(a)).map(a=>`${a.priority} ${a.exchange}`))]),Object.keys(b).length>0?b:null}let a9=new Set([".ts",".tsx",".js",".jsx",".mjs",".py",".css",".html",".md",".go",".rs",".java"]);async function ba(a,b=()=>{}){let c=0,e=0,f=!1,g=null;for(let j of a){let a=600,k=Date.now()+15e3;async function h(b,f){let l;if(!(a<=0||f>6||Date.now()>k)){try{l=await d.promises.readdir(b,{withFileTypes:!0})}catch{return}for(let m of l){if(a<=0||Date.now()>k)return;let l=i().join(b,m.name);if(!m.isSymbolicLink()){if(m.isDirectory()){aK.has(m.name)||m.name.startsWith(".")||await h(l,f+1);continue}if(a9.has(i().extname(m.name))){a--;try{let a=await d.promises.stat(l);if(a.size>262144)continue;let b=(await d.promises.readFile(l,"utf8")).match(/\/\/\s*(TODO|FIXME)[^\n]*|#\s*(TODO|FIXME)[^\n]*|<!--\s*(TODO|FIXME)[^\n]*/g);if(b&&b.length>0&&(c+=b.length,e++,!g||a.mtimeMs<g.mtime)){let c=(0,E.dK)(b[0].replace(/^[/#<!\-\s]+/,"")).slice(0,90);g={mtime:a.mtimeMs,quote:c,from:j.name}}}catch{}}}}}}await h(j.path,0),(a<=0||Date.now()>k)&&(b(j.path.toLowerCase()),f=!0)}let j=g;return{count:c,files:e,bounded:f,quote:j?.quote,from:j?.from}}},23854:(a,b,c)=>{c.d(b,{EH:()=>p,EZ:()=>r,Lr:()=>k,Vk:()=>q,XK:()=>u,aI:()=>t,iS:()=>n,wq:()=>o});var d=c(29021),e=c(35060),f=c(7719),g=c(33873),h=c.n(g);let i=e.EO;async function j(a,b,c){try{let d=i("git",(0,e.tQ)(b,a),{cwd:a,timeout:8e3,windowsHide:!0,maxBuffer:8e6,env:(0,e.tl)()});if(void 0!==c){let a=d.child;a.stdin?.end(c)}let{stdout:f}=await d;return f}catch(a){if("number"==typeof a.code&&1===a.code)return a.stdout??"";return null}}async function k(a){let b={name:a.name,path:a.path,read:!1,total:0,noUpstream:[]};if(!a.isGit)return{...b,read:!0};let c=await j(a.path,["for-each-ref","--format=%(refname:short)%09%(upstream)","refs/heads"]);if(null===c)return b;let d=c.split("\n").map(a=>a.replace(/\r$/,"")).filter(a=>a.length>0),e=[];for(let a of d){let[b,c]=a.split(" ");b&&((c??"").trim()||e.push(b))}return{name:a.name,path:a.path,read:!0,total:d.length,noUpstream:e}}let l=[{rel:".env",kind:"environment file"},{rel:".env.local",kind:"environment file"},{rel:".env.production",kind:"environment file"},{rel:".env.development",kind:"environment file"},{rel:"uploads",kind:"uploads folder"},{rel:"data",kind:"data folder"},{rel:"storage",kind:"storage folder"}],m=new Set([".sqlite",".sqlite3",".db"]);async function n(a){let b,c={name:a.name,path:a.path,read:!1,found:[],lookedFor:"environment files, local databases, and uploads, data and storage folders"};if(!a.isGit)return{...c,read:!0};try{b=await d.promises.readdir(a.path,{withFileTypes:!0})}catch{return c}let e=[];for(let b of l)try{await d.promises.stat(h().join(a.path,b.rel)),e.push(b)}catch{}for(let a of b)a.isFile()&&m.has(h().extname(a.name).toLowerCase())&&e.push({rel:a.name,kind:"local database"});if(0===e.length)return{...c,read:!0};let f=await j(a.path,["check-ignore","--stdin"],e.map(a=>a.rel).join("\n")+"\n");if(null===f)return c;let g=new Set(f.split("\n").map(a=>a.replace(/\r$/,"").trim()).filter(Boolean));return{...c,read:!0,found:e.filter(a=>g.has(a.rel))}}let o=4,p=2e3,q=4e3,r=10,s=new Set(["node_modules",".git",".next","dist","build","out",".venv","venv","__pycache__",".cache","vendor","target"]);async function t(a,b=Date.now()){let c=h().basename(a),e=b+q,g=0,i=!1,j=null;async function k(a,b){let c;if(!j){if(Date.now()>e){j="af360 ran out of time looking inside this folder";return}if(b>o){j="the folder is nested deeper than af360 looks";return}try{c=await d.promises.readdir(a,{withFileTypes:!0})}catch{j="af360 could not open the folder";return}for(let d of c){if(j)return;if(!d.isSymbolicLink()){if(d.isDirectory()){if(".git"===d.name){i=!0;return}if(s.has(d.name)||(0,f.f0)(h().join(a,d.name)))continue;if(await k(h().join(a,d.name),b+1),i)return}else if(d.isFile()&&++g>p){j="the folder holds more files than af360 counts";return}}}}}return(await k(a,0),i)?{name:c,path:a,complete:!0,hasRepoBeneath:!0,files:g}:j?{name:c,path:a,complete:!1,reason:j,hasRepoBeneath:!1,files:g}:{name:c,path:a,complete:!0,hasRepoBeneath:!1,files:g}}async function u(a,b=()=>{}){let c=[];for(let c of a.slice(20))b(c.toLowerCase());for(let b of a.slice(0,20)){let a;try{await d.promises.stat(h().join(b,".git"));continue}catch{}try{a=await d.promises.readdir(b,{withFileTypes:!0})}catch{continue}for(let e of a){if(!e.isDirectory()||e.isSymbolicLink()||e.name.startsWith(".")||s.has(e.name))continue;let a=h().join(b,e.name);if(!(0,f.f0)(a)){try{await d.promises.stat(h().join(a,".git"));continue}catch{}if(c.push(a),c.length>=50)return c}}}return c}},26988:(a,b,c)=>{c.d(b,{Ne:()=>g,cachedGithubScan:()=>h});var d=c(18150);let e=globalThis;function f(a){return{path:a.path,remoteUrl:a.remoteUrl,branch:a.branch,headSha:a.headShaKnown?a.headSha:null,ahead:a.ahead,behind:a.behind,statusKnown:a.statusKnown}}async function g(a,b){if(!await (0,d.fn)())return null;let g=e.__af360GhScan;if(g&&g.key===b)return g.run;let h=d.github.facts(a.map(f)).catch(()=>({at:Date.now(),byPath:{},outage:{kind:"down",reason:"af360 could not complete its check against GitHub"}}));return h.then(async()=>{let a=(0,d.ZL)();if(!a)return;let{logEvent:b}=await Promise.resolve().then(c.bind(c,22191));b(a.ok?"GitHub access is working again - af360 renewed it.":a.reason===d.PC?`${a.reason} - the credential af360 holds stays as it is.`:`GitHub would not renew af360's access - ${a.reason} - reconnect in Connections.`,"connection")}).catch(()=>{}),e.__af360GhScan={key:b,run:h},h}async function h(){let a=e.__af360GhScan;return a?a.run.catch(()=>null):null}},84848:(a,b,c)=>{c.d(b,{W7:()=>k,Wq:()=>h});var d=c(79748),e=c(5731),f=c(65970);let g=new Set([...e.vh,e.G2]);async function h(a=Date.now()){let b=[];for(let a of[e.aC,e.Q6])try{b.push(await (0,d.readFile)(a,"utf8"))}catch{}if(0===b.length)return[];let c=[];for(let d of b.join("").split("\n")){let b=function(a){let b=a.split(" ");if(2!==b.length&&3!==b.length)return null;let c=b[1];if(!g.has(c))return null;let d=Date.parse(b[0]);if(!Number.isFinite(d))return null;if(2===b.length)return{ts:d,tool:c,caller:null};let f=b[2];return f===e.z_||e.Mx.test(f)?{ts:d,tool:c,caller:f}:null}(d);b&&a-b.ts<6048e5&&c.push(b)}return c.sort((a,b)=>a.ts-b.ts),c.slice(-400)}function i(a,b){if(!a)return null;let c=null;for(let d of a.looks??[])d&&"number"==typeof d.scannedAt&&d.scannedAt<=b&&(null===c||d.scannedAt>c)&&(c=d.scannedAt);return"number"==typeof a.scannedAt&&a.scannedAt<=b&&(null===c||a.scannedAt>c)&&(c=a.scannedAt),c}function j(a,b,c=Date.now()){let d=i(b,a.ts),g=null===d?null:(0,f.fF)(d,a.ts),h=a.count>1?` x${a.count}`:"",k=a.caller&&a.caller!==e.z_?a.caller:null,l=k?` by ${k}`:"",m=a.caller===e.z_?" af360 could not identify what asked.":"";if(a.tool===e.G2)return k?`${k} asked for a tool af360 does not have${h} - refused.`:`an agent asked for a tool af360 does not have${h} - refused.${m}`;let n=null===g?"answered from af360's last look.":`answered from last look (${g}).`;return`${a.tool} asked${l}${h} - ${n}${m}`}async function k(a,b=Date.now()){let c=await h(b);return 0===c.length?[]:(function(a){let b=[],c=new Map;for(let d of a){let a=`${d.tool}\u0000${d.caller??""}`,e=c.get(a);if(e&&d.ts-e.ts<6e4){e.count+=1;continue}let f={ts:d.ts,tool:d.tool,caller:d.caller??null,count:1};c.set(a,f),b.push(f)}return b})(c).map(c=>{let d=!!c.caller&&c.caller!==e.z_;return{ts:c.ts,text:j(c,a,b),activityText:function(a,b,c=Date.now()){let d=i(b,a.ts),g=null===d?null:(0,f.fF)(d,a.ts),h=a.count>1?` x${a.count}`:"",k=a.caller&&a.caller!==e.z_?a.caller:null,l=a.caller===e.z_?" af360 could not identify what asked.":"";if(a.tool===e.G2)return j(a,b,c);let m=null===g?"":` - from a look ${g} old`;return`${a.tool} asked${h}${m}.${k?"":l}`}(c,a,b),kind:"mcp",caller:d?c.caller:null,tool:c.tool,count:c.count,callerState:d?"named":c.caller===e.z_?"unidentified":"unrecorded"}})}},87078:(a,b,c)=>{function d(a){try{return new URL(a).hostname.toLowerCase()}catch{return null}}function e(a){if(!a)return null;let b=a.trim();if(!b)return null;let c=null,d=null,e=/^(?:[^@/]+@)?([A-Za-z0-9._-]+):(?!\/\/)(.+)$/.exec(b);if(e&&!/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(b))c=e[1],d=e[2];else try{let a=new URL(b);c=a.hostname,d=a.pathname}catch{return null}let f=(d??"").replace(/^\/+/,"").replace(/\/+$/,"").replace(/\.git$/i,"");return c&&f?`${c.toLowerCase()}/${f.toLowerCase()}`:null}c.d(b,{Pm:()=>d,p8:()=>e})},88804:(a,b,c)=>{c.d(b,{nn:()=>e,ph:()=>f,pl:()=>g,v1:()=>d});let d={and:" and ",comma:", "};function e(a,b,c){return 0===a.length?"":1===a.length?a[0]:a.slice(0,-1).join(c)+b+a[a.length-1]}function f(a,b,...c){return{k:a,en:function(a,b=[]){return a.replace(/\{(\d+)\}/g,(a,c)=>{let d=b[Number(c)];return void 0===d?a:String(d)})}(b,c),slots:c}}function g(a,b){return{k:a,en:b}}}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";exports.id=572,exports.ids=[572],exports.modules={33090:(a,b,c)=>{c.d(b,{c6:()=>i,cN:()=>h,pF:()=>f,pw:()=>g});let d=globalThis,e=d.__af360Incidents??(d.__af360Incidents=[]);function f(a,b,c){!e.some(b=>b.subjectId===a&&!b.endedTs)&&(e.push({id:`${a}:${Date.now()}`,subjectId:a,subject:b,detail:c,startedTs:Date.now(),acknowledged:!1}),e.length>100&&e.splice(0,e.length-100))}function g(a){let b=e.find(b=>b.subjectId===a&&!b.endedTs);b&&(b.endedTs=Date.now())}function h(a){let b=e.find(b=>b.subjectId===a&&!b.endedTs);return!!b&&(b.acknowledged=!0,!0)}function i(){return[...e].reverse()}},39478:(a,b,c)=>{c.d(b,{An:()=>l,Fu:()=>j,I8:()=>r,ml:()=>k});var d=c(22191),e=c(33090),f=c(83720);let g=["server","content-type","x-powered-by","strict-transport-security","cache-control","content-security-policy"],h=globalThis,i=h.__af360Health??(h.__af360Health=new Map);function j(a){for(let[b,c]of i)a.some(a=>a.id===b)||(c.timer&&clearInterval(c.timer),i.delete(b));for(let b of a){let a=i.get(b.id);if(a){a.entry=b;continue}let c={entry:b,history:[]};i.set(b.id,c),n(c),c.timer=setInterval(()=>void n(c),6e4),c.timer.unref?.()}}function k(a){return a.map(a=>{let b=i.get(a.id),c=b?.history??[],d=c[c.length-1];return{entry:a,current:[...c].reverse().find(a=>!a.soft)??null,history:c,sinceText:function(a){let b=a[a.length-1];if(!b)return"First check in progress…";let c=p(a,b.ok)??b.ts;if(b.ok){let a=(0,f.Cp)(c,Date.now());return null===a?`Up since ${f.lt.core}`:`Up for ${a}`}let d=new Date(c),e=new Date().toDateString()===d.toDateString(),g=d.toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});return`Down since ${e?g:`${d.toLocaleDateString([],{month:"short",day:"numeric"})} ${g}`}`}(c.filter(a=>!a.soft)),checking:d?.soft===!0||void 0}})}function l(a,b=!1){let c=a.map(a=>i.get(a.id)).filter(a=>!!a);if(0===c.length)return!1;let d=c.map(a=>a.history[a.history.length-1]);return!(d.some(a=>!a||a.ok)||d.some(a=>"number"==typeof a.status))&&(d.every(a=>a.error===q)||b)}async function m(a){let b=Date.now(),c=new AbortController,d=setTimeout(()=>c.abort(),15e3);try{let d=await fetch(a,{signal:c.signal,cache:"no-store",redirect:"follow"}),e={};for(let a of g){let b=d.headers.get(a);b&&(e[a]=b.slice(0,120))}return{ts:b,ok:d.ok,status:d.status,ms:Date.now()-b,headers:e}}catch(a){return{ts:b,ok:!1,ms:Date.now()-b,error:r(a)}}finally{clearTimeout(d)}}async function n(a){let b=await m(a.entry.url);if(!b.ok){o(a,{...b,soft:!0});for(let c=1;c<3&&(await new Promise(a=>setTimeout(a,4e3)),!(b=await m(a.entry.url)).ok);c++)c<2&&o(a,{...b,soft:!0})}let c=function(a){for(let b=a.history.length-1;b>=0;b--)if(!a.history[b].soft)return a.history[b]}(a);o(a,b);let g=a.entry.label||a.entry.url;if(b.ok||c&&!c.ok){if(b.ok&&c&&!c.ok){let c=p(a.history.filter(a=>!a.soft).slice(0,-1),!1),h=c?(0,f.Cp)(c,b.ts):null,i=c?null===h?` - ${f.I5.outage}`:` - down for ${h}`:"";(0,d.logEvent)(`${g} is back up${i}.`,"closed"),(0,e.pw)(a.entry.id)}}else{let c=b.status?`HTTP ${b.status}`:b.error??"couldn't connect";(0,d.logEvent)(`${g} stopped answering (${c})
|
|
1
|
+
"use strict";exports.id=572,exports.ids=[572],exports.modules={33090:(a,b,c)=>{c.d(b,{c6:()=>i,cN:()=>h,pF:()=>f,pw:()=>g});let d=globalThis,e=d.__af360Incidents??(d.__af360Incidents=[]);function f(a,b,c){!e.some(b=>b.subjectId===a&&!b.endedTs)&&(e.push({id:`${a}:${Date.now()}`,subjectId:a,subject:b,detail:c,startedTs:Date.now(),acknowledged:!1}),e.length>100&&e.splice(0,e.length-100))}function g(a){let b=e.find(b=>b.subjectId===a&&!b.endedTs);b&&(b.endedTs=Date.now())}function h(a){let b=e.find(b=>b.subjectId===a&&!b.endedTs);return!!b&&(b.acknowledged=!0,!0)}function i(){return[...e].reverse()}},39478:(a,b,c)=>{c.d(b,{An:()=>l,Fu:()=>j,I8:()=>r,ml:()=>k});var d=c(22191),e=c(33090),f=c(83720);let g=["server","content-type","x-powered-by","strict-transport-security","cache-control","content-security-policy"],h=globalThis,i=h.__af360Health??(h.__af360Health=new Map);function j(a){for(let[b,c]of i)a.some(a=>a.id===b)||(c.timer&&clearInterval(c.timer),i.delete(b));for(let b of a){let a=i.get(b.id);if(a){a.entry=b;continue}let c={entry:b,history:[]};i.set(b.id,c),n(c),c.timer=setInterval(()=>void n(c),6e4),c.timer.unref?.()}}function k(a){return a.map(a=>{let b=i.get(a.id),c=b?.history??[],d=c[c.length-1];return{entry:a,current:[...c].reverse().find(a=>!a.soft)??null,history:c,sinceText:function(a){let b=a[a.length-1];if(!b)return"First check in progress…";let c=p(a,b.ok)??b.ts;if(b.ok){let a=(0,f.Cp)(c,Date.now());return null===a?`Up since ${f.lt.core}`:`Up for ${a}`}let d=new Date(c),e=new Date().toDateString()===d.toDateString(),g=d.toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});return`Down since ${e?g:`${d.toLocaleDateString([],{month:"short",day:"numeric"})} ${g}`}`}(c.filter(a=>!a.soft)),checking:d?.soft===!0||void 0}})}function l(a,b=!1){let c=a.map(a=>i.get(a.id)).filter(a=>!!a);if(0===c.length)return!1;let d=c.map(a=>a.history[a.history.length-1]);return!(d.some(a=>!a||a.ok)||d.some(a=>"number"==typeof a.status))&&(d.every(a=>a.error===q)||b)}async function m(a){let b=Date.now(),c=new AbortController,d=setTimeout(()=>c.abort(),15e3);try{let d=await fetch(a,{signal:c.signal,cache:"no-store",redirect:"follow"}),e={};for(let a of g){let b=d.headers.get(a);b&&(e[a]=b.slice(0,120))}return{ts:b,ok:d.ok,status:d.status,ms:Date.now()-b,headers:e}}catch(a){return{ts:b,ok:!1,ms:Date.now()-b,error:r(a)}}finally{clearTimeout(d)}}async function n(a){let b=await m(a.entry.url);if(!b.ok){o(a,{...b,soft:!0});for(let c=1;c<3&&(await new Promise(a=>setTimeout(a,4e3)),!(b=await m(a.entry.url)).ok);c++)c<2&&o(a,{...b,soft:!0})}let c=function(a){for(let b=a.history.length-1;b>=0;b--)if(!a.history[b].soft)return a.history[b]}(a);o(a,b);let g=a.entry.label||a.entry.url;if(b.ok||c&&!c.ok){if(b.ok&&c&&!c.ok){let c=p(a.history.filter(a=>!a.soft).slice(0,-1),!1),h=c?(0,f.Cp)(c,b.ts):null,i=c?null===h?` - ${f.I5.outage}`:` - down for ${h}`:"";(0,d.logEvent)(`${g} is back up${i}.`,"closed"),(0,e.pw)(a.entry.id)}}else{let c=b.status?`HTTP ${b.status}`:b.error??"couldn't connect";(0,d.logEvent)(`${g} stopped answering (${c}).`,"change"),(0,e.pF)(a.entry.id,g,c)}}function o(a,b){a.history.push(b);let c=Date.now()-864e5;for(;a.history.length&&a.history[0].ts<c;)a.history.shift()}function p(a,b){let c=null;for(let d=a.length-1;d>=0&&a[d].ok===b;d--)c=a[d].ts;return c}let q="address not found";function r(a){let b=a instanceof Error?"AbortError"===a.name?"timed out":a.message:String(a);return/ENOTFOUND|EAI_AGAIN/.test(b)?q:/ECONNREFUSED/.test(b)?"connection refused":/CERT|certificate/i.test(b)?"certificate problem":/timed out|abort/i.test(b)?"timed out":"couldn't connect"}},40572:(a,b,c)=>{c.d(b,{HF:()=>h,y3:()=>g});var d=c(39478);let e=globalThis,f=e.__af360Services??(e.__af360Services=new Map);function g(a){for(let[b,c]of f)a.some(a=>a.id===b)||(c.timer&&clearInterval(c.timer),f.delete(b));for(let b of a){let a=f.get(b.id);if(a){if(a.entry.endpoint===b.endpoint){a.entry=b;continue}a.timer&&clearInterval(a.timer),f.delete(b.id)}let c={entry:b};f.set(b.id,c),j(c),c.timer=setInterval(()=>void j(c),3e5),c.timer.unref?.()}}function h(a){return a.map(a=>{let b=f.get(a.id),c=b?.last;return{entry:a,current:c&&!c.soft?c:null,since:b?.since??null,checking:c?.soft===!0||void 0}})}async function i(a){let b=Date.now(),c=new AbortController,e=setTimeout(()=>c.abort(),15e3);try{let d=await fetch(a,{signal:c.signal,cache:"no-store",redirect:"follow"});return{ts:b,ok:d.ok,status:d.status,ms:Date.now()-b}}catch(a){return{ts:b,ok:!1,ms:Date.now()-b,error:(0,d.I8)(a)}}finally{clearTimeout(e)}}async function j(a){let b=await i(a.entry.endpoint);if(!b.ok){k(a,{...b,soft:!0});for(let c=1;c<3&&(await new Promise(a=>setTimeout(a,4e3)),!(b=await i(a.entry.endpoint)).ok);c++)c<2&&k(a,{...b,soft:!0})}k(a,b)}function k(a,b){let c=a.last&&!a.last.soft?a.last:void 0;a.last=b,!b.soft&&(c&&c.ok===b.ok||(a.since=b.ts))}}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";exports.id=6992,exports.ids=[6992],exports.modules={65970:(a,b,c)=>{c.d(b,{Cp:()=>k,I:()=>i,I5:()=>g,fF:()=>h,lt:()=>f,nW:()=>d,p0:()=>j});function d(a,b=Date.now()){let c="number"==typeof a?a:new Date(a).getTime();return Number.isFinite(c)&&b-c<0}let e="a time this machine's clock has not reached yet",f={core:e,at:`at ${e}`,recorded:`recorded at ${e}`,since:`since ${e}`},g={outage:"af360 cannot say for how long: this outage is dated as ending before it began"};function h(a,b=Date.now()){let c="number"==typeof a?a:new Date(a).getTime(),d=b-c;if(d<0)return f.recorded;if(d<6e4)return"just now";if(d<36e5)return`${Math.floor(d/6e4)} min ago`;if(d<864e5)return l(Math.floor(d/36e5),"hour")+" ago";if(d<12096e5)return l(Math.floor(d/864e5),"day")+" ago";if(d<48384e5)return l(Math.floor(d/6048e5),"week")+" ago";let e=new Date(c);return e.toLocaleDateString([],{month:"short",day:"numeric",...e.getFullYear()!==new Date(b).getFullYear()?{year:"numeric"}:{}})}function i(a,b=Date.now()){let c=b-("number"==typeof a?a:new Date(a).getTime());return c<0?f.since:`for ${l(Math.floor(c/864e5),"day")}`}function j(a){return a<6e4?"less than a minute":a<36e5?l(Math.floor(a/6e4),"minute"):a<864e5?l(Math.floor(a/36e5),"hour"):l(Math.floor(a/864e5),"day")}function k(a,b){let c=b-a;return c>=0?j(c):null}function l(a,b){return`${a} ${b}${1===a?"":"s"}`}},83720:(a,b,c)=>{c.d(b,{AQ:()=>h,Cp:()=>d.Cp,I:()=>d.I,I5:()=>d.I5,KN:()=>g,OL:()=>f,lt:()=>d.lt,nW:()=>d.nW,p0:()=>d.p0,rq:()=>e});var d=c(65970);function e(a=Date.now()){let b=new Date("number"==typeof a?a:new Date(a).getTime()),c=a=>String(a).padStart(2,"0");return`${b.getFullYear()}-${c(b.getMonth()+1)}-${c(b.getDate())}`}function f(a){return new Date("number"==typeof a?a:new Date(a).getTime()).toLocaleDateString([],{month:"long",day:"numeric",year:"numeric"})}function g(a){return(0,d.fF)(a)}function h(a){return Math.floor((new Date(a).getTime()-Date.now())/864e5)}},86992:(a,b,c)=>{c.d(b,{wt:()=>v,wm:()=>z,R$:()=>E});var d=c(29021),e=c(33873),f=c.n(e),g=c(34631),h=c.n(g),i=c(22191),j=c(83720);let k=new Map;function l(a,b,c=a){let d=Date.now(),e=k.get(c);if(void 0===e||!(d-e<6e5)){if(k.set(c,d),"1"===process.env.AF360_DEBUG)return void console.error(`[af360] ${a}`,b??"");console.error(`[af360] ${a}`)}}let m=globalThis,n=m.__af360Ssl??(m.__af360Ssl=new Map),o=m.__af360Rdap??(m.__af360Rdap=new Map),p=[/let'?s encrypt/i,/google trust/i,/cloudflare/i,/zerossl/i],q={sslManaged:"Auto-renews - nothing to do. I'll flag it if renewal doesn't happen.",sslManagedLate:"This should have auto-renewed by now and hasn't - check with your host.",sslUnknown:"Can't tell if this renews itself - check with your host.",domain:"Usually auto-renews if billing is active.",ladder30:"No sign this one renews itself, and it expires in under a month - a good week to check with your host.",ladder7:"No sign this one renews itself and it expires within the week - this is the one to deal with today.",ladder2:"No sign this one renews itself and it expires in under 48 hours.",ladderGone:"This certificate has already expired - visitors are seeing a warning page."};function r(a){return a<0?0:a<=2?2:a<=7?7:a<=30?30:null}function s(a){let b=r(a);return 0===b?{chip:"red",rung:b,judgment:q.ladderGone}:2===b?{chip:"red",rung:b,judgment:q.ladder2}:7===b?{chip:"red",rung:b,judgment:q.ladder7}:30===b?{chip:"amber",rung:b,judgment:q.ladder30}:{chip:"calm",rung:b,judgment:q.sslUnknown}}async function t(){try{let{github:a,state:b}=await c.e(8150).then(c.bind(c,18150)),d=a.expiry(await b());return d?[d]:[]}catch{return[]}}async function u(a){let b=[],e=[],g=/\.(?:crt|cer|pem)$/i,h=new Set(["node_modules",".git","dist","build",".next","out","vendor",".venv"]),i=new Set;async function j(a,k){let l;if(!(k>3)&&!(b.length+e.length>40)){try{l=await d.promises.readdir(a,{withFileTypes:!0})}catch{return}for(let m of l){let l=f().join(a,m.name);if(m.isDirectory()){if(h.has(m.name)||m.name.startsWith(".")&&".certs"!==m.name)continue;await j(l,k+1)}else if(g.test(m.name)){let a;if(i.has(l.toLowerCase()))continue;i.add(l.toLowerCase());try{a=await d.promises.readFile(l,"utf8")}catch{e.push({kind:"localcert",name:l,reason:"af360 could not open this certificate file"});continue}if(!/-----BEGIN CERTIFICATE-----/.test(a))continue;try{let{X509Certificate:d}=await Promise.resolve().then(c.t.bind(c,55511,23)),g=new d(a),h=new Date(g.validTo);if(isNaN(h.getTime())){e.push({kind:"localcert",name:l,reason:"af360 read this certificate but could not read its expiry date"});continue}b.push({...y(`localcert:${l.toLowerCase()}`,"ssl",f().basename(l),h.toISOString()),issuer:g.issuer?.split("\n")[0]??void 0,note:l})}catch{e.push({kind:"localcert",name:l,reason:"af360 found a certificate here and could not read it"})}}}}}for(let b of a)await j(b,0);return{items:b,unknowns:e}}async function v(a){let b=[],c=[];try{let d=await u(a.roots);b.push(...d.items),c.push(...d.unknowns)}catch{}let d=[...new Set(a.urls.map(a=>{try{return new URL(a.url).hostname}catch{return null}}).filter(a=>!!a))],e=!0===a.domainLookup;for(let a of(await Promise.all(d.map(async a=>{let[d,f]=await Promise.all([function(a){let b=n.get(a);return b&&Date.now()-b.ts<432e5?Promise.resolve(b.value):new Promise(b=>{let c=!1,d=d=>{c||(c=!0,n.set(a,{ts:Date.now(),value:d}),b(d))};try{if(D(a))return d({validTo:null,issuer:null});let b=h().connect({host:a,port:443,servername:a,rejectUnauthorized:!1,timeout:1e4});b.on("secureConnect",()=>{let a=b.getPeerCertificate(),c=a?.valid_to?new Date(a.valid_to):null,e=a?.valid_from?new Date(a.valid_from):null,f=a?.issuer?[a.issuer.O,a.issuer.CN].filter(Boolean).join(" "):null;b.destroy(),d({validTo:c&&!isNaN(c.getTime())?c.toISOString():null,issuer:f||null,serial:a?.serialNumber||void 0,validFrom:e&&!isNaN(e.getTime())?e.toISOString():void 0})}),b.on("timeout",()=>{b.destroy(),d({validTo:null,issuer:null})}),b.on("error",()=>d({validTo:null,issuer:null}))}catch{d({validTo:null,issuer:null})}})}(a),e?B(a):Promise.resolve(A)]);d.validTo?b.push(function(a,b){let c=!!b.issuer&&p.some(a=>a.test(b.issuer)),d={...y(`ssl:${a}`,"ssl",a,b.validTo),issuer:b.issuer??void 0,serial:b.serial,validFrom:b.validFrom};if(!c){let b=s(d.daysLeft),c=`${a}@${b.rung}`;return null===b.rung||x.has(c)||(x.add(c),(0,i.logEvent)(`${a}'s certificate expires in ${d.daysLeft} day${1===d.daysLeft?"":"s"} and af360 has no sign it renews itself
|
|
1
|
+
"use strict";exports.id=6992,exports.ids=[6992],exports.modules={65970:(a,b,c)=>{c.d(b,{Cp:()=>k,I:()=>i,I5:()=>g,fF:()=>h,lt:()=>f,nW:()=>d,p0:()=>j});function d(a,b=Date.now()){let c="number"==typeof a?a:new Date(a).getTime();return Number.isFinite(c)&&b-c<0}let e="a time this machine's clock has not reached yet",f={core:e,at:`at ${e}`,recorded:`recorded at ${e}`,since:`since ${e}`},g={outage:"af360 cannot say for how long: this outage is dated as ending before it began"};function h(a,b=Date.now()){let c="number"==typeof a?a:new Date(a).getTime(),d=b-c;if(d<0)return f.recorded;if(d<6e4)return"just now";if(d<36e5)return`${Math.floor(d/6e4)} min ago`;if(d<864e5)return l(Math.floor(d/36e5),"hour")+" ago";if(d<12096e5)return l(Math.floor(d/864e5),"day")+" ago";if(d<48384e5)return l(Math.floor(d/6048e5),"week")+" ago";let e=new Date(c);return e.toLocaleDateString([],{month:"short",day:"numeric",...e.getFullYear()!==new Date(b).getFullYear()?{year:"numeric"}:{}})}function i(a,b=Date.now()){let c=b-("number"==typeof a?a:new Date(a).getTime());return c<0?f.since:`for ${l(Math.floor(c/864e5),"day")}`}function j(a){return a<6e4?"less than a minute":a<36e5?l(Math.floor(a/6e4),"minute"):a<864e5?l(Math.floor(a/36e5),"hour"):l(Math.floor(a/864e5),"day")}function k(a,b){let c=b-a;return c>=0?j(c):null}function l(a,b){return`${a} ${b}${1===a?"":"s"}`}},83720:(a,b,c)=>{c.d(b,{AQ:()=>h,Cp:()=>d.Cp,I:()=>d.I,I5:()=>d.I5,KN:()=>g,OL:()=>f,lt:()=>d.lt,nW:()=>d.nW,p0:()=>d.p0,rq:()=>e});var d=c(65970);function e(a=Date.now()){let b=new Date("number"==typeof a?a:new Date(a).getTime()),c=a=>String(a).padStart(2,"0");return`${b.getFullYear()}-${c(b.getMonth()+1)}-${c(b.getDate())}`}function f(a){return new Date("number"==typeof a?a:new Date(a).getTime()).toLocaleDateString([],{month:"long",day:"numeric",year:"numeric"})}function g(a){return(0,d.fF)(a)}function h(a){return Math.floor((new Date(a).getTime()-Date.now())/864e5)}},86992:(a,b,c)=>{c.d(b,{wt:()=>v,wm:()=>z,R$:()=>E});var d=c(29021),e=c(33873),f=c.n(e),g=c(34631),h=c.n(g),i=c(22191),j=c(83720);let k=new Map;function l(a,b,c=a){let d=Date.now(),e=k.get(c);if(void 0===e||!(d-e<6e5)){if(k.set(c,d),"1"===process.env.AF360_DEBUG)return void console.error(`[af360] ${a}`,b??"");console.error(`[af360] ${a}`)}}let m=globalThis,n=m.__af360Ssl??(m.__af360Ssl=new Map),o=m.__af360Rdap??(m.__af360Rdap=new Map),p=[/let'?s encrypt/i,/google trust/i,/cloudflare/i,/zerossl/i],q={sslManaged:"Auto-renews - nothing to do. I'll flag it if renewal doesn't happen.",sslManagedLate:"This should have auto-renewed by now and hasn't - check with your host.",sslUnknown:"Can't tell if this renews itself - check with your host.",domain:"Usually auto-renews if billing is active.",ladder30:"No sign this one renews itself, and it expires in under a month - a good week to check with your host.",ladder7:"No sign this one renews itself and it expires within the week - this is the one to deal with today.",ladder2:"No sign this one renews itself and it expires in under 48 hours.",ladderGone:"This certificate has already expired - visitors are seeing a warning page."};function r(a){return a<0?0:a<=2?2:a<=7?7:a<=30?30:null}function s(a){let b=r(a);return 0===b?{chip:"red",rung:b,judgment:q.ladderGone}:2===b?{chip:"red",rung:b,judgment:q.ladder2}:7===b?{chip:"red",rung:b,judgment:q.ladder7}:30===b?{chip:"amber",rung:b,judgment:q.ladder30}:{chip:"calm",rung:b,judgment:q.sslUnknown}}async function t(){try{let{github:a,state:b}=await c.e(8150).then(c.bind(c,18150)),d=a.expiry(await b());return d?[d]:[]}catch{return[]}}async function u(a){let b=[],e=[],g=/\.(?:crt|cer|pem)$/i,h=new Set(["node_modules",".git","dist","build",".next","out","vendor",".venv"]),i=new Set;async function j(a,k){let l;if(!(k>3)&&!(b.length+e.length>40)){try{l=await d.promises.readdir(a,{withFileTypes:!0})}catch{return}for(let m of l){let l=f().join(a,m.name);if(m.isDirectory()){if(h.has(m.name)||m.name.startsWith(".")&&".certs"!==m.name)continue;await j(l,k+1)}else if(g.test(m.name)){let a;if(i.has(l.toLowerCase()))continue;i.add(l.toLowerCase());try{a=await d.promises.readFile(l,"utf8")}catch{e.push({kind:"localcert",name:l,reason:"af360 could not open this certificate file"});continue}if(!/-----BEGIN CERTIFICATE-----/.test(a))continue;try{let{X509Certificate:d}=await Promise.resolve().then(c.t.bind(c,55511,23)),g=new d(a),h=new Date(g.validTo);if(isNaN(h.getTime())){e.push({kind:"localcert",name:l,reason:"af360 read this certificate but could not read its expiry date"});continue}b.push({...y(`localcert:${l.toLowerCase()}`,"ssl",f().basename(l),h.toISOString()),issuer:g.issuer?.split("\n")[0]??void 0,note:l})}catch{e.push({kind:"localcert",name:l,reason:"af360 found a certificate here and could not read it"})}}}}}for(let b of a)await j(b,0);return{items:b,unknowns:e}}async function v(a){let b=[],c=[];try{let d=await u(a.roots);b.push(...d.items),c.push(...d.unknowns)}catch{}let d=[...new Set(a.urls.map(a=>{try{return new URL(a.url).hostname}catch{return null}}).filter(a=>!!a))],e=!0===a.domainLookup;for(let a of(await Promise.all(d.map(async a=>{let[d,f]=await Promise.all([function(a){let b=n.get(a);return b&&Date.now()-b.ts<432e5?Promise.resolve(b.value):new Promise(b=>{let c=!1,d=d=>{c||(c=!0,n.set(a,{ts:Date.now(),value:d}),b(d))};try{if(D(a))return d({validTo:null,issuer:null});let b=h().connect({host:a,port:443,servername:a,rejectUnauthorized:!1,timeout:1e4});b.on("secureConnect",()=>{let a=b.getPeerCertificate(),c=a?.valid_to?new Date(a.valid_to):null,e=a?.valid_from?new Date(a.valid_from):null,f=a?.issuer?[a.issuer.O,a.issuer.CN].filter(Boolean).join(" "):null;b.destroy(),d({validTo:c&&!isNaN(c.getTime())?c.toISOString():null,issuer:f||null,serial:a?.serialNumber||void 0,validFrom:e&&!isNaN(e.getTime())?e.toISOString():void 0})}),b.on("timeout",()=>{b.destroy(),d({validTo:null,issuer:null})}),b.on("error",()=>d({validTo:null,issuer:null}))}catch{d({validTo:null,issuer:null})}})}(a),e?B(a):Promise.resolve(A)]);d.validTo?b.push(function(a,b){let c=!!b.issuer&&p.some(a=>a.test(b.issuer)),d={...y(`ssl:${a}`,"ssl",a,b.validTo),issuer:b.issuer??void 0,serial:b.serial,validFrom:b.validFrom};if(!c){let b=s(d.daysLeft),c=`${a}@${b.rung}`;return null===b.rung||x.has(c)||(x.add(c),(0,i.logEvent)(`${a}'s certificate expires in ${d.daysLeft} day${1===d.daysLeft?"":"s"} and af360 has no sign it renews itself.`,"change")),{...d,managed:!1,chip:b.chip,judgment:b.judgment}}let e=d.daysLeft<0?"red":d.daysLeft<7?"amber":"calm";return"calm"===e||x.has(a)||(x.add(a),(0,i.logEvent)(`${a}'s certificate should have auto-renewed by now - it hasn't.`,"change")),"calm"===e&&x.delete(a),{...d,managed:!0,chip:e,judgment:"calm"===e?q.sslManaged:q.sslManagedLate}}(a,d)):c.push({kind:"ssl",name:a,reason:"couldn't read the certificate"}),f.date?b.push({...y(`domain:${a}`,"domain",a,f.date),judgment:q.domain}):c.push({kind:"domain",name:a,reason:f.reason??"the public registry didn't share a date"})})),await t())){let c=y(a.id,"manual",a.name,a.date),d=s(c.daysLeft);b.push({...c,chip:d.chip,rung:d.rung,judgment:d.judgment,manual:!1})}for(let c of a.manualExpiries){if(isNaN(new Date(c.date).getTime()))continue;let a=y(c.id,"manual",c.name,new Date(c.date).toISOString()),d=function(a,b=!1){if(b)return{chip:"calm",rung:null,passed:a<0};let c=r(a);return 0===c?{chip:"amber",rung:c,passed:!0}:2===c||7===c?{chip:"red",rung:c,passed:!1}:30===c?{chip:"amber",rung:c,passed:!1}:{chip:"calm",rung:c,passed:!1}}(a.daysLeft,!0===c.done);b.push({...a,note:c.note,manual:!0,cost:!0===c.cost||void 0,done:!0===c.done||void 0,passed:d.passed||void 0,rung:d.rung,chip:d.chip})}return b.sort((a,b)=>a.date.localeCompare(b.date)),{items:b,unknowns:c}}let w=globalThis,x=w.__af360SslFlagged??(w.__af360SslFlagged=new Set);function y(a,b,c,d){let e=(0,j.AQ)(d);return{id:a,kind:b,name:c,date:d,daysLeft:e,chip:z(e)}}function z(a){return a<7?"red":a<30?"amber":"calm"}let A={date:null,reason:"af360 hasn't asked - checking renewal dates means sending this address to a public registry, and that is off until you turn it on in Settings"};async function B(a){if(D(a))return{date:null};let b=o.get(a),c=b?.value.soft?6e4:b?.value.failed?6e5:432e5;if(b&&Date.now()-b.ts<c)return b.value;let d=a.split("."),e=[];for(let a=0;a<=d.length-2;a++)e.push(d.slice(a).join("."));let f={date:null};for(let a of e){let b=await C(a);if(b.date){f=b;break}b.reason&&(f=b)}if(!f.date&&b?.value.date){let c={date:b.value.date};return o.set(a,{ts:Date.now(),value:c}),c}return o.set(a,{ts:Date.now(),value:f}),f}async function C(a){let b=new AbortController,c=setTimeout(()=>b.abort(),2e4);try{let c=await fetch(`https://rdap.org/domain/${encodeURIComponent(a)}`,{headers:{accept:"application/rdap+json","user-agent":"af360/0.1"},redirect:"follow",signal:b.signal,cache:"no-store"});if(404===c.status)return{date:null};if(429===c.status)return{date:null,reason:"the registry is limiting lookups right now - I'll retry soon",failed:!0};if(!c.ok)return l(`Could not check the renewal date for ${a} just now, will retry.`,`RDAP answered ${c.status}`,`rdap:${a}`),{date:null,reason:"the registry lookup failed - I'll retry soon",failed:!0};let d=await c.json(),e=(d?.events??[]).find(a=>a?.eventAction==="expiration");if(!e?.eventDate)return{date:null};let f=new Date(e.eventDate);return isNaN(f.getTime())?{date:null}:{date:f.toISOString()}}catch(b){return l(`Could not check the renewal date for ${a} just now, will retry.`,b,`rdap:${a}`),{date:null,reason:"still checking the renewal date - I'll have this shortly",failed:!0,soft:!0}}finally{clearTimeout(c)}}function D(a){return/^\d{1,3}(\.\d{1,3}){3}$/.test(a)||a.includes(":")||/^\[.*\]$/.test(a)}function E(){let a=Date.now();for(let b of o.values())if(b.value.soft&&a-b.ts<12e4)return!0;return!1}}};
|