@howaboua/pi-pet 0.1.2 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @howaboua/pi-pet
2
2
 
3
+ ## 0.1.4
4
+
5
+ - Remove obsolete test-only helper exports without changing tool behavior.
6
+
7
+ ## 0.1.3
8
+
9
+ - Expose Pi Pet's extension from the package root so aggregate extension packages can load it.
10
+
3
11
  ## 0.1.2
4
12
 
5
13
  - Fixed waiting indicators for extension UI prompts.
@@ -15,4 +23,3 @@
15
23
  - Keep authored pets and generated displays in durable Pi agent storage, with optional per-repository pet selection.
16
24
  - Attach the local device directly or remote devices through Pi-owned SSH sessions, with each folder remembering where its sessions appear.
17
25
  - Get exact install and reload guidance when GipPity Control is missing or outdated.
18
-
@@ -1,3 +1,3 @@
1
- import{join as Ee}from"node:path";import{fileURLToPath as be}from"node:url";import{randomUUID as ce}from"node:crypto";import{copyFileSync as Ue,lstatSync as fe,mkdirSync as Le,readFileSync as Fe,statSync as Ve}from"node:fs";import{cp as ue,mkdir as de,readFile as pe,rename as we,rm as me,stat as ge,writeFile as Je}from"node:fs/promises";import{homedir as _}from"node:os";import{dirname as he,join as m}from"node:path";import{readFile as G,realpath as P,stat as v}from"node:fs/promises";import{join as y,resolve as W,sep as A}from"node:path";import{readFile as T}from"node:fs/promises";function D(e,t,n){if(n<10)return;return{format:"webp",width:1+e.readUIntLE(t+4,3),height:1+e.readUIntLE(t+7,3)}}function U(e,t,n){if(n<10||e[t+3]!==157||e[t+4]!==1||e[t+5]!==42)return;return{format:"webp",width:e.readUInt16LE(t+6)&16383,height:e.readUInt16LE(t+8)&16383}}function L(e,t,n){if(n<5||e[t]!==47)return;let r=e[t+1]??0,o=e[t+2]??0,a=e[t+3]??0,s=e[t+4]??0;return{format:"webp",width:1+((r|o<<8)&16383),height:1+((o>>6|a<<2|s<<10)&16383)}}function F(e,t,n,r){if(t==="VP8X")return D(e,n,r);if(t==="VP8 ")return U(e,n,r);if(t==="VP8L")return L(e,n,r);return}function V(e){if(e.length<30||e.toString("ascii",0,4)!=="RIFF"||e.toString("ascii",8,12)!=="WEBP")return;let t=12;while(t+8<=e.length){let n=e.toString("ascii",t,t+4),r=e.readUInt32LE(t+4),o=t+8;if(o+r>e.length)throw Error("Truncated WebP chunk.");let a=F(e,n,o,r);if(a)return a;t=o+r+r%2}throw Error("WebP dimensions were not found.")}function z(e){if(e.length>=24&&e.subarray(0,8).toString("hex")==="89504e470d0a1a0a")return{format:"png",width:e.readUInt32BE(16),height:e.readUInt32BE(20)};let n=V(e);if(n)return n;throw Error("Asset must be a PNG or WebP image.")}async function N(e){return z(await T(e))}var J=/^[a-z0-9][a-z0-9._-]{0,63}$/,B=new Set(["__proto__","constructor"]),u=Object.freeze({actionName:64,noteText:280,manifestBytes:131072,catalogBytes:262144,assetBytes:16777216,frameCount:64,decodedPixels:16000000});class i extends Error{name="ContractError"}function M(e,t,n){if(typeof e!=="string")throw new i(`${t} must be a string.`);let r=e.trim();if(!r)throw new i(`${t} cannot be empty.`);if(r.length>n)throw new i(`${t} exceeds ${n} characters.`);return r}function d(e,t="action"){let n=M(e,t,u.actionName);if(n==="list")throw new i(`${t} cannot use the reserved pet_show discovery name list.`);if(B.has(n))throw new i(`${t} cannot use a reserved object property name.`);if(!J.test(n))throw new i(`${t} must use lowercase letters, numbers, dots, underscores, or hyphens.`);return n}function b(e){if(!e||e.includes("\x00")||e.includes("\\")||e.startsWith("/"))return!1;return e.split("/").every((n)=>n!==""&&n!=="."&&n!=="..")}var p={width:192,height:208},H=[["idle",0,[280,110,110,140,140,320]],["running-right",1,[120,120,120,120,120,120,120,220]],["running-left",2,[120,120,120,120,120,120,120,220]],["waving",3,[140,140,140,280]],["jumping",4,[140,140,140,140,280]],["failed",5,[140,140,140,140,140,140,140,240]],["waiting",6,[150,150,150,150,150,260]],["running",7,[120,120,120,120,120,220]],["review",8,[150,150,150,150,150,280]]],X=Object.freeze({active:"running",error:"failed",hello:"waving",settled:"review",success:"jumping",thinking:"running",working:"running"});function w(e,t){if(!e||typeof e!=="object"||Array.isArray(e))throw new i(`${t} must be an object.`);return e}function h(e,t,n,r){if(!Number.isInteger(e)||e<n||e>r)throw new i(`${t} must be an integer from ${n} to ${r}.`);return e}async function O(e,t){let n=await v(e);if(!n.isFile()||n.size>t)throw new i(`${e} is not a bounded regular file.`);return JSON.parse(await G(e,"utf8"))}function Y(e,t){return t.map((n,r)=>({x:r*p.width,y:e*p.height,width:p.width,height:p.height,durationMs:n}))}function q(e){let t=w(e,"pet.json"),n=new Set(["id","displayName","description","spriteVersionNumber","spritesheetPath"]);for(let s of Object.keys(t))if(!n.has(s))throw new i(`pet.json has unknown field: ${s}.`);let r=d(t.id,"pet id");if(typeof t.displayName!=="string"||!t.displayName.trim()||t.displayName.length>80)throw new i("pet.json displayName must contain 1-80 characters.");if(typeof t.description!=="string"||!t.description.trim()||t.description.length>280)throw new i("pet.json description must contain 1-280 characters.");if(t.spriteVersionNumber!==2)throw new i("Only Codex spriteVersionNumber 2 is supported.");if(typeof t.spritesheetPath!=="string"||!b(t.spritesheetPath))throw new i("pet.json spritesheetPath must be a safe relative path.");let o={},a={};for(let[s,f,c]of H)o[s]={name:s,asset:t.spritesheetPath,frames:Y(f,c),loop:!0};for(let s=0;s<16;s+=1){let f=s*22.5,c=`look-${String(f).padStart(3,"0").replace(".5","_5")}`,E=s<8?9:10,l=s%8;a[c]={name:c,asset:t.spritesheetPath,frames:[{x:l*p.width,y:E*p.height,...p,durationMs:1000}],loop:!0}}return{schemaVersion:1,id:r,displayName:t.displayName.trim(),description:t.description.trim(),defaultAction:"idle",canvas:{...p},actions:o,aliases:{...X},directions:a}}function K(e,t){let n=w(t,`action ${e}`),r=new Set(["asset","frames","loop","next"]);for(let s of Object.keys(n))if(!r.has(s))throw new i(`action ${e} has unknown field: ${s}.`);if(typeof n.asset!=="string"||!b(n.asset))throw new i(`action ${e} asset must be a safe relative path.`);if(!Array.isArray(n.frames)||n.frames.length===0||n.frames.length>u.frameCount)throw new i(`action ${e} must have 1-${u.frameCount} frames.`);let o=n.frames.map((s,f)=>{let c=w(s,`action ${e} frame ${f}`),E=new Set(["x","y","width","height","durationMs"]);for(let k of Object.keys(c))if(!E.has(k))throw new i(`action ${e} frame ${f} has unknown field: ${k}.`);let l=h(c.width,"frame width",1,4096),x=h(c.height,"frame height",1,4096);if(l*x>u.decodedPixels)throw new i(`action ${e} frame ${f} is too large.`);return{x:h(c.x,"frame x",0,32768),y:h(c.y,"frame y",0,32768),width:l,height:x,durationMs:h(c.durationMs,"frame durationMs",16,60000)}});if(typeof n.loop!=="boolean")throw new i(`action ${e} loop must be a boolean.`);let a={name:e,asset:n.asset,frames:o,loop:n.loop};if(n.next!==void 0)a.next=d(n.next,`action ${e} next`);return a}async function Z(e){let t=y(e,"pet.pi.json");try{return w(await O(t,u.manifestBytes),"pet.pi.json")}catch(n){if(n.code==="ENOENT")return;throw n}}function Q(e){let t=new Set(["schemaVersion","defaultAction","actions","aliases"]);for(let n of Object.keys(e))if(!t.has(n))throw new i(`pet.pi.json has unknown field: ${n}.`);if(e.schemaVersion!==1)throw new i("pet.pi.json schemaVersion must be 1.")}function ee(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json actions"))){let o=d(n,"action name");t.actions[o]=K(o,r),delete t.aliases[o]}}function te(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json aliases"))){let o=d(n,"alias name");if(t.actions[o])throw new i(`Alias ${o} conflicts with an action of the same name.`);t.aliases[o]=d(r,`alias ${o} target`)}}function ne(e){if(!e.actions[e.defaultAction])throw new i(`Unknown defaultAction: ${e.defaultAction}.`);for(let t of Object.values(e.actions))if(t.next!==void 0&&!e.actions[t.next])throw new i(`Action ${t.name} has unknown next action: ${t.next}.`);for(let[t,n]of Object.entries(e.aliases))if(!e.actions[n])throw new i(`Alias ${t} has unknown target action: ${n}.`)}function re(e){if(Buffer.byteLength(JSON.stringify(e))>u.catalogBytes)throw new i(`Generated pet catalog exceeds ${u.catalogBytes} bytes.`)}async function oe(e,t){let n=await Z(e);if(!n)return;if(Q(n),ee(n.actions,t),te(n.aliases,t),n.defaultAction!==void 0)t.defaultAction=d(n.defaultAction,"defaultAction");ne(t)}async function ie(e,t){let n=await P(y(e,t));if(n!==e&&!n.startsWith(`${e}${A}`))throw new i(`Asset escapes pet directory: ${t}.`);let r=await v(n);if(!r.isFile()||r.size===0||r.size>u.assetBytes)throw new i(`Asset is not a bounded regular file: ${t}.`);let o=await N(n);if(o.width*o.height>u.decodedPixels)throw new i(`Asset has too many decoded pixels: ${t}.`);return o}function se(e,t,n,r){for(let o of t)for(let a of o.frames)if(a.x+a.width>n||a.y+a.height>r)throw new i(`Action ${o.name} has a frame outside ${e}.`)}async function ae(e,t,n){let r=await P(e),o=[...Object.values(t.actions),...Object.values(t.directions)],a=new Set(o.map((s)=>s.asset));for(let s of a){let f=await ie(r,s);if(s===n&&(f.width!==1536||f.height!==2288))throw new i(`Codex v2 spritesheet must be 1536x2288; got ${f.width}x${f.height}.`);se(s,o.filter((c)=>c.asset===s),f.width,f.height)}}async function S(e,t){let n=d(t,"active pet"),r=await P(W(e)),o=await P(y(r,n));if(o!==r&&!o.startsWith(`${r}${A}`))throw new i("Active pet escapes the pets directory.");let a=await O(y(o,"pet.json"),u.manifestBytes),s=q(a);if(s.id!==n)throw new i(`Active pet directory ${n} does not match manifest id ${s.id}.`);await oe(o,s),re(s);let f=w(a,"pet.json").spritesheetPath;return await ae(o,s,f),{directory:o,catalog:s}}var le=16384;function Pe(e=process.env,t=_()){return e.PI_CODING_AGENT_DIR?.trim()||m(t,".pi","agent")}function g(e=process.env,t=_()){return m(Pe(e,t),"pi-pet")}function ye(e){if(!e||typeof e!=="object"||Array.isArray(e))throw Error("Pi Pet config must be an object.");let t=e,n=Object.keys(t).find((r)=>!["schemaVersion","activePet"].includes(r));if(n)throw Error(`Pi Pet config has unknown field: ${n}.`);if(t.schemaVersion!==1)throw Error("Unsupported Pi Pet config schemaVersion.");return{schemaVersion:1,activePet:d(t.activePet,"active pet")}}async function C(e=g()){try{let t=m(e,"config.json"),n=await ge(t);if(!n.isFile()||n.size>le)throw Error(`Pi Pet config must be a bounded regular file: ${t}`);return ye(JSON.parse(await pe(t,"utf8")))}catch(t){if(t.code==="ENOENT")return;throw t}}async function R(e,t,n=g()){let r=d(t,"pet id"),o=m(e,"pets",r),a=m(n,"pets",r),s=!0;try{let c=fe(a);if(!c.isDirectory()||c.isSymbolicLink())throw Error(`User pet path is not a directory: ${a}`)}catch(c){if(c.code!=="ENOENT")throw c;s=!1}if(s)return await S(m(n,"pets"),r),a;await de(he(a),{recursive:!0,mode:448});let f=`${a}.${process.pid}.${ce()}.tmp`;try{return await ue(o,f,{recursive:!0,errorOnExist:!0,force:!1}),await we(f,a),a}catch(c){throw await me(f,{recursive:!0,force:!0}),c}}async function I(e=g()){return(await C(e))?.activePet||"clawa"}var Se=be(new URL("../../",import.meta.url)),j=process.argv[2]||await I(),$e=await R(Se,j);process.stdout.write(`Pet: ${$e}
1
+ import{join as Ee}from"node:path";import{fileURLToPath as be}from"node:url";import{randomUUID as ce}from"node:crypto";import{copyFileSync as Ue,lstatSync as fe,mkdirSync as Le,readFileSync as Fe,statSync as Ve}from"node:fs";import{cp as ue,mkdir as de,readFile as pe,rename as we,rm as me,stat as ge,writeFile as Je}from"node:fs/promises";import{homedir as _}from"node:os";import{dirname as he,join as m}from"node:path";import{readFile as G,realpath as P,stat as v}from"node:fs/promises";import{join as y,resolve as W,sep as A}from"node:path";import{readFile as T}from"node:fs/promises";function D(e,t,n){if(n<10)return;return{format:"webp",width:1+e.readUIntLE(t+4,3),height:1+e.readUIntLE(t+7,3)}}function U(e,t,n){if(n<10||e[t+3]!==157||e[t+4]!==1||e[t+5]!==42)return;return{format:"webp",width:e.readUInt16LE(t+6)&16383,height:e.readUInt16LE(t+8)&16383}}function L(e,t,n){if(n<5||e[t]!==47)return;let r=e[t+1]??0,o=e[t+2]??0,a=e[t+3]??0,s=e[t+4]??0;return{format:"webp",width:1+((r|o<<8)&16383),height:1+((o>>6|a<<2|s<<10)&16383)}}function F(e,t,n,r){if(t==="VP8X")return D(e,n,r);if(t==="VP8 ")return U(e,n,r);if(t==="VP8L")return L(e,n,r);return}function V(e){if(e.length<30||e.toString("ascii",0,4)!=="RIFF"||e.toString("ascii",8,12)!=="WEBP")return;let t=12;while(t+8<=e.length){let n=e.toString("ascii",t,t+4),r=e.readUInt32LE(t+4),o=t+8;if(o+r>e.length)throw Error("Truncated WebP chunk.");let a=F(e,n,o,r);if(a)return a;t=o+r+r%2}throw Error("WebP dimensions were not found.")}function z(e){if(e.length>=24&&e.subarray(0,8).toString("hex")==="89504e470d0a1a0a")return{format:"png",width:e.readUInt32BE(16),height:e.readUInt32BE(20)};let n=V(e);if(n)return n;throw Error("Asset must be a PNG or WebP image.")}async function N(e){return z(await T(e))}var J=/^[a-z0-9][a-z0-9._-]{0,63}$/,B=new Set(["__proto__","constructor"]),u=Object.freeze({actionName:64,noteText:280,manifestBytes:131072,catalogBytes:262144,assetBytes:16777216,frameCount:64,decodedPixels:16000000});class i extends Error{name="ContractError"}function M(e,t,n){if(typeof e!=="string")throw new i(`${t} must be a string.`);let r=e.trim();if(!r)throw new i(`${t} cannot be empty.`);if(r.length>n)throw new i(`${t} exceeds ${n} characters.`);return r}function d(e,t="action"){let n=M(e,t,u.actionName);if(n==="list")throw new i(`${t} cannot use the reserved pet_show discovery name list.`);if(B.has(n))throw new i(`${t} cannot use a reserved object property name.`);if(!J.test(n))throw new i(`${t} must use lowercase letters, numbers, dots, underscores, or hyphens.`);return n}function b(e){if(!e||e.includes("\x00")||e.includes("\\")||e.startsWith("/"))return!1;return e.split("/").every((n)=>n!==""&&n!=="."&&n!=="..")}var p={width:192,height:208},H=[["idle",0,[280,110,110,140,140,320]],["running-right",1,[120,120,120,120,120,120,120,220]],["running-left",2,[120,120,120,120,120,120,120,220]],["waving",3,[140,140,140,280]],["jumping",4,[140,140,140,140,280]],["failed",5,[140,140,140,140,140,140,140,240]],["waiting",6,[150,150,150,150,150,260]],["running",7,[120,120,120,120,120,220]],["review",8,[150,150,150,150,150,280]]],X=Object.freeze({active:"running",error:"failed",hello:"waving",settled:"review",success:"jumping",thinking:"running",working:"running"});function w(e,t){if(!e||typeof e!=="object"||Array.isArray(e))throw new i(`${t} must be an object.`);return e}function h(e,t,n,r){if(!Number.isInteger(e)||e<n||e>r)throw new i(`${t} must be an integer from ${n} to ${r}.`);return e}async function O(e,t){let n=await v(e);if(!n.isFile()||n.size>t)throw new i(`${e} is not a bounded regular file.`);return JSON.parse(await G(e,"utf8"))}function Y(e,t){return t.map((n,r)=>({x:r*p.width,y:e*p.height,width:p.width,height:p.height,durationMs:n}))}function q(e){let t=w(e,"pet.json"),n=new Set(["id","displayName","description","spriteVersionNumber","spritesheetPath"]);for(let s of Object.keys(t))if(!n.has(s))throw new i(`pet.json has unknown field: ${s}.`);let r=d(t.id,"pet id");if(typeof t.displayName!=="string"||!t.displayName.trim()||t.displayName.length>80)throw new i("pet.json displayName must contain 1-80 characters.");if(typeof t.description!=="string"||!t.description.trim()||t.description.length>280)throw new i("pet.json description must contain 1-280 characters.");if(t.spriteVersionNumber!==2)throw new i("Only Codex spriteVersionNumber 2 is supported.");if(typeof t.spritesheetPath!=="string"||!b(t.spritesheetPath))throw new i("pet.json spritesheetPath must be a safe relative path.");let o={},a={};for(let[s,f,c]of H)o[s]={name:s,asset:t.spritesheetPath,frames:Y(f,c),loop:!0};for(let s=0;s<16;s+=1){let f=s*22.5,c=`look-${String(f).padStart(3,"0").replace(".5","_5")}`,E=s<8?9:10,l=s%8;a[c]={name:c,asset:t.spritesheetPath,frames:[{x:l*p.width,y:E*p.height,...p,durationMs:1000}],loop:!0}}return{schemaVersion:1,id:r,displayName:t.displayName.trim(),description:t.description.trim(),defaultAction:"idle",canvas:{...p},actions:o,aliases:{...X},directions:a}}function K(e,t){let n=w(t,`action ${e}`),r=new Set(["asset","frames","loop","next"]);for(let s of Object.keys(n))if(!r.has(s))throw new i(`action ${e} has unknown field: ${s}.`);if(typeof n.asset!=="string"||!b(n.asset))throw new i(`action ${e} asset must be a safe relative path.`);if(!Array.isArray(n.frames)||n.frames.length===0||n.frames.length>u.frameCount)throw new i(`action ${e} must have 1-${u.frameCount} frames.`);let o=n.frames.map((s,f)=>{let c=w(s,`action ${e} frame ${f}`),E=new Set(["x","y","width","height","durationMs"]);for(let x of Object.keys(c))if(!E.has(x))throw new i(`action ${e} frame ${f} has unknown field: ${x}.`);let l=h(c.width,"frame width",1,4096),k=h(c.height,"frame height",1,4096);if(l*k>u.decodedPixels)throw new i(`action ${e} frame ${f} is too large.`);return{x:h(c.x,"frame x",0,32768),y:h(c.y,"frame y",0,32768),width:l,height:k,durationMs:h(c.durationMs,"frame durationMs",16,60000)}});if(typeof n.loop!=="boolean")throw new i(`action ${e} loop must be a boolean.`);let a={name:e,asset:n.asset,frames:o,loop:n.loop};if(n.next!==void 0)a.next=d(n.next,`action ${e} next`);return a}async function Z(e){let t=y(e,"pet.pi.json");try{return w(await O(t,u.manifestBytes),"pet.pi.json")}catch(n){if(n.code==="ENOENT")return;throw n}}function Q(e){let t=new Set(["schemaVersion","defaultAction","actions","aliases"]);for(let n of Object.keys(e))if(!t.has(n))throw new i(`pet.pi.json has unknown field: ${n}.`);if(e.schemaVersion!==1)throw new i("pet.pi.json schemaVersion must be 1.")}function ee(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json actions"))){let o=d(n,"action name");t.actions[o]=K(o,r),delete t.aliases[o]}}function te(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json aliases"))){let o=d(n,"alias name");if(t.actions[o])throw new i(`Alias ${o} conflicts with an action of the same name.`);t.aliases[o]=d(r,`alias ${o} target`)}}function ne(e){if(!e.actions[e.defaultAction])throw new i(`Unknown defaultAction: ${e.defaultAction}.`);for(let t of Object.values(e.actions))if(t.next!==void 0&&!e.actions[t.next])throw new i(`Action ${t.name} has unknown next action: ${t.next}.`);for(let[t,n]of Object.entries(e.aliases))if(!e.actions[n])throw new i(`Alias ${t} has unknown target action: ${n}.`)}function re(e){if(Buffer.byteLength(JSON.stringify(e))>u.catalogBytes)throw new i(`Generated pet catalog exceeds ${u.catalogBytes} bytes.`)}async function oe(e,t){let n=await Z(e);if(!n)return;if(Q(n),ee(n.actions,t),te(n.aliases,t),n.defaultAction!==void 0)t.defaultAction=d(n.defaultAction,"defaultAction");ne(t)}async function ie(e,t){let n=await P(y(e,t));if(n!==e&&!n.startsWith(`${e}${A}`))throw new i(`Asset escapes pet directory: ${t}.`);let r=await v(n);if(!r.isFile()||r.size===0||r.size>u.assetBytes)throw new i(`Asset is not a bounded regular file: ${t}.`);let o=await N(n);if(o.width*o.height>u.decodedPixels)throw new i(`Asset has too many decoded pixels: ${t}.`);return o}function se(e,t,n,r){for(let o of t)for(let a of o.frames)if(a.x+a.width>n||a.y+a.height>r)throw new i(`Action ${o.name} has a frame outside ${e}.`)}async function ae(e,t,n){let r=await P(e),o=[...Object.values(t.actions),...Object.values(t.directions)],a=new Set(o.map((s)=>s.asset));for(let s of a){let f=await ie(r,s);if(s===n&&(f.width!==1536||f.height!==2288))throw new i(`Codex v2 spritesheet must be 1536x2288; got ${f.width}x${f.height}.`);se(s,o.filter((c)=>c.asset===s),f.width,f.height)}}async function S(e,t){let n=d(t,"active pet"),r=await P(W(e)),o=await P(y(r,n));if(o!==r&&!o.startsWith(`${r}${A}`))throw new i("Active pet escapes the pets directory.");let a=await O(y(o,"pet.json"),u.manifestBytes),s=q(a);if(s.id!==n)throw new i(`Active pet directory ${n} does not match manifest id ${s.id}.`);await oe(o,s),re(s);let f=w(a,"pet.json").spritesheetPath;return await ae(o,s,f),{directory:o,catalog:s}}var le=16384;function Pe(e=process.env,t=_()){return e.PI_CODING_AGENT_DIR?.trim()||m(t,".pi","agent")}function g(e=process.env,t=_()){return m(Pe(e,t),"pi-pet")}function ye(e){if(!e||typeof e!=="object"||Array.isArray(e))throw Error("Pi Pet config must be an object.");let t=e,n=Object.keys(t).find((r)=>!["schemaVersion","activePet"].includes(r));if(n)throw Error(`Pi Pet config has unknown field: ${n}.`);if(t.schemaVersion!==1)throw Error("Unsupported Pi Pet config schemaVersion.");return{schemaVersion:1,activePet:d(t.activePet,"active pet")}}async function C(e=g()){try{let t=m(e,"config.json"),n=await ge(t);if(!n.isFile()||n.size>le)throw Error(`Pi Pet config must be a bounded regular file: ${t}`);return ye(JSON.parse(await pe(t,"utf8")))}catch(t){if(t.code==="ENOENT")return;throw t}}async function R(e,t,n=g()){let r=d(t,"pet id"),o=m(e,"pets",r),a=m(n,"pets",r),s=!0;try{let c=fe(a);if(!c.isDirectory()||c.isSymbolicLink())throw Error(`User pet path is not a directory: ${a}`)}catch(c){if(c.code!=="ENOENT")throw c;s=!1}if(s)return await S(m(n,"pets"),r),a;await de(he(a),{recursive:!0,mode:448});let f=`${a}.${process.pid}.${ce()}.tmp`;try{return await ue(o,f,{recursive:!0,errorOnExist:!0,force:!1}),await we(f,a),a}catch(c){throw await me(f,{recursive:!0,force:!0}),c}}async function I(e=g()){return(await C(e))?.activePet||"clawa"}var Se=be(new URL("../../",import.meta.url)),j=process.argv[2]||await I(),$e=await R(Se,j);process.stdout.write(`Pet: ${$e}
2
2
  Runs: ${Ee(g(),"runs",j)}
3
3
  `);
@@ -1,2 +1,2 @@
1
- import{basename as we,dirname as me,join as ge,resolve as he}from"node:path";import{readFile as G,realpath as l,stat as v}from"node:fs/promises";import{join as P,resolve as W,sep as A}from"node:path";import{readFile as T}from"node:fs/promises";function D(e,t,n){if(n<10)return;return{format:"webp",width:1+e.readUIntLE(t+4,3),height:1+e.readUIntLE(t+7,3)}}function L(e,t,n){if(n<10||e[t+3]!==157||e[t+4]!==1||e[t+5]!==42)return;return{format:"webp",width:e.readUInt16LE(t+6)&16383,height:e.readUInt16LE(t+8)&16383}}function U(e,t,n){if(n<5||e[t]!==47)return;let r=e[t+1]??0,o=e[t+2]??0,a=e[t+3]??0,s=e[t+4]??0;return{format:"webp",width:1+((r|o<<8)&16383),height:1+((o>>6|a<<2|s<<10)&16383)}}function F(e,t,n,r){if(t==="VP8X")return D(e,n,r);if(t==="VP8 ")return L(e,n,r);if(t==="VP8L")return U(e,n,r);return}function V(e){if(e.length<30||e.toString("ascii",0,4)!=="RIFF"||e.toString("ascii",8,12)!=="WEBP")return;let t=12;while(t+8<=e.length){let n=e.toString("ascii",t,t+4),r=e.readUInt32LE(t+4),o=t+8;if(o+r>e.length)throw Error("Truncated WebP chunk.");let a=F(e,n,o,r);if(a)return a;t=o+r+r%2}throw Error("WebP dimensions were not found.")}function z(e){if(e.length>=24&&e.subarray(0,8).toString("hex")==="89504e470d0a1a0a")return{format:"png",width:e.readUInt32BE(16),height:e.readUInt32BE(20)};let n=V(e);if(n)return n;throw Error("Asset must be a PNG or WebP image.")}async function N(e){return z(await T(e))}var J=/^[a-z0-9][a-z0-9._-]{0,63}$/,B=new Set(["__proto__","constructor"]),d=Object.freeze({actionName:64,noteText:280,manifestBytes:131072,catalogBytes:262144,assetBytes:16777216,frameCount:64,decodedPixels:16000000});class i extends Error{name="ContractError"}function M(e,t,n){if(typeof e!=="string")throw new i(`${t} must be a string.`);let r=e.trim();if(!r)throw new i(`${t} cannot be empty.`);if(r.length>n)throw new i(`${t} exceeds ${n} characters.`);return r}function u(e,t="action"){let n=M(e,t,d.actionName);if(n==="list")throw new i(`${t} cannot use the reserved pet_show discovery name list.`);if(B.has(n))throw new i(`${t} cannot use a reserved object property name.`);if(!J.test(n))throw new i(`${t} must use lowercase letters, numbers, dots, underscores, or hyphens.`);return n}function b(e){if(!e||e.includes("\x00")||e.includes("\\")||e.startsWith("/"))return!1;return e.split("/").every((n)=>n!==""&&n!=="."&&n!=="..")}var p={width:192,height:208},H=[["idle",0,[280,110,110,140,140,320]],["running-right",1,[120,120,120,120,120,120,120,220]],["running-left",2,[120,120,120,120,120,120,120,220]],["waving",3,[140,140,140,280]],["jumping",4,[140,140,140,140,280]],["failed",5,[140,140,140,140,140,140,140,240]],["waiting",6,[150,150,150,150,150,260]],["running",7,[120,120,120,120,120,220]],["review",8,[150,150,150,150,150,280]]],X=Object.freeze({active:"running",error:"failed",hello:"waving",settled:"review",success:"jumping",thinking:"running",working:"running"});function w(e,t){if(!e||typeof e!=="object"||Array.isArray(e))throw new i(`${t} must be an object.`);return e}function m(e,t,n,r){if(!Number.isInteger(e)||e<n||e>r)throw new i(`${t} must be an integer from ${n} to ${r}.`);return e}async function O(e,t){let n=await v(e);if(!n.isFile()||n.size>t)throw new i(`${e} is not a bounded regular file.`);return JSON.parse(await G(e,"utf8"))}function Y(e,t){return t.map((n,r)=>({x:r*p.width,y:e*p.height,width:p.width,height:p.height,durationMs:n}))}function q(e){let t=w(e,"pet.json"),n=new Set(["id","displayName","description","spriteVersionNumber","spritesheetPath"]);for(let s of Object.keys(t))if(!n.has(s))throw new i(`pet.json has unknown field: ${s}.`);let r=u(t.id,"pet id");if(typeof t.displayName!=="string"||!t.displayName.trim()||t.displayName.length>80)throw new i("pet.json displayName must contain 1-80 characters.");if(typeof t.description!=="string"||!t.description.trim()||t.description.length>280)throw new i("pet.json description must contain 1-280 characters.");if(t.spriteVersionNumber!==2)throw new i("Only Codex spriteVersionNumber 2 is supported.");if(typeof t.spritesheetPath!=="string"||!b(t.spritesheetPath))throw new i("pet.json spritesheetPath must be a safe relative path.");let o={},a={};for(let[s,c,f]of H)o[s]={name:s,asset:t.spritesheetPath,frames:Y(c,f),loop:!0};for(let s=0;s<16;s+=1){let c=s*22.5,f=`look-${String(c).padStart(3,"0").replace(".5","_5")}`,E=s<8?9:10,h=s%8;a[f]={name:f,asset:t.spritesheetPath,frames:[{x:h*p.width,y:E*p.height,...p,durationMs:1000}],loop:!0}}return{schemaVersion:1,id:r,displayName:t.displayName.trim(),description:t.description.trim(),defaultAction:"idle",canvas:{...p},actions:o,aliases:{...X},directions:a}}function K(e,t){let n=w(t,`action ${e}`),r=new Set(["asset","frames","loop","next"]);for(let s of Object.keys(n))if(!r.has(s))throw new i(`action ${e} has unknown field: ${s}.`);if(typeof n.asset!=="string"||!b(n.asset))throw new i(`action ${e} asset must be a safe relative path.`);if(!Array.isArray(n.frames)||n.frames.length===0||n.frames.length>d.frameCount)throw new i(`action ${e} must have 1-${d.frameCount} frames.`);let o=n.frames.map((s,c)=>{let f=w(s,`action ${e} frame ${c}`),E=new Set(["x","y","width","height","durationMs"]);for(let k of Object.keys(f))if(!E.has(k))throw new i(`action ${e} frame ${c} has unknown field: ${k}.`);let h=m(f.width,"frame width",1,4096),x=m(f.height,"frame height",1,4096);if(h*x>d.decodedPixels)throw new i(`action ${e} frame ${c} is too large.`);return{x:m(f.x,"frame x",0,32768),y:m(f.y,"frame y",0,32768),width:h,height:x,durationMs:m(f.durationMs,"frame durationMs",16,60000)}});if(typeof n.loop!=="boolean")throw new i(`action ${e} loop must be a boolean.`);let a={name:e,asset:n.asset,frames:o,loop:n.loop};if(n.next!==void 0)a.next=u(n.next,`action ${e} next`);return a}async function Z(e){let t=P(e,"pet.pi.json");try{return w(await O(t,d.manifestBytes),"pet.pi.json")}catch(n){if(n.code==="ENOENT")return;throw n}}function Q(e){let t=new Set(["schemaVersion","defaultAction","actions","aliases"]);for(let n of Object.keys(e))if(!t.has(n))throw new i(`pet.pi.json has unknown field: ${n}.`);if(e.schemaVersion!==1)throw new i("pet.pi.json schemaVersion must be 1.")}function ee(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json actions"))){let o=u(n,"action name");t.actions[o]=K(o,r),delete t.aliases[o]}}function te(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json aliases"))){let o=u(n,"alias name");if(t.actions[o])throw new i(`Alias ${o} conflicts with an action of the same name.`);t.aliases[o]=u(r,`alias ${o} target`)}}function ne(e){if(!e.actions[e.defaultAction])throw new i(`Unknown defaultAction: ${e.defaultAction}.`);for(let t of Object.values(e.actions))if(t.next!==void 0&&!e.actions[t.next])throw new i(`Action ${t.name} has unknown next action: ${t.next}.`);for(let[t,n]of Object.entries(e.aliases))if(!e.actions[n])throw new i(`Alias ${t} has unknown target action: ${n}.`)}function re(e){if(Buffer.byteLength(JSON.stringify(e))>d.catalogBytes)throw new i(`Generated pet catalog exceeds ${d.catalogBytes} bytes.`)}async function oe(e,t){let n=await Z(e);if(!n)return;if(Q(n),ee(n.actions,t),te(n.aliases,t),n.defaultAction!==void 0)t.defaultAction=u(n.defaultAction,"defaultAction");ne(t)}async function ie(e,t){let n=await l(P(e,t));if(n!==e&&!n.startsWith(`${e}${A}`))throw new i(`Asset escapes pet directory: ${t}.`);let r=await v(n);if(!r.isFile()||r.size===0||r.size>d.assetBytes)throw new i(`Asset is not a bounded regular file: ${t}.`);let o=await N(n);if(o.width*o.height>d.decodedPixels)throw new i(`Asset has too many decoded pixels: ${t}.`);return o}function se(e,t,n,r){for(let o of t)for(let a of o.frames)if(a.x+a.width>n||a.y+a.height>r)throw new i(`Action ${o.name} has a frame outside ${e}.`)}async function ae(e,t,n){let r=await l(e),o=[...Object.values(t.actions),...Object.values(t.directions)],a=new Set(o.map((s)=>s.asset));for(let s of a){let c=await ie(r,s);if(s===n&&(c.width!==1536||c.height!==2288))throw new i(`Codex v2 spritesheet must be 1536x2288; got ${c.width}x${c.height}.`);se(s,o.filter((f)=>f.asset===s),c.width,c.height)}}async function y(e,t){let n=u(t,"active pet"),r=await l(W(e)),o=await l(P(r,n));if(o!==r&&!o.startsWith(`${r}${A}`))throw new i("Active pet escapes the pets directory.");let a=await O(P(o,"pet.json"),d.manifestBytes),s=q(a);if(s.id!==n)throw new i(`Active pet directory ${n} does not match manifest id ${s.id}.`);await oe(o,s),re(s);let c=w(a,"pet.json").spritesheetPath;return await ae(o,s,c),{directory:o,catalog:s}}import{cp as _e,mkdir as Ce,readFile as ce,rename as je,rm as Ie,stat as fe,writeFile as Re}from"node:fs/promises";import{homedir as _}from"node:os";import{dirname as Le,join as S}from"node:path";var de=16384;function ue(e=process.env,t=_()){return e.PI_CODING_AGENT_DIR?.trim()||S(t,".pi","agent")}function g(e=process.env,t=_()){return S(ue(e,t),"pi-pet")}function pe(e){if(!e||typeof e!=="object"||Array.isArray(e))throw Error("Pi Pet config must be an object.");let t=e,n=Object.keys(t).find((r)=>!["schemaVersion","activePet"].includes(r));if(n)throw Error(`Pi Pet config has unknown field: ${n}.`);if(t.schemaVersion!==1)throw Error("Unsupported Pi Pet config schemaVersion.");return{schemaVersion:1,activePet:u(t.activePet,"active pet")}}async function C(e=g()){try{let t=S(e,"config.json"),n=await fe(t);if(!n.isFile()||n.size>de)throw Error(`Pi Pet config must be a bounded regular file: ${t}`);return pe(JSON.parse(await ce(t,"utf8")))}catch(t){if(t.code==="ENOENT")return;throw t}}async function j(e=g()){return(await C(e))?.activePet||"clawa"}var le=await j(),I=he(process.argv[2]||ge(g(),"pets",le)),R=await y(me(I),we(I));process.stdout.write(`${R.catalog.displayName}: ${Object.keys(R.catalog.actions).length} actions valid
1
+ import{basename as we,dirname as me,join as ge,resolve as he}from"node:path";import{readFile as G,realpath as l,stat as v}from"node:fs/promises";import{join as P,resolve as W,sep as A}from"node:path";import{readFile as T}from"node:fs/promises";function D(e,t,n){if(n<10)return;return{format:"webp",width:1+e.readUIntLE(t+4,3),height:1+e.readUIntLE(t+7,3)}}function L(e,t,n){if(n<10||e[t+3]!==157||e[t+4]!==1||e[t+5]!==42)return;return{format:"webp",width:e.readUInt16LE(t+6)&16383,height:e.readUInt16LE(t+8)&16383}}function U(e,t,n){if(n<5||e[t]!==47)return;let r=e[t+1]??0,o=e[t+2]??0,a=e[t+3]??0,s=e[t+4]??0;return{format:"webp",width:1+((r|o<<8)&16383),height:1+((o>>6|a<<2|s<<10)&16383)}}function F(e,t,n,r){if(t==="VP8X")return D(e,n,r);if(t==="VP8 ")return L(e,n,r);if(t==="VP8L")return U(e,n,r);return}function V(e){if(e.length<30||e.toString("ascii",0,4)!=="RIFF"||e.toString("ascii",8,12)!=="WEBP")return;let t=12;while(t+8<=e.length){let n=e.toString("ascii",t,t+4),r=e.readUInt32LE(t+4),o=t+8;if(o+r>e.length)throw Error("Truncated WebP chunk.");let a=F(e,n,o,r);if(a)return a;t=o+r+r%2}throw Error("WebP dimensions were not found.")}function z(e){if(e.length>=24&&e.subarray(0,8).toString("hex")==="89504e470d0a1a0a")return{format:"png",width:e.readUInt32BE(16),height:e.readUInt32BE(20)};let n=V(e);if(n)return n;throw Error("Asset must be a PNG or WebP image.")}async function N(e){return z(await T(e))}var J=/^[a-z0-9][a-z0-9._-]{0,63}$/,B=new Set(["__proto__","constructor"]),d=Object.freeze({actionName:64,noteText:280,manifestBytes:131072,catalogBytes:262144,assetBytes:16777216,frameCount:64,decodedPixels:16000000});class i extends Error{name="ContractError"}function M(e,t,n){if(typeof e!=="string")throw new i(`${t} must be a string.`);let r=e.trim();if(!r)throw new i(`${t} cannot be empty.`);if(r.length>n)throw new i(`${t} exceeds ${n} characters.`);return r}function u(e,t="action"){let n=M(e,t,d.actionName);if(n==="list")throw new i(`${t} cannot use the reserved pet_show discovery name list.`);if(B.has(n))throw new i(`${t} cannot use a reserved object property name.`);if(!J.test(n))throw new i(`${t} must use lowercase letters, numbers, dots, underscores, or hyphens.`);return n}function b(e){if(!e||e.includes("\x00")||e.includes("\\")||e.startsWith("/"))return!1;return e.split("/").every((n)=>n!==""&&n!=="."&&n!=="..")}var p={width:192,height:208},H=[["idle",0,[280,110,110,140,140,320]],["running-right",1,[120,120,120,120,120,120,120,220]],["running-left",2,[120,120,120,120,120,120,120,220]],["waving",3,[140,140,140,280]],["jumping",4,[140,140,140,140,280]],["failed",5,[140,140,140,140,140,140,140,240]],["waiting",6,[150,150,150,150,150,260]],["running",7,[120,120,120,120,120,220]],["review",8,[150,150,150,150,150,280]]],X=Object.freeze({active:"running",error:"failed",hello:"waving",settled:"review",success:"jumping",thinking:"running",working:"running"});function w(e,t){if(!e||typeof e!=="object"||Array.isArray(e))throw new i(`${t} must be an object.`);return e}function m(e,t,n,r){if(!Number.isInteger(e)||e<n||e>r)throw new i(`${t} must be an integer from ${n} to ${r}.`);return e}async function O(e,t){let n=await v(e);if(!n.isFile()||n.size>t)throw new i(`${e} is not a bounded regular file.`);return JSON.parse(await G(e,"utf8"))}function Y(e,t){return t.map((n,r)=>({x:r*p.width,y:e*p.height,width:p.width,height:p.height,durationMs:n}))}function q(e){let t=w(e,"pet.json"),n=new Set(["id","displayName","description","spriteVersionNumber","spritesheetPath"]);for(let s of Object.keys(t))if(!n.has(s))throw new i(`pet.json has unknown field: ${s}.`);let r=u(t.id,"pet id");if(typeof t.displayName!=="string"||!t.displayName.trim()||t.displayName.length>80)throw new i("pet.json displayName must contain 1-80 characters.");if(typeof t.description!=="string"||!t.description.trim()||t.description.length>280)throw new i("pet.json description must contain 1-280 characters.");if(t.spriteVersionNumber!==2)throw new i("Only Codex spriteVersionNumber 2 is supported.");if(typeof t.spritesheetPath!=="string"||!b(t.spritesheetPath))throw new i("pet.json spritesheetPath must be a safe relative path.");let o={},a={};for(let[s,c,f]of H)o[s]={name:s,asset:t.spritesheetPath,frames:Y(c,f),loop:!0};for(let s=0;s<16;s+=1){let c=s*22.5,f=`look-${String(c).padStart(3,"0").replace(".5","_5")}`,E=s<8?9:10,h=s%8;a[f]={name:f,asset:t.spritesheetPath,frames:[{x:h*p.width,y:E*p.height,...p,durationMs:1000}],loop:!0}}return{schemaVersion:1,id:r,displayName:t.displayName.trim(),description:t.description.trim(),defaultAction:"idle",canvas:{...p},actions:o,aliases:{...X},directions:a}}function K(e,t){let n=w(t,`action ${e}`),r=new Set(["asset","frames","loop","next"]);for(let s of Object.keys(n))if(!r.has(s))throw new i(`action ${e} has unknown field: ${s}.`);if(typeof n.asset!=="string"||!b(n.asset))throw new i(`action ${e} asset must be a safe relative path.`);if(!Array.isArray(n.frames)||n.frames.length===0||n.frames.length>d.frameCount)throw new i(`action ${e} must have 1-${d.frameCount} frames.`);let o=n.frames.map((s,c)=>{let f=w(s,`action ${e} frame ${c}`),E=new Set(["x","y","width","height","durationMs"]);for(let x of Object.keys(f))if(!E.has(x))throw new i(`action ${e} frame ${c} has unknown field: ${x}.`);let h=m(f.width,"frame width",1,4096),k=m(f.height,"frame height",1,4096);if(h*k>d.decodedPixels)throw new i(`action ${e} frame ${c} is too large.`);return{x:m(f.x,"frame x",0,32768),y:m(f.y,"frame y",0,32768),width:h,height:k,durationMs:m(f.durationMs,"frame durationMs",16,60000)}});if(typeof n.loop!=="boolean")throw new i(`action ${e} loop must be a boolean.`);let a={name:e,asset:n.asset,frames:o,loop:n.loop};if(n.next!==void 0)a.next=u(n.next,`action ${e} next`);return a}async function Z(e){let t=P(e,"pet.pi.json");try{return w(await O(t,d.manifestBytes),"pet.pi.json")}catch(n){if(n.code==="ENOENT")return;throw n}}function Q(e){let t=new Set(["schemaVersion","defaultAction","actions","aliases"]);for(let n of Object.keys(e))if(!t.has(n))throw new i(`pet.pi.json has unknown field: ${n}.`);if(e.schemaVersion!==1)throw new i("pet.pi.json schemaVersion must be 1.")}function ee(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json actions"))){let o=u(n,"action name");t.actions[o]=K(o,r),delete t.aliases[o]}}function te(e,t){if(e===void 0)return;for(let[n,r]of Object.entries(w(e,"pet.pi.json aliases"))){let o=u(n,"alias name");if(t.actions[o])throw new i(`Alias ${o} conflicts with an action of the same name.`);t.aliases[o]=u(r,`alias ${o} target`)}}function ne(e){if(!e.actions[e.defaultAction])throw new i(`Unknown defaultAction: ${e.defaultAction}.`);for(let t of Object.values(e.actions))if(t.next!==void 0&&!e.actions[t.next])throw new i(`Action ${t.name} has unknown next action: ${t.next}.`);for(let[t,n]of Object.entries(e.aliases))if(!e.actions[n])throw new i(`Alias ${t} has unknown target action: ${n}.`)}function re(e){if(Buffer.byteLength(JSON.stringify(e))>d.catalogBytes)throw new i(`Generated pet catalog exceeds ${d.catalogBytes} bytes.`)}async function oe(e,t){let n=await Z(e);if(!n)return;if(Q(n),ee(n.actions,t),te(n.aliases,t),n.defaultAction!==void 0)t.defaultAction=u(n.defaultAction,"defaultAction");ne(t)}async function ie(e,t){let n=await l(P(e,t));if(n!==e&&!n.startsWith(`${e}${A}`))throw new i(`Asset escapes pet directory: ${t}.`);let r=await v(n);if(!r.isFile()||r.size===0||r.size>d.assetBytes)throw new i(`Asset is not a bounded regular file: ${t}.`);let o=await N(n);if(o.width*o.height>d.decodedPixels)throw new i(`Asset has too many decoded pixels: ${t}.`);return o}function se(e,t,n,r){for(let o of t)for(let a of o.frames)if(a.x+a.width>n||a.y+a.height>r)throw new i(`Action ${o.name} has a frame outside ${e}.`)}async function ae(e,t,n){let r=await l(e),o=[...Object.values(t.actions),...Object.values(t.directions)],a=new Set(o.map((s)=>s.asset));for(let s of a){let c=await ie(r,s);if(s===n&&(c.width!==1536||c.height!==2288))throw new i(`Codex v2 spritesheet must be 1536x2288; got ${c.width}x${c.height}.`);se(s,o.filter((f)=>f.asset===s),c.width,c.height)}}async function y(e,t){let n=u(t,"active pet"),r=await l(W(e)),o=await l(P(r,n));if(o!==r&&!o.startsWith(`${r}${A}`))throw new i("Active pet escapes the pets directory.");let a=await O(P(o,"pet.json"),d.manifestBytes),s=q(a);if(s.id!==n)throw new i(`Active pet directory ${n} does not match manifest id ${s.id}.`);await oe(o,s),re(s);let c=w(a,"pet.json").spritesheetPath;return await ae(o,s,c),{directory:o,catalog:s}}import{cp as _e,mkdir as Ce,readFile as ce,rename as je,rm as Ie,stat as fe,writeFile as Re}from"node:fs/promises";import{homedir as _}from"node:os";import{dirname as Le,join as S}from"node:path";var de=16384;function ue(e=process.env,t=_()){return e.PI_CODING_AGENT_DIR?.trim()||S(t,".pi","agent")}function g(e=process.env,t=_()){return S(ue(e,t),"pi-pet")}function pe(e){if(!e||typeof e!=="object"||Array.isArray(e))throw Error("Pi Pet config must be an object.");let t=e,n=Object.keys(t).find((r)=>!["schemaVersion","activePet"].includes(r));if(n)throw Error(`Pi Pet config has unknown field: ${n}.`);if(t.schemaVersion!==1)throw Error("Unsupported Pi Pet config schemaVersion.");return{schemaVersion:1,activePet:u(t.activePet,"active pet")}}async function C(e=g()){try{let t=S(e,"config.json"),n=await fe(t);if(!n.isFile()||n.size>de)throw Error(`Pi Pet config must be a bounded regular file: ${t}`);return pe(JSON.parse(await ce(t,"utf8")))}catch(t){if(t.code==="ENOENT")return;throw t}}async function j(e=g()){return(await C(e))?.activePet||"clawa"}var le=await j(),I=he(process.argv[2]||ge(g(),"pets",le)),R=await y(me(I),we(I));process.stdout.write(`${R.catalog.displayName}: ${Object.keys(R.catalog.actions).length} actions valid
2
2
  `);
@@ -175,7 +175,7 @@ main()
175
175
  .finally(() => { clearInterval(heartbeat); clearTimeout(stopTimer); });
176
176
  `;
177
177
 
178
- export interface DesktopProcessSpec {
178
+ interface DesktopProcessSpec {
179
179
  program: string;
180
180
  args: string[];
181
181
  source: string;
@@ -197,11 +197,7 @@ function packagedDesktopSource(): { files: Record<string, string>; packageVersio
197
197
  return { files, packageVersion: manifest.version, sourceDigest: digest.digest("hex") };
198
198
  }
199
199
 
200
- export function remoteDesktopProcessSpec(
201
- target: string,
202
- gippityUrl: string,
203
- useSshSourceAddress = false,
204
- ): DesktopProcessSpec {
200
+ function remoteDesktopProcessSpec(target: string, gippityUrl: string, useSshSourceAddress = false): DesktopProcessSpec {
205
201
  const origin = parseDesktopConfig({ schemaVersion: 1, gippityUrl }).gippityUrl;
206
202
  const options = { ...packagedDesktopSource(), gippityUrl: origin, useSshSourceAddress };
207
203
  return {
@@ -211,7 +207,7 @@ export function remoteDesktopProcessSpec(
211
207
  };
212
208
  }
213
209
 
214
- export function localDesktopProcessSpec(gippityUrl: string): DesktopProcessSpec {
210
+ function localDesktopProcessSpec(gippityUrl: string): DesktopProcessSpec {
215
211
  const origin = parseDesktopConfig({ schemaVersion: 1, gippityUrl }).gippityUrl;
216
212
  const agentDirectory = process.env["PI_CODING_AGENT_DIR"]?.trim() || join(homedir(), ".pi", "agent");
217
213
  const options = {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@howaboua/pi-pet",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Animated companions for GipPity Remote.",
5
+ "license": "MIT",
5
6
  "repository": {
6
7
  "type": "git",
7
8
  "url": "git+https://github.com/IgorWarzocha/howaboua-pi-stuff.git",
@@ -12,6 +13,8 @@
12
13
  "url": "https://github.com/IgorWarzocha/howaboua-pi-stuff/issues"
13
14
  },
14
15
  "type": "module",
16
+ "main": "./extensions/index.ts",
17
+ "exports": "./extensions/index.ts",
15
18
  "keywords": [
16
19
  "pi-package",
17
20
  "pi-extension",
@@ -73,7 +76,7 @@
73
76
  },
74
77
  "packageManager": "bun@1.4.0",
75
78
  "peerDependencies": {
76
- "@howaboua/pi-gippity-control": ">=0.0.17",
79
+ "@howaboua/pi-gippity-control": ">=0.0.20",
77
80
  "@earendil-works/pi-coding-agent": ">=0.84.3",
78
81
  "@earendil-works/pi-tui": ">=0.84.3",
79
82
  "typebox": "*"
@@ -85,8 +88,8 @@
85
88
  },
86
89
  "devDependencies": {
87
90
  "@biomejs/biome": "2.5.10",
88
- "@earendil-works/pi-coding-agent": "0.84.4",
89
- "@earendil-works/pi-tui": "0.84.4",
91
+ "@earendil-works/pi-coding-agent": "0.86.1",
92
+ "@earendil-works/pi-tui": "0.86.1",
90
93
  "@types/node": "^26.2.0",
91
94
  "electron": "43.4.1",
92
95
  "knip": "^6.32.2",
@@ -22,7 +22,7 @@ export function hyprlandCursorSocket(env: NodeJS.ProcessEnv): string | undefined
22
22
  return join(runtime, "hypr", signature, ".socket.sock");
23
23
  }
24
24
 
25
- export function parseHyprlandCursorResponse(value: string): DesktopCursorPosition {
25
+ function parseHyprlandCursorResponse(value: string): DesktopCursorPosition {
26
26
  const position = parseDesktopCursorPosition(JSON.parse(value) as unknown);
27
27
  if (!position) throw new Error("Hyprland returned an empty cursor position.");
28
28
  return position;
@@ -42,7 +42,7 @@ function parseCoordinatePair(value: unknown, label: string): [number, number] {
42
42
  return [value[0], value[1]];
43
43
  }
44
44
 
45
- export function parseHyprlandClientBounds(value: string, pid: number): DesktopWindowBounds {
45
+ function parseHyprlandClientBounds(value: string, pid: number): DesktopWindowBounds {
46
46
  const clients = JSON.parse(value) as unknown;
47
47
  if (!Array.isArray(clients)) throw new Error("Hyprland clients response must be an array.");
48
48
  const client = clients.find(
@@ -57,7 +57,7 @@ function webpSize(buffer: Buffer): ImageSize | undefined {
57
57
  throw new Error("WebP dimensions were not found.");
58
58
  }
59
59
 
60
- export function imageSize(buffer: Buffer): ImageSize {
60
+ function imageSize(buffer: Buffer): ImageSize {
61
61
  const pngSignature = "89504e470d0a1a0a";
62
62
  if (buffer.length >= 24 && buffer.subarray(0, 8).toString("hex") === pngSignature) {
63
63
  return { format: "png", width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };