@danypops/pi-packed 0.19.12 → 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":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../service/src/public/client.ts"],"names":[],"mappings":"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"}
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.12",
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": {
@@ -6,6 +6,7 @@
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";
10
11
  import { createNodeServiceInstallDeps } from "@danypops/vehicle-server/service";
11
12
  import { listPackageResources, type PackageResources, resolveInstalledDir } from "../packages/resources.ts";
@@ -116,7 +117,10 @@ export async function runDoctor(piHome: string, projectRoot?: string, options: S
116
117
  }
117
118
  conflicts.sort((a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
118
119
 
119
- const serviceReport = checkServiceUnitPaths(piHome, projectRoot, createNodeServiceInstallDeps());
120
+ const serviceReport = checkServiceUnitPaths(piHome, projectRoot, {
121
+ ...createNodeServiceInstallDeps(),
122
+ fileExists: existsSync,
123
+ });
120
124
 
121
125
  return {
122
126
  ok: conflicts.length === 0 && !anyNotOk && serviceReport.ok,
@@ -1,27 +1,9 @@
1
- /**
2
- * service-doctor.ts — checks every installed package's own daemon systemd
3
- * --user unit (if one is registered) for an ExecStart path that no longer
4
- * exists on disk. A unit is written once, at install/service-install time,
5
- * and never re-validated against the package's current on-disk layout --
6
- * confirmed live: a refactor that moved a package's own CLI entrypoint left
7
- * a real, already-registered unit pointing at a deleted file. The process
8
- * kept running only because it had been loaded into memory before the file
9
- * vanished; the next crash or reboot would have failed to restart at all,
10
- * silently, until someone happened to notice the daemon was down.
11
- *
12
- * Deliberately separate from doctor.ts's own DoctorReport (extension
13
- * registration-conflict scanning) -- a different subject (installed
14
- * services vs. loaded extensions), composed alongside it in runDoctor()
15
- * rather than folded into its shape.
16
- */
17
- import { existsSync } from "node:fs";
18
1
  import type { ServiceInstallDeps } from "@danypops/vehicle-server/service";
19
- import { isServiceInstalled } from "@danypops/vehicle-server/service";
20
2
  import { readInstalledPackagesAcrossScopes } from "../packages/installed.ts";
21
3
  import { resolveDaemonServiceSpec } from "../daemon/daemon-service.ts";
22
4
 
23
5
  export interface ServiceUnitDiagnostic {
24
- code: "SERVICE_EXEC_PATH_MISSING" | "SERVICE_EXEC_UNPARSEABLE";
6
+ code: "SERVICE_EXEC_PATH_MISSING" | "SERVICE_STATUS_UNAVAILABLE";
25
7
  severity: "error" | "warning";
26
8
  package: string;
27
9
  unitName: string;
@@ -34,95 +16,86 @@ export interface ServiceDoctorReport {
34
16
  checked: number;
35
17
  }
36
18
 
37
- /**
38
- * Tokenizes ExecStart's own shell-quoted argument list -- matching
39
- * generateSystemdUnit's shellQuote, which wraps every argument in double
40
- * quotes and backslash-escapes only ", \, $, and ` inside them. Returns
41
- * undefined for a missing or unrecognizable ExecStart line rather than
42
- * guessing; the caller degrades that to a warning, never a false error.
43
- */
44
- export function parseExecStartTokens(unitText: string): string[] | undefined {
45
- const line = unitText.split("\n").find((entry) => entry.trimStart().startsWith("ExecStart="));
46
- if (!line) return undefined;
47
- const raw = line.slice(line.indexOf("=") + 1);
48
- const tokens: string[] = [];
49
- const pattern = /"((?:[^"\\]|\\.)*)"/g;
50
- let match: RegExpExecArray | null = pattern.exec(raw);
51
- while (match !== null) {
52
- tokens.push(match[1]!.replace(/\\(["\\$`])/g, "$1"));
53
- match = pattern.exec(raw);
54
- }
55
- return tokens.length > 0 ? tokens : undefined;
56
- }
57
-
58
- /** A token worth existence-checking is one that's actually path-shaped -- a bare word like "serve" (a real subcommand argument, not a path) would otherwise resolve relative to this check's own cwd and produce a false SERVICE_EXEC_PATH_MISSING. */
59
- function looksLikePath(token: string): boolean {
60
- return token.includes("/");
19
+ export interface ServiceDoctorDeps extends ServiceInstallDeps {
20
+ fileExists(path: string): boolean;
61
21
  }
62
22
 
63
- export interface ServiceDoctorDeps extends Pick<ServiceInstallDeps, "fileExists" | "readFile"> {
64
- platform?: string;
23
+ interface ArmadaStatus {
24
+ vehicles?: Array<{ name?: string; executable?: string }>;
65
25
  }
66
26
 
67
- /**
68
- * For every installed package that resolves to a real Vehicle daemon spec
69
- * (same resolution daemon-service.ts's own install/restart commands use)
70
- * and currently has a systemd --user unit registered, reads that unit's
71
- * actual on-disk ExecStart (not a freshly recomputed "correct" one -- the
72
- * whole point is catching a real unit that has drifted from what's true
73
- * today) and flags any referenced path that no longer exists.
74
- *
75
- * Linux/systemd only, matching installUserService's own platform coverage
76
- * -- macOS/Windows unit shapes differ and aren't checked here yet.
77
- */
78
- export function checkServiceUnitPaths(piHome: string, projectRoot: string | undefined, deps: ServiceDoctorDeps): ServiceDoctorReport {
79
- const diagnostics: ServiceUnitDiagnostic[] = [];
80
- const platform = deps.platform ?? process.platform;
81
- let checked = 0;
82
- if (platform !== "linux") return { ok: true, diagnostics, checked };
83
-
84
- for (const pkg of readInstalledPackagesAcrossScopes(piHome, projectRoot)) {
27
+ export function checkServiceUnitPaths(
28
+ piHome: string,
29
+ projectRoot: string | undefined,
30
+ deps: ServiceDoctorDeps,
31
+ ): ServiceDoctorReport {
32
+ const managedPackages = readInstalledPackagesAcrossScopes(piHome, projectRoot).flatMap((pkg) => {
85
33
  const resolved = resolveDaemonServiceSpec(piHome, `npm:${pkg.name}`);
86
- if (!resolved.ok) continue;
87
- const spec = resolved.spec;
88
- if (!isServiceInstalled(spec, deps as ServiceInstallDeps)) continue;
89
- checked++;
34
+ return resolved.ok ? [{ packageName: pkg.name, spec: resolved.spec }] : [];
35
+ });
36
+ if (managedPackages.length === 0) return { ok: true, diagnostics: [], checked: 0 };
90
37
 
91
- const unitText = deps.readFile(spec.descriptorPath);
92
- if (unitText === null) continue; // isServiceInstalled said yes but the read raced/failed -- transient, not a real finding.
93
-
94
- const tokens = parseExecStartTokens(unitText);
95
- if (!tokens) {
96
- diagnostics.push({
97
- code: "SERVICE_EXEC_UNPARSEABLE",
98
- severity: "warning",
99
- package: pkg.name,
100
- unitName: spec.name,
101
- message: `could not parse ExecStart in ${spec.descriptorPath}`,
102
- });
103
- continue;
104
- }
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
+ }
105
54
 
106
- for (const token of tokens) {
107
- if (!looksLikePath(token) || existsSync(token)) continue;
108
- diagnostics.push({
109
- code: "SERVICE_EXEC_PATH_MISSING",
110
- severity: "error",
111
- package: pkg.name,
112
- unitName: spec.name,
113
- message: `${spec.name}.service's ExecStart references a path that no longer exists: ${token} -- the service will fail to (re)start the next time it stops. Reinstall it (packed install-service npm:${pkg.name}) to regenerate.`,
114
- });
115
- }
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
+ };
116
72
  }
117
73
 
118
- return { ok: diagnostics.every((diagnostic) => diagnostic.severity !== "error"), diagnostics, checked };
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 };
119
92
  }
120
93
 
121
94
  export function formatServiceDoctorReport(report: ServiceDoctorReport, json: boolean): string {
122
95
  if (json) return `${JSON.stringify(report)}\n`;
123
- let out = `${report.ok ? "PASS" : "FAIL"} — ${report.checked} service unit(s) checked, ${report.diagnostics.length} issue(s)\n`;
96
+ let out = `${report.ok ? "PASS" : "FAIL"} — ${report.checked} managed service(s) checked, ${report.diagnostics.length} issue(s)\n`;
124
97
  for (const diagnostic of report.diagnostics) {
125
- out += `${diagnostic.severity.toUpperCase()} ${diagnostic.code} ${diagnostic.package} (${diagnostic.unitName}.service): ${diagnostic.message}\n`;
98
+ out += `${diagnostic.severity.toUpperCase()} ${diagnostic.code} ${diagnostic.package} (${diagnostic.unitName}): ${diagnostic.message}\n`;
126
99
  }
127
100
  return out;
128
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,