@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.
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -1
- package/dist/protocol.d.ts +0 -1
- package/dist/protocol.d.ts.map +1 -1
- package/extension/src/model.ts +11 -4
- package/package.json +17 -5
- package/service/src/adoption/check.ts +39 -4
- package/service/src/adoption/doctor.ts +21 -1
- package/service/src/adoption/service-doctor.ts +101 -0
- package/service/src/cli/cli.ts +32 -60
- package/service/src/daemon/client.ts +20 -26
- package/service/src/daemon/daemon-service.ts +127 -37
- package/service/src/daemon/daemon.ts +24 -2
- package/service/src/daemon/service.ts +76 -11
- package/service/src/daemon/vehicle-registration.ts +173 -0
- package/service/src/public/client.ts +7 -19
- package/service/src/public/protocol.ts +0 -1
- package/service/src/security/security.ts +2 -0
- package/service/src/shared/constants.ts +2 -0
- package/service/test/check.test.ts +76 -0
- package/service/test/cli.test.ts +123 -138
- package/service/test/daemon-kit-migration.test.ts +1 -0
- package/service/test/daemon-maintenance.test.ts +127 -0
- package/service/test/daemon-service.test.ts +93 -40
- package/service/test/public-client.test.ts +1 -1
- package/service/test/security.test.ts +1 -0
- package/service/test/service-doctor.test.ts +100 -0
- package/service/test/service.test.ts +89 -13
- package/service/test/vehicle-registration.test.ts +121 -0
package/dist/client.d.ts.map
CHANGED
|
@@ -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
|
|
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};
|
package/dist/protocol.d.ts
CHANGED
|
@@ -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
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -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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"}
|
package/extension/src/model.ts
CHANGED
|
@@ -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
|
|
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
|
|
24
|
-
latest:
|
|
25
|
-
hasUpdate:
|
|
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.
|
|
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.
|
|
22
|
-
"@danypops/vehicle-core": "^0.
|
|
23
|
-
"@danypops/vehicle-server": "^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.
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
+
}
|
package/service/src/cli/cli.ts
CHANGED
|
@@ -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] [--
|
|
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] [--
|
|
104
|
-
packed install-service <source> --approve [--json]
|
|
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
|
|
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://… [--
|
|
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
|
-
|
|
580
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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,
|