@danypops/pi-packed 0.19.11 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../service/src/public/client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAGX,yBAAyB,EACzB,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,aAAa,EACb,MAAM,eAAe,CAAC;AAEvB,6NAA6N;AAC7N,qBAAa,mBAAoB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO;gBAD7B,OAAO,EAAE,MAAM,EACN,UAAU,CAAC,EAAE,OAAO,YAAA;CAK9B;AAED,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAGD,MAAM,WAAW,iBAAiB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B;AACD,MAAM,WAAW,qBAAqB;IACrC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC,CAAC;IAC9H,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACzC,SAAS,IAAI,OAAO,CAAC,yBAAyB,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACrE,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAClC,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtC,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,kBAAkB,CAAA;KAAE,CAAC,CAAC;IAC3G,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACrE,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjG,aAAa,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1G,cAAc,CACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC9B;AAED,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,iBAAsB,GAAG,iBAAiB,CAiB3F;AAmBD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAMrE,qBAAa,YAAa,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsG;gBAC9G,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,cAAsB;IAG1E,OAAO,CAAC,IAAI;IAMZ,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAG1B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,UAAQ;;;;;;IAGpD,IAAI,CAAC,IAAI,EAAE,MAAM;IAGjB,SAAS;IAGH,OAAO;IAGb,QAAQ;IAGR,mBAAmB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,EAAE,QAAQ,UAAQ;IAGtF,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ;IAKxC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ;;;;IAK/C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,UAAQ;IAKrC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAYtE,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,UAAQ;IAG7C,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,UAAQ,EAAE,KAAK,UAAQ;IAGhE,aAAa,CAAC,WAAW,CAAC,EAAE,MAAM;;;;IAG5B,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,UAAQ;IAKjI,QAAQ;CAGR;AAED,wBAAsB,mBAAmB,CAAC,KAAK,oBAA6B,EAAE,SAAS,GAAE,cAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAatI;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IACjC;;mCAE+B;IAC/B,kBAAkB,IAAI,OAAO,CAAC;IAC9B;yDACqD;IACrD,KAAK,IAAI,IAAI,CAAC;IACd,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoBhF;AAED,wBAAsB,kBAAkB,CAAC,KAAK,oBAA6B,EAAE,SAAS,GAAE,cAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkBrI"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../service/src/public/client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAGX,yBAAyB,EACzB,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,aAAa,EACb,MAAM,eAAe,CAAC;AAEvB,6NAA6N;AAC7N,qBAAa,mBAAoB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO;gBAD7B,OAAO,EAAE,MAAM,EACN,UAAU,CAAC,EAAE,OAAO,YAAA;CAK9B;AAED,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAGD,MAAM,WAAW,iBAAiB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B;AACD,MAAM,WAAW,qBAAqB;IACrC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC,CAAC;IAC9H,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACzC,SAAS,IAAI,OAAO,CAAC,yBAAyB,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACrE,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAClC,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtC,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,kBAAkB,CAAA;KAAE,CAAC,CAAC;IAC3G,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACrE,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjG,aAAa,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1G,cAAc,CACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC9B;AAED,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,iBAAsB,GAAG,iBAAiB,CAiB3F;AAOD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAMrE,qBAAa,YAAa,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsG;gBAC9G,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,cAAsB;IAG1E,OAAO,CAAC,IAAI;IAMZ,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAG1B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,UAAQ;;;;;;IAGpD,IAAI,CAAC,IAAI,EAAE,MAAM;IAGjB,SAAS;IAGH,OAAO;IAGb,QAAQ;IAGR,mBAAmB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,EAAE,QAAQ,UAAQ;IAGtF,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ;IAKxC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ;;;;IAK/C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,UAAQ;IAKrC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAYtE,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,UAAQ;IAG7C,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,UAAQ,EAAE,KAAK,UAAQ;IAGhE,aAAa,CAAC,WAAW,CAAC,EAAE,MAAM;;;;IAG5B,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,UAAQ;IAKjI,QAAQ;CAGR;AAED,wBAAsB,mBAAmB,CAAC,KAAK,oBAA6B,EAAE,SAAS,GAAE,cAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAatI;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IACjC;;mCAE+B;IAC/B,kBAAkB,IAAI,OAAO,CAAC;IAC9B;yDACqD;IACrD,KAAK,IAAI,IAAI,CAAC;IACd,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoBhF;AAED,wBAAsB,kBAAkB,CAAC,KAAK,oBAA6B,EAAE,SAAS,GAAE,cAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkBrI"}
package/dist/client.js CHANGED
@@ -1 +1 @@
1
- import{spawn as N}from"node:child_process";import{existsSync as A,readFileSync as L}from"node:fs";import{dirname as w,join as E,resolve as I}from"node:path";import{fileURLToPath as b}from"node:url";class q{baseUrl;token;opsPath;transport;label;constructor(z,G,J){this.baseUrl=z;this.token=G;this.label=J.label,this.opsPath=J.opsPath??"/api/v1/ops",this.transport=J.transport??fetch}authedRequest(z,G={}){return this.transport(new Request(`${this.baseUrl}${z}`,{...G,headers:{...G.headers,authorization:`Bearer ${this.token}`}}))}async call(z,G){let J=await this.authedRequest(this.opsPath,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({op:z,input:G})}),Z=await J.json();if(!J.ok)throw Error(Z.error??`${this.label} operation failed with HTTP ${J.status}`);return Z.result}async operations(){let z=await this.authedRequest(this.opsPath),G=await z.json();if(!z.ok)throw Error(G.error??`${this.label} discovery failed with HTTP ${z.status}`);return G.operations??[]}async ready(){let z=await this.authedRequest("/ready");if(z.status===503)return!1;if(!z.ok)throw Error(`${this.label} readiness check failed with HTTP ${z.status}`);return!0}async health(){let z=await this.authedRequest("/health"),G=await z.json();if(!z.ok||G.ok!==!0||typeof G.version!=="string")throw Error(G.error??`${this.label} health check failed`);return{ok:!0,version:G.version}}}import{chmodSync as h,existsSync as m,mkdirSync as l,readFileSync as D,renameSync as d,rmSync as c,writeFileSync as i}from"node:fs";import{homedir as g,tmpdir as C}from"node:os";import{dirname as n,join as $,win32 as Q}from"node:path";var x="127.0.0.1";function V(z,G={}){let J=G.platform??process.platform,Z=G.home??g();if(J==="darwin")return _(z,Z);if(J==="win32")return j(z,G.env??process.env,Z);return R(z,G,Z)}function R(z,G,J){let Z=G.env??process.env,K=G.uid??process.getuid?.()??0,W=Z.XDG_DATA_HOME??$(J,".local","share"),Y=Z.XDG_STATE_HOME??$(J,".local","state"),F=Z.XDG_RUNTIME_DIR??$("/run","user",String(K)),O=Z.XDG_CONFIG_HOME??$(J,".config");return{database:$(W,z.stateDirectoryName,z.databaseFilename),token:$(Y,z.stateDirectoryName,z.tokenFilename),handle:$(F,z.stateDirectoryName,z.handleFilename),serviceDescriptor:$(O,"systemd","user",z.systemdUnitName)}}function _(z,G){let J=$(G,"Library"),Z=$(J,"Application Support",z.stateDirectoryName);return{database:$(Z,z.databaseFilename),token:$(Z,z.tokenFilename),handle:$(C(),z.stateDirectoryName,z.handleFilename),serviceDescriptor:$(Z,z.systemdUnitName)}}function j(z,G,J){let Z=G.LOCALAPPDATA??Q.join(J,"AppData","Local"),K=G.APPDATA??Q.join(J,"AppData","Roaming"),W=Q.join(Z,z.stateDirectoryName,"Data");return{database:Q.join(W,z.databaseFilename),token:Q.join(W,z.tokenFilename),handle:Q.join(Z,"Temp",z.stateDirectoryName,z.handleFilename),serviceDescriptor:Q.join(K,z.stateDirectoryName,"Config",z.systemdUnitName)}}function B(z){try{let G=JSON.parse(D(z,"utf8"));if(G.host!==x||!Number.isInteger(G.port)||G.port<1||G.port>65535||!Number.isInteger(G.pid))return null;return G}catch{return null}}function X(z,G){if((G.platform??process.platform)==="win32")return G.runCommand("reg.exe",["query","HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run","/v",z.name]).ok;return G.fileExists(z.descriptorPath)}class U extends Error{notADaemon;constructor(z,G){super(z);this.notADaemon=G;this.name="InstallServiceError"}}var f="pi-packed",H="pi-packed.service";function T(z={}){let G=z.env??process.env;if(G.PI_PACKED_HOME){let Z=I(G.PI_PACKED_HOME);return{token:`${Z}/token`,handle:`${Z}/handle.json`,serviceDescriptor:`${Z}/${H}`}}let J=V({stateDirectoryName:"pi-packed",databaseFilename:"packages.db",tokenFilename:"token",handleFilename:"handle.json",systemdUnitName:H},z);return{token:J.token,handle:J.handle,serviceDescriptor:J.serviceDescriptor}}function S(z){return X({name:f,binPath:"",descriptorPath:z},{fileExists:A,writeFile:()=>{},readFile:()=>null,removeFile:()=>{},mkdirp:()=>{},runCommand:()=>({ok:!1,output:""}),which:()=>!1})}function k(z,G=30000){return(J)=>z(new Request(J,{signal:AbortSignal.any([J.signal,AbortSignal.timeout(G)])}))}class M{rpc;constructor(z,G,J=fetch){this.rpc=new q(z,G,{label:"Packed",transport:k(J)})}call(z,G){return this.rpc.call(z,G)}health(){return this.rpc.health()}search(z,G,J=!1){return this.call("package.search",{query:z,limit:G,offline:J})}info(z){return this.call("package.info",{name:z})}installed(){return this.call("package.installed",{})}async updates(){return(await this.call("package.updates",{})).updates}security(){return this.call("package.security.get",{})}setMutationApproval(z,G=!1){return this.call("package.security.set",{mutationApproval:z,approved:G})}async install(z,G=!1){let J=await this.call("package.install",{source:z,approved:G});if(!J.ok)throw Error(J.output);return J.output}async installService(z,G=!1){let J=await this.call("package.install_service",{source:z,approved:G});if(!J.ok)throw new U(J.output,J.notADaemon);return{output:J.output,spec:J.spec}}async remove(z,G=!1){let J=await this.call("package.remove",{name:z,approved:G});if(!J.ok)throw Error(J.output);return J.output}async update(z,G=!1){let J=await this.call("package.update",{source:z,approved:G});if(!J.ok)throw Error(J.output);return{output:J.output,reloadRequired:J.reloadRequired??!0,alreadyUpToDate:J.alreadyUpToDate??!1,pinned:J.pinned??!1,previousVersion:J.previousVersion,currentVersion:J.currentVersion}}setupPlan(z,G=!1){return this.call("setup.plan",{manifestPath:z,prune:G})}setupApply(z,G=!1,J=!1){return this.call("setup.apply",{manifestPath:z,approved:G,prune:J})}listResources(z){return this.call("resources.list",{projectRoot:z})}async toggleResource(z,G,J,Z,K,W=!1){let Y=await this.call("resources.toggle",{source:z,field:G,path:J,enabled:Z,projectRoot:K,approved:W});if(!Y.ok)throw Error(Y.output);return Y.output}piStatus(){return this.call("pi.status",{})}}async function y(z=T(),G=fetch){let J=B(z.handle);if(!J)throw Error("Packed daemon is not running");let Z;try{Z=L(z.token,"utf8").trim()}catch{throw Error("Packed daemon token is unavailable")}if(!/^[a-f0-9]{64}$/.test(Z))throw Error("Packed daemon token is invalid");let K=new M(`http://${J.host}:${J.port}`,Z,G);return await K.health(),K}async function P(z){try{return await z.connect()}catch{}let G=z.retryAttempts??30,J=z.retryDelayMs??100,Z=z.isServiceInstalled();if(!Z)z.spawn();for(let W=0;W<G;W++){await z.sleep(J);try{return await z.connect()}catch{}}let K=G*J/1000;throw Error(Z?`Packed daemon did not become ready within ${K} seconds, but a supervised service is installed -- check it directly (e.g. systemctl --user status pi-packed.service) rather than auto-spawning a second one`:`Packed daemon did not become ready within ${K} seconds`)}async function $z(z=T(),G=fetch){return P({connect:()=>y(z,G),isServiceInstalled:()=>S(z.serviceDescriptor),spawn:()=>{let J=process.env.PI_PACKED_BIN,Z=J??process.env.PI_PACKED_BUN??"bun",K=J?["serve"]:[E(w(b(import.meta.url)),"../service/src/cli/cli.ts"),"serve"],W=N(Z,K,{detached:!0,stdio:"ignore",env:{...process.env,DAEMON_KIT_LAUNCH_PROVENANCE:"auto-spawn"}});W.once("error",()=>{}),W.unref()},sleep:(J)=>new Promise((Z)=>setTimeout(Z,J))})}export{T as resolvePackedClientPaths,$z as ensurePackedClient,P as ensureClient,y as connectPackedClient,M as PackedClient,U as InstallServiceError};
1
+ import{spawn as E}from"node:child_process";import{readFileSync as g}from"node:fs";import{dirname as S,join as f,resolve as A}from"node:path";import{fileURLToPath as I}from"node:url";class j{baseUrl;token;opsPath;transport;label;constructor(z,J,G){this.baseUrl=z;this.token=J;this.label=G.label,this.opsPath=G.opsPath??"/api/v1/ops",this.transport=G.transport??fetch}authedRequest(z,J={}){return this.transport(new Request(`${this.baseUrl}${z}`,{...J,headers:{...J.headers,authorization:`Bearer ${this.token}`}}))}async call(z,J){let G=await this.authedRequest(this.opsPath,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({op:z,input:J})}),Z=await G.json();if(!G.ok)throw Error(Z.error??`${this.label} operation failed with HTTP ${G.status}`);return Z.result}async operations(){let z=await this.authedRequest(this.opsPath),J=await z.json();if(!z.ok)throw Error(J.error??`${this.label} discovery failed with HTTP ${z.status}`);return J.operations??[]}async ready(){let z=await this.authedRequest("/ready");if(z.status===503)return!1;if(!z.ok)throw Error(`${this.label} readiness check failed with HTTP ${z.status}`);return!0}async health(){let z=await this.authedRequest("/health"),J=await z.json();if(!z.ok||J.ok!==!0||typeof J.version!=="string")throw Error(J.error??`${this.label} health check failed`);return{ok:!0,version:J.version}}}import{chmodSync as d,existsSync as c,mkdirSync as l,readFileSync as C,renameSync as i,rmSync as r,writeFileSync as p}from"node:fs";import{homedir as D,tmpdir as b}from"node:os";import{dirname as t,join as K,win32 as Q}from"node:path";var N="127.0.0.1";function q(z,J={}){let G=J.platform??process.platform,Z=J.home??D();if(G==="darwin")return R(z,Z);if(G==="win32")return L(z,J.env??process.env,Z);return _(z,J,Z)}function _(z,J,G){let Z=J.env??process.env,$=J.uid??process.getuid?.()??0,W=Z.XDG_DATA_HOME??K(G,".local","share"),Y=Z.XDG_STATE_HOME??K(G,".local","state"),V=Z.XDG_RUNTIME_DIR??K("/run","user",String($)),x=Z.XDG_CONFIG_HOME??K(G,".config");return{database:K(W,z.stateDirectoryName,z.databaseFilename),token:K(Y,z.stateDirectoryName,z.tokenFilename),handle:K(V,z.stateDirectoryName,z.handleFilename),serviceDescriptor:K(x,"systemd","user",z.systemdUnitName)}}function R(z,J){let G=K(J,"Library"),Z=K(G,"Application Support",z.stateDirectoryName);return{database:K(Z,z.databaseFilename),token:K(Z,z.tokenFilename),handle:K(b(),z.stateDirectoryName,z.handleFilename),serviceDescriptor:K(Z,z.systemdUnitName)}}function L(z,J,G){let Z=J.LOCALAPPDATA??Q.join(G,"AppData","Local"),$=J.APPDATA??Q.join(G,"AppData","Roaming"),W=Q.join(Z,z.stateDirectoryName,"Data");return{database:Q.join(W,z.databaseFilename),token:Q.join(W,z.tokenFilename),handle:Q.join(Z,"Temp",z.stateDirectoryName,z.handleFilename),serviceDescriptor:Q.join($,z.stateDirectoryName,"Config",z.systemdUnitName)}}function B(z){try{let J=JSON.parse(C(z,"utf8"));if(J.host!==N||!Number.isInteger(J.port)||J.port<1||J.port>65535||!Number.isInteger(J.pid))return null;return J}catch{return null}}import{execFileSync as M}from"node:child_process";import{fileURLToPath as w}from"node:url";function X(z,J){let G=J.runCommand(process.execPath,[J.armadaCliPath,"status","--json"]);if(!G.ok)return!1;try{return JSON.parse(G.output).vehicles?.some(($)=>$.name===z)??!1}catch{return!1}}function H(){return{armadaCliPath:w(import.meta.resolve("@danypops/armada/cli")),runCommand:(z,J,G)=>{try{return{ok:!0,output:M(z,J,{encoding:"utf8",input:G,stdio:[G===void 0?"ignore":"pipe","pipe","pipe"]})}}catch(Z){let $=Z;return{ok:!1,output:($.stdout??"")+($.stderr??$.message)}}}}}class O extends Error{notADaemon;constructor(z,J){super(z);this.notADaemon=J;this.name="InstallServiceError"}}var y="pi-packed",F="pi-packed.service";function U(z={}){let J=z.env??process.env;if(J.PI_PACKED_HOME){let Z=A(J.PI_PACKED_HOME);return{token:`${Z}/token`,handle:`${Z}/handle.json`,serviceDescriptor:`${Z}/${F}`}}let G=q({stateDirectoryName:"pi-packed",databaseFilename:"packages.db",tokenFilename:"token",handleFilename:"handle.json",systemdUnitName:F},z);return{token:G.token,handle:G.handle,serviceDescriptor:G.serviceDescriptor}}function k(){return X(y,H())}function P(z,J=30000){return(G)=>z(new Request(G,{signal:AbortSignal.any([G.signal,AbortSignal.timeout(J)])}))}class T{rpc;constructor(z,J,G=fetch){this.rpc=new j(z,J,{label:"Packed",transport:P(G)})}call(z,J){return this.rpc.call(z,J)}health(){return this.rpc.health()}search(z,J,G=!1){return this.call("package.search",{query:z,limit:J,offline:G})}info(z){return this.call("package.info",{name:z})}installed(){return this.call("package.installed",{})}async updates(){return(await this.call("package.updates",{})).updates}security(){return this.call("package.security.get",{})}setMutationApproval(z,J=!1){return this.call("package.security.set",{mutationApproval:z,approved:J})}async install(z,J=!1){let G=await this.call("package.install",{source:z,approved:J});if(!G.ok)throw Error(G.output);return G.output}async installService(z,J=!1){let G=await this.call("package.install_service",{source:z,approved:J});if(!G.ok)throw new O(G.output,G.notADaemon);return{output:G.output,spec:G.spec}}async remove(z,J=!1){let G=await this.call("package.remove",{name:z,approved:J});if(!G.ok)throw Error(G.output);return G.output}async update(z,J=!1){let G=await this.call("package.update",{source:z,approved:J});if(!G.ok)throw Error(G.output);return{output:G.output,reloadRequired:G.reloadRequired??!0,alreadyUpToDate:G.alreadyUpToDate??!1,pinned:G.pinned??!1,previousVersion:G.previousVersion,currentVersion:G.currentVersion}}setupPlan(z,J=!1){return this.call("setup.plan",{manifestPath:z,prune:J})}setupApply(z,J=!1,G=!1){return this.call("setup.apply",{manifestPath:z,approved:J,prune:G})}listResources(z){return this.call("resources.list",{projectRoot:z})}async toggleResource(z,J,G,Z,$,W=!1){let Y=await this.call("resources.toggle",{source:z,field:J,path:G,enabled:Z,projectRoot:$,approved:W});if(!Y.ok)throw Error(Y.output);return Y.output}piStatus(){return this.call("pi.status",{})}}async function u(z=U(),J=fetch){let G=B(z.handle);if(!G)throw Error("Packed daemon is not running");let Z;try{Z=g(z.token,"utf8").trim()}catch{throw Error("Packed daemon token is unavailable")}if(!/^[a-f0-9]{64}$/.test(Z))throw Error("Packed daemon token is invalid");let $=new T(`http://${G.host}:${G.port}`,Z,J);return await $.health(),$}async function v(z){try{return await z.connect()}catch{}let J=z.retryAttempts??30,G=z.retryDelayMs??100,Z=z.isServiceInstalled();if(!Z)z.spawn();for(let W=0;W<J;W++){await z.sleep(G);try{return await z.connect()}catch{}}let $=J*G/1000;throw Error(Z?`Packed daemon did not become ready within ${$} seconds, but a managed service is installed -- run packed doctor rather than auto-spawning a second one`:`Packed daemon did not become ready within ${$} seconds`)}async function Yz(z=U(),J=fetch){return v({connect:()=>u(z,J),isServiceInstalled:()=>k(),spawn:()=>{let G=process.env.PI_PACKED_BIN,Z=G??process.env.PI_PACKED_BUN??"bun",$=G?["serve"]:[f(S(I(import.meta.url)),"../service/src/cli/cli.ts"),"serve"],W=E(Z,$,{detached:!0,stdio:"ignore",env:{...process.env,DAEMON_KIT_LAUNCH_PROVENANCE:"auto-spawn"}});W.once("error",()=>{}),W.unref()},sleep:(G)=>new Promise((Z)=>setTimeout(Z,G))})}export{U as resolvePackedClientPaths,Yz as ensurePackedClient,v as ensureClient,u as connectPackedClient,T as PackedClient,O as InstallServiceError};
@@ -100,7 +100,6 @@ export type PackageResources = {
100
100
  export interface ServiceSpecSummary {
101
101
  name: string;
102
102
  binPath: string;
103
- descriptorPath: string;
104
103
  }
105
104
  /** Mirrors service/src/pi/pi-version.ts's CURRENT_PI_PACKAGE_NAME
106
105
  * literal -- not a shared import, since the public boundary deliberately
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../service/src/public/protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../service/src/public/protocol.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  * model.ts — pure row logic for the /packed panel. No I/O: vitest drives
3
3
  * this directly (the TUI component is a thin shell over these functions).
4
4
  */
5
+ import { gt, valid } from "semver";
5
6
  import type { InstalledPkg, UpdateEntry } from "./packed.js";
6
7
 
7
8
  export interface Row {
@@ -17,12 +18,18 @@ export function mergeRows(installed: InstalledPkg[], updates: UpdateEntry[]): Ro
17
18
  const byName = new Map(updates.map((u) => [u.name, u]));
18
19
  return installed
19
20
  .map((p) => {
20
- const u = byName.get(p.name);
21
+ const version = p.pinned ?? p.installed ?? "?";
22
+ const snapshot = byName.get(p.name);
23
+ const comparable = valid(version) !== null && valid(snapshot?.latest) !== null;
24
+ const update =
25
+ snapshot && (comparable ? gt(snapshot.latest, version) : snapshot.installed === version && snapshot.latest !== version)
26
+ ? snapshot
27
+ : undefined;
21
28
  return {
22
29
  name: p.name,
23
- version: p.pinned ?? p.installed ?? "?",
24
- latest: u?.latest,
25
- hasUpdate: u !== undefined,
30
+ version,
31
+ latest: update?.latest,
32
+ hasUpdate: update !== undefined,
26
33
  };
27
34
  })
28
35
  .sort((a, b) => a.name.localeCompare(b.name));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-packed",
3
- "version": "0.19.11",
3
+ "version": "0.20.0",
4
4
  "description": "Pi package lifecycle, validation, daemon, tools, profiles, and TUI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,24 +10,36 @@
10
10
  "pi": {
11
11
  "extensions": ["extension/src/index.ts"]
12
12
  },
13
+ "packed": {
14
+ "daemonService": {
15
+ "name": "pi-packed",
16
+ "displayName": "Packed package service",
17
+ "binPath": "service/src/cli/cli.ts",
18
+ "args": ["serve"],
19
+ "handleFilename": "handle.json",
20
+ "restartOnFailure": true,
21
+ "restartSec": 2
22
+ }
23
+ },
13
24
  "scripts": {
14
25
  "build": "rm -rf dist && bun build service/src/public/client.ts service/src/public/protocol.ts --outdir dist --target node --format esm --minify && bunx tsc -p service/tsconfig.public.json",
15
26
  "test": "bun run build && bun test service/test test",
16
27
  "typecheck": "bunx tsc --noEmit"
17
28
  },
18
29
  "dependencies": {
30
+ "@danypops/armada": "^0.2.0",
19
31
  "@danypops/packed": "^0.6.0",
20
32
  "@danypops/pi-extension-harness": "^0.2.0",
21
- "@danypops/vehicle-client": "^0.2.0",
22
- "@danypops/vehicle-core": "^0.6.1",
23
- "@danypops/vehicle-server": "^0.7.0",
33
+ "@danypops/vehicle-client": "^0.5.1",
34
+ "@danypops/vehicle-core": "^0.10.0",
35
+ "@danypops/vehicle-server": "^0.15.0",
24
36
  "jiti": "2.6.1",
25
37
  "malevich-tui-components": "^0.16.1",
26
38
  "publint": "0.3.22",
27
39
  "semver": "^7.8.5"
28
40
  },
29
41
  "devDependencies": {
30
- "@danypops/vehicle-client-pi": "^0.1.5",
42
+ "@danypops/vehicle-client-pi": "^0.16.0",
31
43
  "@types/semver": "^7.7.1"
32
44
  },
33
45
  "peerDependencies": {
@@ -487,11 +487,40 @@ function cleanupManifestCheck(context: Context): void {
487
487
  }
488
488
  }
489
489
 
490
+ /** A conventional test file (path under a test/tests/__tests__ directory, or
491
+ * a .test./.spec. basename) never runs on a consumer's machine at
492
+ * install/import time -- it has no bearing on what a package's *shipped
493
+ * runtime* code actually imports, even though npm's own `files` field
494
+ * (a plain directory-prefix allowlist, e.g. "service") has no way to exclude
495
+ * it short of a package author remembering to. Real bug this fixes: a test
496
+ * file's own fixture data can contain string literals shaped exactly like
497
+ * real import statements (deliberately, to test this very checker's
498
+ * detection logic) without being real imports of the file that contains them. */
499
+ function isTestFile(path: string): boolean {
500
+ if (/(?:^|\/)(?:test|tests|__tests__)\//.test(path)) return true;
501
+ return /\.(?:test|spec)\.[cm]?[jt]sx?$/.test(path);
502
+ }
503
+
490
504
  /** Shipped .js/.ts/.cjs/.mjs files that are actually contained within the
491
- * package root (never a symlink escape) -- the same file universe every
492
- * source-scanning check works over. */
505
+ * package root (never a symlink escape), excluding test files -- the same
506
+ * file universe every source-scanning check works over. */
493
507
  function shippedSourceFiles(context: Context): string[] {
494
- return shippedFiles(context).filter((path) => /\.[cm]?[jt]s$/.test(path) && isContainedFile(context.root, path));
508
+ return shippedFiles(context).filter(
509
+ (path) => /\.[cm]?[jt]s$/.test(path) && !isTestFile(path) && isContainedFile(context.root, path),
510
+ );
511
+ }
512
+
513
+ /**
514
+ * Strips line and block comments before the import-specifier regex below runs
515
+ * -- a comment can contain an arbitrary quoted "from ..." substring the regex
516
+ * would otherwise match as if it were real code (confirmed live against a
517
+ * JSDoc comment describing a bug fix in prose that happened to contain
518
+ * exactly that shape). Not a full lexer: a string literal that itself
519
+ * contains a comment delimiter could still confuse this, a pre-existing
520
+ * limit of a regex-based scan, not a regression this introduces.
521
+ */
522
+ function stripComments(source: string): string {
523
+ return source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, "");
495
524
  }
496
525
 
497
526
  /** Every import/require specifier literal in one file, bounded by
@@ -500,7 +529,7 @@ function shippedSourceFiles(context: Context): string[] {
500
529
  function importSpecifiersIn(context: Context, file: string): string[] | undefined {
501
530
  const absolute = join(context.root, file);
502
531
  if (lstatSync(absolute).size > MAX_SOURCE_BYTES) return undefined;
503
- const source = readFileSync(absolute, "utf8");
532
+ const source = stripComments(readFileSync(absolute, "utf8"));
504
533
  return [...source.matchAll(/(?:from\s*|import\s*\(|require\s*\()\s*["']([^"']+)["']/g)].map((match) => match[1]!);
505
534
  }
506
535
 
@@ -523,6 +552,12 @@ function dependencyCheck(context: Context): void {
523
552
  for (const specifier of importSpecifiersIn(context, file) ?? []) {
524
553
  if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("node:") || specifier.startsWith("bun:")) continue;
525
554
  const name = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0]!;
555
+ // A package importing its own subpath exports (e.g. "@scope/pkg/client")
556
+ // is not a runtime dependency on itself -- there is no real range to
557
+ // declare, and node/bun resolve a self-reference via the package's own
558
+ // name field, not a node_modules entry that a `dependencies` line would
559
+ // describe.
560
+ if (name === context.pkg.name) continue;
526
561
  if (CORE_PACKAGES.has(name)) {
527
562
  if (peers[name] !== "*")
528
563
  context.add({
@@ -6,8 +6,11 @@
6
6
  * kind of collision at actual startup, one package at a time; doctor
7
7
  * answers the same question proactively, before pi ever runs.
8
8
  */
9
+ import { existsSync } from "node:fs";
9
10
  import { join } from "node:path";
11
+ import { createNodeServiceInstallDeps } from "@danypops/vehicle-server/service";
10
12
  import { listPackageResources, type PackageResources, resolveInstalledDir } from "../packages/resources.ts";
13
+ import { checkServiceUnitPaths, type ServiceUnitDiagnostic } from "./service-doctor.ts";
11
14
  import { runExtensionSmoke, type SmokeOptions, type SmokeRegistrations } from "./smoke.ts";
12
15
 
13
16
  const REGISTRATION_KINDS = ["tools", "commands", "shortcuts", "flags"] as const;
@@ -50,6 +53,8 @@ export interface DoctorReport {
50
53
  extensions: DoctorExtensionResult[];
51
54
  scanned: number;
52
55
  truncated: boolean;
56
+ /** A daemon-backed package's systemd --user unit referencing a path that no longer exists on disk -- see service-doctor.ts. Empty (not omitted) on a platform without systemd --user coverage. */
57
+ serviceUnits: ServiceUnitDiagnostic[];
53
58
  }
54
59
 
55
60
  interface ScanJob {
@@ -112,7 +117,19 @@ export async function runDoctor(piHome: string, projectRoot?: string, options: S
112
117
  }
113
118
  conflicts.sort((a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
114
119
 
115
- return { ok: conflicts.length === 0 && !anyNotOk, conflicts, extensions, scanned: bounded.length, truncated };
120
+ const serviceReport = checkServiceUnitPaths(piHome, projectRoot, {
121
+ ...createNodeServiceInstallDeps(),
122
+ fileExists: existsSync,
123
+ });
124
+
125
+ return {
126
+ ok: conflicts.length === 0 && !anyNotOk && serviceReport.ok,
127
+ conflicts,
128
+ extensions,
129
+ scanned: bounded.length,
130
+ truncated,
131
+ serviceUnits: serviceReport.diagnostics,
132
+ };
116
133
  }
117
134
 
118
135
  function formatClaimant(claim: DoctorClaim): string {
@@ -130,6 +147,9 @@ export function formatDoctorReport(report: DoctorReport, json: boolean): string
130
147
  if (extension.status === "ok") continue;
131
148
  out += `\n${extension.status.toUpperCase()} ${formatClaimant({ name: extension.name, source: extension.source, scope: extension.scope, extension: extension.extension })}${extension.message ? `: ${extension.message}` : ""}\n`;
132
149
  }
150
+ for (const diagnostic of report.serviceUnits) {
151
+ out += `\n${diagnostic.severity.toUpperCase()} ${diagnostic.code} ${diagnostic.package} (${diagnostic.unitName}.service): ${diagnostic.message}\n`;
152
+ }
133
153
  if (report.truncated) out += "\nOutput truncated: more enabled extensions exist than this run's bound.\n";
134
154
  return out;
135
155
  }
@@ -0,0 +1,101 @@
1
+ import type { ServiceInstallDeps } from "@danypops/vehicle-server/service";
2
+ import { readInstalledPackagesAcrossScopes } from "../packages/installed.ts";
3
+ import { resolveDaemonServiceSpec } from "../daemon/daemon-service.ts";
4
+
5
+ export interface ServiceUnitDiagnostic {
6
+ code: "SERVICE_EXEC_PATH_MISSING" | "SERVICE_STATUS_UNAVAILABLE";
7
+ severity: "error" | "warning";
8
+ package: string;
9
+ unitName: string;
10
+ message: string;
11
+ }
12
+
13
+ export interface ServiceDoctorReport {
14
+ ok: boolean;
15
+ diagnostics: ServiceUnitDiagnostic[];
16
+ checked: number;
17
+ }
18
+
19
+ export interface ServiceDoctorDeps extends ServiceInstallDeps {
20
+ fileExists(path: string): boolean;
21
+ }
22
+
23
+ interface ArmadaStatus {
24
+ vehicles?: Array<{ name?: string; executable?: string }>;
25
+ }
26
+
27
+ export function checkServiceUnitPaths(
28
+ piHome: string,
29
+ projectRoot: string | undefined,
30
+ deps: ServiceDoctorDeps,
31
+ ): ServiceDoctorReport {
32
+ const managedPackages = readInstalledPackagesAcrossScopes(piHome, projectRoot).flatMap((pkg) => {
33
+ const resolved = resolveDaemonServiceSpec(piHome, `npm:${pkg.name}`);
34
+ return resolved.ok ? [{ packageName: pkg.name, spec: resolved.spec }] : [];
35
+ });
36
+ if (managedPackages.length === 0) return { ok: true, diagnostics: [], checked: 0 };
37
+
38
+ const statusResult = deps.runCommand(process.execPath, [deps.armadaCliPath, "status", "--json"]);
39
+ if (!statusResult.ok) {
40
+ return {
41
+ ok: false,
42
+ checked: 0,
43
+ diagnostics: [
44
+ {
45
+ code: "SERVICE_STATUS_UNAVAILABLE",
46
+ severity: "error",
47
+ package: "@danypops/armada",
48
+ unitName: "armada",
49
+ message: `could not read Armada fleet status: ${statusResult.output}`,
50
+ },
51
+ ],
52
+ };
53
+ }
54
+
55
+ let status: ArmadaStatus;
56
+ try {
57
+ status = JSON.parse(statusResult.output) as ArmadaStatus;
58
+ } catch {
59
+ return {
60
+ ok: false,
61
+ checked: 0,
62
+ diagnostics: [
63
+ {
64
+ code: "SERVICE_STATUS_UNAVAILABLE",
65
+ severity: "error",
66
+ package: "@danypops/armada",
67
+ unitName: "armada",
68
+ message: "Armada returned malformed fleet status",
69
+ },
70
+ ],
71
+ };
72
+ }
73
+
74
+ const vehicles = new Map((status.vehicles ?? []).flatMap((vehicle) => (vehicle.name ? [[vehicle.name, vehicle]] : [])));
75
+ const diagnostics: ServiceUnitDiagnostic[] = [];
76
+ let checked = 0;
77
+ for (const managedPackage of managedPackages) {
78
+ const vehicle = vehicles.get(managedPackage.spec.name);
79
+ if (!vehicle) continue;
80
+ checked++;
81
+ const executable = vehicle.executable ?? managedPackage.spec.binPath;
82
+ if (deps.fileExists(executable)) continue;
83
+ diagnostics.push({
84
+ code: "SERVICE_EXEC_PATH_MISSING",
85
+ severity: "error",
86
+ package: managedPackage.packageName,
87
+ unitName: managedPackage.spec.name,
88
+ message: `${managedPackage.spec.name}'s Armada declaration references a path that no longer exists: ${executable}`,
89
+ });
90
+ }
91
+ return { ok: diagnostics.length === 0, diagnostics, checked };
92
+ }
93
+
94
+ export function formatServiceDoctorReport(report: ServiceDoctorReport, json: boolean): string {
95
+ if (json) return `${JSON.stringify(report)}\n`;
96
+ let out = `${report.ok ? "PASS" : "FAIL"} — ${report.checked} managed service(s) checked, ${report.diagnostics.length} issue(s)\n`;
97
+ for (const diagnostic of report.diagnostics) {
98
+ out += `${diagnostic.severity.toUpperCase()} ${diagnostic.code} ${diagnostic.package} (${diagnostic.unitName}): ${diagnostic.message}\n`;
99
+ }
100
+ return out;
101
+ }
@@ -8,6 +8,7 @@ import { formatDoctorReport, runDoctor } from "../adoption/doctor.ts";
8
8
  import { formatPackReport, NpmPackVerifier, type PackReport } from "../adoption/pack.ts";
9
9
  import { type AdoptionReport, formatAdoptionReport, scoreTarget } from "../adoption/score.ts";
10
10
  import type { PackageDaemonPort } from "../daemon/client.ts";
11
+ import type { OperationOutputs } from "../daemon/service.ts";
11
12
  import { checkUpdates } from "../daemon/watcher.ts";
12
13
  import { generateIndex, indexPath, readIndex } from "../index/build-index.ts";
13
14
  import { syncCatalog } from "../packages/catalog.ts";
@@ -83,7 +84,7 @@ usage:
83
84
  packed search <query> [--offline] [--json] search npm (or the local mirror with --offline)
84
85
  packed info <name> [--json] package details
85
86
  packed updates [--project <path>] [--json] updates per the local mirror; --project also checks that project's own .pi/settings.json pins
86
- packed update <source> [--approve] [--no-service] [--json] update one configured package through Pi; restarts its registered persistent service (if any) to pick up the new code, unless --no-service
87
+ packed update <source> [--approve] [--json] update one configured package through Pi and reconcile its registered Vehicle
87
88
  packed update --self [--approve] [--json] update Packed itself (npm-global installs only) and restart its supervised service
88
89
  packed mirror [--json] sync upstream into the local SQLite index
89
90
  packed installed [--json] installed pi packages
@@ -100,9 +101,10 @@ usage:
100
101
  packed setup update [manifest] [--json] deliberately refresh immutable package resolutions
101
102
  packed setup plan [manifest] [--prune] [--json] show an additive or exact setup diff without mutation
102
103
  packed setup apply [manifest] [--prune] [--approve] [--json] apply an approved setup plan
103
- packed install <source> [--approve] [--no-service] [--json] pi install npm:|git:|https://… via daemon; auto-registers a detected npm: Vehicle daemon as a service unless --no-service
104
- packed install-service <source> --approve [--json] register a package's own daemon as a persistent login/boot service (also useful standalone, e.g. re-registering after --no-service)
104
+ packed install <source> [--approve] [--json] pi install npm:|git:|https://… via daemon and reconcile any declared Vehicle
105
+ packed install-service <source> --approve [--json] reconcile a package's declared Vehicle
105
106
  packed restart-service <source> --approve [--json] restart a package's already-registered persistent service standalone (packed update already does this automatically)
107
+ packed reconcile-services --approve [--project <path>] [--json] sweep every installed package and self-heal its Vehicle registration (the daemon also runs this on its own interval and at startup)
106
108
  packed remove <name> [--approve] [--json] remove by bare npm name via daemon
107
109
  packed security [always|never] [--approve] [--json] read or set mutation approval policy
108
110
  packed serve run the long-running daemon
@@ -138,14 +140,12 @@ export interface CliDeps {
138
140
  apply(manifestPath: string, options?: { prune?: boolean }): Promise<SetupApplyResult>;
139
141
  };
140
142
  daemonService?: {
141
- install(
142
- source: string,
143
- approved?: boolean,
144
- ): Promise<{ output: string; spec?: { name: string; binPath: string; descriptorPath: string } }>;
143
+ install(source: string, approved?: boolean): Promise<{ output: string; spec?: { name: string; binPath: string } }>;
145
144
  restart(
146
145
  source: string,
147
146
  approved?: boolean,
148
- ): Promise<{ output: string; restarted?: boolean; spec?: { name: string; binPath: string; descriptorPath: string } }>;
147
+ ): Promise<{ output: string; restarted?: boolean; spec?: { name: string; binPath: string } }>;
148
+ reconcileAll(approved?: boolean, projectRoot?: string): Promise<OperationOutputs["package.reconcile_services"]>;
149
149
  };
150
150
  piVersion?: { check(): Promise<PiVersionReport> };
151
151
  selfUpdater?: { run(): Promise<SelfUpdateReport> };
@@ -166,7 +166,6 @@ interface Flags {
166
166
  force: boolean;
167
167
  prune: boolean;
168
168
  machineLocal: boolean;
169
- noService: boolean;
170
169
  ecosystem: boolean;
171
170
  self: boolean;
172
171
  project?: string;
@@ -183,7 +182,6 @@ function parseFlags(rest: string[]): { flags: Flags; pos: string[] } {
183
182
  force: false,
184
183
  prune: false,
185
184
  machineLocal: false,
186
- noService: false,
187
185
  ecosystem: false,
188
186
  self: false,
189
187
  };
@@ -198,7 +196,6 @@ function parseFlags(rest: string[]): { flags: Flags; pos: string[] } {
198
196
  else if (a === "--force") flags.force = true;
199
197
  else if (a === "--prune") flags.prune = true;
200
198
  else if (a === "--machine-local") flags.machineLocal = true;
201
- else if (a === "--no-service") flags.noService = true;
202
199
  else if (a === "--ecosystem") flags.ecosystem = true;
203
200
  else if (a === "--self") flags.self = true;
204
201
  else if (a === "--limit" && i + 1 < rest.length) flags.limit = Number(rest[++i]) || SEARCH_DEFAULT_LIMIT;
@@ -236,6 +233,7 @@ const PACKAGE_COMMAND_OPERATIONS: Record<string, PackageOperation | undefined> =
236
233
  install: "install",
237
234
  "install-service": "install_service",
238
235
  "restart-service": "restart_service",
236
+ "reconcile-services": "reconcile_services",
239
237
  remove: "remove",
240
238
  pi: "pi.status",
241
239
  advisories: "advisories.scan",
@@ -570,36 +568,14 @@ const commands: Record<string, { usage: string; run: Command }> = {
570
568
  },
571
569
 
572
570
  install: {
573
- usage: "packed install npm:<pkg>[@ver] | git:<host>/<owner>/<repo>[@ref] | https://… [--no-service] [--json]",
571
+ usage: "packed install npm:<pkg>[@ver] | git:<host>/<owner>/<repo>[@ref] | https://… [--json]",
574
572
  async run(_rest, d, flags, pos) {
575
573
  const source = pos[0] ?? "";
576
574
  if (!SOURCE_RE.test(source)) return usageErr(`usage: ${commands.install!.usage}\n`);
577
575
  try {
578
576
  const output = await d.inst.install(source, { approved: flags.approved });
579
- // A package's own persistent-service registration piggybacks on the same
580
- // approval already granted for install -- both are the same "code-execution"
581
- // mutation tier, so this isn't a new consent surface. Silent for the
582
- // overwhelmingly common case (most Pi packages aren't daemons at all);
583
- // --no-service skips the attempt entirely, and a genuine failure (detected
584
- // a daemon but couldn't register it) is reported without failing the
585
- // install itself, which already succeeded.
586
- let serviceInstall: { detected: true; ok: boolean; output: string } | undefined;
587
- if (!flags.noService && source.startsWith("npm:") && d.daemonService) {
588
- try {
589
- const svc = await d.daemonService.install(source, flags.approved);
590
- serviceInstall = { detected: true, ok: true, output: svc.output };
591
- } catch (e) {
592
- if (!(e instanceof Error) || !(e as { notADaemon?: boolean }).notADaemon) {
593
- serviceInstall = { detected: true, ok: false, output: e instanceof Error ? e.message : String(e) };
594
- }
595
- }
596
- }
597
- if (flags.json) return ok(`${JSON.stringify({ ok: true, source, output, ...(serviceInstall ? { serviceInstall } : {}) })}\n`);
598
- let human = `${output}\n`;
599
- if (serviceInstall?.ok) human += `${serviceInstall.output}\n`;
600
- else if (serviceInstall && !serviceInstall.ok)
601
- human += `note: detected a persistent-service daemon but could not register it: ${serviceInstall.output}\n`;
602
- return ok(human);
577
+ if (flags.json) return ok(`${JSON.stringify({ ok: true, source, output })}\n`);
578
+ return ok(`${output}\n`);
603
579
  } catch (e) {
604
580
  const error = e instanceof Error ? e.message : String(e);
605
581
  return flags.json ? fail(`${JSON.stringify({ ok: false, source, error })}\n`) : fail(`${error}\n`);
@@ -640,6 +616,21 @@ const commands: Record<string, { usage: string; run: Command }> = {
640
616
  },
641
617
  },
642
618
 
619
+ "reconcile-services": {
620
+ usage:
621
+ "packed reconcile-services --approve [--project <path>] [--json] (sweeps every installed package and self-heals its Vehicle registration through Armada)",
622
+ async run(_rest, d, flags) {
623
+ if (!d.daemonService) return fail("reconcile-services requires a running packed daemon\n");
624
+ try {
625
+ const result = await d.daemonService.reconcileAll(flags.approved, flags.project);
626
+ return flags.json ? ok(`${JSON.stringify(result)}\n`) : ok(`${result.output}\n`);
627
+ } catch (e) {
628
+ const error = e instanceof Error ? e.message : String(e);
629
+ return flags.json ? fail(`${JSON.stringify({ ok: false, error })}\n`) : fail(`${error}\n`);
630
+ }
631
+ },
632
+ },
633
+
643
634
  security: {
644
635
  usage: "packed security [always|never] [--json]",
645
636
  async run(_rest, d, flags, pos) {
@@ -685,27 +676,10 @@ const commands: Record<string, { usage: string; run: Command }> = {
685
676
  : `is already up to date${version ? ` at ${version}` : ""}`;
686
677
  return ok(`${source} ${reason}\n`);
687
678
  }
688
- // Mirrors install's own auto-registration composition: same approval tier, silent for
689
- // the common non-daemon case, a failure reported without failing the update itself.
690
- let serviceRestart: { detected: true; ok: boolean; output: string } | undefined;
691
- if (!flags.noService && source.startsWith("npm:") && d.daemonService) {
692
- try {
693
- const svc = await d.daemonService.restart(source, flags.approved);
694
- serviceRestart = { detected: true, ok: true, output: svc.output };
695
- } catch (e) {
696
- if (!(e instanceof Error) || !(e as { notADaemon?: boolean }).notADaemon) {
697
- serviceRestart = { detected: true, ok: false, output: e instanceof Error ? e.message : String(e) };
698
- }
699
- }
700
- }
701
- if (flags.json) return ok(`${JSON.stringify({ ok: true, source, ...outcome, ...(serviceRestart ? { serviceRestart } : {}) })}\n`);
679
+ if (flags.json) return ok(`${JSON.stringify({ ok: true, source, ...outcome })}\n`);
702
680
  const transition =
703
681
  outcome.previousVersion && outcome.currentVersion ? ` (${outcome.previousVersion} → ${outcome.currentVersion})` : "";
704
- let human = `${outcome.output}${transition}\n`;
705
- if (serviceRestart?.ok) human += `${serviceRestart.output}\n`;
706
- else if (serviceRestart && !serviceRestart.ok)
707
- human += `note: could not restart its persistent service: ${serviceRestart.output}\n`;
708
- return ok(`${human}Reload Pi with /reload to activate the updated package.\n`);
682
+ return ok(`${outcome.output}${transition}\nReload Pi with /reload to activate the updated package.\n`);
709
683
  } catch (error) {
710
684
  const message = error instanceof Error ? error.message : String(error);
711
685
  return flags.json
@@ -755,10 +729,7 @@ const commands: Record<string, { usage: string; run: Command }> = {
755
729
  },
756
730
  };
757
731
 
758
- /** systemd user unit, delegating to vehicle-server's shared generateSystemdUnit -- idle self-exit
759
- * is disabled (PI_PACKED_IDLE_SECS=0) since systemd's own Restart=always takes over lifecycle. This
760
- * only ever prints (see the `service` command above); nothing here calls installUserService, so
761
- * descriptorPath is never actually read/written -- required by ServiceSpec's shape regardless. */
732
+ /** Renders the legacy diagnostic unit without installing it; Armada owns service-manager mutation. */
762
733
  export function renderUnit(execPath: string, cliPath: string, piBin?: string): string {
763
734
  // systemd does not read shell rc files: PI_BIN must be explicit so the
764
735
  // daemon's install/remove execs can find the pi binary.
@@ -767,10 +738,11 @@ export function renderUnit(execPath: string, cliPath: string, piBin?: string): s
767
738
  return generateSystemdUnit({
768
739
  name: "pi-packed",
769
740
  displayName: "pi-packed package service (Pi agent)",
741
+ version: VERSION,
770
742
  binPath: execPath,
771
743
  args: [cliPath, "serve"],
772
744
  env,
773
- descriptorPath: resolvePackedPaths().serviceDescriptor,
745
+ handlePath: resolvePackedPaths().handle,
774
746
  // Packed's own client (connectPackageDaemon) never auto-spawns -- "start packed.service"
775
747
  // or this unit's own systemd supervision is its only recovery path.
776
748
  restartOnFailure: true,