petdex 0.4.2 → 0.4.4

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/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  The Petdex CLI: browse, install, and submit animated pets for [OpenAI Codex](https://openai.com/codex) from your terminal.
4
4
 
5
- - **Gallery & docs:** <https://petdex.crafter.run>
5
+ - **Gallery & docs:** <https://petdex.dev>
6
6
  - **Repo:** <https://github.com/crafter-station/petdex>
7
- - **Hatch a new pet:** <https://petdex.crafter.run/create>
7
+ - **Hatch a new pet:** <https://petdex.dev/create>
8
8
 
9
9
  ## Install
10
10
 
@@ -102,7 +102,7 @@ This CLI distributes pets. It does not generate them. To create one:
102
102
  4. Codex generates the spritesheet and animations into `~/.codex/pets/<slug>/`.
103
103
  5. Submit it: `petdex submit ~/.codex/pets/<slug>`.
104
104
 
105
- The full step-by-step (with tips on what makes a great pet) lives at <https://petdex.crafter.run/create>.
105
+ The full step-by-step (with tips on what makes a great pet) lives at <https://petdex.dev/create>.
106
106
 
107
107
  ## Failure modes
108
108
 
@@ -125,7 +125,7 @@ install path is just `fetch a JSON manifest, write two files to
125
125
  | --- | --- | --- |
126
126
  | Hangs at `Need to install the following packages: petdex@x` | `npx`'s own confirmation prompt, not a hang. Press `y` or auto-confirm | `npx -y petdex install <slug>` |
127
127
  | `npm ERR! engine Unsupported engine` | Node < 20 | Upgrade Node to 20+ (`nvm install 20` is the easiest path) |
128
- | `manifest fetch 5xx` / network timeout | Slow connection or corporate/national firewall blocking `petdex.crafter.run` | Set a proxy: `HTTPS_PROXY=http://your.proxy:port npx petdex install <slug>` |
128
+ | `manifest fetch 5xx` / network timeout | Slow connection or corporate/national firewall blocking `petdex.dev` | Set a proxy: `HTTPS_PROXY=http://your.proxy:port npx petdex install <slug>` |
129
129
  | `EACCES: permission denied … ~/.codex/pets/` | Pets dir owned by another user | `sudo chown -R "$USER" ~/.codex` or remove the dir and retry |
130
130
  | Windows: `'sh' is not recognized` | CLI version older than 0.1.1 piped through `curl … \| sh` | Upgrade: `npm i -g petdex@latest` or `npx petdex@latest install <slug>` |
131
131
 
package/dist/petdex.js CHANGED
@@ -398,9 +398,9 @@ ${U.message}`),U.fix){let Z=await m8({message:U.fix.prompt,initialValue:!0});if(
398
398
  ${Q}`);let $=process.platform,q=$==="darwin"?"open":$==="win32"?"cmd":"xdg-open",J=$==="win32"?["/c","start","",Q]:[Q];await new Promise((Y)=>{let V=sZ(q,J,{detached:!0,stdio:"ignore"});V.once("error",()=>Y()),V.unref(),Y()})}class g2{config;constructor(Q){if(!Q.clientId)throw new f("config","clientId is required");if(!Q.issuer)throw new f("config","issuer is required");let $=Q.environment??"default",q=typeof Q.storage==="object"&&Q.storage!==null?Q.storage:eJ(Q.storage??"keychain",{environment:$,keychainService:Q.keychainService});this.config={clientId:Q.clientId,issuer:eZ(Q.issuer),scopes:Q.scopes??tZ,storage:q,keychainService:Q.keychainService??"clerk-cli-auth",environment:$,callbackPort:Q.callbackPort??0,timeoutMs:Q.timeoutMs??120000,openBrowser:Q.openBrowser}}async login(){let Q=$Y(),$=await qY(Q),q=JY(),J=await oJ({expectedState:q,port:this.config.callbackPort,timeoutMs:this.config.timeoutMs});try{let Y=new URL(`${this.config.issuer}/oauth/authorize`);Y.searchParams.set("response_type","code"),Y.searchParams.set("client_id",this.config.clientId),Y.searchParams.set("redirect_uri",J.redirectUri),Y.searchParams.set("scope",this.config.scopes.join(" ")),Y.searchParams.set("state",q),Y.searchParams.set("code_challenge",$),Y.searchParams.set("code_challenge_method","S256");try{await(this.config.openBrowser??Qz)(Y.toString())}catch(W){throw new f("config",`Failed to open authorization URL: ${W.message}`)}let{code:V}=await J.waitForCallback(),X=await GY({issuer:this.config.issuer,clientId:this.config.clientId,code:V,codeVerifier:Q,redirectUri:J.redirectUri});await this.setJson("tokens",X);let K=await UQ({issuer:this.config.issuer,accessToken:X.accessToken});return await this.setJson("user",K),{tokens:X,user:K}}finally{J.close()}}async getAccessToken(){let Q=await this.getTokenSet();if(!Q)return null;if((Q.expiresAt??Number.POSITIVE_INFINITY)>=Date.now()+30000)return Q.accessToken;if(!Q.refreshToken)return null;let q=await WY({issuer:this.config.issuer,clientId:this.config.clientId,refreshToken:Q.refreshToken,scopes:this.config.scopes}),J={...Q,...q,refreshToken:q.refreshToken??Q.refreshToken};return await this.setJson("tokens",J),J.accessToken}async whoami(){let Q=await this.getJson("user");if(Q)return Q;let $=await this.getAccessToken();if(!$)return null;let q=await UQ({issuer:this.config.issuer,accessToken:$});return await this.setJson("user",q),q}async logout(){try{await Promise.all([this.config.storage.delete("tokens"),this.config.storage.delete("user")])}catch(Q){throw y2("clear stored credentials",Q)}}async getTokenSet(){return this.getJson("tokens")}async getJson(Q){let $;try{$=await this.config.storage.get(Q)}catch(q){throw y2(`read ${Q}`,q)}if(!$)return null;try{return JSON.parse($)}catch(q){throw y2(`parse stored ${Q}`,q)}}async setJson(Q,$){try{await this.config.storage.set(Q,JSON.stringify($))}catch(q){throw y2(`write ${Q}`,q)}}}L8();var L0=p0(R1(),1);import{execSync as xz}from"node:child_process";import{existsSync as O0,readFileSync as p2,statSync as AQ}from"node:fs";import{homedir as vz}from"node:os";import E1 from"node:path";import{existsSync as Gz,mkdirSync as Wz,unlinkSync as Uz,writeFileSync as jz}from"node:fs";import{homedir as Zz}from"node:os";import jQ from"node:path";function ZY(){return process.env.HOME??Zz()}function zY(){return jQ.join(ZY(),".petdex","runtime","hooks-disabled")}function O8(){return jQ.join(ZY(),".petdex","runtime","update-token")}function T6(){return Gz(zY())?"off":"on"}function A6(Q){let $=zY();if(Q==="off")Wz(jQ.dirname($),{recursive:!0}),jz($,`# Petdex hook killswitch.
399
399
  # Hooks are DISABLED while this file exists.
400
400
  # Re-enable: petdex hooks on (or /petdex from inside your agent).
401
- `,{mode:384});else try{Uz($)}catch{}return Q}function HY(){return A6(T6()==="on"?"off":"on")}import{randomBytes as OY}from"node:crypto";import{existsSync as FQ,statSync as zz}from"node:fs";import{chmod as Hz,mkdir as D6,rename as N8,rm as e0,writeFile as b2}from"node:fs/promises";import{homedir as h2,arch as Fz,platform as w6}from"node:os";import Q0 from"node:path";t1();var o=p0(R1(),1),Lz="https://api.github.com/repos/crafter-station/petdex/releases",FY=30,Oz=5,LY="desktop-v",NY="petdex-desktop-sidecar.js";function LQ(){let Q=w6(),$=Fz(),q=Q==="darwin"?"darwin":Q==="linux"?"linux":Q==="win32"?"win32":Q,J=$==="arm64"?"arm64":$==="x64"?"x64":$;return{osLabel:q,archLabel:J,assetSuffix:`${q}-${J}`}}function E0(){let Q=w6()==="win32"?".exe":"";if(w6()==="darwin"){let $=I0(),q=["/Applications/Petdex.app/Contents/MacOS/petdex-desktop",Q0.join($,"Applications","Petdex.app","Contents","MacOS","petdex-desktop")];for(let J of q)if(FQ(J))return J}return Q0.join(h2(),".petdex","bin",`petdex-desktop${Q}`)}function f2(){if(w6()==="darwin"){let Q=I0(),$=["/Applications/Petdex.app/Contents/Resources/sidecar/server.js",Q0.join(Q,"Applications","Petdex.app","Contents","Resources","sidecar","server.js")];for(let q of $)if(FQ(q))return q}return Q0.join(h2(),".petdex","sidecar","server.js")}async function OQ(Q={}){let $=Q.fetchOverride??fetch,q=Q.maxPages??Oz,J=0;for(let Y=1;Y<=q;Y++){let V=`${Lz}?per_page=${FY}&page=${Y}`,X=await $(V,{headers:{Accept:"application/vnd.github+json"}});if(!X.ok)throw Error(`GitHub API ${X.status}`);let K=await X.json();if(!Array.isArray(K)||K.length===0)break;J+=K.length;let W=K.find((G)=>!G.draft&&!G.prerelease&&typeof G.tag_name==="string"&&G.tag_name.startsWith(LY));if(W)return W;if(K.length<FY)break}throw Error(`No ${LY}* release found in the last ${J} releases (scanned ${q} page(s))`)}function Nz(Q){return Q.assets.find(($)=>$.name===NY)??null}function m2(Q,$=LQ()){let q=`petdex-desktop-${$.assetSuffix}`,J=Q.assets.find((Y)=>Y.name===q)??Q.assets.find((Y)=>Y.name.startsWith(q));if(J)return{kind:"bare",target:$,binAsset:J,sidecarAsset:Nz(Q)};if($.osLabel==="darwin"){let Y=BY(Q,$.archLabel);if(Y)return{kind:"macos-dmg",target:$,dmgAsset:Y}}if($.osLabel==="linux")return{kind:"unsupported",target:$,reason:`No Linux desktop binary for ${$.archLabel} in ${Q.tag_name}.`,hint:"Hooks-only setup still works on Linux: run `petdex hooks install`. Desktop Linux support is tracked in issue #296."};return{kind:"unsupported",target:$,reason:`No desktop binary for ${$.assetSuffix} in ${Q.tag_name}.`,hint:"Run `petdex hooks install` for hooks-only setup, or download a supported desktop build from https://petdex.crafter.run/download."}}function c2(Q){if(Q.kind!=="unsupported")return Error("Desktop install plan is supported");return Error(`${Q.reason}
401
+ `,{mode:384});else try{Uz($)}catch{}return Q}function HY(){return A6(T6()==="on"?"off":"on")}import{randomBytes as OY}from"node:crypto";import{existsSync as FQ,statSync as zz}from"node:fs";import{chmod as Hz,mkdir as D6,rename as N8,rm as e0,writeFile as b2}from"node:fs/promises";import{homedir as h2,arch as Fz,platform as w6}from"node:os";import Q0 from"node:path";t1();var o=p0(R1(),1),Lz="https://api.github.com/repos/crafter-station/petdex/releases",FY=30,Oz=5,LY="desktop-v",NY="petdex-desktop-sidecar.js";function LQ(){let Q=w6(),$=Fz(),q=Q==="darwin"?"darwin":Q==="linux"?"linux":Q==="win32"?"win32":Q,J=$==="arm64"?"arm64":$==="x64"?"x64":$;return{osLabel:q,archLabel:J,assetSuffix:`${q}-${J}`}}function E0(){let Q=w6()==="win32"?".exe":"";if(w6()==="darwin"){let $=I0(),q=["/Applications/Petdex.app/Contents/MacOS/petdex-desktop",Q0.join($,"Applications","Petdex.app","Contents","MacOS","petdex-desktop")];for(let J of q)if(FQ(J))return J}return Q0.join(h2(),".petdex","bin",`petdex-desktop${Q}`)}function f2(){if(w6()==="darwin"){let Q=I0(),$=["/Applications/Petdex.app/Contents/Resources/sidecar/server.js",Q0.join(Q,"Applications","Petdex.app","Contents","Resources","sidecar","server.js")];for(let q of $)if(FQ(q))return q}return Q0.join(h2(),".petdex","sidecar","server.js")}async function OQ(Q={}){let $=Q.fetchOverride??fetch,q=Q.maxPages??Oz,J=0;for(let Y=1;Y<=q;Y++){let V=`${Lz}?per_page=${FY}&page=${Y}`,X=await $(V,{headers:{Accept:"application/vnd.github+json"}});if(!X.ok)throw Error(`GitHub API ${X.status}`);let K=await X.json();if(!Array.isArray(K)||K.length===0)break;J+=K.length;let W=K.find((G)=>!G.draft&&!G.prerelease&&typeof G.tag_name==="string"&&G.tag_name.startsWith(LY));if(W)return W;if(K.length<FY)break}throw Error(`No ${LY}* release found in the last ${J} releases (scanned ${q} page(s))`)}function Nz(Q){return Q.assets.find(($)=>$.name===NY)??null}function m2(Q,$=LQ()){let q=`petdex-desktop-${$.assetSuffix}`,J=Q.assets.find((Y)=>Y.name===q)??Q.assets.find((Y)=>Y.name.startsWith(q));if(J)return{kind:"bare",target:$,binAsset:J,sidecarAsset:Nz(Q)};if($.osLabel==="darwin"){let Y=BY(Q,$.archLabel);if(Y)return{kind:"macos-dmg",target:$,dmgAsset:Y}}if($.osLabel==="linux")return{kind:"unsupported",target:$,reason:`No Linux desktop binary for ${$.archLabel} in ${Q.tag_name}.`,hint:"Hooks-only setup still works on Linux: run `petdex hooks install`. Desktop Linux support is tracked in issue #296."};return{kind:"unsupported",target:$,reason:`No desktop binary for ${$.assetSuffix} in ${Q.tag_name}.`,hint:"Run `petdex hooks install` for hooks-only setup, or download a supported desktop build from https://petdex.dev/download."}}function c2(Q){if(Q.kind!=="unsupported")return Error("Desktop install plan is supported");return Error(`${Q.reason}
402
402
  ${Q.hint}`)}function BY(Q,$){let q=`Petdex-${$}.dmg`;return Q.assets.find((J)=>J.name===q)??null}function MY(Q){let q=Q.indexOf("/Contents/MacOS/");if(q===-1)return null;return Q.slice(0,q)}async function HQ(Q,$,q){let J=await fetch(Q);if(!J.ok||!J.body)throw Error(`download ${Q} → ${J.status}`);let Y=Buffer.from(await J.arrayBuffer()),V=`${$}.tmp`;try{if(await b2(V,Y),q!==void 0)await Hz(V,q);if(w6()==="darwin")try{let{spawnSync:X}=await import("node:child_process");X("xattr",["-d","com.apple.quarantine",V],{stdio:"ignore"})}catch{}return{tmpPath:V,destPath:$}}catch(X){try{await e0(V,{force:!0})}catch{}throw X}}async function Bz(Q){let $=[];for(let q of Q){let J=null;try{let Y=`${q.destPath}.prev`;try{await N8(q.destPath,Y),J=Y}catch(V){if(V.code!=="ENOENT")throw V}await N8(q.tmpPath,q.destPath),$.push({dest:q.destPath,backup:J})}catch(Y){if(J)try{await e0(q.destPath,{force:!0}),await N8(J,q.destPath)}catch{}for(let V of $.reverse())try{if(V.backup)await e0(V.dest,{force:!0}),await N8(V.backup,V.dest);else await e0(V.dest,{force:!0})}catch{}for(let V of Q)try{await e0(V.tmpPath,{force:!0})}catch{}throw Y}}for(let q of $){if(!q.backup)continue;try{await e0(q.backup,{force:!0})}catch{}}}async function NQ(Q){let $=m2(Q);if($.kind==="unsupported")throw c2($);if($.kind==="macos-dmg")throw Error(`No bare desktop binary for ${$.target.assetSuffix} in ${Q.tag_name}. Use the DMG install path instead.`);let{binAsset:q,sidecarAsset:J}=$,Y=E0(),V=f2();await D6(Q0.dirname(Y),{recursive:!0}),await D6(Q0.dirname(V),{recursive:!0});let X=[];try{if(X.push(await HQ(q.browser_download_url,Y,493)),J)X.push(await HQ(J.browser_download_url,V))}catch(K){for(let W of X)try{await e0(W.tmpPath,{force:!0})}catch{}throw K}return{binAsset:q,sidecarAsset:J,staged:X}}async function BQ(Q){await Bz(Q.staged)}async function Mz(Q){let $=await NQ(Q);return await BQ($),{binAsset:$.binAsset,sidecarAsset:$.sidecarAsset}}async function MQ(Q,$){let q=LQ(),J=BY(Q,q.archLabel);if(!J)throw Error(`No DMG asset (Petdex-${q.archLabel}.dmg) in ${Q.tag_name}. App-bundle update requires a DMG; falling back is not safe.`);let{join:Y}=await import("node:path"),{tmpdir:V}=await import("node:os"),X=Y(V(),`petdex-${OY(8).toString("hex")}-${J.name}`),K=await HQ(J.browser_download_url,X),{rename:W}=await import("node:fs/promises");await W(K.tmpPath,X);let{spawnSync:G}=await import("node:child_process"),U=G("hdiutil",["attach","-nobrowse","-quiet",X],{encoding:"utf8"});if(U.status!==0)throw Error(`hdiutil attach failed (exit ${U.status}): ${U.stderr||U.stdout||"no output"}`);let z=Pz(U.stdout)??"/Volumes/Petdex";try{let Z=`${z}/Petdex.app`,{existsSync:H}=await import("node:fs");if(!H(Z))throw Error(`mounted DMG at ${z} does not contain Petdex.app — is the release shape unchanged?`);let F=G("ditto",[Z,$],{encoding:"utf8"});if(F.status!==0)throw Error(`ditto failed (exit ${F.status}): ${F.stderr||F.stdout||"no output"}`);G("xattr",["-dr","com.apple.quarantine",$],{encoding:"utf8"})}finally{G("hdiutil",["detach","-quiet",z],{encoding:"utf8"});try{await e0(X,{force:!0})}catch{}}return{appBundleRoot:$,dmgAsset:J}}function u2(){return Q0.join(I0(),"Applications","Petdex.app")}async function PQ(Q,$=u2()){return await D6(Q0.dirname($),{recursive:!0}),MQ(Q,$)}function Pz(Q){let $=Q.split(`
403
- `),q=null;for(let J of $){let Y=J.indexOf("/Volumes/");if(Y===-1)continue;let V=J.slice(Y).trim();if(!q||V.length>q.length)q=V}return q}var Iz="boba",Cz=process.env.PETDEX_URL??"https://petdex.crafter.run",Tz=new Set(["petdex-assets.raillyhugo.workers.dev","pub-94495283df974cfea5e98d6a9e3fa462.r2.dev","yu2vz9gndp.ufs.sh"]);function B8(Q){try{let $=new URL(Q);if($.protocol!=="https:")return!1;return Tz.has($.hostname)}catch{return!1}}function I0(){if(w6()==="win32")return process.env.USERPROFILE??process.env.HOME??h2();return process.env.HOME??process.env.USERPROFILE??h2()}function PY(){return Q0.join(I0(),".petdex","pets")}function IY(){return Q0.join(I0(),".codex","pets")}var Az=16777216;function IQ(Q){for(let $ of["spritesheet.webp","spritesheet.png"]){let q=Q0.join(Q,$);try{let J=zz(q);if(J.isFile()&&J.size>0&&J.size<=Az)return!0}catch{}}return!1}async function Dz(){let{readdir:Q}=await import("node:fs/promises");for(let $ of[PY(),IY()]){let q;try{q=await Q($)}catch{continue}for(let J of q)if(IQ(Q0.join($,J)))return!0}return!1}async function CQ(){if(await Dz())return{status:"present"};let Q=await wz();return Q?{status:"installed",slug:Q}:{status:"missing"}}async function wz(Q={}){let $=Q.fetchOverride??fetch,q=Q.petdexUrl??Cz,J=`${q.replace(/\/+$/,"")}/`,Y;try{let W=await $(`${q}/api/manifest`,{signal:AbortSignal.timeout(8000)});if(!W.ok)return null;let G=await W.json();Y=Array.isArray(G?.pets)?G.pets:[]}catch{return null}if(Y.length===0)return null;let V=[],X=new Set,K=Y.find((W)=>W.slug===Iz);if(K)V.push(K),X.add(K.slug);for(let W of Y)if(!X.has(W.slug))V.push(W),X.add(W.slug);for(let W of V){let G=await Rz(W,$,J);if(G)return G}return null}async function Rz(Q,$,q){if(!B8(Q.spritesheetUrl)||!B8(Q.petJsonUrl))return null;let J=Q.spritesheetUrl.endsWith(".png")?"png":"webp",Y=[Q0.join(PY(),Q.slug),Q0.join(IY(),Q.slug)];for(let U of Y)if(FQ(U))return null;let V=async(U)=>{let z=await $(U,{headers:{Referer:q},signal:AbortSignal.timeout(15000)});if(!z.ok)throw Error(`download ${U} → ${z.status}`);return z.arrayBuffer()},X,K;try{[X,K]=await Promise.all([V(Q.petJsonUrl),V(Q.spritesheetUrl)])}catch{return null}let W=[],G=[];try{for(let U of Y){let z=`${U}.partial-${OY(6).toString("hex")}`;await D6(z,{recursive:!0}),W.push(z),await Promise.all([b2(Q0.join(z,"pet.json"),Buffer.from(X)),b2(Q0.join(z,`spritesheet.${J}`),Buffer.from(K))]),await D6(Q0.dirname(U),{recursive:!0}),await N8(z,U),W.pop(),G.push(U)}return Q.slug}catch{return await Promise.all([...W.map((U)=>e0(U,{recursive:!0,force:!0})),...G.map((U)=>e0(U,{recursive:!0,force:!0}))]),null}}async function TQ(){N0(o.default.bgMagenta(o.default.white(" petdex install desktop ")));let Q=LQ();i.info(`Platform: ${o.default.cyan(`${Q.osLabel} ${Q.archLabel}`)}`);let $=U0();$.start("Looking up the latest release");let q;try{q=await OQ()}catch(W){throw $.stop(o.default.red("failed")),Error(`Could not reach GitHub. Check your connection.
403
+ `),q=null;for(let J of $){let Y=J.indexOf("/Volumes/");if(Y===-1)continue;let V=J.slice(Y).trim();if(!q||V.length>q.length)q=V}return q}var Iz="boba",Cz=process.env.PETDEX_URL??"https://petdex.dev",Tz=new Set(["assets.petdex.dev"]);function B8(Q){try{let $=new URL(Q);if($.protocol!=="https:")return!1;return Tz.has($.hostname)}catch{return!1}}function I0(){if(w6()==="win32")return process.env.USERPROFILE??process.env.HOME??h2();return process.env.HOME??process.env.USERPROFILE??h2()}function PY(){return Q0.join(I0(),".petdex","pets")}function IY(){return Q0.join(I0(),".codex","pets")}var Az=16777216;function IQ(Q){for(let $ of["spritesheet.webp","spritesheet.png"]){let q=Q0.join(Q,$);try{let J=zz(q);if(J.isFile()&&J.size>0&&J.size<=Az)return!0}catch{}}return!1}async function Dz(){let{readdir:Q}=await import("node:fs/promises");for(let $ of[PY(),IY()]){let q;try{q=await Q($)}catch{continue}for(let J of q)if(IQ(Q0.join($,J)))return!0}return!1}async function CQ(){if(await Dz())return{status:"present"};let Q=await wz();return Q?{status:"installed",slug:Q}:{status:"missing"}}async function wz(Q={}){let $=Q.fetchOverride??fetch,q=Q.petdexUrl??Cz,J=`${q.replace(/\/+$/,"")}/`,Y;try{let W=await $(`${q}/api/manifest`,{signal:AbortSignal.timeout(8000)});if(!W.ok)return null;let G=await W.json();Y=Array.isArray(G?.pets)?G.pets:[]}catch{return null}if(Y.length===0)return null;let V=[],X=new Set,K=Y.find((W)=>W.slug===Iz);if(K)V.push(K),X.add(K.slug);for(let W of Y)if(!X.has(W.slug))V.push(W),X.add(W.slug);for(let W of V){let G=await Rz(W,$,J);if(G)return G}return null}async function Rz(Q,$,q){if(!B8(Q.spritesheetUrl)||!B8(Q.petJsonUrl))return null;let J=Q.spritesheetUrl.endsWith(".png")?"png":"webp",Y=[Q0.join(PY(),Q.slug),Q0.join(IY(),Q.slug)];for(let U of Y)if(FQ(U))return null;let V=async(U)=>{let z=await $(U,{headers:{Referer:q},signal:AbortSignal.timeout(15000)});if(!z.ok)throw Error(`download ${U} → ${z.status}`);return z.arrayBuffer()},X,K;try{[X,K]=await Promise.all([V(Q.petJsonUrl),V(Q.spritesheetUrl)])}catch{return null}let W=[],G=[];try{for(let U of Y){let z=`${U}.partial-${OY(6).toString("hex")}`;await D6(z,{recursive:!0}),W.push(z),await Promise.all([b2(Q0.join(z,"pet.json"),Buffer.from(X)),b2(Q0.join(z,`spritesheet.${J}`),Buffer.from(K))]),await D6(Q0.dirname(U),{recursive:!0}),await N8(z,U),W.pop(),G.push(U)}return Q.slug}catch{return await Promise.all([...W.map((U)=>e0(U,{recursive:!0,force:!0})),...G.map((U)=>e0(U,{recursive:!0,force:!0}))]),null}}async function TQ(){N0(o.default.bgMagenta(o.default.white(" petdex install desktop ")));let Q=LQ();i.info(`Platform: ${o.default.cyan(`${Q.osLabel} ${Q.archLabel}`)}`);let $=U0();$.start("Looking up the latest release");let q;try{q=await OQ()}catch(W){throw $.stop(o.default.red("failed")),Error(`Could not reach GitHub. Check your connection.
404
404
  ${W.message}`)}$.stop(`${o.default.green("✓")} Latest: ${o.default.bold(q.tag_name)}`);let J=m2(q);if(J.kind==="unsupported")throw c2(J);if(J.kind==="bare"){let W=U0();W.start("Downloading desktop binary and sidecar");let G;try{G=await Mz(q)}catch(Z){throw W.stop(o.default.red("failed")),Z}let U=E0(),z=G.sidecarAsset?`
405
405
  ${o.default.dim("•")} Sidecar at ${o.default.cyan(zQ(f2()))} (${ZQ(G.sidecarAsset.size)})`:`
406
406
  ${o.default.yellow("!")} No sidecar in this release. Hooks won't reach the mascot until a release ships ${NY}.`;W.stop(`${o.default.green("✓")} Binary at ${o.default.cyan(zQ(U))} (${ZQ(G.binAsset.size)})${z}`)}else{let W=U0();W.start("Installing macOS app from DMG");try{await PQ(q)}catch(G){throw W.stop(o.default.red("failed")),G}W.stop(`${o.default.green("✓")} App at ${o.default.cyan(zQ(u2()))} (${ZQ(J.dmgAsset.size)})`)}let Y=Q0.join(I0(),".petdex","version");await D6(Q0.dirname(Y),{recursive:!0}),await b2(Y,`${q.tag_name}
@@ -408,8 +408,8 @@ ${o.default.yellow("!")} No sidecar in this release. Hooks won't reach the masco
408
408
  `),"Next"),w0(`${o.default.green("✓")} ${q.tag_name}`),{tag:q.tag_name}}function ZQ(Q){if(Q<1024)return`${Q} B`;if(Q<1048576)return`${(Q/1024).toFixed(1)} KB`;return`${(Q/1024/1024).toFixed(1)} MB`}function zQ(Q){let $=I0();if(Q.startsWith($))return`~${Q.slice($.length)}`;return Q}function M8(){return process.env.HOME??vz()}function Sz(){return E1.join(M8(),".petdex","runtime","update-token")}function Ez(){return E1.join(M8(),".petdex","desktop.pid")}function kz(){let Q=E0();if(!O0(Q))return{status:"fail",label:"Desktop binary",detail:`not found at ${Q}`,hint:"Run `petdex install desktop` to download it."};try{if(!(AQ(Q).mode&73))return{status:"fail",label:"Desktop binary",detail:`${Q} exists but is not executable`,hint:`chmod +x ${Q}`}}catch{return{status:"fail",label:"Desktop binary",detail:`${Q} unreadable`,hint:"Reinstall: `petdex install desktop`"}}return{status:"ok",label:"Desktop binary",detail:Q}}function yz(){let Q=f2();if(!O0(Q))return{status:"fail",label:"Sidecar bundle",detail:`not found at ${Q}`,hint:"Reinstall: `petdex install desktop`"};return{status:"ok",label:"Sidecar bundle",detail:Q}}async function gz(){try{let Q=await fetch("http://127.0.0.1:7777/health",{signal:AbortSignal.timeout(500)});if(!Q.ok)return{status:"warn",label:"Sidecar reachable",detail:`:7777 responded ${Q.status}`,hint:"Restart: `petdex desktop stop && petdex desktop start`"};let $=await Q.json().catch(()=>null);if($?.ok&&$?.port===7777)return{status:"ok",label:"Sidecar reachable",detail:":7777 healthy"};return{status:"warn",label:"Sidecar reachable",detail:":7777 responded but body shape was unexpected"}}catch{return{status:"info",label:"Sidecar reachable",detail:"not running (this is fine if you haven't started the desktop)",hint:"Start with `petdex desktop start`"}}}function _z(){let Q=Ez();if(!O0(Q))return{status:"info",label:"PID file",detail:"not present (desktop not running)"};let $;try{$=p2(Q,"utf8").trim()}catch{return{status:"warn",label:"PID file",detail:`unreadable at ${Q}`,hint:`Delete it: rm ${Q}`}}if(!$.startsWith("{"))return{status:"warn",label:"PID file",detail:"legacy bare-pid format detected",hint:"Run `petdex desktop start` to rewrite it in the new format."};try{let q=JSON.parse($);if(typeof q.pid!=="number"||typeof q.lstart!=="string")return{status:"warn",label:"PID file",detail:"unexpected JSON shape",hint:`Delete it: rm ${Q}`};return{status:"ok",label:"PID file",detail:`pid ${q.pid}, started ${q.lstart}`}}catch{return{status:"warn",label:"PID file",detail:"JSON parse failed",hint:`Delete it: rm ${Q}`}}}function bz(){let Q=Sz();if(!O0(Q))return{status:"info",label:"Update token",detail:"absent (will be created when sidecar starts)"};try{if(p2(Q,"utf8").trim().length<16)return{status:"warn",label:"Update token",detail:"present but suspiciously short",hint:"Stop the desktop and run `petdex desktop start` to regenerate."};let J=AQ(Q).mode&511;if(J!==384)return{status:"warn",label:"Update token",detail:`present, mode ${J.toString(8)} (expected 600)`,hint:`chmod 600 ${Q}`};return{status:"ok",label:"Update token",detail:"present, mode 600"}}catch($){return{status:"warn",label:"Update token",detail:`read failed: ${$.message}`}}}function hz(){if(T6()==="off")return{status:"warn",label:"Killswitch",detail:"DISABLED. Agent hooks are short-circuited.",hint:"Re-enable with `petdex hooks on` or `/petdex on` from inside your agent."};return{status:"ok",label:"Killswitch",detail:"hooks enabled"}}function fz(){let Q=[];for(let $ of H1){let q=$.id==="antigravity"?(()=>{let X=v1(),[K]=X;return X.find((W)=>O0(W))??K})():$.configFile,J=O0(q),Y=O0($.slashCommandPath);if(!mz($)){Q.push({status:"info",label:$.displayName,detail:"not installed on this machine"});continue}let V=!1;if(J)try{let X=p2(q,"utf8");if($.id==="antigravity")V=X.includes('"petdex"')||X.includes("petdex");else V=X.includes("127.0.0.1:7777/state")||X.includes("/state")}catch{V=!1}if(!V){Q.push({status:"warn",label:$.displayName,detail:"petdex hook NOT detected",hint:"Run `petdex hooks install`"});continue}if($.id==="antigravity"){let X=O0(S1());Q.push({status:X?"ok":"warn",label:$.displayName,detail:X?"MCP server + Skill installed":"MCP server configured, but Skill not found",hint:X?void 0:"Re-run `petdex hooks install` to install the Agent Skill."});continue}if(!Y){Q.push({status:"warn",label:$.displayName,detail:"hook installed, but /petdex slash command missing",hint:"Re-run `petdex hooks install` to add the slash command."});continue}Q.push({status:"ok",label:$.displayName,detail:"hooks + /petdex installed"})}return Q}function mz(Q){if(Q.id!=="antigravity")return O0(Q.configDir);return v1().some(($)=>O0(E1.dirname($)))||O0(S1())}function cz(){let Q=E1.join(M8(),".codex","config.toml");if(!O0(Q))return{status:"info",label:"Codex codex_hooks flag",detail:"config.toml absent (Codex not installed?)"};try{let $=p2(Q,"utf8");if(/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test($))return{status:"ok",label:"Codex codex_hooks flag",detail:"[features] codex_hooks = true"};return{status:"warn",label:"Codex codex_hooks flag",detail:"missing or set to non-true",hint:"Re-run `petdex hooks install` and accept the auto-fix prompt."}}catch{return{status:"warn",label:"Codex codex_hooks flag",detail:"config.toml unreadable"}}}function uz(){let Q=[E1.join(M8(),".petdex","pets"),E1.join(M8(),".codex","pets")],$=0;for(let q of Q){if(!O0(q))continue;try{let{readdirSync:J}=_("node:fs"),Y=J(q);for(let V of Y){let X=E1.join(q,V);for(let K of["spritesheet.webp","spritesheet.png"]){let W=E1.join(X,K);if(!O0(W))continue;try{let G=AQ(W);if(G.size>0&&G.size<=16777216){$+=1;break}}catch{}}}}catch{}}if($===0)return{status:"fail",label:"Installed pets",detail:"no usable pets in ~/.petdex/pets or ~/.codex/pets",hint:"Install one with `petdex install <slug>`"};return{status:"ok",label:"Installed pets",detail:`${$} usable spritesheet(s) on disk`}}function pz(Q){switch(Q){case"ok":return L0.default.green("✓");case"warn":return L0.default.yellow("!");case"fail":return L0.default.red("✗");case"info":return L0.default.dim("○")}}function k0(Q){let $=Q.detail?` ${L0.default.dim(Q.detail)}`:"";if(console.log(` ${pz(Q.status)} ${L0.default.bold(Q.label)}${$}`),Q.hint)console.log(` ${L0.default.cyan("→")} ${Q.hint}`)}async function CY(){console.log(L0.default.bgMagenta(L0.default.white(" petdex doctor "))),console.log(""),console.log(L0.default.bold("Install")),k0(kz()),k0(yz()),k0(uz()),console.log(""),console.log(L0.default.bold("Runtime")),k0(await gz()),k0(_z()),k0(bz()),k0(hz()),console.log(""),console.log(L0.default.bold("Agents"));for(let $ of fz())k0($);k0(cz()),console.log(""),console.log(L0.default.bold("Network"));let Q=null;try{Q=xz("lsof -nP -iTCP:7777 -sTCP:LISTEN 2>/dev/null || true",{encoding:"utf8"}).trim()}catch{Q=null}if(Q===null)k0({status:"info",label:":7777 listener",detail:"lsof unavailable"});else if(Q.length===0)k0({status:"info",label:":7777 listener",detail:"nothing bound (sidecar offline)"});else{let $=Q.split(`
409
409
  `).length-1;k0({status:$===1?"ok":"warn",label:":7777 listener",detail:$===1?"1 process bound":`${$} processes bound`,hint:$>1?"Multiple sidecars contending for the same port. Stop both and start fresh.":void 0})}console.log("")}var y0=p0(R1(),1);import{execFileSync as T8,spawn as TY}from"node:child_process";import{existsSync as AY,readFileSync as dz,unlinkSync as lz}from"node:fs";import{mkdir as oz,writeFile as DY}from"node:fs/promises";import F1 from"node:path";function C8(){return F1.join(I0(),".petdex","desktop.pid")}function P8(){return F1.join(I0(),".petdex","desktop.log")}function wY(){if(!AY(C8()))return null;let Q;try{Q=dz(C8(),"utf8").trim()}catch{return null}if(Q.startsWith("{")){try{let q=JSON.parse(Q);if(typeof q.pid==="number"&&Number.isFinite(q.pid)&&q.pid>0&&typeof q.lstart==="string"&&q.lstart.length>0)return{pid:q.pid,lstart:q.lstart}}catch{}return null}let $=Number(Q);if(Number.isFinite($)&&$>0)return{pid:$,lstart:""};return null}function RY(Q){if(process.platform==="win32")return null;try{let q=T8("ps",["-p",String(Q),"-o","lstart="],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();return q.length>0?q:null}catch{return null}}function xY(Q){try{let q=T8("tasklist",["/fi",`PID eq ${Q}`,"/fo","csv","/nh"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).match(/^"([^"]+)"/m);return q?q[1].toLowerCase():null}catch{return null}}function vY(Q){if(process.platform==="win32")return F1.basename(E0()).toLowerCase();return RY(Q)??""}function DQ(Q){if(process.platform==="win32")return xY(Q)!==null;try{return T8("ps",["-p",String(Q)],{stdio:["ignore","ignore","ignore"]}),!0}catch{return!1}}function SY(Q){if(Q.lstart.length===0)return!1;if(process.platform==="win32"){let q=xY(Q.pid);return q!==null&&q===Q.lstart}let $=RY(Q.pid);return $!==null&&$===Q.lstart}function R6(){try{lz(C8())}catch{}}function Q1(){let Q=wY();if(Q==null)return{state:"stopped"};if(SY(Q))return{state:"running",pid:Q.pid};return{state:"stale",pid:Q.pid}}async function k1(){let Q=Q1();if(Q.state==="running")return{ok:!0,pid:Q.pid,alreadyRunning:!0};if(Q.state==="stale")R6();let $=E0();if(!AY($))return{ok:!1,reason:`petdex-desktop binary not found at ${$}. Run \`petdex install desktop\` first.`};await oz(F1.dirname(P8()),{recursive:!0});let q=await import("node:fs").then((K)=>K.openSync(P8(),"a")),J=await import("node:fs").then((K)=>K.openSync(P8(),"a")),Y=iz($);if(Y)return nz(Y);let V=TY($,[],{detached:!0,stdio:["ignore",q,J]});if(V.unref(),!V.pid)return{ok:!1,reason:"Failed to spawn petdex-desktop"};let X={pid:V.pid,lstart:vY(V.pid)};return await DY(C8(),JSON.stringify(X)),{ok:!0,pid:V.pid,alreadyRunning:!1}}function iz(Q){let $=F1.dirname(Q);if(F1.basename($)!=="MacOS")return null;let q=F1.dirname($);if(F1.basename(q)!=="Contents")return null;let J=F1.dirname(q);if(!J.endsWith(".app"))return null;return J}async function nz(Q){TY("open",["-gj",Q],{stdio:"ignore",detached:!0}).unref();let q=Date.now()+3000,J=null;while(Date.now()<q){if(J=rz(),J)break;await new Promise((V)=>setTimeout(V,100))}if(!J)return{ok:!1,reason:`Launched ${Q} but couldn't find the resulting process. Check ${P8()}.`};let Y={pid:J,lstart:vY(J)};return await DY(C8(),JSON.stringify(Y)),{ok:!0,pid:J,alreadyRunning:!1}}function rz(){try{let Q=T8("pgrep",["-f","petdex-desktop"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!Q)return null;let $=Q.split(`
410
410
  `).filter((J)=>J.length>0),q=Number($[$.length-1]);return Number.isFinite(q)&&q>0?q:null}catch{return null}}var EY=7777;async function a1(Q={}){let $=Q.sidecarPort??EY,q=Q.portWaitTimeoutMs??5000,J=Q1();if(J.state==="stopped")return{ok:!1,reason:"petdex-desktop is not running"};if(J.state==="stale")return R6(),{ok:!1,reason:`petdex-desktop is not running (stale pid ${J.pid} cleared)`};let Y=J.pid,V=wY();if(V===null||!SY(V))return R6(),{ok:!1,reason:`petdex-desktop exited before stop (pid ${Y} no longer alive)`};if(process.platform==="win32")try{T8("taskkill",["/pid",String(Y),"/t","/f"],{stdio:"ignore",timeout:5000})}catch{if(!DQ(Y)){R6();let K=await I8($,{timeoutMs:q});return{ok:!0,pid:Y,portReleased:K}}return{ok:!1,reason:`taskkill failed and pid ${Y} is still alive`}}else try{process.kill(Y,"SIGTERM")}catch(K){if(R6(),K.code==="ESRCH"){let G=await I8($,{timeoutMs:q});return{ok:!0,pid:Y,portReleased:G}}return{ok:!1,reason:`Failed to signal pid ${Y}: ${K.message}`}}R6();let X=await I8($,{timeoutMs:q});return{ok:!0,pid:Y,portReleased:X}}async function kY(){let Q=await k1();if(!Q.ok)console.error(`${y0.default.red("✗")} ${Q.reason}`),process.exit(1);if(Q.alreadyRunning)console.log(`${y0.default.dim("•")} petdex-desktop already running (pid ${Q.pid})`);else console.log(`${y0.default.green("✓")} petdex-desktop started (pid ${Q.pid})`),console.log(y0.default.dim(` log: ${P8()}`))}async function yY(){let Q=await a1();if(!Q.ok)console.error(`${y0.default.dim("•")} ${Q.reason}`),process.exit(Q.reason.includes("not running")?0:1);if(Q.portReleased)console.log(`${y0.default.green("✓")} stopped pid ${Q.pid}`);else console.log(`${y0.default.yellow("!")} stopped pid ${Q.pid}, but :${EY} still busy`),console.log(y0.default.dim(" if 'petdex desktop start' fails with EADDRINUSE, wait a moment and retry"))}async function I8(Q,$={}){let q=$.timeoutMs??1e4,J=$.intervalMs??100,Y=$.host??"127.0.0.1",{createConnection:V}=await import("node:net"),X=Date.now()+q;while(Date.now()<X){if(await new Promise((W)=>{let G=V({host:Y,port:Q}),U=(z)=>{G.removeAllListeners(),G.destroy(),W(z)};G.once("connect",()=>U(!1)),G.once("error",(z)=>{U(z.code==="ECONNREFUSED")}),G.setTimeout(J,()=>U(!1))}))return!0;await new Promise((W)=>setTimeout(W,J))}return!1}function gY(){let Q=Q1();switch(Q.state){case"running":console.log(`${y0.default.green("●")} running (pid ${Q.pid})`);break;case"stopped":console.log(`${y0.default.dim("○")} stopped`);break;case"stale":console.log(`${y0.default.yellow("?")} pid ${Q.pid} written but not alive (run \`petdex desktop start\` to restart)`);break}}import{mkdir as az,readdir as sz,writeFile as tz}from"node:fs/promises";import A8 from"node:path";function ez(Q=I0()){return[A8.join(Q,".petdex","pets"),A8.join(Q,".codex","pets")]}function QH(Q=I0()){return A8.join(Q,".petdex","active.json")}async function _Y(Q=ez()){let $=new Set;for(let q of Q)try{let J=await sz(q,{withFileTypes:!0});for(let Y of J){if(!Y.isDirectory())continue;if(IQ(A8.join(q,Y.name)))$.add(Y.name)}}catch{}return[...$].sort()}async function bY(Q,$=QH()){await az(A8.dirname($),{recursive:!0}),await tz($,`${JSON.stringify({slug:Q})}
411
- `,"utf8")}async function hY(Q={stopDesktop:a1,startDesktop:k1}){let $=await Q.stopDesktop();if(!$.ok&&!$.reason.includes("not running"))return{status:"manual_restart_required",reason:$.reason};if($.ok&&!$.portReleased)return{status:"manual_restart_required",reason:"desktop sidecar port is still busy"};let q=await Q.startDesktop();if(!q.ok)return{status:"manual_restart_required",reason:q.reason};return{status:"reloaded"}}t1();var E=p0(R1(),1);import{existsSync as SH,readFileSync as J3}from"node:fs";import{mkdir as EH,writeFile as kH}from"node:fs/promises";import{homedir as Y3}from"node:os";import cQ from"node:path";import{spawn as $H}from"node:child_process";import{randomUUID as fY}from"node:crypto";import{existsSync as qH,mkdirSync as JH,readFileSync as YH,writeFileSync as VH}from"node:fs";import{homedir as XH}from"node:os";import mY from"node:path";function d2(){let Q=process.env.HOME??XH();return mY.join(Q,".petdex","telemetry.json")}var KH=process.env.PETDEX_TELEMETRY_URL??"https://petdex.crafter.run/api/telemetry/event",GH=2000;function l2(){if(!qH(d2()))return{kind:"missing"};let Q;try{Q=YH(d2(),"utf8")}catch($){return{kind:"error",reason:`read failed: ${$.message}`}}try{return{kind:"ok",config:JSON.parse(Q)}}catch($){return{kind:"error",reason:`parse failed: ${$.message}`}}}function wQ(Q){try{return JH(mY.dirname(d2()),{recursive:!0}),VH(d2(),`${JSON.stringify(Q,null,2)}
412
- `),!0}catch{return!1}}function cY(){if(process.env.PETDEX_TELEMETRY==="0")return null;let Q=l2();if(Q.kind==="ok")return Q.config;if(Q.kind==="error")return null;let $={install_id:fY(),enabled:!0,notice_seen:!1,first_seen:new Date().toISOString()};return wQ($)?$:null}function uY(){if(process.env.PETDEX_TELEMETRY==="0")return!1;let Q=l2();if(Q.kind==="missing")return!0;if(Q.kind==="error")return!1;return Q.config.enabled}function pY(Q){let $=l2(),q;if($.kind==="ok")q=$.config,q.enabled=Q;else q={install_id:fY(),enabled:Q,notice_seen:!0,first_seen:new Date().toISOString()};return wQ(q)}function dY(){let Q=l2();return{enabled:uY(),install_id:Q.kind==="ok"?Q.config.install_id:null}}function lY(){if(process.env.PETDEX_TELEMETRY==="0")return;let Q=cY();if(!Q)return;if(Q.notice_seen)return;console.log(["","petdex collects anonymous usage stats (install volume, OS, agents wired up).","No personal data, no file contents. Disable any time:"," petdex telemetry off","Details: https://petdex.crafter.run/legal/telemetry",""].join(`
411
+ `,"utf8")}async function hY(Q={stopDesktop:a1,startDesktop:k1}){let $=await Q.stopDesktop();if(!$.ok&&!$.reason.includes("not running"))return{status:"manual_restart_required",reason:$.reason};if($.ok&&!$.portReleased)return{status:"manual_restart_required",reason:"desktop sidecar port is still busy"};let q=await Q.startDesktop();if(!q.ok)return{status:"manual_restart_required",reason:q.reason};return{status:"reloaded"}}t1();var E=p0(R1(),1);import{existsSync as SH,readFileSync as J3}from"node:fs";import{mkdir as EH,writeFile as kH}from"node:fs/promises";import{homedir as Y3}from"node:os";import cQ from"node:path";import{spawn as $H}from"node:child_process";import{randomUUID as fY}from"node:crypto";import{existsSync as qH,mkdirSync as JH,readFileSync as YH,writeFileSync as VH}from"node:fs";import{homedir as XH}from"node:os";import mY from"node:path";function d2(){let Q=process.env.HOME??XH();return mY.join(Q,".petdex","telemetry.json")}var KH=process.env.PETDEX_TELEMETRY_URL??"https://petdex.dev/api/telemetry/event",GH=2000;function l2(){if(!qH(d2()))return{kind:"missing"};let Q;try{Q=YH(d2(),"utf8")}catch($){return{kind:"error",reason:`read failed: ${$.message}`}}try{return{kind:"ok",config:JSON.parse(Q)}}catch($){return{kind:"error",reason:`parse failed: ${$.message}`}}}function wQ(Q){try{return JH(mY.dirname(d2()),{recursive:!0}),VH(d2(),`${JSON.stringify(Q,null,2)}
412
+ `),!0}catch{return!1}}function cY(){if(process.env.PETDEX_TELEMETRY==="0")return null;let Q=l2();if(Q.kind==="ok")return Q.config;if(Q.kind==="error")return null;let $={install_id:fY(),enabled:!0,notice_seen:!1,first_seen:new Date().toISOString()};return wQ($)?$:null}function uY(){if(process.env.PETDEX_TELEMETRY==="0")return!1;let Q=l2();if(Q.kind==="missing")return!0;if(Q.kind==="error")return!1;return Q.config.enabled}function pY(Q){let $=l2(),q;if($.kind==="ok")q=$.config,q.enabled=Q;else q={install_id:fY(),enabled:Q,notice_seen:!0,first_seen:new Date().toISOString()};return wQ(q)}function dY(){let Q=l2();return{enabled:uY(),install_id:Q.kind==="ok"?Q.config.install_id:null}}function lY(){if(process.env.PETDEX_TELEMETRY==="0")return;let Q=cY();if(!Q)return;if(Q.notice_seen)return;console.log(["","petdex collects anonymous usage stats (install volume, OS, agents wired up).","No personal data, no file contents. Disable any time:"," petdex telemetry off","Details: https://petdex.dev/legal/telemetry",""].join(`
413
413
  `)),Q.notice_seen=!0,wQ(Q)}function C0(Q,$={}){if(!uY())return;let q=cY();if(!q)return;let J=JSON.stringify({install_id:q.install_id,event:Q,...$});try{let Y=`
414
414
  const controller = new AbortController();
415
415
  const t = setTimeout(() => controller.abort(), ${GH});
@@ -426,7 +426,7 @@ ${E.default.dim(" Relaunch Petdex from /Applications to use it.")}`);return}let
426
426
  ${E.default.dim(" Run petdex up to launch the app.")}`);return}let A=z();A.start(`Downloading ${F.tag_name}`);let B;try{B=await J.stageDesktopAssets(F)}catch(M){throw A.stop(X?"failed":E.default.red("failed")),M}if(A.stop(X?`Downloaded ${F.tag_name} (${x6(B.binAsset.size)})`:`${E.default.green("✓")} Downloaded ${E.default.bold(F.tag_name)} (${x6(B.binAsset.size)})`),P){W(X?"Stopping running petdex-desktop":`${E.default.dim("•")} Stopping running petdex-desktop`);let M=await J.stopDesktop();if(process.platform==="win32"&&!M.ok){let T=J.desktopStatus();if(T.state==="running"&&DQ(T.pid))throw Error("Cannot replace petdex-desktop-win32-x64.exe: process is still running. Run `petdex desktop stop` first.")}}if(process.platform==="win32"&&P)await new Promise((M)=>setTimeout(M,500));if(await J.commitDesktopAssets(B),await mQ(J.versionFile,F.tag_name),P){W(X?"Asking sidecar to release port":`${E.default.dim("•")} Asking sidecar to release port`);let M=await J.requestSidecarHandoff();if(W(X?M?"Sidecar acknowledged handoff":"Sidecar handoff unavailable, falling back to port wait":M?`${E.default.dim("•")} Sidecar acknowledged handoff`:`${E.default.dim("•")} Sidecar handoff unavailable, falling back to port wait`),!await J.waitForPortRelease(n2,{timeoutMs:1e4})){G(X?`Port ${n2} still in use after 10s. Run 'petdex desktop stop && petdex desktop start' to recover.`:`${E.default.yellow("!")} Port ${n2} still in use after 10s. Run \`petdex desktop stop && petdex desktop start\` to recover.`);return}W(X?"Restarting petdex-desktop":`${E.default.dim("•")} Restarting petdex-desktop`);let D=await J.startDesktop();if(D.ok)W(X?`Restarted (pid ${D.pid})`:`${E.default.green("✓")} Restarted (pid ${D.pid})`);else G(X?`Could not restart: ${D.reason}. Run 'petdex desktop start' manually.`:`${E.default.yellow("!")} Could not restart: ${D.reason}. Run \`petdex desktop start\` manually.`)}C0("cli_update_applied",{cli_version:$,from_version:Z??void 0,to_version:F.tag_name,duration_ms:Date.now()-Y}),await J.hookRefresh(W,G,X);let O=Z?`${Z} -> ${F.tag_name}`:F.tag_name;U(X?O:`${E.default.green("✓")} ${O}`)}async function hH(Q,$,q){try{let{runRefresh:J}=await Promise.resolve().then(() => (fQ(),hQ)),Y=await J();if(Y.refreshed.length>0)Q(q?`Refreshed hooks for: ${Y.refreshed.join(", ")}`:`Refreshed hooks for ${E.default.cyan(Y.refreshed.join(", "))}`)}catch(J){$(`Hook refresh failed: ${J.message}`)}}function x6(Q){if(Q<1024)return`${Q} B`;if(Q<1048576)return`${(Q/1024).toFixed(1)} KB`;return`${(Q/1024/1024).toFixed(1)} MB`}bQ();t1();L8();var V0=p0(R1(),1);import{existsSync as y1}from"node:fs";import{readFile as pQ,rm as dQ,stat as fH,writeFile as lQ}from"node:fs/promises";import K3 from"node:path";vQ();async function G3(Q={}){N0(V0.default.bgMagenta(V0.default.white(" petdex hooks uninstall ")));let q=(await mH()).filter((V)=>V.installed);if(q.length===0)return w0(V0.default.dim("No agent configs found. Nothing to uninstall.")),{uninstalledAgents:[]};let J=[],Y=[];for(let{agent:V}of q)try{let X=await uH(V);if(X.removed){Y.push(V.id);let K=X.backupPath?` ${V0.default.dim(`(backup: ${K3.basename(X.backupPath)})`)}`:"";J.push(` ${V0.default.green("✓")} ${V0.default.bold(V.displayName)}${K}`)}else J.push(` ${V0.default.dim("•")} ${V0.default.bold(V.displayName)} ${V0.default.dim("(no petdex entries found)")}`);if(V.id!=="antigravity")await o2(V)}catch(X){J.push(` ${V0.default.red("✗")} ${V0.default.bold(V.displayName)} ${V0.default.red(X instanceof Error?X.message:String(X))}`)}if(b0(J.join(`
427
427
  `),"Done"),Q.removeToken)try{await dQ(O8(),{force:!0}),i.info(`${V0.default.green("✓")} Removed ${O8()}`)}catch{}else if(y1(O8()))i.info(V0.default.dim(`Token file at ${O8()} kept. Pass --remove-token to delete it.`));return w0(`${V0.default.green("✓")} Done. Run ${V0.default.cyan("petdex hooks install")} to wire petdex back in.`),{uninstalledAgents:Y}}async function mH(){return Promise.all(H1.map(async(Q)=>({agent:Q,installed:Q.id==="antigravity"?cH():y1(Q.configDir)})))}function cH(){return v1().some((Q)=>y1(K3.dirname(Q)))||y1(S1())}async function uH(Q){if(Q.id==="opencode"){if(!y1(Q.configFile))return{removed:!1,backupPath:null};let V=await uQ(Q.configFile);return await dQ(Q.configFile,{force:!0}),{removed:!0,backupPath:V}}if(Q.id==="antigravity"){let V=!1;for(let X of v1())try{if(y1(X)){let K=await pQ(X,"utf8"),W=JSON.parse(K),G=W.mcpServers??{};if(G.petdex){let{petdex:U,...z}=G;W.mcpServers=z,await uQ(X),await lQ(X,`${JSON.stringify(W,null,2)}
428
428
  `,"utf8"),V=!0}}}catch{}try{let X=S1(),K=!1;try{await fH(X),K=!0}catch{}if(K)await dQ(X,{recursive:!0,force:!0}),V=!0}catch{}return{removed:V,backupPath:null}}if(!y1(Q.configFile))return{removed:!1,backupPath:null};let $;try{$=await pQ(Q.configFile,"utf8")}catch(V){throw Error(`Could not read ${Q.configFile}: ${V.message}`)}let q;try{q=JSON.parse($)}catch(V){throw Error(`Refusing to overwrite ${Q.configFile}: invalid JSON (${V.message}). Hand-edit the file to remove petdex entries.`)}let J=pH(q);if(!J.changed)return{removed:!1,backupPath:null};let Y=await uQ(Q.configFile);return await lQ(Q.configFile,`${JSON.stringify(J.value,null,2)}
429
- `,"utf8"),{removed:!0,backupPath:Y}}function pH(Q){let $={...Q},q=$.hooks;if(!q||typeof q!=="object"||Array.isArray(q))return{value:$,changed:!1};let J=!1,Y={};for(let[V,X]of Object.entries(q)){if(!Array.isArray(X)){Y[V]=X;continue}let K=X.filter((W)=>!dH(W));if(K.length!==X.length)J=!0;if(K.length>0)Y[V]=K}if(!J)return{value:$,changed:!1};if(Object.keys(Y).length===0){let{hooks:V,...X}=$;return{value:X,changed:!0}}return $.hooks=Y,{value:$,changed:!0}}function dH(Q){if(typeof Q!=="object"||Q==null)return!1;return lH(Q).some((q)=>q.includes(`localhost:${F8}/state`)||q.includes(r1))}function lH(Q){let $=[];function q(J){if(typeof J==="string"){$.push(J);return}if(Array.isArray(J)){for(let Y of J)q(Y);return}if(typeof J==="object"&&J!=null)for(let Y of Object.values(J))q(Y)}return q(Q),$}async function uQ(Q){if(!y1(Q))return null;let $=new Date().toISOString().replace(/[:.]/g,"-"),q=`${Q}.${$}.bak`,J=await pQ(Q);return await lQ(q,J),q}var q0=process.env.PETDEX_URL??"https://petdex.crafter.run",NF=`${q0.replace(/\/+$/,"")}/`,BF="https://clerk.petdex.crafter.run",MF="LcThwEayl6KAA1Qm",aQ=["profile","email","openid","offline_access"];async function PF(){let Q=process.env.CLERK_ISSUER,$=process.env.CLERK_OAUTH_CLIENT_ID;if(Q&&$)return{issuer:Q,clientId:$,scopes:aQ};try{let q=await fetch(`${q0}/api/cli/auth-config`,{signal:AbortSignal.timeout(3000)});if(q.ok){let J=await q.json(),Y=typeof J.issuer==="string"?J.issuer:null,V=typeof J.clientId==="string"?J.clientId:null,X=Array.isArray(J.scopes)?J.scopes.filter((K)=>typeof K==="string"):null;if(Y&&V)return{issuer:Q??Y,clientId:$??V,scopes:X&&X.length>0?X:aQ}}}catch{}return{issuer:Q??BF,clientId:$??MF,scopes:aQ}}var a2=null;async function S8(){if(a2)return a2;let Q=await PF();return a2=new g2({clientId:Q.clientId,issuer:Q.issuer,scopes:Q.scopes,storage:"keychain",keychainService:"petdex-cli"}),a2}var u0="0.4.2";IF().catch((Q)=>{d(`petdex: ${Q.message}`),process.exit(1)});async function IF(){let Q=process.argv.slice(2),$=Q[0];if($==="bubble"){let{runBubble:J}=await Promise.resolve().then(() => (B3(),N3));await J(Q.slice(1));return}if($==="mcp-server"){let{runMcpServer:J}=await Promise.resolve().then(() => (A3(),T3));await J();return}if(!$||$==="--help"||$==="-h"||$==="help"){x3();return}if(!new Set(["version","--version","-v","telemetry"]).has($))lY();switch($){case"login":await CF();break;case"logout":await TF();break;case"whoami":await AF();break;case"submit":await SF(Q.slice(1));break;case"edit":await EF(Q.slice(1));break;case"install":await RF(Q.slice(1));break;case"list":await xF();break;case"select":await vF(Q.slice(1));break;case"hooks":await uF(Q.slice(1));break;case"desktop":await iF(Q.slice(1));break;case"init":await dF();break;case"up":await h3();break;case"down":await f3();break;case"toggle":await oF();break;case"update":await X3(Q.slice(1),u0);break;case"doctor":await CY();break;case"telemetry":nF(Q.slice(1));break;case"version":case"--version":case"-v":console.log(u0);break;default:console.error(j.default.red(`Unknown command: ${$}`)),x3(),process.exit(1)}}function x3(){let Q=j.default.cyan,$=j.default.dim;console.log(["",` ${j.default.bold(j.default.magenta("petdex"))} ${$(u0)} ${$("Codex pet gallery CLI")}`,"",` ${Q("Usage")}`," petdex <command> [args]","",` ${Q("Commands")}`,` ${j.default.bold("init")} First-run setup: wires hooks across your agents AND wakes the mascot ${j.default.green("(start here)")}`,` ${j.default.bold("login")} Sign in with Clerk OAuth`,` ${j.default.bold("logout")} Clear stored credentials`,` ${j.default.bold("whoami")} Show signed-in user`,` ${j.default.bold("submit")} <path> Submit a pet folder, zip, or parent of pets (bulk)`,` ${j.default.bold("edit")} <slug> Edit a pet you own (--desc, --displayName, --sprite, --meta, --zip)`,` ${j.default.bold("install")} <slug...> Install one or more pets into ~/.petdex/pets and ~/.codex/pets`,` ${j.default.bold("install desktop")} Install the petdex-desktop binary (alternative to the .dmg)`,` ${j.default.bold("list")} List approved pets`,` ${j.default.bold("select")} [slug] Set the active pet shown by the desktop mascot`,` ${j.default.bold("mcp-server")} Start the MCP protocol server for Antigravity integration`,` ${j.default.bold("hooks install")} Wire petdex-desktop into your coding agents`,` ${j.default.bold("toggle")} One-shot wake/sleep. Flips the mascot on or off depending on current state`,` ${j.default.bold("up")} Force-wake the mascot. Enables hooks AND launches petdex-desktop`,` ${j.default.bold("down")} Force-sleep the mascot. Disables hooks AND stops petdex-desktop`,` ${j.default.bold("desktop")} <cmd> Manage petdex-desktop (start | stop | status)`,` ${j.default.bold("update")} Pull the latest petdex-desktop release and restart`,` ${j.default.bold("doctor")} Diagnose install/runtime/agents and surface fixes`,` ${j.default.bold("telemetry")} [on|off|status] Manage anonymous usage telemetry`,"",` ${Q("Examples")}`,` ${$("$")} petdex init ${$("# after dragging Petdex.app from the .dmg → just run this")}`,` ${$("$")} petdex login`,` ${$("$")} petdex submit ~/.codex/pets/boba ${$("# single folder")}`,` ${$("$")} petdex install boba ${$("# install a pet by slug")}`,` ${$("$")} petdex install boba doraemon mochi ${$("# install several at once")}`,` ${$("$")} petdex select ${$("# pick active mascot from installed pets")}`,` ${$("$")} petdex select boba ${$("# set active mascot directly")}`,` ${$("$")} petdex toggle ${$("# wake or sleep the mascot")}`,` ${$("$")} petdex doctor ${$("# diagnose install + agents")}`,` ${$("$")} petdex update ${$("# pull the latest release")}`,"",` ${$("Gallery & docs:")} ${j.default.underline(q0)}`,""].join(`
429
+ `,"utf8"),{removed:!0,backupPath:Y}}function pH(Q){let $={...Q},q=$.hooks;if(!q||typeof q!=="object"||Array.isArray(q))return{value:$,changed:!1};let J=!1,Y={};for(let[V,X]of Object.entries(q)){if(!Array.isArray(X)){Y[V]=X;continue}let K=X.filter((W)=>!dH(W));if(K.length!==X.length)J=!0;if(K.length>0)Y[V]=K}if(!J)return{value:$,changed:!1};if(Object.keys(Y).length===0){let{hooks:V,...X}=$;return{value:X,changed:!0}}return $.hooks=Y,{value:$,changed:!0}}function dH(Q){if(typeof Q!=="object"||Q==null)return!1;return lH(Q).some((q)=>q.includes(`localhost:${F8}/state`)||q.includes(r1))}function lH(Q){let $=[];function q(J){if(typeof J==="string"){$.push(J);return}if(Array.isArray(J)){for(let Y of J)q(Y);return}if(typeof J==="object"&&J!=null)for(let Y of Object.values(J))q(Y)}return q(Q),$}async function uQ(Q){if(!y1(Q))return null;let $=new Date().toISOString().replace(/[:.]/g,"-"),q=`${Q}.${$}.bak`,J=await pQ(Q);return await lQ(q,J),q}var q0=process.env.PETDEX_URL??"https://petdex.dev",NF=`${q0.replace(/\/+$/,"")}/`,BF="https://clerk.petdex.dev",MF="LcThwEayl6KAA1Qm",aQ=["profile","email","openid","offline_access"];async function PF(){let Q=process.env.CLERK_ISSUER,$=process.env.CLERK_OAUTH_CLIENT_ID;if(Q&&$)return{issuer:Q,clientId:$,scopes:aQ};try{let q=await fetch(`${q0}/api/cli/auth-config`,{signal:AbortSignal.timeout(3000)});if(q.ok){let J=await q.json(),Y=typeof J.issuer==="string"?J.issuer:null,V=typeof J.clientId==="string"?J.clientId:null,X=Array.isArray(J.scopes)?J.scopes.filter((K)=>typeof K==="string"):null;if(Y&&V)return{issuer:Q??Y,clientId:$??V,scopes:X&&X.length>0?X:aQ}}}catch{}return{issuer:Q??BF,clientId:$??MF,scopes:aQ}}var a2=null;async function S8(){if(a2)return a2;let Q=await PF();return a2=new g2({clientId:Q.clientId,issuer:Q.issuer,scopes:Q.scopes,storage:"keychain",keychainService:"petdex-cli"}),a2}var u0="0.4.2";IF().catch((Q)=>{d(`petdex: ${Q.message}`),process.exit(1)});async function IF(){let Q=process.argv.slice(2),$=Q[0];if($==="bubble"){let{runBubble:J}=await Promise.resolve().then(() => (B3(),N3));await J(Q.slice(1));return}if($==="mcp-server"){let{runMcpServer:J}=await Promise.resolve().then(() => (A3(),T3));await J();return}if(!$||$==="--help"||$==="-h"||$==="help"){x3();return}if(!new Set(["version","--version","-v","telemetry"]).has($))lY();switch($){case"login":await CF();break;case"logout":await TF();break;case"whoami":await AF();break;case"submit":await SF(Q.slice(1));break;case"edit":await EF(Q.slice(1));break;case"install":await RF(Q.slice(1));break;case"list":await xF();break;case"select":await vF(Q.slice(1));break;case"hooks":await uF(Q.slice(1));break;case"desktop":await iF(Q.slice(1));break;case"init":await dF();break;case"up":await h3();break;case"down":await f3();break;case"toggle":await oF();break;case"update":await X3(Q.slice(1),u0);break;case"doctor":await CY();break;case"telemetry":nF(Q.slice(1));break;case"version":case"--version":case"-v":console.log(u0);break;default:console.error(j.default.red(`Unknown command: ${$}`)),x3(),process.exit(1)}}function x3(){let Q=j.default.cyan,$=j.default.dim;console.log(["",` ${j.default.bold(j.default.magenta("petdex"))} ${$(u0)} ${$("Codex pet gallery CLI")}`,"",` ${Q("Usage")}`," petdex <command> [args]","",` ${Q("Commands")}`,` ${j.default.bold("init")} First-run setup: wires hooks across your agents AND wakes the mascot ${j.default.green("(start here)")}`,` ${j.default.bold("login")} Sign in with Clerk OAuth`,` ${j.default.bold("logout")} Clear stored credentials`,` ${j.default.bold("whoami")} Show signed-in user`,` ${j.default.bold("submit")} <path> Submit a pet folder, zip, or parent of pets (bulk)`,` ${j.default.bold("edit")} <slug> Edit a pet you own (--desc, --displayName, --sprite, --meta, --zip)`,` ${j.default.bold("install")} <slug...> Install one or more pets into ~/.petdex/pets and ~/.codex/pets`,` ${j.default.bold("install desktop")} Install the petdex-desktop binary (alternative to the .dmg)`,` ${j.default.bold("list")} List approved pets`,` ${j.default.bold("select")} [slug] Set the active pet shown by the desktop mascot`,` ${j.default.bold("mcp-server")} Start the MCP protocol server for Antigravity integration`,` ${j.default.bold("hooks install")} Wire petdex-desktop into your coding agents`,` ${j.default.bold("toggle")} One-shot wake/sleep. Flips the mascot on or off depending on current state`,` ${j.default.bold("up")} Force-wake the mascot. Enables hooks AND launches petdex-desktop`,` ${j.default.bold("down")} Force-sleep the mascot. Disables hooks AND stops petdex-desktop`,` ${j.default.bold("desktop")} <cmd> Manage petdex-desktop (start | stop | status)`,` ${j.default.bold("update")} Pull the latest petdex-desktop release and restart`,` ${j.default.bold("doctor")} Diagnose install/runtime/agents and surface fixes`,` ${j.default.bold("telemetry")} [on|off|status] Manage anonymous usage telemetry`,"",` ${Q("Examples")}`,` ${$("$")} petdex init ${$("# after dragging Petdex.app from the .dmg → just run this")}`,` ${$("$")} petdex login`,` ${$("$")} petdex submit ~/.codex/pets/boba ${$("# single folder")}`,` ${$("$")} petdex install boba ${$("# install a pet by slug")}`,` ${$("$")} petdex install boba doraemon mochi ${$("# install several at once")}`,` ${$("$")} petdex select ${$("# pick active mascot from installed pets")}`,` ${$("$")} petdex select boba ${$("# set active mascot directly")}`,` ${$("$")} petdex toggle ${$("# wake or sleep the mascot")}`,` ${$("$")} petdex doctor ${$("# diagnose install + agents")}`,` ${$("$")} petdex update ${$("# pull the latest release")}`,"",` ${$("Gallery & docs:")} ${j.default.underline(q0)}`,""].join(`
430
430
  `))}async function CF(){N0(j.default.bgMagenta(j.default.white(" petdex login ")));let Q=U0();Q.start("Opening your browser to sign in with Clerk");try{let $=await S8(),{user:q}=await $.login(),J=g3(q.email,q.username,q.sub)??"unknown";Q.stop(`${j.default.green("✓ ")}Signed in as ${j.default.cyan(J)}`),w0(`Try ${j.default.cyan("petdex submit ~/.codex/pets")} to share your pets.`)}catch($){throw Q.stop(j.default.red("× login failed")),Error(fF($.message))}}async function TF(){await(await S8()).logout(),console.log(`${j.default.green("✓ ")}Signed out`)}async function AF(){try{let $=await(await S8()).whoami();if(!$)throw Error("not signed in");let q=[t2($.given_name),t2($.family_name)].filter(Boolean).join(" ");b0([`${j.default.dim("user: ")}${$.sub}`,`${j.default.dim("email: ")}${$.email??"—"}`,`${j.default.dim("name: ")}${q||"—"}`,`${j.default.dim("username:")}${t2($.preferred_username)??"—"}`].join(`
431
431
  `),"Signed in")}catch{d(`Not signed in. Run ${j.default.cyan("petdex login")}.`),process.exit(1)}}async function DF(){let Q=await fetch(`${q0}/api/manifest`);if(!Q.ok)throw Error(`manifest fetch ${Q.status}`);return(await Q.json()).pets}async function wF(Q){let $=Q.slug;if(!B8(Q.spritesheetUrl)||!B8(Q.petJsonUrl))throw Error(`untrusted asset host for ${$} (admin needs to re-upload)`);let q=Z0.join(R3(),".petdex","pets",$),J=Z0.join(R3(),".codex","pets",$);await Promise.all([w3(q,{recursive:!0}),w3(J,{recursive:!0})]);let Y=Q.spritesheetUrl.endsWith(".png")?"png":"webp",V=async(W)=>{let G=await fetch(W,{headers:{Referer:NF}});if(!G.ok)throw Error(`download ${W} -> ${G.status} ${G.statusText}`);return G.arrayBuffer()},[X,K]=await Promise.all([V(Q.petJsonUrl),V(Q.spritesheetUrl)]);await Promise.all([r2(Z0.join(q,"pet.json"),Buffer.from(X)),r2(Z0.join(q,`spritesheet.${Y}`),Buffer.from(K)),r2(Z0.join(J,"pet.json"),Buffer.from(X)),r2(Z0.join(J,`spritesheet.${Y}`),Buffer.from(K))]),fetch(`${q0}/install/${$}`,{method:"GET"}).catch(()=>{})}async function RF(Q){let $=Q[0];if(!$)d(`Usage: ${j.default.cyan("petdex install <slug> [slug...]")} or ${j.default.cyan("petdex install desktop")}`),process.exit(1);if($==="desktop"){let{tag:U}=await TQ();C0("cli_install_desktop_success",{cli_version:u0,os:process.platform,arch:process.arch,binary_version:U.replace(/^desktop-v/,"")});return}let q=Array.from(new Set(Q)),J=U0();J.start(q.length===1?`Resolving ${q[0]}`:`Resolving ${q.length} pets`);let Y;try{Y=await DF()}catch(U){throw J.stop(j.default.red("manifest failed")),U}let V=[],X=[];for(let U of q){let z=Y.find((Z)=>Z.slug===U);if(z)V.push(z);else X.push(U)}if(V.length===0)J.stop(j.default.red("none found")),d(`No pets matched. Try ${j.default.cyan("petdex list")} to see what's available.`),process.exit(1);let K=[],W=[];for(let U=0;U<V.length;U++){let z=V[U];J.message(V.length===1?`Downloading ${z.slug}`:`Downloading ${z.slug} (${U+1}/${V.length})`);try{await wF(z),K.push(z.displayName)}catch(Z){W.push({slug:z.slug,reason:Z instanceof Error?Z.message:String(Z)})}}if(K.length===V.length)J.stop(K.length===1?`Installed ${j.default.cyan(K[0])}`:`Installed ${j.default.cyan(K.length)} pets`);else if(K.length>0)J.stop(`Installed ${j.default.cyan(K.length)} of ${V.length} (${j.default.red(`${W.length} failed`)})`);else J.stop(j.default.red("all failed"));let G=[];if(K.length>0)G.push("Paths:"),G.push(` ${j.default.dim("~/.petdex/pets/")} (Petdex Desktop)`),G.push(` ${j.default.dim("~/.codex/pets/")} (Codex Desktop)`),G.push(""),G.push("Activate in Petdex Desktop: right-click the mascot."),G.push("Activate in Codex Desktop:"),G.push(` ${j.default.cyan("Settings -> Appearance -> Pets")}`);if(X.length>0){if(G.length>0)G.push("");G.push(j.default.yellow("Skipped (slug not found):"));for(let U of X)G.push(` ${U}`)}if(W.length>0){if(G.length>0)G.push("");G.push(j.default.red("Failed:"));for(let U of W)G.push(` ${U.slug}: ${U.reason}`)}if(G.length>0)b0(G.join(`
432
432
  `),"Next steps");if(W.length>0&&K.length===0)process.exit(1)}async function xF(){let Q=U0();Q.start("Fetching gallery");let $=await fetch(`${q0}/api/manifest`);if(!$.ok)throw Q.stop(j.default.red("failed")),Error(`failed to fetch manifest: ${$.status}`);let q=await $.json();Q.stop(`${q.total} pets`);let J=q.pets.map((Y)=>{let V=Y.submittedBy?j.default.dim(` by ${Y.submittedBy}`):"";return` ${j.default.cyan(Y.slug.padEnd(26))} ${Y.displayName}${V}`});console.log(J.join(`
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "petdex",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "CLI for Petdex. Install, browse, and submit Codex pets from your terminal.",
5
- "homepage": "https://petdex.crafter.run",
5
+ "homepage": "https://petdex.dev",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/crafter-station/petdex.git",