lastgen 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lastgen",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Check if you started coding before or after AI agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -120,7 +120,8 @@ async function searchFirstCommitByQuery(
120
120
  message: ((commit.message as string) ?? '').split('\n')[0] ?? '',
121
121
  committerDate: (commitCommitter?.date as string | undefined) ?? undefined,
122
122
  };
123
- } catch {
123
+ } catch (err) {
124
+ if (err instanceof Error && err.message.includes('rate limit')) throw err;
124
125
  return null;
125
126
  }
126
127
  }
@@ -0,0 +1 @@
1
+ *,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg: #0a0a0a;--fg: #e0e0e0;--dim: #666;--blue: #3b82f6;--green: #22c55e;--red: #ef4444;--border: #333;--surface: #111}html{font-size:16px}body{background:var(--bg);color:var(--fg);font-family:JetBrains Mono,monospace;line-height:1.6;min-height:100vh}#app{max-width:640px;margin:0 auto;padding:3rem 1.5rem}header{text-align:center;margin-bottom:3rem}h1{font-size:2rem;font-weight:700;letter-spacing:-.02em;color:var(--fg)}.subtitle{color:var(--dim);font-size:.875rem;margin-top:.25rem}.cutoff{color:var(--dim);font-size:.75rem;margin-top:.25rem}.terminal-input{display:flex;align-items:center;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:.75rem 1rem;font-family:inherit;font-size:1rem;transition:border-color .15s}.terminal-input:focus-within{border-color:var(--blue)}.prompt{color:var(--green);font-weight:700;-webkit-user-select:none;user-select:none;flex-shrink:0;margin-right:.5rem}.terminal-input input{flex:1;background:transparent;border:none;outline:none;color:var(--fg);font-family:inherit;font-size:inherit;caret-color:var(--green)}.terminal-input input::placeholder{color:var(--dim)}.terminal-input input.loading{opacity:.5}.lookup-btn{flex-shrink:0;background:transparent;border:none;color:var(--dim);font-family:inherit;font-size:1.25rem;cursor:pointer;padding:0 .25rem;line-height:1;transition:color .15s}.lookup-btn:hover{color:var(--fg)}.status{text-align:center;color:var(--dim);font-size:.875rem;margin-top:1rem}.status.error{color:var(--red)}#certificate-output{margin-top:2rem}pre.certificate,pre.verification{font-family:JetBrains Mono,monospace;font-size:.8rem;line-height:1.4;overflow-x:auto;padding:0;margin:0 auto;width:fit-content;background:transparent;color:var(--fg)}pre.certificate .dim,pre.verification .dim{color:var(--dim)}pre.certificate .bold,pre.verification .bold{font-weight:700}pre.certificate .era-lastgen,.era-lastgen{color:var(--blue)}pre.certificate .era-ainative,.era-ainative{color:var(--green)}pre.verification .pass{color:var(--green);font-weight:700}pre.verification .fail{color:var(--red);font-weight:700}#share-buttons{display:flex;align-items:center;gap:.5rem;margin-top:1rem;justify-content:center}#share-buttons button{background:none;border:none;color:var(--dim);font-family:inherit;font-size:.75rem;padding:0;cursor:pointer;transition:color .15s}#share-buttons button:hover{color:var(--fg)}.share-sep{color:var(--border);font-size:.75rem}#verify-section{margin-top:3rem}.drop-zone{border:1px dashed var(--border);border-radius:6px;padding:1.25rem;text-align:center;color:var(--dim);font-size:.75rem;cursor:pointer;transition:border-color .15s,color .15s}.drop-zone:hover,.drop-zone.dragover{border-color:var(--blue);color:var(--fg)}.drop-zone-link{color:var(--blue)}.drop-zone input[type=file]{display:none}#verify-output{margin-top:1rem}footer{margin-top:3rem;text-align:center;color:var(--dim);font-size:.75rem}footer a{color:var(--blue);text-decoration:none}footer a:hover{text-decoration:underline}footer code{color:var(--dim);font-family:inherit}
@@ -1,5 +1,5 @@
1
- (function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))n(a);new MutationObserver(a=>{for(const i of a)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&n(s)}).observe(document,{childList:!0,subtree:!0});function o(a){const i={};return a.integrity&&(i.integrity=a.integrity),a.referrerPolicy&&(i.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?i.credentials="include":a.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(a){if(a.ep)return;a.ep=!0;const i=o(a);fetch(a.href,i)}})();const V="2025-02-21T00:00:00Z",lt={LAST_GEN:{title:"Last Generation Coder",description:"Wrote code before AI agents shipped"},AI_NATIVE:{title:"AI Native Coder",description:"First verifiable commit after AI agents shipped"}},ft="1.0",et="lastgen_v1",nt=720*60*60*1e3,M="https://api.github.com",pt="lastgen";function mt(t){return{Accept:"application/vnd.github.v3+json","User-Agent":pt}}async function G(t,e,o){const n=await fetch(t,{headers:{...mt(),...o}});if(n.status===403&&n.headers.get("x-ratelimit-remaining")==="0"){const i=n.headers.get("x-ratelimit-reset"),s=i?new Date(Number(i)*1e3).toLocaleTimeString():"soon";throw new Error(`GitHub API rate limit exceeded. Resets at ${s}.`)}if(n.status===404){const a=t.match(/\/users\/([^/?]+)/);throw a!=null&&a[1]?new Error(`GitHub user '${decodeURIComponent(a[1])}' not found. Check the spelling?`):new Error(`Not found: ${t}`)}if(!n.ok)throw new Error(`GitHub API error: ${n.status} ${n.statusText}`);return n}async function ht(t,e){const n=await(await G(`${M}/users/${encodeURIComponent(t)}`)).json();return{login:n.login,id:n.id,name:n.name??null,createdAt:n.created_at}}async function gt(t,e){const o=await wt(t,e);return o!=null&&o.repo&&(o.repoCreatedAt=await yt(o.repo,e)),o}async function yt(t,e){try{return(await(await G(`${M}/repos/${t}`,e)).json()).created_at??void 0}catch{return}}async function _(t,e,o="asc"){try{const n=`${M}/search/commits?q=${encodeURIComponent(t)}&sort=committer-date&order=${o}&per_page=1`,s=(await(await G(n,e,{Accept:"application/vnd.github.cloak-preview+json"})).json()).items,u=s==null?void 0:s[0];if(!u)return null;const l=u.commit,c=l.author,d=l.committer,f=u.repository;return{date:c.date,repo:f.full_name??"",sha:u.sha,message:(l.message??"").split(`
2
- `)[0]??"",committerDate:(d==null?void 0:d.date)??void 0}}catch{return null}}async function wt(t,e){const o=V.slice(0,10);return await _(`author:${t} user:${t} committer-date:<${o}`,e,"desc")??await _(`author:${t} committer-date:<${o}`,e,"desc")??await _(`author:${t}`,e)}async function Ct(t,e,o){const n=`${M}/repos/${t}/commits/${e}`,i=await(await G(n)).json(),s=i.commit,u=s.author,l=s.committer,c=s.verification,d=i.author,f=i.committer,h=i.parents;return{sha:i.sha,authorLogin:(d==null?void 0:d.login)??null,committerLogin:(f==null?void 0:f.login)??null,authorEmail:u.email??null,authorDate:u.date??null,committerDate:(l==null?void 0:l.date)??null,authorId:(d==null?void 0:d.id)??null,verificationReason:(c==null?void 0:c.reason)??null,isRootCommit:Array.isArray(h)&&h.length===0,message:(s.message??"").split(`
3
- `)[0]??"",verified:!!(c!=null&&c.verified)}}function Lt(t){const e=new Date(V).getTime();return new Date(t).getTime()<e?"LAST_GEN":"AI_NATIVE"}function ot(t,e){if(!e)return new Date().toISOString();const o=Tt(e),n=new Date(o).getTime(),a=new Date(t.createdAt).getTime(),i=e.repoCreatedAt?new Date(e.repoCreatedAt).getTime():1/0;return n<i?e.repoCreatedAt??t.createdAt:n<a?o:t.createdAt}function Tt(t){if(!t.committerDate)return t.date;const e=new Date(t.date).getTime();return new Date(t.committerDate).getTime()-e>nt?t.committerDate:t.date}async function at(t,e,o,n,a){const i=JSON.stringify({username:e,githubId:o,proofDate:n,era:a,salt:et});return t(i)}function bt(t){const e=t.slice(0,4).toUpperCase(),o=parseInt(t.slice(4,12),16)%1e6,n=String(o).padStart(6,"0");return`LGC-${e}-${n}`}async function Dt(t,e,o){const n=ot(e,o),a=Lt(n),i=await at(t,e.login,e.id,n,a),s=bt(i);return{version:ft,type:"LASTGEN_CERTIFICATE",identity:{username:e.login,githubId:e.id,name:e.name},proof:{accountCreated:e.createdAt,firstCommit:o??{date:e.createdAt,repo:"",sha:"",message:"(no public commits found - using account creation date)"},proofDate:n},era:a,verification:{hash:`sha256:${i}`,salt:et},certificateNumber:s,issuedAt:new Date().toISOString()}}const it=async t=>{const e=new TextEncoder().encode(t),o=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(o)).map(a=>a.toString(16).padStart(2,"0")).join("")},$t=300*1e3,j=new Map;function vt(t){const e=t.toLowerCase(),o=j.get(e);return o?Date.now()-o.timestamp>$t?(j.delete(e),null):o.certificate:null}function Et(t,e){j.set(t.toLowerCase(),{certificate:e,timestamp:Date.now()})}async function At(t,e){const o=vt(t);if(o)return e("Using cached result..."),o;e("Fetching GitHub profile...");const n=await ht(t);e("Searching for earliest commit...");const a=await gt(n.login);e("Generating certificate...");const i=await Dt(it,n,a);return Et(t,i),i}const m=50;function w(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function L(t){return`<span class="dim">${w(t)}</span>`}function F(t){return`<span class="bold">${w(t)}</span>`}function S(t,e){return`<span class="${t}">${w(e)}</span>`}function b(){return L("+"+"-".repeat(m+2)+"+")}function C(t,e){const o=m-e;return L("|")+" "+t+" ".repeat(Math.max(o,0))+" "+L("|")}function P(){return C("",0)}const k=13;function g(t,e,o){const n=m-k;if(e.length<=n)return[C(L(t)+o,k+e.length)];const a=" ".repeat(k),i=[];let s=e,u=!0;for(;s.length>0;){const l=s.slice(0,n);s=s.slice(n);const c=u?L(t):a;i.push(C(c+w(l),k+l.length)),u=!1}return i}function It(t){const e=t.era==="LAST_GEN",o=lt[t.era],n=[];n.push(b());const a="LASTGEN CERTIFICATE",i=Math.floor((m-a.length)/2),s=m-a.length-i;n.push(C(" ".repeat(i)+F(a)+" ".repeat(s),m)),n.push(b()),n.push(...g("Certificate ",t.certificateNumber,w(t.certificateNumber)));const u=new Date(t.issuedAt).toISOString().slice(0,10);n.push(...g("Issued ",u,w(u))),n.push(P());const l=t.identity.name?`${t.identity.username} (${t.identity.name})`:t.identity.username;n.push(...g("Developer ",l,w(l)));const c=e?"era-lastgen":"era-ainative";if(n.push(...g("Era ",o.title,S(c,o.title))),n.push(...g(" ",o.description,L(o.description))),t.proof.firstCommit.sha){n.push(P());const f=t.proof.firstCommit.repo;n.push(...g("Proof Commit ",f,w(f)));const E=`${t.proof.firstCommit.sha.slice(0,7)} ${t.proof.firstCommit.message.replace(/\n/g," ")}`;n.push(...g(" ",E,L(E)));const r=new Date(t.proof.firstCommit.date).toISOString().slice(0,10);n.push(...g("Commit Date ",r,w(r)))}n.push(P());const d=t.verification.hash;return n.push(...g("Hash ",d,L(d))),n.push(b()),`<pre class="certificate ${c}">${n.join(`
4
- `)}</pre>`}function St(t,e){const o=[];o.push(b());const n="VERIFICATION",a=Math.floor((m-n.length)/2),i=m-n.length-a;o.push(C(" ".repeat(a)+F(n)+" ".repeat(i),m)),o.push(b());for(const s of t){const l=`${s.passed?S("pass","PASS"):S("fail","FAIL")} ${F(s.check)}`;o.push(C(l,6+s.check.length));const c=6,d=m-c;let f=s.detail;for(;f.length>0;){const h=f.slice(0,d);f=f.slice(d);const E=" ".repeat(c)+L(h);o.push(C(E,c+h.length))}}if(o.push(b()),e){const s=S("pass","Certificate is valid.");o.push(C(s,21))}else{const s=S("fail","Certificate verification failed.");o.push(C(s,32))}return o.push(b()),`<pre class="verification">${o.join(`
5
- `)}</pre>`}function Nt(t){const e=t.era==="LAST_GEN"?"Last%20Gen":"AI%20Native",o=t.era==="LAST_GEN"?"blue":"brightgreen";return`[![Last Gen Coder](${`https://img.shields.io/badge/lastgen-${e}-${o}?style=for-the-badge`})](https://github.com/pgagnidze/lastgen)`}async function Rt(t){const e=Nt(t);await navigator.clipboard.writeText(e)}function xt(t){const e=JSON.stringify(t,null,2),o=new Blob([e],{type:"application/json"}),n=URL.createObjectURL(o),a=document.createElement("a");a.href=n,a.download=`lastgen-${t.identity.username}.json`,a.click(),URL.revokeObjectURL(n)}function kt(t){if(typeof t!="object"||t===null)return!1;const e=t;return e.type==="LASTGEN_CERTIFICATE"&&typeof e.version=="string"&&typeof e.identity=="object"&&typeof e.proof=="object"&&typeof e.verification=="object"&&typeof e.certificateNumber=="string"}function Mt(t,e){const o=t.toLowerCase(),n=e.toLowerCase();return new RegExp(`^(\\d+\\+)?${Gt(n)}@users\\.noreply\\.github\\.com$`).test(o)}function Gt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function Ht(t,e,o){const n=[],a=await at(e,t.identity.username,t.identity.githubId,t.proof.proofDate,t.era),i=t.verification.hash.replace("sha256:","");n.push({check:"Hash integrity",passed:a===i,detail:a===i?"Certificate hash is valid":"Certificate hash does not match - data may have been tampered with"});const s=new Date(t.proof.proofDate),u=new Date(V),c=s.getTime()<u.getTime()?"LAST_GEN":"AI_NATIVE";n.push({check:"Era classification",passed:t.era===c,detail:t.era===c?`Era ${t.era} is correct for proof date ${t.proof.proofDate}`:`Era should be ${c} but certificate claims ${t.era}`});const d={login:t.identity.username,id:t.identity.githubId,name:t.identity.name,createdAt:t.proof.accountCreated},f=ot(d,t.proof.firstCommit.sha?t.proof.firstCommit:null),h=Math.abs(new Date(f).getTime()-s.getTime())<6e4;if(n.push({check:"Proof date",passed:h,detail:h?`Proof date ${t.proof.proofDate} is consistent with commit and account data`:`Proof date should be ${f} but certificate claims ${t.proof.proofDate}`}),t.proof.firstCommit.sha)try{const r=await Ct(t.proof.firstCommit.repo,t.proof.firstCommit.sha,o),R=t.identity.username.toLowerCase(),J=(r.authorLogin??"").toLowerCase()===R,q=(r.committerLogin??"").toLowerCase()===R,W=r.authorEmail?Mt(r.authorEmail,t.identity.username):!1,z=J||q||W,x=[];J&&x.push("author login"),q&&x.push("committer login"),W&&x.push("noreply email"),n.push({check:"Identity",passed:z,detail:z?`Matched via: ${x.join(", ")}`:`Commit author (${r.authorLogin}) does not match ${t.identity.username}`});const ct=(t.proof.firstCommit.repo.split("/")[0]??"").toLowerCase()===t.identity.username.toLowerCase();if(n.push({check:"Repo ownership",passed:!0,detail:ct?`Commit is in a repo owned by ${t.identity.username}`:`Commit is in a third-party repo (${t.proof.firstCommit.repo})`}),r.authorId!==null){const T=r.authorId===t.identity.githubId;n.push({check:"GitHub ID",passed:T,detail:T?`GitHub ID ${r.authorId} matches certificate`:`Commit author ID ${r.authorId} does not match certificate ID ${t.identity.githubId}`})}const dt=new Date(r.authorDate??""),ut=new Date(t.proof.firstCommit.date),Y=Math.abs(dt.getTime()-ut.getTime())<6e4;if(n.push({check:"Commit date",passed:Y,detail:Y?`Commit date matches certificate (${r.authorDate})`:`Commit date ${r.authorDate} differs from certificate ${t.proof.firstCommit.date}`}),r.authorDate&&r.committerDate){const T=new Date(r.authorDate).getTime(),H=new Date(r.committerDate).getTime(),Z=Math.abs(H-T),K=Math.round(Z/(1440*60*1e3)),Q=Z<=nt;n.push({check:"Date consistency",passed:Q,detail:Q?`Author/committer date drift: ${K}d (within 30d threshold)`:`Author/committer date drift: ${K}d exceeds 30d - author date may be forged`})}if(r.isRootCommit&&n.push({check:"Root commit",passed:!0,detail:"Commit has no parents (first commit in repo - higher trust)"}),r.verified){const T=r.verificationReason,H=T&&T!=="valid"?` (${T})`:"";n.push({check:"GPG signature",passed:!0,detail:`Commit is GPG-signed${H}`})}}catch(r){const R=r instanceof Error?r.message:String(r);n.push({check:"Commit verification",passed:!1,detail:`Could not fetch commit from GitHub: ${R}`})}return{valid:n.every(r=>r.passed),results:n,certificateNumber:t.certificateNumber,username:t.identity.username}}const p=t=>document.querySelector(t),_t=p("#lookup-form"),N=p("#username-input"),D=p("#status"),O=p("#certificate-output"),X=p("#share-buttons"),A=p("#copy-link"),I=p("#copy-badge"),Pt=p("#download-json"),$=p("#drop-zone"),B=p("#file-input"),y=p("#verify-output");let v=null;function Ot(t){D.textContent=t,D.hidden=!1,D.className="status"}function Ut(t){D.textContent=t,D.hidden=!1,D.className="status error"}function jt(){D.hidden=!0}function tt(t){N.disabled=t,t?N.classList.add("loading"):N.classList.remove("loading")}async function st(t){const e=t.trim();if(e){tt(!0),O.hidden=!0,X.hidden=!0,v=null;try{const o=await At(e,Ot);v=o,O.innerHTML=It(o),O.hidden=!1,X.hidden=!1,jt();const n=new URL(window.location.href);n.searchParams.set("u",e),history.replaceState(null,"",n)}catch(o){const n=o instanceof Error?o.message:String(o);Ut(n)}finally{tt(!1)}}}_t.addEventListener("submit",t=>{t.preventDefault(),st(N.value)});A.addEventListener("click",async()=>{if(v)try{await navigator.clipboard.writeText(window.location.href),A.textContent="copied!",setTimeout(()=>{A.textContent="copy link"},2e3)}catch{A.textContent="failed",setTimeout(()=>{A.textContent="copy link"},2e3)}});I.addEventListener("click",async()=>{if(v)try{await Rt(v),I.textContent="copied!",setTimeout(()=>{I.textContent="copy badge"},2e3)}catch{I.textContent="failed",setTimeout(()=>{I.textContent="copy badge"},2e3)}});Pt.addEventListener("click",()=>{v&&xt(v)});async function rt(t){y.hidden=!0;let e;try{const o=await t.text();e=JSON.parse(o)}catch{y.innerHTML='<p class="error">Invalid JSON file.</p>',y.hidden=!1;return}if(!kt(e)){y.innerHTML='<p class="error">File is not a valid lastgen certificate.</p>',y.hidden=!1;return}y.innerHTML='<p class="status">Verifying...</p>',y.hidden=!1;try{const o=await Ht(e,it);y.innerHTML=St(o.results,o.valid)}catch(o){const n=o instanceof Error?o.message:String(o);y.innerHTML=`<p class="error">${Ft(n)}</p>`}}function Ft(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}$.addEventListener("click",()=>{B.click()});B.addEventListener("change",()=>{var e;const t=(e=B.files)==null?void 0:e[0];t&&rt(t)});$.addEventListener("dragover",t=>{t.preventDefault(),$.classList.add("dragover")});$.addEventListener("dragleave",()=>{$.classList.remove("dragover")});$.addEventListener("drop",t=>{var o;t.preventDefault(),$.classList.remove("dragover");const e=(o=t.dataTransfer)==null?void 0:o.files[0];e&&rt(e)});const Bt=new URLSearchParams(window.location.search),U=Bt.get("u");U&&(N.value=U,st(U));
1
+ (function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&n(s)}).observe(document,{childList:!0,subtree:!0});function o(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function n(i){if(i.ep)return;i.ep=!0;const a=o(i);fetch(i.href,a)}})();const V="2025-02-21T00:00:00Z",lt={LAST_GEN:{title:"Last Generation Coder",description:"Wrote code before AI agents shipped"},AI_NATIVE:{title:"AI Native Coder",description:"First verifiable commit after AI agents shipped"}},ft="1.0",et="lastgen_v1",nt=720*60*60*1e3,M="https://api.github.com",mt="lastgen";function pt(t){return{Accept:"application/vnd.github.v3+json","User-Agent":mt}}async function G(t,e,o){const n=await fetch(t,{headers:{...pt(),...o}});if(n.status===403&&n.headers.get("x-ratelimit-remaining")==="0"){const a=n.headers.get("x-ratelimit-reset"),s=a?new Date(Number(a)*1e3).toLocaleTimeString():"soon";throw new Error(`GitHub API rate limit exceeded. Resets at ${s}.`)}if(n.status===404){const i=t.match(/\/users\/([^/?]+)/);throw i!=null&&i[1]?new Error(`GitHub user '${decodeURIComponent(i[1])}' not found. Check the spelling?`):new Error(`Not found: ${t}`)}if(!n.ok)throw new Error(`GitHub API error: ${n.status} ${n.statusText}`);return n}async function ht(t,e){const n=await(await G(`${M}/users/${encodeURIComponent(t)}`)).json();return{login:n.login,id:n.id,name:n.name??null,createdAt:n.created_at}}async function gt(t,e){const o=await wt(t,e);return o!=null&&o.repo&&(o.repoCreatedAt=await yt(o.repo,e)),o}async function yt(t,e){try{return(await(await G(`${M}/repos/${t}`,e)).json()).created_at??void 0}catch{return}}async function _(t,e,o="asc"){try{const n=`${M}/search/commits?q=${encodeURIComponent(t)}&sort=committer-date&order=${o}&per_page=1`,s=(await(await G(n,e,{Accept:"application/vnd.github.cloak-preview+json"})).json()).items,u=s==null?void 0:s[0];if(!u)return null;const l=u.commit,c=l.author,d=l.committer,f=u.repository;return{date:c.date,repo:f.full_name??"",sha:u.sha,message:(l.message??"").split(`
2
+ `)[0]??"",committerDate:(d==null?void 0:d.date)??void 0}}catch(n){if(n instanceof Error&&n.message.includes("rate limit"))throw n;return null}}async function wt(t,e){const o=V.slice(0,10);return await _(`author:${t} user:${t} committer-date:<${o}`,e,"desc")??await _(`author:${t} committer-date:<${o}`,e,"desc")??await _(`author:${t}`,e)}async function Ct(t,e,o){const n=`${M}/repos/${t}/commits/${e}`,a=await(await G(n)).json(),s=a.commit,u=s.author,l=s.committer,c=s.verification,d=a.author,f=a.committer,h=a.parents;return{sha:a.sha,authorLogin:(d==null?void 0:d.login)??null,committerLogin:(f==null?void 0:f.login)??null,authorEmail:u.email??null,authorDate:u.date??null,committerDate:(l==null?void 0:l.date)??null,authorId:(d==null?void 0:d.id)??null,verificationReason:(c==null?void 0:c.reason)??null,isRootCommit:Array.isArray(h)&&h.length===0,message:(s.message??"").split(`
3
+ `)[0]??"",verified:!!(c!=null&&c.verified)}}function Lt(t){const e=new Date(V).getTime();return new Date(t).getTime()<e?"LAST_GEN":"AI_NATIVE"}function ot(t,e){if(!e)return new Date().toISOString();const o=Tt(e),n=new Date(o).getTime(),i=new Date(t.createdAt).getTime(),a=e.repoCreatedAt?new Date(e.repoCreatedAt).getTime():1/0;return n<a?e.repoCreatedAt??t.createdAt:n<i?o:t.createdAt}function Tt(t){if(!t.committerDate)return t.date;const e=new Date(t.date).getTime();return new Date(t.committerDate).getTime()-e>nt?t.committerDate:t.date}async function it(t,e,o,n,i){const a=JSON.stringify({username:e,githubId:o,proofDate:n,era:i,salt:et});return t(a)}function bt(t){const e=t.slice(0,4).toUpperCase(),o=parseInt(t.slice(4,12),16)%1e6,n=String(o).padStart(6,"0");return`LGC-${e}-${n}`}async function Dt(t,e,o){const n=ot(e,o),i=Lt(n),a=await it(t,e.login,e.id,n,i),s=bt(a);return{version:ft,type:"LASTGEN_CERTIFICATE",identity:{username:e.login,githubId:e.id,name:e.name},proof:{accountCreated:e.createdAt,firstCommit:o??{date:e.createdAt,repo:"",sha:"",message:"(no public commits found - using account creation date)"},proofDate:n},era:i,verification:{hash:`sha256:${a}`,salt:et},certificateNumber:s,issuedAt:new Date().toISOString()}}const at=async t=>{const e=new TextEncoder().encode(t),o=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(o)).map(i=>i.toString(16).padStart(2,"0")).join("")},$t=300*1e3,j=new Map;function vt(t){const e=t.toLowerCase(),o=j.get(e);return o?Date.now()-o.timestamp>$t?(j.delete(e),null):o.certificate:null}function Et(t,e){j.set(t.toLowerCase(),{certificate:e,timestamp:Date.now()})}async function At(t,e){const o=vt(t);if(o)return e("Using cached result..."),o;e("Fetching GitHub profile...");const n=await ht(t);e("Searching for earliest commit...");const i=await gt(n.login);e("Generating certificate...");const a=await Dt(at,n,i);return Et(t,a),a}const p=50;function w(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function L(t){return`<span class="dim">${w(t)}</span>`}function F(t){return`<span class="bold">${w(t)}</span>`}function S(t,e){return`<span class="${t}">${w(e)}</span>`}function b(){return L("+"+"-".repeat(p+2)+"+")}function C(t,e){const o=p-e;return L("|")+" "+t+" ".repeat(Math.max(o,0))+" "+L("|")}function P(){return C("",0)}const k=13;function g(t,e,o){const n=p-k;if(e.length<=n)return[C(L(t)+o,k+e.length)];const i=" ".repeat(k),a=[];let s=e,u=!0;for(;s.length>0;){const l=s.slice(0,n);s=s.slice(n);const c=u?L(t):i;a.push(C(c+w(l),k+l.length)),u=!1}return a}function It(t){const e=t.era==="LAST_GEN",o=lt[t.era],n=[];n.push(b());const i="LASTGEN CERTIFICATE",a=Math.floor((p-i.length)/2),s=p-i.length-a;n.push(C(" ".repeat(a)+F(i)+" ".repeat(s),p)),n.push(b()),n.push(...g("Certificate ",t.certificateNumber,w(t.certificateNumber)));const u=new Date(t.issuedAt).toISOString().slice(0,10);n.push(...g("Issued ",u,w(u))),n.push(P());const l=t.identity.name?`${t.identity.username} (${t.identity.name})`:t.identity.username;n.push(...g("Developer ",l,w(l)));const c=e?"era-lastgen":"era-ainative";if(n.push(...g("Era ",o.title,S(c,o.title))),n.push(...g(" ",o.description,L(o.description))),t.proof.firstCommit.sha){n.push(P());const f=t.proof.firstCommit.repo;n.push(...g("Proof Commit ",f,w(f)));const E=`${t.proof.firstCommit.sha.slice(0,7)} ${t.proof.firstCommit.message.replace(/\n/g," ")}`;n.push(...g(" ",E,L(E)));const r=new Date(t.proof.firstCommit.date).toISOString().slice(0,10);n.push(...g("Commit Date ",r,w(r)))}n.push(P());const d=t.verification.hash;return n.push(...g("Hash ",d,L(d))),n.push(b()),`<pre class="certificate ${c}">${n.join(`
4
+ `)}</pre>`}function St(t,e){const o=[];o.push(b());const n="VERIFICATION",i=Math.floor((p-n.length)/2),a=p-n.length-i;o.push(C(" ".repeat(i)+F(n)+" ".repeat(a),p)),o.push(b());for(const s of t){const l=`${s.passed?S("pass","PASS"):S("fail","FAIL")} ${F(s.check)}`;o.push(C(l,6+s.check.length));const c=6,d=p-c;let f=s.detail;for(;f.length>0;){const h=f.slice(0,d);f=f.slice(d);const E=" ".repeat(c)+L(h);o.push(C(E,c+h.length))}}if(o.push(b()),e){const s=S("pass","Certificate is valid.");o.push(C(s,21))}else{const s=S("fail","Certificate verification failed.");o.push(C(s,32))}return o.push(b()),`<pre class="verification">${o.join(`
5
+ `)}</pre>`}function Nt(t){const e=t.era==="LAST_GEN"?"Last%20Gen":"AI%20Native",o=t.era==="LAST_GEN"?"blue":"brightgreen";return`[![Last Gen Coder](${`https://img.shields.io/badge/lastgen-${e}-${o}?style=for-the-badge`})](https://github.com/pgagnidze/lastgen)`}async function Rt(t){const e=Nt(t);await navigator.clipboard.writeText(e)}function xt(t){const e=JSON.stringify(t,null,2),o=new Blob([e],{type:"application/json"}),n=URL.createObjectURL(o),i=document.createElement("a");i.href=n,i.download=`lastgen-${t.identity.username}.json`,i.click(),URL.revokeObjectURL(n)}function kt(t){if(typeof t!="object"||t===null)return!1;const e=t;return e.type==="LASTGEN_CERTIFICATE"&&typeof e.version=="string"&&typeof e.identity=="object"&&typeof e.proof=="object"&&typeof e.verification=="object"&&typeof e.certificateNumber=="string"}function Mt(t,e){const o=t.toLowerCase(),n=e.toLowerCase();return new RegExp(`^(\\d+\\+)?${Gt(n)}@users\\.noreply\\.github\\.com$`).test(o)}function Gt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function Ht(t,e,o){const n=[],i=await it(e,t.identity.username,t.identity.githubId,t.proof.proofDate,t.era),a=t.verification.hash.replace("sha256:","");n.push({check:"Hash integrity",passed:i===a,detail:i===a?"Certificate hash is valid":"Certificate hash does not match - data may have been tampered with"});const s=new Date(t.proof.proofDate),u=new Date(V),c=s.getTime()<u.getTime()?"LAST_GEN":"AI_NATIVE";n.push({check:"Era classification",passed:t.era===c,detail:t.era===c?`Era ${t.era} is correct for proof date ${t.proof.proofDate}`:`Era should be ${c} but certificate claims ${t.era}`});const d={login:t.identity.username,id:t.identity.githubId,name:t.identity.name,createdAt:t.proof.accountCreated},f=ot(d,t.proof.firstCommit.sha?t.proof.firstCommit:null),h=Math.abs(new Date(f).getTime()-s.getTime())<6e4;if(n.push({check:"Proof date",passed:h,detail:h?`Proof date ${t.proof.proofDate} is consistent with commit and account data`:`Proof date should be ${f} but certificate claims ${t.proof.proofDate}`}),t.proof.firstCommit.sha)try{const r=await Ct(t.proof.firstCommit.repo,t.proof.firstCommit.sha,o),R=t.identity.username.toLowerCase(),J=(r.authorLogin??"").toLowerCase()===R,q=(r.committerLogin??"").toLowerCase()===R,W=r.authorEmail?Mt(r.authorEmail,t.identity.username):!1,z=J||q||W,x=[];J&&x.push("author login"),q&&x.push("committer login"),W&&x.push("noreply email"),n.push({check:"Identity",passed:z,detail:z?`Matched via: ${x.join(", ")}`:`Commit author (${r.authorLogin}) does not match ${t.identity.username}`});const ct=(t.proof.firstCommit.repo.split("/")[0]??"").toLowerCase()===t.identity.username.toLowerCase();if(n.push({check:"Repo ownership",passed:!0,detail:ct?`Commit is in a repo owned by ${t.identity.username}`:`Commit is in a third-party repo (${t.proof.firstCommit.repo})`}),r.authorId!==null){const T=r.authorId===t.identity.githubId;n.push({check:"GitHub ID",passed:T,detail:T?`GitHub ID ${r.authorId} matches certificate`:`Commit author ID ${r.authorId} does not match certificate ID ${t.identity.githubId}`})}const dt=new Date(r.authorDate??""),ut=new Date(t.proof.firstCommit.date),Y=Math.abs(dt.getTime()-ut.getTime())<6e4;if(n.push({check:"Commit date",passed:Y,detail:Y?`Commit date matches certificate (${r.authorDate})`:`Commit date ${r.authorDate} differs from certificate ${t.proof.firstCommit.date}`}),r.authorDate&&r.committerDate){const T=new Date(r.authorDate).getTime(),H=new Date(r.committerDate).getTime(),Z=Math.abs(H-T),K=Math.round(Z/(1440*60*1e3)),Q=Z<=nt;n.push({check:"Date consistency",passed:Q,detail:Q?`Author/committer date drift: ${K}d (within 30d threshold)`:`Author/committer date drift: ${K}d exceeds 30d - author date may be forged`})}if(r.isRootCommit&&n.push({check:"Root commit",passed:!0,detail:"Commit has no parents (first commit in repo - higher trust)"}),r.verified){const T=r.verificationReason,H=T&&T!=="valid"?` (${T})`:"";n.push({check:"GPG signature",passed:!0,detail:`Commit is GPG-signed${H}`})}}catch(r){const R=r instanceof Error?r.message:String(r);n.push({check:"Commit verification",passed:!1,detail:`Could not fetch commit from GitHub: ${R}`})}return{valid:n.every(r=>r.passed),results:n,certificateNumber:t.certificateNumber,username:t.identity.username}}const m=t=>document.querySelector(t),_t=m("#lookup-form"),N=m("#username-input"),D=m("#status"),O=m("#certificate-output"),X=m("#share-buttons"),A=m("#copy-link"),I=m("#copy-badge"),Pt=m("#download-json"),$=m("#drop-zone"),B=m("#file-input"),y=m("#verify-output");let v=null;function Ot(t){D.textContent=t,D.hidden=!1,D.className="status"}function Ut(t){D.textContent=t,D.hidden=!1,D.className="status error"}function jt(){D.hidden=!0}function tt(t){N.disabled=t,t?N.classList.add("loading"):N.classList.remove("loading")}async function st(t){const e=t.trim();if(e){tt(!0),O.hidden=!0,X.hidden=!0,v=null;try{const o=await At(e,Ot);v=o,O.innerHTML=It(o),O.hidden=!1,X.hidden=!1,jt();const n=new URL(window.location.href);n.searchParams.set("u",e),history.replaceState(null,"",n)}catch(o){const n=o instanceof Error?o.message:String(o);Ut(n)}finally{tt(!1)}}}_t.addEventListener("submit",t=>{t.preventDefault(),st(N.value)});A.addEventListener("click",async()=>{if(v)try{await navigator.clipboard.writeText(window.location.href),A.textContent="copied!",setTimeout(()=>{A.textContent="copy link"},2e3)}catch{A.textContent="failed",setTimeout(()=>{A.textContent="copy link"},2e3)}});I.addEventListener("click",async()=>{if(v)try{await Rt(v),I.textContent="copied!",setTimeout(()=>{I.textContent="copy badge"},2e3)}catch{I.textContent="failed",setTimeout(()=>{I.textContent="copy badge"},2e3)}});Pt.addEventListener("click",()=>{v&&xt(v)});async function rt(t){y.hidden=!0;let e;try{const o=await t.text();e=JSON.parse(o)}catch{y.innerHTML='<p class="error">Invalid JSON file.</p>',y.hidden=!1;return}if(!kt(e)){y.innerHTML='<p class="error">File is not a valid lastgen certificate.</p>',y.hidden=!1;return}y.innerHTML='<p class="status">Verifying...</p>',y.hidden=!1;try{const o=await Ht(e,at);y.innerHTML=St(o.results,o.valid)}catch(o){const n=o instanceof Error?o.message:String(o);y.innerHTML=`<p class="error">${Ft(n)}</p>`}}function Ft(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}$.addEventListener("click",()=>{B.click()});B.addEventListener("change",()=>{var e;const t=(e=B.files)==null?void 0:e[0];t&&rt(t)});$.addEventListener("dragover",t=>{t.preventDefault(),$.classList.add("dragover")});$.addEventListener("dragleave",()=>{$.classList.remove("dragover")});$.addEventListener("drop",t=>{var o;t.preventDefault(),$.classList.remove("dragover");const e=(o=t.dataTransfer)==null?void 0:o.files[0];e&&rt(e)});const Bt=new URLSearchParams(window.location.search),U=Bt.get("u");U&&(N.value=U,st(U));
@@ -8,14 +8,15 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
10
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet" />
11
- <script type="module" crossorigin src="/lastgen/assets/index-B5YmfSOd.js"></script>
12
- <link rel="stylesheet" crossorigin href="/lastgen/assets/index-xuvA0IWh.css">
11
+ <script type="module" crossorigin src="/lastgen/assets/index-DEnj1SWa.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/lastgen/assets/index-BMC1WEqV.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="app">
16
16
  <header>
17
17
  <h1>lastgen</h1>
18
18
  <p class="subtitle">Check if you started coding before or after AI agents.</p>
19
+ <p class="cutoff">Cutoff: February 21, 2025 · Claude Code public launch</p>
19
20
  </header>
20
21
 
21
22
  <main>
@@ -1 +0,0 @@
1
- *,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg: #0a0a0a;--fg: #e0e0e0;--dim: #666;--blue: #3b82f6;--green: #22c55e;--red: #ef4444;--border: #333;--surface: #111}html{font-size:16px}body{background:var(--bg);color:var(--fg);font-family:JetBrains Mono,monospace;line-height:1.6;min-height:100vh}#app{max-width:640px;margin:0 auto;padding:3rem 1.5rem}header{text-align:center;margin-bottom:3rem}h1{font-size:2rem;font-weight:700;letter-spacing:-.02em;color:var(--fg)}.subtitle{color:var(--dim);font-size:.875rem;margin-top:.25rem}.terminal-input{display:flex;align-items:center;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:.75rem 1rem;font-family:inherit;font-size:1rem;transition:border-color .15s}.terminal-input:focus-within{border-color:var(--blue)}.prompt{color:var(--green);font-weight:700;-webkit-user-select:none;user-select:none;flex-shrink:0;margin-right:.5rem}.terminal-input input{flex:1;background:transparent;border:none;outline:none;color:var(--fg);font-family:inherit;font-size:inherit;caret-color:var(--green)}.terminal-input input::placeholder{color:var(--dim)}.terminal-input input.loading{opacity:.5}.lookup-btn{flex-shrink:0;background:transparent;border:none;color:var(--dim);font-family:inherit;font-size:1.25rem;cursor:pointer;padding:0 .25rem;line-height:1;transition:color .15s}.lookup-btn:hover{color:var(--fg)}.status{text-align:center;color:var(--dim);font-size:.875rem;margin-top:1rem}.status.error{color:var(--red)}#certificate-output{margin-top:2rem}pre.certificate,pre.verification{font-family:JetBrains Mono,monospace;font-size:.8rem;line-height:1.4;overflow-x:auto;padding:0;margin:0 auto;width:fit-content;background:transparent;color:var(--fg)}pre.certificate .dim,pre.verification .dim{color:var(--dim)}pre.certificate .bold,pre.verification .bold{font-weight:700}pre.certificate .era-lastgen,.era-lastgen{color:var(--blue)}pre.certificate .era-ainative,.era-ainative{color:var(--green)}pre.verification .pass{color:var(--green);font-weight:700}pre.verification .fail{color:var(--red);font-weight:700}#share-buttons{display:flex;align-items:center;gap:.5rem;margin-top:1rem;justify-content:center}#share-buttons button{background:none;border:none;color:var(--dim);font-family:inherit;font-size:.75rem;padding:0;cursor:pointer;transition:color .15s}#share-buttons button:hover{color:var(--fg)}.share-sep{color:var(--border);font-size:.75rem}#verify-section{margin-top:3rem}.drop-zone{border:1px dashed var(--border);border-radius:6px;padding:1.25rem;text-align:center;color:var(--dim);font-size:.75rem;cursor:pointer;transition:border-color .15s,color .15s}.drop-zone:hover,.drop-zone.dragover{border-color:var(--blue);color:var(--fg)}.drop-zone-link{color:var(--blue)}.drop-zone input[type=file]{display:none}#verify-output{margin-top:1rem}footer{margin-top:3rem;text-align:center;color:var(--dim);font-size:.75rem}footer a{color:var(--blue);text-decoration:none}footer a:hover{text-decoration:underline}footer code{color:var(--dim);font-family:inherit}