@elpapi42/pi-fleet 0.1.0-beta.1 → 0.1.0-beta.2

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.
@@ -64,7 +64,7 @@
64
64
  "format": "esm"
65
65
  },
66
66
  "src/platform/install/runtime-release.ts": {
67
- "bytes": 4620,
67
+ "bytes": 9583,
68
68
  "imports": [
69
69
  {
70
70
  "path": "node:crypto",
@@ -152,7 +152,7 @@
152
152
  "imports": [],
153
153
  "exports": [],
154
154
  "inputs": {},
155
- "bytes": 29042
155
+ "bytes": 37234
156
156
  },
157
157
  "dist/installer.mjs": {
158
158
  "imports": [
@@ -259,7 +259,7 @@
259
259
  "bytesInOutput": 1889
260
260
  },
261
261
  "src/platform/install/runtime-release.ts": {
262
- "bytesInOutput": 4023
262
+ "bytesInOutput": 8512
263
263
  },
264
264
  "src/platform/install/tree-integrity.ts": {
265
265
  "bytesInOutput": 1890
@@ -268,7 +268,7 @@
268
268
  "bytesInOutput": 1207
269
269
  }
270
270
  },
271
- "bytes": 15347
271
+ "bytes": 19836
272
272
  }
273
273
  }
274
274
  }
@@ -156,8 +156,8 @@ async function atomicWrite(path, contents) {
156
156
 
157
157
  // src/platform/install/runtime-release.ts
158
158
  import { createHash as createHash2, randomUUID } from "node:crypto";
159
- import { chmod as chmod2, cp, lstat as lstat2, mkdir as mkdir2, readFile as readFile3, rename as rename2, rm as rm2, stat as stat2 } from "node:fs/promises";
160
- import { join as join3, resolve as resolve2, sep as sep2 } from "node:path";
159
+ import { chmod as chmod2, cp, lstat as lstat2, mkdir as mkdir2, readFile as readFile3, rename as rename2, rm as rm2 } from "node:fs/promises";
160
+ import { join as join3, posix, resolve as resolve2, sep as sep2 } from "node:path";
161
161
 
162
162
  // src/platform/install/tree-integrity.ts
163
163
  import { createHash } from "node:crypto";
@@ -211,12 +211,21 @@ async function collectFiles(root, directory) {
211
211
  }
212
212
 
213
213
  // src/platform/install/runtime-release.ts
214
+ var requiredRuntimeArtifacts = /* @__PURE__ */ new Set([
215
+ "package.json",
216
+ "bin/pifleet.mjs",
217
+ "bin/pifleet-runtime.mjs",
218
+ "dist/cli.mjs",
219
+ "dist/runtime.mjs",
220
+ "dist/sqlite-worker.mjs"
221
+ ]);
214
222
  async function materializeRuntime(options) {
215
223
  const sourceRoot = resolve2(options.sourceRoot);
224
+ const manifestBytes = await readFile3(join3(sourceRoot, "dist", "runtime-manifest.json"));
225
+ const manifest = await parseRuntimeManifest(manifestBytes, sourceRoot);
226
+ await verifyRuntime(sourceRoot, manifest);
216
227
  const applicationRoot = resolve2(options.applicationRoot);
217
228
  await ensurePrivateDirectory(applicationRoot);
218
- const manifestBytes = await readFile3(join3(sourceRoot, "dist", "runtime-manifest.json"));
219
- const manifest = JSON.parse(manifestBytes.toString("utf8"));
220
229
  const manifestHash = createHash2("sha256").update(manifestBytes).digest("hex").slice(0, 16);
221
230
  const releasesRoot = join3(applicationRoot, "releases");
222
231
  await ensurePrivateDirectory(releasesRoot);
@@ -242,21 +251,30 @@ async function materializeRuntime(options) {
242
251
  }
243
252
  await verifyRuntime(staging, manifest);
244
253
  await chmod2(staging, 448);
245
- await rename2(staging, destination);
254
+ try {
255
+ await rename2(staging, destination);
256
+ } catch (error) {
257
+ if (!isDestinationRace(error) || !await pathExists(destination)) throw error;
258
+ await verifyRuntime(destination, manifest);
259
+ }
246
260
  return destination;
247
- } catch (error) {
261
+ } finally {
248
262
  await rm2(staging, { recursive: true, force: true });
249
- throw error;
250
263
  }
251
264
  }
252
265
  async function verifyRuntime(root, manifest) {
253
- const expected = manifest ?? JSON.parse(
254
- await readFile3(join3(root, "dist", "runtime-manifest.json"), "utf8")
255
- );
256
266
  const resolvedRoot = resolve2(root);
267
+ const expected = manifest ?? await parseRuntimeManifest(
268
+ await readFile3(join3(resolvedRoot, "dist", "runtime-manifest.json")),
269
+ resolvedRoot
270
+ );
271
+ if (manifest !== void 0) await validateRuntimeManifest(manifest, resolvedRoot);
257
272
  for (const file of expected.files) {
258
273
  const path = resolveInside(resolvedRoot, file.path);
259
- const info = await stat2(path);
274
+ const info = await lstat2(path);
275
+ if (info.isSymbolicLink()) {
276
+ throw new Error(`Runtime artifact ${file.path} must not be a symbolic link`);
277
+ }
260
278
  if (!info.isFile() || info.size !== file.bytes) {
261
279
  throw new Error(`Runtime artifact ${file.path} has changed`);
262
280
  }
@@ -273,13 +291,100 @@ async function verifyRuntime(root, manifest) {
273
291
  }
274
292
  }
275
293
  }
294
+ async function parseRuntimeManifest(bytes, root) {
295
+ let candidate;
296
+ try {
297
+ candidate = JSON.parse(bytes.toString("utf8"));
298
+ } catch {
299
+ throw new Error("Runtime manifest is not valid JSON");
300
+ }
301
+ await validateRuntimeManifest(candidate, root);
302
+ return candidate;
303
+ }
304
+ async function validateRuntimeManifest(candidate, root) {
305
+ if (!isRecord(candidate) || candidate.schemaVersion !== 2) {
306
+ throw new Error("Runtime manifest has an unsupported schema version");
307
+ }
308
+ if (!isRecord(candidate.package) || typeof candidate.package.name !== "string" || typeof candidate.package.version !== "string") {
309
+ throw new Error("Runtime manifest has an invalid package identity");
310
+ }
311
+ const packageIdentity = await readPackageIdentity(root);
312
+ if (candidate.package.name !== packageIdentity.name || candidate.package.version !== packageIdentity.version) {
313
+ throw new Error("Runtime manifest package identity does not match package.json");
314
+ }
315
+ if (!Array.isArray(candidate.files) || !Array.isArray(candidate.trees)) {
316
+ throw new Error("Runtime manifest has invalid artifact lists");
317
+ }
318
+ const filePaths = /* @__PURE__ */ new Set();
319
+ for (const file of candidate.files) {
320
+ if (!isRecord(file) || !isManifestPath(file.path) || !isValidSize(file.bytes) || !isSha256(file.sha256)) {
321
+ throw new Error("Runtime manifest contains an invalid artifact");
322
+ }
323
+ if (filePaths.has(file.path))
324
+ throw new Error(`Runtime manifest has duplicate path ${file.path}`);
325
+ filePaths.add(file.path);
326
+ }
327
+ for (const required of requiredRuntimeArtifacts) {
328
+ if (!filePaths.has(required)) {
329
+ throw new Error(`Runtime manifest is missing required artifact ${required}`);
330
+ }
331
+ }
332
+ const treePaths = /* @__PURE__ */ new Set();
333
+ for (const tree of candidate.trees) {
334
+ if (!isRecord(tree) || !isManifestPath(tree.path) || !isValidSize(tree.files) || !isValidSize(tree.bytes) || !isSha256(tree.sha256)) {
335
+ throw new Error("Runtime manifest contains an invalid dependency tree");
336
+ }
337
+ if (treePaths.has(tree.path))
338
+ throw new Error(`Runtime manifest has duplicate path ${tree.path}`);
339
+ treePaths.add(tree.path);
340
+ }
341
+ const allPaths = [...filePaths, ...treePaths];
342
+ for (let index = 0; index < allPaths.length; index += 1) {
343
+ for (let other = index + 1; other < allPaths.length; other += 1) {
344
+ const left = allPaths[index];
345
+ const right = allPaths[other];
346
+ if (left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`)) {
347
+ throw new Error(`Runtime manifest has overlapping paths ${left} and ${right}`);
348
+ }
349
+ }
350
+ }
351
+ }
352
+ async function readPackageIdentity(root) {
353
+ let candidate;
354
+ try {
355
+ candidate = JSON.parse(await readFile3(join3(root, "package.json"), "utf8"));
356
+ } catch {
357
+ throw new Error("Runtime package.json is not valid JSON");
358
+ }
359
+ if (!isRecord(candidate) || typeof candidate.name !== "string" || typeof candidate.version !== "string") {
360
+ throw new Error("Runtime package.json has an invalid identity");
361
+ }
362
+ return { name: candidate.name, version: candidate.version };
363
+ }
276
364
  function resolveInside(root, path) {
365
+ if (!isManifestPath(path)) throw new Error(`Runtime manifest contains an unsafe path ${path}`);
277
366
  const resolved = resolve2(root, path);
278
367
  if (!resolved.startsWith(`${root}${sep2}`)) {
279
368
  throw new Error(`Runtime manifest contains an unsafe path ${path}`);
280
369
  }
281
370
  return resolved;
282
371
  }
372
+ function isRecord(value) {
373
+ return typeof value === "object" && value !== null;
374
+ }
375
+ function isManifestPath(value) {
376
+ return typeof value === "string" && value.length > 0 && !value.includes("\\") && !value.startsWith("/") && posix.normalize(value) === value && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
377
+ }
378
+ function isValidSize(value) {
379
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
380
+ }
381
+ function isSha256(value) {
382
+ return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
383
+ }
384
+ function isDestinationRace(error) {
385
+ const code = error.code;
386
+ return code === "EEXIST" || code === "ENOTEMPTY";
387
+ }
283
388
  async function ensurePrivateDirectory(path) {
284
389
  await mkdir2(path, { recursive: true, mode: 448 });
285
390
  const info = await lstat2(path);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/entry/installer.ts", "../src/platform/install/service-installer.ts", "../src/platform/install/service-definition.ts", "../src/platform/install/runtime-release.ts", "../src/platform/install/tree-integrity.ts", "../src/platform/shared/paths.ts"],
4
- "sourcesContent": ["import { execFile } from \"node:child_process\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { promisify } from \"node:util\";\n\nimport {\n installUserService,\n uninstallUserService,\n type CommandExecutor,\n} from \"../platform/install/service-installer.js\";\nimport { materializeRuntime } from \"../platform/install/runtime-release.js\";\nimport { resolveApplicationRoot, resolveFleetPaths } from \"../platform/shared/paths.js\";\n\nconst executor: CommandExecutor = {\n async run(command, args) {\n await promisify(execFile)(command, [...args]);\n },\n};\n\nexport async function installRuntimeService(): Promise<string> {\n return installMaterializedService(resolveFleetPaths().stateRoot);\n}\n\nexport async function repairRuntimeService(): Promise<string> {\n return installMaterializedService(process.env.PIFLEET_STATE_ROOT);\n}\n\nasync function installMaterializedService(stateRoot: string | undefined): Promise<string> {\n const sourceRoot = dirname(dirname(fileURLToPath(import.meta.url)));\n const release = await materializeRuntime({\n sourceRoot,\n applicationRoot: resolveApplicationRoot(),\n });\n return installUserService({\n platform: process.platform,\n definition: {\n nodePath: process.execPath,\n runtimePath: join(release, \"bin\", \"pifleet-runtime.mjs\"),\n ...(stateRoot === undefined ? {} : { stateRoot }),\n },\n executor,\n });\n}\n\nexport async function uninstallRuntimeService(): Promise<void> {\n await uninstallUserService({ platform: process.platform, executor });\n}\n\nasync function main(): Promise<void> {\n const command = process.argv[2];\n if (command === \"install\") {\n process.stdout.write(`${await installRuntimeService()}\\n`);\n return;\n }\n if (command === \"repair\") {\n process.stdout.write(`${await repairRuntimeService()}\\n`);\n return;\n }\n if (command === \"uninstall\") {\n await uninstallRuntimeService();\n return;\n }\n throw new Error(\"Usage: node dist/installer.mjs <install|repair|uninstall>\");\n}\n\nif (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {\n await main();\n}\n", "import { constants } from \"node:fs\";\nimport { access, chmod, mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport {\n launchdAgentPlist,\n systemdUserUnit,\n type ServiceDefinitionOptions,\n} from \"./service-definition.js\";\n\nexport interface CommandExecutor {\n run(command: string, args: readonly string[]): Promise<void>;\n}\n\nexport async function installUserService(options: {\n readonly platform: NodeJS.Platform;\n readonly definition: ServiceDefinitionOptions;\n readonly executor: CommandExecutor;\n readonly home?: string;\n readonly uid?: number;\n}): Promise<string> {\n const home = options.home ?? homedir();\n if (options.platform === \"linux\") {\n const path = join(home, \".config\", \"systemd\", \"user\", \"pi-fleet.service\");\n const effectiveDefinition = await preserveInstalledStateRoot(path, options.definition, \"linux\");\n const definition = systemdUserUnit(effectiveDefinition);\n const needsRepair = await serviceDefinitionNeedsRepair(path, definition, effectiveDefinition);\n if (needsRepair) {\n await atomicWrite(path, definition);\n await options.executor.run(\"systemctl\", [\"--user\", \"daemon-reload\"]);\n }\n await options.executor.run(\"systemctl\", [\"--user\", \"enable\", \"--now\", \"pi-fleet.service\"]);\n if (needsRepair) {\n await options.executor.run(\"systemctl\", [\"--user\", \"restart\", \"pi-fleet.service\"]);\n }\n return path;\n }\n if (options.platform === \"darwin\") {\n const path = join(home, \"Library\", \"LaunchAgents\", \"works.elpapi.pifleet.plist\");\n const effectiveDefinition = await preserveInstalledStateRoot(\n path,\n options.definition,\n \"darwin\",\n );\n await atomicWrite(path, launchdAgentPlist(effectiveDefinition));\n const domain = `gui/${options.uid ?? process.getuid?.() ?? 0}`;\n await options.executor.run(\"launchctl\", [\"bootout\", domain, path]).catch(() => undefined);\n await options.executor.run(\"launchctl\", [\"bootstrap\", domain, path]);\n await options.executor.run(\"launchctl\", [\"kickstart\", `${domain}/works.elpapi.pifleet`]);\n return path;\n }\n throw new Error(`Native Pi Fleet supervision is unsupported on ${options.platform}`);\n}\n\nexport async function uninstallUserService(options: {\n readonly platform: NodeJS.Platform;\n readonly executor: CommandExecutor;\n readonly home?: string;\n readonly uid?: number;\n}): Promise<void> {\n const home = options.home ?? homedir();\n if (options.platform === \"linux\") {\n const path = join(home, \".config\", \"systemd\", \"user\", \"pi-fleet.service\");\n await options.executor.run(\"systemctl\", [\"--user\", \"disable\", \"--now\", \"pi-fleet.service\"]);\n await rm(path, { force: true });\n await options.executor.run(\"systemctl\", [\"--user\", \"daemon-reload\"]);\n return;\n }\n if (options.platform === \"darwin\") {\n const path = join(home, \"Library\", \"LaunchAgents\", \"works.elpapi.pifleet.plist\");\n const domain = `gui/${options.uid ?? process.getuid?.() ?? 0}`;\n await options.executor.run(\"launchctl\", [\"bootout\", domain, path]).catch(() => undefined);\n await rm(path, { force: true });\n return;\n }\n throw new Error(`Native Pi Fleet supervision is unsupported on ${options.platform}`);\n}\n\nexport async function serviceDefinitionNeedsRepair(\n path: string,\n expectedContents: string,\n definition: ServiceDefinitionOptions,\n): Promise<boolean> {\n const current = await readFile(path, \"utf8\").catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return null;\n throw error;\n });\n if (current !== expectedContents) return true;\n try {\n await access(definition.nodePath, constants.X_OK);\n await access(definition.runtimePath, constants.R_OK);\n return false;\n } catch {\n return true;\n }\n}\n\nasync function preserveInstalledStateRoot(\n path: string,\n definition: ServiceDefinitionOptions,\n platform: \"linux\" | \"darwin\",\n): Promise<ServiceDefinitionOptions> {\n if (definition.stateRoot !== undefined) return definition;\n const current = await readFile(path, \"utf8\").catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return null;\n throw error;\n });\n if (current === null) return definition;\n const encoded =\n platform === \"linux\"\n ? /^Environment=PIFLEET_STATE_ROOT=(.+)$/m.exec(current)?.[1]\n : /<key>PIFLEET_STATE_ROOT<\\/key><string>([^<]+)<\\/string>/.exec(current)?.[1];\n if (encoded === undefined) return definition;\n const stateRoot =\n platform === \"linux\"\n ? encoded\n : encoded\n .replaceAll(\"&apos;\", \"'\")\n .replaceAll(\"&quot;\", '\"')\n .replaceAll(\"&gt;\", \">\")\n .replaceAll(\"&lt;\", \"<\")\n .replaceAll(\"&amp;\", \"&\");\n return { ...definition, stateRoot };\n}\n\nasync function atomicWrite(path: string, contents: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n const temporary = `${path}.tmp-${process.pid}`;\n await writeFile(temporary, contents, { mode: 0o600 });\n await chmod(temporary, 0o600);\n await rename(temporary, path);\n}\n", "import { isAbsolute } from \"node:path\";\n\nexport interface ServiceDefinitionOptions {\n readonly nodePath: string;\n readonly runtimePath: string;\n readonly stateRoot?: string;\n}\n\nexport function systemdUserUnit(options: ServiceDefinitionOptions): string {\n validate(options);\n const environment =\n options.stateRoot === undefined\n ? \"\"\n : `Environment=PIFLEET_STATE_ROOT=${systemdEscape(options.stateRoot)}\\n`;\n return `[Unit]\nDescription=Pi Fleet user runtime\nAfter=default.target\n\n[Service]\nType=simple\nExecStart=${systemdEscape(options.nodePath)} ${systemdEscape(options.runtimePath)}\n${environment}Restart=on-failure\nRestartSec=1\nTimeoutStopSec=10\nUMask=0077\nKillMode=control-group\n\n[Install]\nWantedBy=default.target\n`;\n}\n\nexport function launchdAgentPlist(options: ServiceDefinitionOptions): string {\n validate(options);\n const environment =\n options.stateRoot === undefined\n ? \"\"\n : ` <key>EnvironmentVariables</key>\n <dict><key>PIFLEET_STATE_ROOT</key><string>${xmlEscape(options.stateRoot)}</string></dict>\n`;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key><string>works.elpapi.pifleet</string>\n <key>ProgramArguments</key>\n <array>\n <string>${xmlEscape(options.nodePath)}</string>\n <string>${xmlEscape(options.runtimePath)}</string>\n </array>\n${environment} <key>RunAtLoad</key><true/>\n <key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>\n <key>ProcessType</key><string>Background</string>\n</dict>\n</plist>\n`;\n}\n\nfunction validate(options: ServiceDefinitionOptions): void {\n if (!isAbsolute(options.nodePath) || !isAbsolute(options.runtimePath)) {\n throw new Error(\"Service executables must use absolute paths\");\n }\n}\n\nfunction systemdEscape(value: string): string {\n if (!/^[A-Za-z0-9_./:@-]+$/.test(value)) throw new Error(`Unsafe systemd path ${value}`);\n return value;\n}\n\nfunction xmlEscape(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&apos;\");\n}\n", "import { createHash, randomUUID } from \"node:crypto\";\nimport { chmod, cp, lstat, mkdir, readFile, rename, rm, stat } from \"node:fs/promises\";\nimport { join, resolve, sep } from \"node:path\";\n\nimport { hashDirectoryTree, type TreeIntegrity } from \"./tree-integrity.js\";\n\ninterface RuntimeManifest {\n readonly schemaVersion: 2;\n readonly package: { readonly name: string; readonly version: string };\n readonly files: readonly {\n readonly path: string;\n readonly bytes: number;\n readonly sha256: string;\n }[];\n readonly trees: readonly TreeIntegrity[];\n}\n\nexport async function materializeRuntime(options: {\n readonly sourceRoot: string;\n readonly applicationRoot: string;\n}): Promise<string> {\n const sourceRoot = resolve(options.sourceRoot);\n const applicationRoot = resolve(options.applicationRoot);\n await ensurePrivateDirectory(applicationRoot);\n const manifestBytes = await readFile(join(sourceRoot, \"dist\", \"runtime-manifest.json\"));\n const manifest = JSON.parse(manifestBytes.toString(\"utf8\")) as RuntimeManifest;\n const manifestHash = createHash(\"sha256\").update(manifestBytes).digest(\"hex\").slice(0, 16);\n const releasesRoot = join(applicationRoot, \"releases\");\n await ensurePrivateDirectory(releasesRoot);\n const destination = join(releasesRoot, `${manifest.package.version}-${manifestHash}`);\n\n if (await pathExists(destination)) {\n await verifyRuntime(destination, manifest);\n return destination;\n }\n\n const staging = join(releasesRoot, `.staging-${randomUUID()}`);\n await mkdir(staging, { mode: 0o700 });\n try {\n await cp(join(sourceRoot, \"dist\"), join(staging, \"dist\"), {\n recursive: true,\n dereference: true,\n });\n await cp(join(sourceRoot, \"bin\"), join(staging, \"bin\"), { recursive: true, dereference: true });\n await cp(join(sourceRoot, \"package.json\"), join(staging, \"package.json\"));\n for (const tree of manifest.trees) {\n const source = resolveInside(sourceRoot, tree.path);\n const destination = resolveInside(staging, tree.path);\n await mkdir(resolve(destination, \"..\"), { recursive: true, mode: 0o700 });\n await cp(source, destination, { recursive: true, dereference: true });\n }\n await verifyRuntime(staging, manifest);\n await chmod(staging, 0o700);\n await rename(staging, destination);\n return destination;\n } catch (error: unknown) {\n await rm(staging, { recursive: true, force: true });\n throw error;\n }\n}\n\nexport async function verifyRuntime(root: string, manifest?: RuntimeManifest): Promise<void> {\n const expected =\n manifest ??\n (JSON.parse(\n await readFile(join(root, \"dist\", \"runtime-manifest.json\"), \"utf8\"),\n ) as RuntimeManifest);\n const resolvedRoot = resolve(root);\n for (const file of expected.files) {\n const path = resolveInside(resolvedRoot, file.path);\n const info = await stat(path);\n if (!info.isFile() || info.size !== file.bytes) {\n throw new Error(`Runtime artifact ${file.path} has changed`);\n }\n const hash = createHash(\"sha256\")\n .update(await readFile(path))\n .digest(\"hex\");\n if (hash !== file.sha256) {\n throw new Error(`Runtime artifact ${file.path} failed verification`);\n }\n }\n for (const expectedTree of expected.trees) {\n const treeRoot = resolveInside(resolvedRoot, expectedTree.path);\n const actualTree = await hashDirectoryTree(treeRoot, expectedTree.path);\n if (\n actualTree.files !== expectedTree.files ||\n actualTree.bytes !== expectedTree.bytes ||\n actualTree.sha256 !== expectedTree.sha256\n ) {\n throw new Error(`Runtime dependency tree ${expectedTree.path} failed verification`);\n }\n }\n}\n\nfunction resolveInside(root: string, path: string): string {\n const resolved = resolve(root, path);\n if (!resolved.startsWith(`${root}${sep}`)) {\n throw new Error(`Runtime manifest contains an unsafe path ${path}`);\n }\n return resolved;\n}\n\nasync function ensurePrivateDirectory(path: string): Promise<void> {\n await mkdir(path, { recursive: true, mode: 0o700 });\n const info = await lstat(path);\n if (!info.isDirectory() || info.isSymbolicLink()) {\n throw new Error(`Refusing unsafe runtime release path ${path}`);\n }\n if (typeof process.getuid === \"function\" && info.uid !== process.getuid()) {\n throw new Error(`Runtime release path ${path} is not owned by the current user`);\n }\n await chmod(path, 0o700);\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await lstat(path);\n return true;\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n}\n", "import { createHash } from \"node:crypto\";\nimport { lstat, readFile, readdir, stat } from \"node:fs/promises\";\nimport { join, relative, resolve, sep } from \"node:path\";\n\nexport interface TreeIntegrity {\n readonly path: string;\n readonly files: number;\n readonly bytes: number;\n readonly sha256: string;\n}\n\nexport async function hashDirectoryTree(\n root: string,\n manifestPath: string,\n): Promise<TreeIntegrity> {\n const resolvedRoot = resolve(root);\n const entries = await collectFiles(resolvedRoot, resolvedRoot);\n const hash = createHash(\"sha256\");\n let bytes = 0;\n for (const entry of entries) {\n const contents = await readFile(entry.absolutePath);\n bytes += contents.length;\n hash.update(entry.relativePath);\n hash.update(\"\\0\");\n hash.update(String(contents.length));\n hash.update(\"\\0\");\n hash.update(contents);\n hash.update(\"\\0\");\n }\n return {\n path: manifestPath,\n files: entries.length,\n bytes,\n sha256: hash.digest(\"hex\"),\n };\n}\n\nasync function collectFiles(\n root: string,\n directory: string,\n): Promise<Array<{ readonly absolutePath: string; readonly relativePath: string }>> {\n const output: Array<{ absolutePath: string; relativePath: string }> = [];\n for (const name of (await readdir(directory)).sort()) {\n const absolutePath = join(directory, name);\n const linkInfo = await lstat(absolutePath);\n const info = linkInfo.isSymbolicLink() ? await stat(absolutePath) : linkInfo;\n if (info.isDirectory()) {\n if (linkInfo.isSymbolicLink()) {\n throw new Error(`Runtime dependency tree contains a directory symlink: ${absolutePath}`);\n }\n output.push(...(await collectFiles(root, absolutePath)));\n continue;\n }\n if (!info.isFile()) {\n throw new Error(`Runtime dependency tree contains an unsupported entry: ${absolutePath}`);\n }\n const relativePath = relative(root, absolutePath).split(sep).join(\"/\");\n if (relativePath.length === 0 || relativePath.startsWith(\"../\")) {\n throw new Error(`Runtime dependency path escapes its root: ${absolutePath}`);\n }\n output.push({ absolutePath, relativePath });\n }\n return output.sort((left, right) => left.relativePath.localeCompare(right.relativePath));\n}\n", "import { homedir, tmpdir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\n\nexport interface FleetPaths {\n readonly runtimeRoot: string;\n readonly stateRoot: string;\n readonly socketPath: string;\n readonly databasePath: string;\n}\n\nexport function resolveApplicationRoot(env: NodeJS.ProcessEnv = process.env): string {\n return (\n env.PIFLEET_APPLICATION_ROOT ??\n (process.platform === \"darwin\"\n ? join(homedir(), \"Library\", \"Application Support\", \"Pi Fleet\", \"runtime\")\n : join(env.XDG_DATA_HOME ?? join(homedir(), \".local\", \"share\"), \"pi-fleet\"))\n );\n}\n\nexport function resolveFleetPaths(env: NodeJS.ProcessEnv = process.env): FleetPaths {\n const explicitRoot = env.PIFLEET_STATE_ROOT;\n if (explicitRoot !== undefined) {\n const root = resolve(explicitRoot);\n return {\n runtimeRoot: root,\n stateRoot: root,\n socketPath: join(root, \"control.sock\"),\n databasePath: join(root, \"fleet.sqlite\"),\n };\n }\n\n const runtimeRoot = join(\n env.XDG_RUNTIME_DIR ?? tmpdir(),\n `pifleet-${process.getuid?.() ?? \"user\"}`,\n );\n const stateRoot =\n process.platform === \"darwin\"\n ? join(homedir(), \"Library\", \"Application Support\", \"Pi Fleet\")\n : join(env.XDG_STATE_HOME ?? join(homedir(), \".local\", \"state\"), \"pi-fleet\");\n return {\n runtimeRoot,\n stateRoot,\n socketPath: join(runtimeRoot, \"control.sock\"),\n databasePath: join(stateRoot, \"fleet.sqlite\"),\n };\n}\n"],
5
- "mappings": ";AAAA,SAAS,gBAAgB;AACzB,SAAS,WAAAA,UAAS,QAAAC,aAAY;AAC9B,SAAS,eAAe,qBAAqB;AAC7C,SAAS,iBAAiB;;;ACH1B,SAAS,iBAAiB;AAC1B,SAAS,QAAQ,OAAO,OAAO,UAAU,QAAQ,IAAI,iBAAiB;AACtE,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;;;ACH9B,SAAS,kBAAkB;AAQpB,SAAS,gBAAgB,SAA2C;AACzE,WAAS,OAAO;AAChB,QAAM,cACJ,QAAQ,cAAc,SAClB,KACA,kCAAkC,cAAc,QAAQ,SAAS,CAAC;AAAA;AACxE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMG,cAAc,QAAQ,QAAQ,CAAC,IAAI,cAAc,QAAQ,WAAW,CAAC;AAAA,EAC/E,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASb;AAEO,SAAS,kBAAkB,SAA2C;AAC3E,WAAS,OAAO;AAChB,QAAM,cACJ,QAAQ,cAAc,SAClB,KACA;AAAA,iDACyC,UAAU,QAAQ,SAAS,CAAC;AAAA;AAE3E,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAOS,UAAU,QAAQ,QAAQ,CAAC;AAAA,kBAC3B,UAAU,QAAQ,WAAW,CAAC;AAAA;AAAA,EAE9C,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAMb;AAEA,SAAS,SAAS,SAAyC;AACzD,MAAI,CAAC,WAAW,QAAQ,QAAQ,KAAK,CAAC,WAAW,QAAQ,WAAW,GAAG;AACrE,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACF;AAEA,SAAS,cAAc,OAAuB;AAC5C,MAAI,CAAC,uBAAuB,KAAK,KAAK,EAAG,OAAM,IAAI,MAAM,uBAAuB,KAAK,EAAE;AACvF,SAAO;AACT;AAEA,SAAS,UAAU,OAAuB;AACxC,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,QAAQ;AAC7B;;;AD7DA,eAAsB,mBAAmB,SAMrB;AAClB,QAAM,OAAO,QAAQ,QAAQ,QAAQ;AACrC,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,OAAO,KAAK,MAAM,WAAW,WAAW,QAAQ,kBAAkB;AACxE,UAAM,sBAAsB,MAAM,2BAA2B,MAAM,QAAQ,YAAY,OAAO;AAC9F,UAAM,aAAa,gBAAgB,mBAAmB;AACtD,UAAM,cAAc,MAAM,6BAA6B,MAAM,YAAY,mBAAmB;AAC5F,QAAI,aAAa;AACf,YAAM,YAAY,MAAM,UAAU;AAClC,YAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,eAAe,CAAC;AAAA,IACrE;AACA,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,UAAU,SAAS,kBAAkB,CAAC;AACzF,QAAI,aAAa;AACf,YAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,WAAW,kBAAkB,CAAC;AAAA,IACnF;AACA,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,aAAa,UAAU;AACjC,UAAM,OAAO,KAAK,MAAM,WAAW,gBAAgB,4BAA4B;AAC/E,UAAM,sBAAsB,MAAM;AAAA,MAChC;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF;AACA,UAAM,YAAY,MAAM,kBAAkB,mBAAmB,CAAC;AAC9D,UAAM,SAAS,OAAO,QAAQ,OAAO,QAAQ,SAAS,KAAK,CAAC;AAC5D,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,WAAW,QAAQ,IAAI,CAAC,EAAE,MAAM,MAAM,MAAS;AACxF,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,aAAa,QAAQ,IAAI,CAAC;AACnE,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,aAAa,GAAG,MAAM,uBAAuB,CAAC;AACvF,WAAO;AAAA,EACT;AACA,QAAM,IAAI,MAAM,iDAAiD,QAAQ,QAAQ,EAAE;AACrF;AAEA,eAAsB,qBAAqB,SAKzB;AAChB,QAAM,OAAO,QAAQ,QAAQ,QAAQ;AACrC,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,OAAO,KAAK,MAAM,WAAW,WAAW,QAAQ,kBAAkB;AACxE,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,WAAW,SAAS,kBAAkB,CAAC;AAC1F,UAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAC9B,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,eAAe,CAAC;AACnE;AAAA,EACF;AACA,MAAI,QAAQ,aAAa,UAAU;AACjC,UAAM,OAAO,KAAK,MAAM,WAAW,gBAAgB,4BAA4B;AAC/E,UAAM,SAAS,OAAO,QAAQ,OAAO,QAAQ,SAAS,KAAK,CAAC;AAC5D,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,WAAW,QAAQ,IAAI,CAAC,EAAE,MAAM,MAAM,MAAS;AACxF,UAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAC9B;AAAA,EACF;AACA,QAAM,IAAI,MAAM,iDAAiD,QAAQ,QAAQ,EAAE;AACrF;AAEA,eAAsB,6BACpB,MACA,kBACA,YACkB;AAClB,QAAM,UAAU,MAAM,SAAS,MAAM,MAAM,EAAE,MAAM,CAAC,UAAiC;AACnF,QAAI,MAAM,SAAS,SAAU,QAAO;AACpC,UAAM;AAAA,EACR,CAAC;AACD,MAAI,YAAY,iBAAkB,QAAO;AACzC,MAAI;AACF,UAAM,OAAO,WAAW,UAAU,UAAU,IAAI;AAChD,UAAM,OAAO,WAAW,aAAa,UAAU,IAAI;AACnD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,2BACb,MACA,YACA,UACmC;AACnC,MAAI,WAAW,cAAc,OAAW,QAAO;AAC/C,QAAM,UAAU,MAAM,SAAS,MAAM,MAAM,EAAE,MAAM,CAAC,UAAiC;AACnF,QAAI,MAAM,SAAS,SAAU,QAAO;AACpC,UAAM;AAAA,EACR,CAAC;AACD,MAAI,YAAY,KAAM,QAAO;AAC7B,QAAM,UACJ,aAAa,UACT,yCAAyC,KAAK,OAAO,IAAI,CAAC,IAC1D,0DAA0D,KAAK,OAAO,IAAI,CAAC;AACjF,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,YACJ,aAAa,UACT,UACA,QACG,WAAW,UAAU,GAAG,EACxB,WAAW,UAAU,GAAG,EACxB,WAAW,QAAQ,GAAG,EACtB,WAAW,QAAQ,GAAG,EACtB,WAAW,SAAS,GAAG;AAChC,SAAO,EAAE,GAAG,YAAY,UAAU;AACpC;AAEA,eAAe,YAAY,MAAc,UAAiC;AACxE,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,QAAM,YAAY,GAAG,IAAI,QAAQ,QAAQ,GAAG;AAC5C,QAAM,UAAU,WAAW,UAAU,EAAE,MAAM,IAAM,CAAC;AACpD,QAAM,MAAM,WAAW,GAAK;AAC5B,QAAM,OAAO,WAAW,IAAI;AAC9B;;;AEpIA,SAAS,cAAAC,aAAY,kBAAkB;AACvC,SAAS,SAAAC,QAAO,IAAI,SAAAC,QAAO,SAAAC,QAAO,YAAAC,WAAU,UAAAC,SAAQ,MAAAC,KAAI,QAAAC,aAAY;AACpE,SAAS,QAAAC,OAAM,WAAAC,UAAS,OAAAC,YAAW;;;ACFnC,SAAS,kBAAkB;AAC3B,SAAS,OAAO,YAAAC,WAAU,SAAS,YAAY;AAC/C,SAAS,QAAAC,OAAM,UAAU,SAAS,WAAW;AAS7C,eAAsB,kBACpB,MACA,cACwB;AACxB,QAAM,eAAe,QAAQ,IAAI;AACjC,QAAM,UAAU,MAAM,aAAa,cAAc,YAAY;AAC7D,QAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,QAAQ;AACZ,aAAW,SAAS,SAAS;AAC3B,UAAM,WAAW,MAAMD,UAAS,MAAM,YAAY;AAClD,aAAS,SAAS;AAClB,SAAK,OAAO,MAAM,YAAY;AAC9B,SAAK,OAAO,IAAI;AAChB,SAAK,OAAO,OAAO,SAAS,MAAM,CAAC;AACnC,SAAK,OAAO,IAAI;AAChB,SAAK,OAAO,QAAQ;AACpB,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,QAAQ;AAAA,IACf;AAAA,IACA,QAAQ,KAAK,OAAO,KAAK;AAAA,EAC3B;AACF;AAEA,eAAe,aACb,MACA,WACkF;AAClF,QAAM,SAAgE,CAAC;AACvE,aAAW,SAAS,MAAM,QAAQ,SAAS,GAAG,KAAK,GAAG;AACpD,UAAM,eAAeC,MAAK,WAAW,IAAI;AACzC,UAAM,WAAW,MAAM,MAAM,YAAY;AACzC,UAAM,OAAO,SAAS,eAAe,IAAI,MAAM,KAAK,YAAY,IAAI;AACpE,QAAI,KAAK,YAAY,GAAG;AACtB,UAAI,SAAS,eAAe,GAAG;AAC7B,cAAM,IAAI,MAAM,yDAAyD,YAAY,EAAE;AAAA,MACzF;AACA,aAAO,KAAK,GAAI,MAAM,aAAa,MAAM,YAAY,CAAE;AACvD;AAAA,IACF;AACA,QAAI,CAAC,KAAK,OAAO,GAAG;AAClB,YAAM,IAAI,MAAM,0DAA0D,YAAY,EAAE;AAAA,IAC1F;AACA,UAAM,eAAe,SAAS,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AACrE,QAAI,aAAa,WAAW,KAAK,aAAa,WAAW,KAAK,GAAG;AAC/D,YAAM,IAAI,MAAM,6CAA6C,YAAY,EAAE;AAAA,IAC7E;AACA,WAAO,KAAK,EAAE,cAAc,aAAa,CAAC;AAAA,EAC5C;AACA,SAAO,OAAO,KAAK,CAAC,MAAM,UAAU,KAAK,aAAa,cAAc,MAAM,YAAY,CAAC;AACzF;;;AD9CA,eAAsB,mBAAmB,SAGrB;AAClB,QAAM,aAAaC,SAAQ,QAAQ,UAAU;AAC7C,QAAM,kBAAkBA,SAAQ,QAAQ,eAAe;AACvD,QAAM,uBAAuB,eAAe;AAC5C,QAAM,gBAAgB,MAAMC,UAASC,MAAK,YAAY,QAAQ,uBAAuB,CAAC;AACtF,QAAM,WAAW,KAAK,MAAM,cAAc,SAAS,MAAM,CAAC;AAC1D,QAAM,eAAeC,YAAW,QAAQ,EAAE,OAAO,aAAa,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACzF,QAAM,eAAeD,MAAK,iBAAiB,UAAU;AACrD,QAAM,uBAAuB,YAAY;AACzC,QAAM,cAAcA,MAAK,cAAc,GAAG,SAAS,QAAQ,OAAO,IAAI,YAAY,EAAE;AAEpF,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,cAAc,aAAa,QAAQ;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,UAAUA,MAAK,cAAc,YAAY,WAAW,CAAC,EAAE;AAC7D,QAAME,OAAM,SAAS,EAAE,MAAM,IAAM,CAAC;AACpC,MAAI;AACF,UAAM,GAAGF,MAAK,YAAY,MAAM,GAAGA,MAAK,SAAS,MAAM,GAAG;AAAA,MACxD,WAAW;AAAA,MACX,aAAa;AAAA,IACf,CAAC;AACD,UAAM,GAAGA,MAAK,YAAY,KAAK,GAAGA,MAAK,SAAS,KAAK,GAAG,EAAE,WAAW,MAAM,aAAa,KAAK,CAAC;AAC9F,UAAM,GAAGA,MAAK,YAAY,cAAc,GAAGA,MAAK,SAAS,cAAc,CAAC;AACxE,eAAW,QAAQ,SAAS,OAAO;AACjC,YAAM,SAAS,cAAc,YAAY,KAAK,IAAI;AAClD,YAAMG,eAAc,cAAc,SAAS,KAAK,IAAI;AACpD,YAAMD,OAAMJ,SAAQK,cAAa,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACxE,YAAM,GAAG,QAAQA,cAAa,EAAE,WAAW,MAAM,aAAa,KAAK,CAAC;AAAA,IACtE;AACA,UAAM,cAAc,SAAS,QAAQ;AACrC,UAAMC,OAAM,SAAS,GAAK;AAC1B,UAAMC,QAAO,SAAS,WAAW;AACjC,WAAO;AAAA,EACT,SAAS,OAAgB;AACvB,UAAMC,IAAG,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAClD,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,cAAc,MAAc,UAA2C;AAC3F,QAAM,WACJ,YACC,KAAK;AAAA,IACJ,MAAMP,UAASC,MAAK,MAAM,QAAQ,uBAAuB,GAAG,MAAM;AAAA,EACpE;AACF,QAAM,eAAeF,SAAQ,IAAI;AACjC,aAAW,QAAQ,SAAS,OAAO;AACjC,UAAM,OAAO,cAAc,cAAc,KAAK,IAAI;AAClD,UAAM,OAAO,MAAMS,MAAK,IAAI;AAC5B,QAAI,CAAC,KAAK,OAAO,KAAK,KAAK,SAAS,KAAK,OAAO;AAC9C,YAAM,IAAI,MAAM,oBAAoB,KAAK,IAAI,cAAc;AAAA,IAC7D;AACA,UAAM,OAAON,YAAW,QAAQ,EAC7B,OAAO,MAAMF,UAAS,IAAI,CAAC,EAC3B,OAAO,KAAK;AACf,QAAI,SAAS,KAAK,QAAQ;AACxB,YAAM,IAAI,MAAM,oBAAoB,KAAK,IAAI,sBAAsB;AAAA,IACrE;AAAA,EACF;AACA,aAAW,gBAAgB,SAAS,OAAO;AACzC,UAAM,WAAW,cAAc,cAAc,aAAa,IAAI;AAC9D,UAAM,aAAa,MAAM,kBAAkB,UAAU,aAAa,IAAI;AACtE,QACE,WAAW,UAAU,aAAa,SAClC,WAAW,UAAU,aAAa,SAClC,WAAW,WAAW,aAAa,QACnC;AACA,YAAM,IAAI,MAAM,2BAA2B,aAAa,IAAI,sBAAsB;AAAA,IACpF;AAAA,EACF;AACF;AAEA,SAAS,cAAc,MAAc,MAAsB;AACzD,QAAM,WAAWD,SAAQ,MAAM,IAAI;AACnC,MAAI,CAAC,SAAS,WAAW,GAAG,IAAI,GAAGU,IAAG,EAAE,GAAG;AACzC,UAAM,IAAI,MAAM,4CAA4C,IAAI,EAAE;AAAA,EACpE;AACA,SAAO;AACT;AAEA,eAAe,uBAAuB,MAA6B;AACjE,QAAMN,OAAM,MAAM,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAClD,QAAM,OAAO,MAAMO,OAAM,IAAI;AAC7B,MAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,GAAG;AAChD,UAAM,IAAI,MAAM,wCAAwC,IAAI,EAAE;AAAA,EAChE;AACA,MAAI,OAAO,QAAQ,WAAW,cAAc,KAAK,QAAQ,QAAQ,OAAO,GAAG;AACzE,UAAM,IAAI,MAAM,wBAAwB,IAAI,mCAAmC;AAAA,EACjF;AACA,QAAML,OAAM,MAAM,GAAK;AACzB;AAEA,eAAe,WAAW,MAAgC;AACxD,MAAI;AACF,UAAMK,OAAM,IAAI;AAChB,WAAO;AAAA,EACT,SAAS,OAAgB;AACvB,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACF;;;AE1HA,SAAS,WAAAC,UAAS,cAAc;AAChC,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AASvB,SAAS,uBAAuB,MAAyB,QAAQ,KAAa;AACnF,SACE,IAAI,6BACH,QAAQ,aAAa,WAClBD,MAAKD,SAAQ,GAAG,WAAW,uBAAuB,YAAY,SAAS,IACvEC,MAAK,IAAI,iBAAiBA,MAAKD,SAAQ,GAAG,UAAU,OAAO,GAAG,UAAU;AAEhF;AAEO,SAAS,kBAAkB,MAAyB,QAAQ,KAAiB;AAClF,QAAM,eAAe,IAAI;AACzB,MAAI,iBAAiB,QAAW;AAC9B,UAAM,OAAOE,SAAQ,YAAY;AACjC,WAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW;AAAA,MACX,YAAYD,MAAK,MAAM,cAAc;AAAA,MACrC,cAAcA,MAAK,MAAM,cAAc;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,cAAcA;AAAA,IAClB,IAAI,mBAAmB,OAAO;AAAA,IAC9B,WAAW,QAAQ,SAAS,KAAK,MAAM;AAAA,EACzC;AACA,QAAM,YACJ,QAAQ,aAAa,WACjBA,MAAKD,SAAQ,GAAG,WAAW,uBAAuB,UAAU,IAC5DC,MAAK,IAAI,kBAAkBA,MAAKD,SAAQ,GAAG,UAAU,OAAO,GAAG,UAAU;AAC/E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAYC,MAAK,aAAa,cAAc;AAAA,IAC5C,cAAcA,MAAK,WAAW,cAAc;AAAA,EAC9C;AACF;;;ALhCA,IAAM,WAA4B;AAAA,EAChC,MAAM,IAAI,SAAS,MAAM;AACvB,UAAM,UAAU,QAAQ,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC;AAAA,EAC9C;AACF;AAEA,eAAsB,wBAAyC;AAC7D,SAAO,2BAA2B,kBAAkB,EAAE,SAAS;AACjE;AAEA,eAAsB,uBAAwC;AAC5D,SAAO,2BAA2B,QAAQ,IAAI,kBAAkB;AAClE;AAEA,eAAe,2BAA2B,WAAgD;AACxF,QAAM,aAAaE,SAAQA,SAAQ,cAAc,YAAY,GAAG,CAAC,CAAC;AAClE,QAAM,UAAU,MAAM,mBAAmB;AAAA,IACvC;AAAA,IACA,iBAAiB,uBAAuB;AAAA,EAC1C,CAAC;AACD,SAAO,mBAAmB;AAAA,IACxB,UAAU,QAAQ;AAAA,IAClB,YAAY;AAAA,MACV,UAAU,QAAQ;AAAA,MAClB,aAAaC,MAAK,SAAS,OAAO,qBAAqB;AAAA,MACvD,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,UAAU;AAAA,IACjD;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,0BAAyC;AAC7D,QAAM,qBAAqB,EAAE,UAAU,QAAQ,UAAU,SAAS,CAAC;AACrE;AAEA,eAAe,OAAsB;AACnC,QAAM,UAAU,QAAQ,KAAK,CAAC;AAC9B,MAAI,YAAY,WAAW;AACzB,YAAQ,OAAO,MAAM,GAAG,MAAM,sBAAsB,CAAC;AAAA,CAAI;AACzD;AAAA,EACF;AACA,MAAI,YAAY,UAAU;AACxB,YAAQ,OAAO,MAAM,GAAG,MAAM,qBAAqB,CAAC;AAAA,CAAI;AACxD;AAAA,EACF;AACA,MAAI,YAAY,aAAa;AAC3B,UAAM,wBAAwB;AAC9B;AAAA,EACF;AACA,QAAM,IAAI,MAAM,2DAA2D;AAC7E;AAEA,IAAI,QAAQ,KAAK,CAAC,MAAM,UAAa,YAAY,QAAQ,cAAc,QAAQ,KAAK,CAAC,CAAC,EAAE,MAAM;AAC5F,QAAM,KAAK;AACb;",
6
- "names": ["dirname", "join", "createHash", "chmod", "lstat", "mkdir", "readFile", "rename", "rm", "stat", "join", "resolve", "sep", "readFile", "join", "resolve", "readFile", "join", "createHash", "mkdir", "destination", "chmod", "rename", "rm", "stat", "sep", "lstat", "homedir", "join", "resolve", "dirname", "join"]
4
+ "sourcesContent": ["import { execFile } from \"node:child_process\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { promisify } from \"node:util\";\n\nimport {\n installUserService,\n uninstallUserService,\n type CommandExecutor,\n} from \"../platform/install/service-installer.js\";\nimport { materializeRuntime } from \"../platform/install/runtime-release.js\";\nimport { resolveApplicationRoot, resolveFleetPaths } from \"../platform/shared/paths.js\";\n\nconst executor: CommandExecutor = {\n async run(command, args) {\n await promisify(execFile)(command, [...args]);\n },\n};\n\nexport async function installRuntimeService(): Promise<string> {\n return installMaterializedService(resolveFleetPaths().stateRoot);\n}\n\nexport async function repairRuntimeService(): Promise<string> {\n return installMaterializedService(process.env.PIFLEET_STATE_ROOT);\n}\n\nasync function installMaterializedService(stateRoot: string | undefined): Promise<string> {\n const sourceRoot = dirname(dirname(fileURLToPath(import.meta.url)));\n const release = await materializeRuntime({\n sourceRoot,\n applicationRoot: resolveApplicationRoot(),\n });\n return installUserService({\n platform: process.platform,\n definition: {\n nodePath: process.execPath,\n runtimePath: join(release, \"bin\", \"pifleet-runtime.mjs\"),\n ...(stateRoot === undefined ? {} : { stateRoot }),\n },\n executor,\n });\n}\n\nexport async function uninstallRuntimeService(): Promise<void> {\n await uninstallUserService({ platform: process.platform, executor });\n}\n\nasync function main(): Promise<void> {\n const command = process.argv[2];\n if (command === \"install\") {\n process.stdout.write(`${await installRuntimeService()}\\n`);\n return;\n }\n if (command === \"repair\") {\n process.stdout.write(`${await repairRuntimeService()}\\n`);\n return;\n }\n if (command === \"uninstall\") {\n await uninstallRuntimeService();\n return;\n }\n throw new Error(\"Usage: node dist/installer.mjs <install|repair|uninstall>\");\n}\n\nif (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {\n await main();\n}\n", "import { constants } from \"node:fs\";\nimport { access, chmod, mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport {\n launchdAgentPlist,\n systemdUserUnit,\n type ServiceDefinitionOptions,\n} from \"./service-definition.js\";\n\nexport interface CommandExecutor {\n run(command: string, args: readonly string[]): Promise<void>;\n}\n\nexport async function installUserService(options: {\n readonly platform: NodeJS.Platform;\n readonly definition: ServiceDefinitionOptions;\n readonly executor: CommandExecutor;\n readonly home?: string;\n readonly uid?: number;\n}): Promise<string> {\n const home = options.home ?? homedir();\n if (options.platform === \"linux\") {\n const path = join(home, \".config\", \"systemd\", \"user\", \"pi-fleet.service\");\n const effectiveDefinition = await preserveInstalledStateRoot(path, options.definition, \"linux\");\n const definition = systemdUserUnit(effectiveDefinition);\n const needsRepair = await serviceDefinitionNeedsRepair(path, definition, effectiveDefinition);\n if (needsRepair) {\n await atomicWrite(path, definition);\n await options.executor.run(\"systemctl\", [\"--user\", \"daemon-reload\"]);\n }\n await options.executor.run(\"systemctl\", [\"--user\", \"enable\", \"--now\", \"pi-fleet.service\"]);\n if (needsRepair) {\n await options.executor.run(\"systemctl\", [\"--user\", \"restart\", \"pi-fleet.service\"]);\n }\n return path;\n }\n if (options.platform === \"darwin\") {\n const path = join(home, \"Library\", \"LaunchAgents\", \"works.elpapi.pifleet.plist\");\n const effectiveDefinition = await preserveInstalledStateRoot(\n path,\n options.definition,\n \"darwin\",\n );\n await atomicWrite(path, launchdAgentPlist(effectiveDefinition));\n const domain = `gui/${options.uid ?? process.getuid?.() ?? 0}`;\n await options.executor.run(\"launchctl\", [\"bootout\", domain, path]).catch(() => undefined);\n await options.executor.run(\"launchctl\", [\"bootstrap\", domain, path]);\n await options.executor.run(\"launchctl\", [\"kickstart\", `${domain}/works.elpapi.pifleet`]);\n return path;\n }\n throw new Error(`Native Pi Fleet supervision is unsupported on ${options.platform}`);\n}\n\nexport async function uninstallUserService(options: {\n readonly platform: NodeJS.Platform;\n readonly executor: CommandExecutor;\n readonly home?: string;\n readonly uid?: number;\n}): Promise<void> {\n const home = options.home ?? homedir();\n if (options.platform === \"linux\") {\n const path = join(home, \".config\", \"systemd\", \"user\", \"pi-fleet.service\");\n await options.executor.run(\"systemctl\", [\"--user\", \"disable\", \"--now\", \"pi-fleet.service\"]);\n await rm(path, { force: true });\n await options.executor.run(\"systemctl\", [\"--user\", \"daemon-reload\"]);\n return;\n }\n if (options.platform === \"darwin\") {\n const path = join(home, \"Library\", \"LaunchAgents\", \"works.elpapi.pifleet.plist\");\n const domain = `gui/${options.uid ?? process.getuid?.() ?? 0}`;\n await options.executor.run(\"launchctl\", [\"bootout\", domain, path]).catch(() => undefined);\n await rm(path, { force: true });\n return;\n }\n throw new Error(`Native Pi Fleet supervision is unsupported on ${options.platform}`);\n}\n\nexport async function serviceDefinitionNeedsRepair(\n path: string,\n expectedContents: string,\n definition: ServiceDefinitionOptions,\n): Promise<boolean> {\n const current = await readFile(path, \"utf8\").catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return null;\n throw error;\n });\n if (current !== expectedContents) return true;\n try {\n await access(definition.nodePath, constants.X_OK);\n await access(definition.runtimePath, constants.R_OK);\n return false;\n } catch {\n return true;\n }\n}\n\nasync function preserveInstalledStateRoot(\n path: string,\n definition: ServiceDefinitionOptions,\n platform: \"linux\" | \"darwin\",\n): Promise<ServiceDefinitionOptions> {\n if (definition.stateRoot !== undefined) return definition;\n const current = await readFile(path, \"utf8\").catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return null;\n throw error;\n });\n if (current === null) return definition;\n const encoded =\n platform === \"linux\"\n ? /^Environment=PIFLEET_STATE_ROOT=(.+)$/m.exec(current)?.[1]\n : /<key>PIFLEET_STATE_ROOT<\\/key><string>([^<]+)<\\/string>/.exec(current)?.[1];\n if (encoded === undefined) return definition;\n const stateRoot =\n platform === \"linux\"\n ? encoded\n : encoded\n .replaceAll(\"&apos;\", \"'\")\n .replaceAll(\"&quot;\", '\"')\n .replaceAll(\"&gt;\", \">\")\n .replaceAll(\"&lt;\", \"<\")\n .replaceAll(\"&amp;\", \"&\");\n return { ...definition, stateRoot };\n}\n\nasync function atomicWrite(path: string, contents: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n const temporary = `${path}.tmp-${process.pid}`;\n await writeFile(temporary, contents, { mode: 0o600 });\n await chmod(temporary, 0o600);\n await rename(temporary, path);\n}\n", "import { isAbsolute } from \"node:path\";\n\nexport interface ServiceDefinitionOptions {\n readonly nodePath: string;\n readonly runtimePath: string;\n readonly stateRoot?: string;\n}\n\nexport function systemdUserUnit(options: ServiceDefinitionOptions): string {\n validate(options);\n const environment =\n options.stateRoot === undefined\n ? \"\"\n : `Environment=PIFLEET_STATE_ROOT=${systemdEscape(options.stateRoot)}\\n`;\n return `[Unit]\nDescription=Pi Fleet user runtime\nAfter=default.target\n\n[Service]\nType=simple\nExecStart=${systemdEscape(options.nodePath)} ${systemdEscape(options.runtimePath)}\n${environment}Restart=on-failure\nRestartSec=1\nTimeoutStopSec=10\nUMask=0077\nKillMode=control-group\n\n[Install]\nWantedBy=default.target\n`;\n}\n\nexport function launchdAgentPlist(options: ServiceDefinitionOptions): string {\n validate(options);\n const environment =\n options.stateRoot === undefined\n ? \"\"\n : ` <key>EnvironmentVariables</key>\n <dict><key>PIFLEET_STATE_ROOT</key><string>${xmlEscape(options.stateRoot)}</string></dict>\n`;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key><string>works.elpapi.pifleet</string>\n <key>ProgramArguments</key>\n <array>\n <string>${xmlEscape(options.nodePath)}</string>\n <string>${xmlEscape(options.runtimePath)}</string>\n </array>\n${environment} <key>RunAtLoad</key><true/>\n <key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>\n <key>ProcessType</key><string>Background</string>\n</dict>\n</plist>\n`;\n}\n\nfunction validate(options: ServiceDefinitionOptions): void {\n if (!isAbsolute(options.nodePath) || !isAbsolute(options.runtimePath)) {\n throw new Error(\"Service executables must use absolute paths\");\n }\n}\n\nfunction systemdEscape(value: string): string {\n if (!/^[A-Za-z0-9_./:@-]+$/.test(value)) throw new Error(`Unsafe systemd path ${value}`);\n return value;\n}\n\nfunction xmlEscape(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&apos;\");\n}\n", "import { createHash, randomUUID } from \"node:crypto\";\nimport { chmod, cp, lstat, mkdir, readFile, rename, rm } from \"node:fs/promises\";\nimport { join, posix, resolve, sep } from \"node:path\";\n\nimport { hashDirectoryTree, type TreeIntegrity } from \"./tree-integrity.js\";\n\ninterface RuntimeManifestFile {\n readonly path: string;\n readonly bytes: number;\n readonly sha256: string;\n}\n\ninterface RuntimeManifest {\n readonly schemaVersion: 2;\n readonly package: { readonly name: string; readonly version: string };\n readonly files: readonly RuntimeManifestFile[];\n readonly trees: readonly TreeIntegrity[];\n}\n\nconst requiredRuntimeArtifacts = new Set([\n \"package.json\",\n \"bin/pifleet.mjs\",\n \"bin/pifleet-runtime.mjs\",\n \"dist/cli.mjs\",\n \"dist/runtime.mjs\",\n \"dist/sqlite-worker.mjs\",\n]);\n\nexport async function materializeRuntime(options: {\n readonly sourceRoot: string;\n readonly applicationRoot: string;\n}): Promise<string> {\n const sourceRoot = resolve(options.sourceRoot);\n const manifestBytes = await readFile(join(sourceRoot, \"dist\", \"runtime-manifest.json\"));\n const manifest = await parseRuntimeManifest(manifestBytes, sourceRoot);\n await verifyRuntime(sourceRoot, manifest);\n\n const applicationRoot = resolve(options.applicationRoot);\n await ensurePrivateDirectory(applicationRoot);\n const manifestHash = createHash(\"sha256\").update(manifestBytes).digest(\"hex\").slice(0, 16);\n const releasesRoot = join(applicationRoot, \"releases\");\n await ensurePrivateDirectory(releasesRoot);\n const destination = join(releasesRoot, `${manifest.package.version}-${manifestHash}`);\n\n if (await pathExists(destination)) {\n await verifyRuntime(destination, manifest);\n return destination;\n }\n\n const staging = join(releasesRoot, `.staging-${randomUUID()}`);\n await mkdir(staging, { mode: 0o700 });\n try {\n await cp(join(sourceRoot, \"dist\"), join(staging, \"dist\"), {\n recursive: true,\n dereference: true,\n });\n await cp(join(sourceRoot, \"bin\"), join(staging, \"bin\"), { recursive: true, dereference: true });\n await cp(join(sourceRoot, \"package.json\"), join(staging, \"package.json\"));\n for (const tree of manifest.trees) {\n const source = resolveInside(sourceRoot, tree.path);\n const destination = resolveInside(staging, tree.path);\n await mkdir(resolve(destination, \"..\"), { recursive: true, mode: 0o700 });\n await cp(source, destination, { recursive: true, dereference: true });\n }\n await verifyRuntime(staging, manifest);\n await chmod(staging, 0o700);\n try {\n await rename(staging, destination);\n } catch (error: unknown) {\n if (!isDestinationRace(error) || !(await pathExists(destination))) throw error;\n await verifyRuntime(destination, manifest);\n }\n return destination;\n } finally {\n await rm(staging, { recursive: true, force: true });\n }\n}\n\nexport async function verifyRuntime(root: string, manifest?: RuntimeManifest): Promise<void> {\n const resolvedRoot = resolve(root);\n const expected =\n manifest ??\n (await parseRuntimeManifest(\n await readFile(join(resolvedRoot, \"dist\", \"runtime-manifest.json\")),\n resolvedRoot,\n ));\n if (manifest !== undefined) await validateRuntimeManifest(manifest, resolvedRoot);\n\n for (const file of expected.files) {\n const path = resolveInside(resolvedRoot, file.path);\n const info = await lstat(path);\n if (info.isSymbolicLink()) {\n throw new Error(`Runtime artifact ${file.path} must not be a symbolic link`);\n }\n if (!info.isFile() || info.size !== file.bytes) {\n throw new Error(`Runtime artifact ${file.path} has changed`);\n }\n const hash = createHash(\"sha256\")\n .update(await readFile(path))\n .digest(\"hex\");\n if (hash !== file.sha256) {\n throw new Error(`Runtime artifact ${file.path} failed verification`);\n }\n }\n for (const expectedTree of expected.trees) {\n const treeRoot = resolveInside(resolvedRoot, expectedTree.path);\n const actualTree = await hashDirectoryTree(treeRoot, expectedTree.path);\n if (\n actualTree.files !== expectedTree.files ||\n actualTree.bytes !== expectedTree.bytes ||\n actualTree.sha256 !== expectedTree.sha256\n ) {\n throw new Error(`Runtime dependency tree ${expectedTree.path} failed verification`);\n }\n }\n}\n\nasync function parseRuntimeManifest(bytes: Buffer, root: string): Promise<RuntimeManifest> {\n let candidate: unknown;\n try {\n candidate = JSON.parse(bytes.toString(\"utf8\"));\n } catch {\n throw new Error(\"Runtime manifest is not valid JSON\");\n }\n await validateRuntimeManifest(candidate, root);\n return candidate as RuntimeManifest;\n}\n\nasync function validateRuntimeManifest(candidate: unknown, root: string): Promise<void> {\n if (!isRecord(candidate) || candidate.schemaVersion !== 2) {\n throw new Error(\"Runtime manifest has an unsupported schema version\");\n }\n if (\n !isRecord(candidate.package) ||\n typeof candidate.package.name !== \"string\" ||\n typeof candidate.package.version !== \"string\"\n ) {\n throw new Error(\"Runtime manifest has an invalid package identity\");\n }\n const packageIdentity = await readPackageIdentity(root);\n if (\n candidate.package.name !== packageIdentity.name ||\n candidate.package.version !== packageIdentity.version\n ) {\n throw new Error(\"Runtime manifest package identity does not match package.json\");\n }\n if (!Array.isArray(candidate.files) || !Array.isArray(candidate.trees)) {\n throw new Error(\"Runtime manifest has invalid artifact lists\");\n }\n\n const filePaths = new Set<string>();\n for (const file of candidate.files) {\n if (\n !isRecord(file) ||\n !isManifestPath(file.path) ||\n !isValidSize(file.bytes) ||\n !isSha256(file.sha256)\n ) {\n throw new Error(\"Runtime manifest contains an invalid artifact\");\n }\n if (filePaths.has(file.path))\n throw new Error(`Runtime manifest has duplicate path ${file.path}`);\n filePaths.add(file.path);\n }\n for (const required of requiredRuntimeArtifacts) {\n if (!filePaths.has(required)) {\n throw new Error(`Runtime manifest is missing required artifact ${required}`);\n }\n }\n\n const treePaths = new Set<string>();\n for (const tree of candidate.trees) {\n if (\n !isRecord(tree) ||\n !isManifestPath(tree.path) ||\n !isValidSize(tree.files) ||\n !isValidSize(tree.bytes) ||\n !isSha256(tree.sha256)\n ) {\n throw new Error(\"Runtime manifest contains an invalid dependency tree\");\n }\n if (treePaths.has(tree.path))\n throw new Error(`Runtime manifest has duplicate path ${tree.path}`);\n treePaths.add(tree.path);\n }\n const allPaths = [...filePaths, ...treePaths];\n for (let index = 0; index < allPaths.length; index += 1) {\n for (let other = index + 1; other < allPaths.length; other += 1) {\n const left = allPaths[index]!;\n const right = allPaths[other]!;\n if (left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`)) {\n throw new Error(`Runtime manifest has overlapping paths ${left} and ${right}`);\n }\n }\n }\n}\n\nasync function readPackageIdentity(root: string): Promise<{ name: string; version: string }> {\n let candidate: unknown;\n try {\n candidate = JSON.parse(await readFile(join(root, \"package.json\"), \"utf8\"));\n } catch {\n throw new Error(\"Runtime package.json is not valid JSON\");\n }\n if (\n !isRecord(candidate) ||\n typeof candidate.name !== \"string\" ||\n typeof candidate.version !== \"string\"\n ) {\n throw new Error(\"Runtime package.json has an invalid identity\");\n }\n return { name: candidate.name, version: candidate.version };\n}\n\nfunction resolveInside(root: string, path: string): string {\n if (!isManifestPath(path)) throw new Error(`Runtime manifest contains an unsafe path ${path}`);\n const resolved = resolve(root, path);\n if (!resolved.startsWith(`${root}${sep}`)) {\n throw new Error(`Runtime manifest contains an unsafe path ${path}`);\n }\n return resolved;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isManifestPath(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length > 0 &&\n !value.includes(\"\\\\\") &&\n !value.startsWith(\"/\") &&\n posix.normalize(value) === value &&\n value.split(\"/\").every((part) => part.length > 0 && part !== \".\" && part !== \"..\")\n );\n}\n\nfunction isValidSize(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction isSha256(value: unknown): value is string {\n return typeof value === \"string\" && /^[a-f0-9]{64}$/.test(value);\n}\n\nfunction isDestinationRace(error: unknown): boolean {\n const code = (error as NodeJS.ErrnoException).code;\n return code === \"EEXIST\" || code === \"ENOTEMPTY\";\n}\n\nasync function ensurePrivateDirectory(path: string): Promise<void> {\n await mkdir(path, { recursive: true, mode: 0o700 });\n const info = await lstat(path);\n if (!info.isDirectory() || info.isSymbolicLink()) {\n throw new Error(`Refusing unsafe runtime release path ${path}`);\n }\n if (typeof process.getuid === \"function\" && info.uid !== process.getuid()) {\n throw new Error(`Runtime release path ${path} is not owned by the current user`);\n }\n await chmod(path, 0o700);\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await lstat(path);\n return true;\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n}\n", "import { createHash } from \"node:crypto\";\nimport { lstat, readFile, readdir, stat } from \"node:fs/promises\";\nimport { join, relative, resolve, sep } from \"node:path\";\n\nexport interface TreeIntegrity {\n readonly path: string;\n readonly files: number;\n readonly bytes: number;\n readonly sha256: string;\n}\n\nexport async function hashDirectoryTree(\n root: string,\n manifestPath: string,\n): Promise<TreeIntegrity> {\n const resolvedRoot = resolve(root);\n const entries = await collectFiles(resolvedRoot, resolvedRoot);\n const hash = createHash(\"sha256\");\n let bytes = 0;\n for (const entry of entries) {\n const contents = await readFile(entry.absolutePath);\n bytes += contents.length;\n hash.update(entry.relativePath);\n hash.update(\"\\0\");\n hash.update(String(contents.length));\n hash.update(\"\\0\");\n hash.update(contents);\n hash.update(\"\\0\");\n }\n return {\n path: manifestPath,\n files: entries.length,\n bytes,\n sha256: hash.digest(\"hex\"),\n };\n}\n\nasync function collectFiles(\n root: string,\n directory: string,\n): Promise<Array<{ readonly absolutePath: string; readonly relativePath: string }>> {\n const output: Array<{ absolutePath: string; relativePath: string }> = [];\n for (const name of (await readdir(directory)).sort()) {\n const absolutePath = join(directory, name);\n const linkInfo = await lstat(absolutePath);\n const info = linkInfo.isSymbolicLink() ? await stat(absolutePath) : linkInfo;\n if (info.isDirectory()) {\n if (linkInfo.isSymbolicLink()) {\n throw new Error(`Runtime dependency tree contains a directory symlink: ${absolutePath}`);\n }\n output.push(...(await collectFiles(root, absolutePath)));\n continue;\n }\n if (!info.isFile()) {\n throw new Error(`Runtime dependency tree contains an unsupported entry: ${absolutePath}`);\n }\n const relativePath = relative(root, absolutePath).split(sep).join(\"/\");\n if (relativePath.length === 0 || relativePath.startsWith(\"../\")) {\n throw new Error(`Runtime dependency path escapes its root: ${absolutePath}`);\n }\n output.push({ absolutePath, relativePath });\n }\n return output.sort((left, right) => left.relativePath.localeCompare(right.relativePath));\n}\n", "import { homedir, tmpdir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\n\nexport interface FleetPaths {\n readonly runtimeRoot: string;\n readonly stateRoot: string;\n readonly socketPath: string;\n readonly databasePath: string;\n}\n\nexport function resolveApplicationRoot(env: NodeJS.ProcessEnv = process.env): string {\n return (\n env.PIFLEET_APPLICATION_ROOT ??\n (process.platform === \"darwin\"\n ? join(homedir(), \"Library\", \"Application Support\", \"Pi Fleet\", \"runtime\")\n : join(env.XDG_DATA_HOME ?? join(homedir(), \".local\", \"share\"), \"pi-fleet\"))\n );\n}\n\nexport function resolveFleetPaths(env: NodeJS.ProcessEnv = process.env): FleetPaths {\n const explicitRoot = env.PIFLEET_STATE_ROOT;\n if (explicitRoot !== undefined) {\n const root = resolve(explicitRoot);\n return {\n runtimeRoot: root,\n stateRoot: root,\n socketPath: join(root, \"control.sock\"),\n databasePath: join(root, \"fleet.sqlite\"),\n };\n }\n\n const runtimeRoot = join(\n env.XDG_RUNTIME_DIR ?? tmpdir(),\n `pifleet-${process.getuid?.() ?? \"user\"}`,\n );\n const stateRoot =\n process.platform === \"darwin\"\n ? join(homedir(), \"Library\", \"Application Support\", \"Pi Fleet\")\n : join(env.XDG_STATE_HOME ?? join(homedir(), \".local\", \"state\"), \"pi-fleet\");\n return {\n runtimeRoot,\n stateRoot,\n socketPath: join(runtimeRoot, \"control.sock\"),\n databasePath: join(stateRoot, \"fleet.sqlite\"),\n };\n}\n"],
5
+ "mappings": ";AAAA,SAAS,gBAAgB;AACzB,SAAS,WAAAA,UAAS,QAAAC,aAAY;AAC9B,SAAS,eAAe,qBAAqB;AAC7C,SAAS,iBAAiB;;;ACH1B,SAAS,iBAAiB;AAC1B,SAAS,QAAQ,OAAO,OAAO,UAAU,QAAQ,IAAI,iBAAiB;AACtE,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;;;ACH9B,SAAS,kBAAkB;AAQpB,SAAS,gBAAgB,SAA2C;AACzE,WAAS,OAAO;AAChB,QAAM,cACJ,QAAQ,cAAc,SAClB,KACA,kCAAkC,cAAc,QAAQ,SAAS,CAAC;AAAA;AACxE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMG,cAAc,QAAQ,QAAQ,CAAC,IAAI,cAAc,QAAQ,WAAW,CAAC;AAAA,EAC/E,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASb;AAEO,SAAS,kBAAkB,SAA2C;AAC3E,WAAS,OAAO;AAChB,QAAM,cACJ,QAAQ,cAAc,SAClB,KACA;AAAA,iDACyC,UAAU,QAAQ,SAAS,CAAC;AAAA;AAE3E,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAOS,UAAU,QAAQ,QAAQ,CAAC;AAAA,kBAC3B,UAAU,QAAQ,WAAW,CAAC;AAAA;AAAA,EAE9C,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAMb;AAEA,SAAS,SAAS,SAAyC;AACzD,MAAI,CAAC,WAAW,QAAQ,QAAQ,KAAK,CAAC,WAAW,QAAQ,WAAW,GAAG;AACrE,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACF;AAEA,SAAS,cAAc,OAAuB;AAC5C,MAAI,CAAC,uBAAuB,KAAK,KAAK,EAAG,OAAM,IAAI,MAAM,uBAAuB,KAAK,EAAE;AACvF,SAAO;AACT;AAEA,SAAS,UAAU,OAAuB;AACxC,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,QAAQ;AAC7B;;;AD7DA,eAAsB,mBAAmB,SAMrB;AAClB,QAAM,OAAO,QAAQ,QAAQ,QAAQ;AACrC,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,OAAO,KAAK,MAAM,WAAW,WAAW,QAAQ,kBAAkB;AACxE,UAAM,sBAAsB,MAAM,2BAA2B,MAAM,QAAQ,YAAY,OAAO;AAC9F,UAAM,aAAa,gBAAgB,mBAAmB;AACtD,UAAM,cAAc,MAAM,6BAA6B,MAAM,YAAY,mBAAmB;AAC5F,QAAI,aAAa;AACf,YAAM,YAAY,MAAM,UAAU;AAClC,YAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,eAAe,CAAC;AAAA,IACrE;AACA,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,UAAU,SAAS,kBAAkB,CAAC;AACzF,QAAI,aAAa;AACf,YAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,WAAW,kBAAkB,CAAC;AAAA,IACnF;AACA,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,aAAa,UAAU;AACjC,UAAM,OAAO,KAAK,MAAM,WAAW,gBAAgB,4BAA4B;AAC/E,UAAM,sBAAsB,MAAM;AAAA,MAChC;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF;AACA,UAAM,YAAY,MAAM,kBAAkB,mBAAmB,CAAC;AAC9D,UAAM,SAAS,OAAO,QAAQ,OAAO,QAAQ,SAAS,KAAK,CAAC;AAC5D,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,WAAW,QAAQ,IAAI,CAAC,EAAE,MAAM,MAAM,MAAS;AACxF,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,aAAa,QAAQ,IAAI,CAAC;AACnE,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,aAAa,GAAG,MAAM,uBAAuB,CAAC;AACvF,WAAO;AAAA,EACT;AACA,QAAM,IAAI,MAAM,iDAAiD,QAAQ,QAAQ,EAAE;AACrF;AAEA,eAAsB,qBAAqB,SAKzB;AAChB,QAAM,OAAO,QAAQ,QAAQ,QAAQ;AACrC,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,OAAO,KAAK,MAAM,WAAW,WAAW,QAAQ,kBAAkB;AACxE,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,WAAW,SAAS,kBAAkB,CAAC;AAC1F,UAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAC9B,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,UAAU,eAAe,CAAC;AACnE;AAAA,EACF;AACA,MAAI,QAAQ,aAAa,UAAU;AACjC,UAAM,OAAO,KAAK,MAAM,WAAW,gBAAgB,4BAA4B;AAC/E,UAAM,SAAS,OAAO,QAAQ,OAAO,QAAQ,SAAS,KAAK,CAAC;AAC5D,UAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,WAAW,QAAQ,IAAI,CAAC,EAAE,MAAM,MAAM,MAAS;AACxF,UAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAC9B;AAAA,EACF;AACA,QAAM,IAAI,MAAM,iDAAiD,QAAQ,QAAQ,EAAE;AACrF;AAEA,eAAsB,6BACpB,MACA,kBACA,YACkB;AAClB,QAAM,UAAU,MAAM,SAAS,MAAM,MAAM,EAAE,MAAM,CAAC,UAAiC;AACnF,QAAI,MAAM,SAAS,SAAU,QAAO;AACpC,UAAM;AAAA,EACR,CAAC;AACD,MAAI,YAAY,iBAAkB,QAAO;AACzC,MAAI;AACF,UAAM,OAAO,WAAW,UAAU,UAAU,IAAI;AAChD,UAAM,OAAO,WAAW,aAAa,UAAU,IAAI;AACnD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,2BACb,MACA,YACA,UACmC;AACnC,MAAI,WAAW,cAAc,OAAW,QAAO;AAC/C,QAAM,UAAU,MAAM,SAAS,MAAM,MAAM,EAAE,MAAM,CAAC,UAAiC;AACnF,QAAI,MAAM,SAAS,SAAU,QAAO;AACpC,UAAM;AAAA,EACR,CAAC;AACD,MAAI,YAAY,KAAM,QAAO;AAC7B,QAAM,UACJ,aAAa,UACT,yCAAyC,KAAK,OAAO,IAAI,CAAC,IAC1D,0DAA0D,KAAK,OAAO,IAAI,CAAC;AACjF,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,YACJ,aAAa,UACT,UACA,QACG,WAAW,UAAU,GAAG,EACxB,WAAW,UAAU,GAAG,EACxB,WAAW,QAAQ,GAAG,EACtB,WAAW,QAAQ,GAAG,EACtB,WAAW,SAAS,GAAG;AAChC,SAAO,EAAE,GAAG,YAAY,UAAU;AACpC;AAEA,eAAe,YAAY,MAAc,UAAiC;AACxE,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,QAAM,YAAY,GAAG,IAAI,QAAQ,QAAQ,GAAG;AAC5C,QAAM,UAAU,WAAW,UAAU,EAAE,MAAM,IAAM,CAAC;AACpD,QAAM,MAAM,WAAW,GAAK;AAC5B,QAAM,OAAO,WAAW,IAAI;AAC9B;;;AEpIA,SAAS,cAAAC,aAAY,kBAAkB;AACvC,SAAS,SAAAC,QAAO,IAAI,SAAAC,QAAO,SAAAC,QAAO,YAAAC,WAAU,UAAAC,SAAQ,MAAAC,WAAU;AAC9D,SAAS,QAAAC,OAAM,OAAO,WAAAC,UAAS,OAAAC,YAAW;;;ACF1C,SAAS,kBAAkB;AAC3B,SAAS,OAAO,YAAAC,WAAU,SAAS,YAAY;AAC/C,SAAS,QAAAC,OAAM,UAAU,SAAS,WAAW;AAS7C,eAAsB,kBACpB,MACA,cACwB;AACxB,QAAM,eAAe,QAAQ,IAAI;AACjC,QAAM,UAAU,MAAM,aAAa,cAAc,YAAY;AAC7D,QAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,QAAQ;AACZ,aAAW,SAAS,SAAS;AAC3B,UAAM,WAAW,MAAMD,UAAS,MAAM,YAAY;AAClD,aAAS,SAAS;AAClB,SAAK,OAAO,MAAM,YAAY;AAC9B,SAAK,OAAO,IAAI;AAChB,SAAK,OAAO,OAAO,SAAS,MAAM,CAAC;AACnC,SAAK,OAAO,IAAI;AAChB,SAAK,OAAO,QAAQ;AACpB,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,QAAQ;AAAA,IACf;AAAA,IACA,QAAQ,KAAK,OAAO,KAAK;AAAA,EAC3B;AACF;AAEA,eAAe,aACb,MACA,WACkF;AAClF,QAAM,SAAgE,CAAC;AACvE,aAAW,SAAS,MAAM,QAAQ,SAAS,GAAG,KAAK,GAAG;AACpD,UAAM,eAAeC,MAAK,WAAW,IAAI;AACzC,UAAM,WAAW,MAAM,MAAM,YAAY;AACzC,UAAM,OAAO,SAAS,eAAe,IAAI,MAAM,KAAK,YAAY,IAAI;AACpE,QAAI,KAAK,YAAY,GAAG;AACtB,UAAI,SAAS,eAAe,GAAG;AAC7B,cAAM,IAAI,MAAM,yDAAyD,YAAY,EAAE;AAAA,MACzF;AACA,aAAO,KAAK,GAAI,MAAM,aAAa,MAAM,YAAY,CAAE;AACvD;AAAA,IACF;AACA,QAAI,CAAC,KAAK,OAAO,GAAG;AAClB,YAAM,IAAI,MAAM,0DAA0D,YAAY,EAAE;AAAA,IAC1F;AACA,UAAM,eAAe,SAAS,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AACrE,QAAI,aAAa,WAAW,KAAK,aAAa,WAAW,KAAK,GAAG;AAC/D,YAAM,IAAI,MAAM,6CAA6C,YAAY,EAAE;AAAA,IAC7E;AACA,WAAO,KAAK,EAAE,cAAc,aAAa,CAAC;AAAA,EAC5C;AACA,SAAO,OAAO,KAAK,CAAC,MAAM,UAAU,KAAK,aAAa,cAAc,MAAM,YAAY,CAAC;AACzF;;;AD5CA,IAAM,2BAA2B,oBAAI,IAAI;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,eAAsB,mBAAmB,SAGrB;AAClB,QAAM,aAAaC,SAAQ,QAAQ,UAAU;AAC7C,QAAM,gBAAgB,MAAMC,UAASC,MAAK,YAAY,QAAQ,uBAAuB,CAAC;AACtF,QAAM,WAAW,MAAM,qBAAqB,eAAe,UAAU;AACrE,QAAM,cAAc,YAAY,QAAQ;AAExC,QAAM,kBAAkBF,SAAQ,QAAQ,eAAe;AACvD,QAAM,uBAAuB,eAAe;AAC5C,QAAM,eAAeG,YAAW,QAAQ,EAAE,OAAO,aAAa,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACzF,QAAM,eAAeD,MAAK,iBAAiB,UAAU;AACrD,QAAM,uBAAuB,YAAY;AACzC,QAAM,cAAcA,MAAK,cAAc,GAAG,SAAS,QAAQ,OAAO,IAAI,YAAY,EAAE;AAEpF,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,cAAc,aAAa,QAAQ;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,UAAUA,MAAK,cAAc,YAAY,WAAW,CAAC,EAAE;AAC7D,QAAME,OAAM,SAAS,EAAE,MAAM,IAAM,CAAC;AACpC,MAAI;AACF,UAAM,GAAGF,MAAK,YAAY,MAAM,GAAGA,MAAK,SAAS,MAAM,GAAG;AAAA,MACxD,WAAW;AAAA,MACX,aAAa;AAAA,IACf,CAAC;AACD,UAAM,GAAGA,MAAK,YAAY,KAAK,GAAGA,MAAK,SAAS,KAAK,GAAG,EAAE,WAAW,MAAM,aAAa,KAAK,CAAC;AAC9F,UAAM,GAAGA,MAAK,YAAY,cAAc,GAAGA,MAAK,SAAS,cAAc,CAAC;AACxE,eAAW,QAAQ,SAAS,OAAO;AACjC,YAAM,SAAS,cAAc,YAAY,KAAK,IAAI;AAClD,YAAMG,eAAc,cAAc,SAAS,KAAK,IAAI;AACpD,YAAMD,OAAMJ,SAAQK,cAAa,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACxE,YAAM,GAAG,QAAQA,cAAa,EAAE,WAAW,MAAM,aAAa,KAAK,CAAC;AAAA,IACtE;AACA,UAAM,cAAc,SAAS,QAAQ;AACrC,UAAMC,OAAM,SAAS,GAAK;AAC1B,QAAI;AACF,YAAMC,QAAO,SAAS,WAAW;AAAA,IACnC,SAAS,OAAgB;AACvB,UAAI,CAAC,kBAAkB,KAAK,KAAK,CAAE,MAAM,WAAW,WAAW,EAAI,OAAM;AACzE,YAAM,cAAc,aAAa,QAAQ;AAAA,IAC3C;AACA,WAAO;AAAA,EACT,UAAE;AACA,UAAMC,IAAG,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACpD;AACF;AAEA,eAAsB,cAAc,MAAc,UAA2C;AAC3F,QAAM,eAAeR,SAAQ,IAAI;AACjC,QAAM,WACJ,YACC,MAAM;AAAA,IACL,MAAMC,UAASC,MAAK,cAAc,QAAQ,uBAAuB,CAAC;AAAA,IAClE;AAAA,EACF;AACF,MAAI,aAAa,OAAW,OAAM,wBAAwB,UAAU,YAAY;AAEhF,aAAW,QAAQ,SAAS,OAAO;AACjC,UAAM,OAAO,cAAc,cAAc,KAAK,IAAI;AAClD,UAAM,OAAO,MAAMO,OAAM,IAAI;AAC7B,QAAI,KAAK,eAAe,GAAG;AACzB,YAAM,IAAI,MAAM,oBAAoB,KAAK,IAAI,8BAA8B;AAAA,IAC7E;AACA,QAAI,CAAC,KAAK,OAAO,KAAK,KAAK,SAAS,KAAK,OAAO;AAC9C,YAAM,IAAI,MAAM,oBAAoB,KAAK,IAAI,cAAc;AAAA,IAC7D;AACA,UAAM,OAAON,YAAW,QAAQ,EAC7B,OAAO,MAAMF,UAAS,IAAI,CAAC,EAC3B,OAAO,KAAK;AACf,QAAI,SAAS,KAAK,QAAQ;AACxB,YAAM,IAAI,MAAM,oBAAoB,KAAK,IAAI,sBAAsB;AAAA,IACrE;AAAA,EACF;AACA,aAAW,gBAAgB,SAAS,OAAO;AACzC,UAAM,WAAW,cAAc,cAAc,aAAa,IAAI;AAC9D,UAAM,aAAa,MAAM,kBAAkB,UAAU,aAAa,IAAI;AACtE,QACE,WAAW,UAAU,aAAa,SAClC,WAAW,UAAU,aAAa,SAClC,WAAW,WAAW,aAAa,QACnC;AACA,YAAM,IAAI,MAAM,2BAA2B,aAAa,IAAI,sBAAsB;AAAA,IACpF;AAAA,EACF;AACF;AAEA,eAAe,qBAAqB,OAAe,MAAwC;AACzF,MAAI;AACJ,MAAI;AACF,gBAAY,KAAK,MAAM,MAAM,SAAS,MAAM,CAAC;AAAA,EAC/C,QAAQ;AACN,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACA,QAAM,wBAAwB,WAAW,IAAI;AAC7C,SAAO;AACT;AAEA,eAAe,wBAAwB,WAAoB,MAA6B;AACtF,MAAI,CAAC,SAAS,SAAS,KAAK,UAAU,kBAAkB,GAAG;AACzD,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,MACE,CAAC,SAAS,UAAU,OAAO,KAC3B,OAAO,UAAU,QAAQ,SAAS,YAClC,OAAO,UAAU,QAAQ,YAAY,UACrC;AACA,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,QAAM,kBAAkB,MAAM,oBAAoB,IAAI;AACtD,MACE,UAAU,QAAQ,SAAS,gBAAgB,QAC3C,UAAU,QAAQ,YAAY,gBAAgB,SAC9C;AACA,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACA,MAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,KAAK,CAAC,MAAM,QAAQ,UAAU,KAAK,GAAG;AACtE,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AAEA,QAAM,YAAY,oBAAI,IAAY;AAClC,aAAW,QAAQ,UAAU,OAAO;AAClC,QACE,CAAC,SAAS,IAAI,KACd,CAAC,eAAe,KAAK,IAAI,KACzB,CAAC,YAAY,KAAK,KAAK,KACvB,CAAC,SAAS,KAAK,MAAM,GACrB;AACA,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AACA,QAAI,UAAU,IAAI,KAAK,IAAI;AACzB,YAAM,IAAI,MAAM,uCAAuC,KAAK,IAAI,EAAE;AACpE,cAAU,IAAI,KAAK,IAAI;AAAA,EACzB;AACA,aAAW,YAAY,0BAA0B;AAC/C,QAAI,CAAC,UAAU,IAAI,QAAQ,GAAG;AAC5B,YAAM,IAAI,MAAM,iDAAiD,QAAQ,EAAE;AAAA,IAC7E;AAAA,EACF;AAEA,QAAM,YAAY,oBAAI,IAAY;AAClC,aAAW,QAAQ,UAAU,OAAO;AAClC,QACE,CAAC,SAAS,IAAI,KACd,CAAC,eAAe,KAAK,IAAI,KACzB,CAAC,YAAY,KAAK,KAAK,KACvB,CAAC,YAAY,KAAK,KAAK,KACvB,CAAC,SAAS,KAAK,MAAM,GACrB;AACA,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AACA,QAAI,UAAU,IAAI,KAAK,IAAI;AACzB,YAAM,IAAI,MAAM,uCAAuC,KAAK,IAAI,EAAE;AACpE,cAAU,IAAI,KAAK,IAAI;AAAA,EACzB;AACA,QAAM,WAAW,CAAC,GAAG,WAAW,GAAG,SAAS;AAC5C,WAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;AACvD,aAAS,QAAQ,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;AAC/D,YAAM,OAAO,SAAS,KAAK;AAC3B,YAAM,QAAQ,SAAS,KAAK;AAC5B,UAAI,SAAS,SAAS,KAAK,WAAW,GAAG,KAAK,GAAG,KAAK,MAAM,WAAW,GAAG,IAAI,GAAG,GAAG;AAClF,cAAM,IAAI,MAAM,0CAA0C,IAAI,QAAQ,KAAK,EAAE;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,oBAAoB,MAA0D;AAC3F,MAAI;AACJ,MAAI;AACF,gBAAY,KAAK,MAAM,MAAMA,UAASC,MAAK,MAAM,cAAc,GAAG,MAAM,CAAC;AAAA,EAC3E,QAAQ;AACN,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AACA,MACE,CAAC,SAAS,SAAS,KACnB,OAAO,UAAU,SAAS,YAC1B,OAAO,UAAU,YAAY,UAC7B;AACA,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO,EAAE,MAAM,UAAU,MAAM,SAAS,UAAU,QAAQ;AAC5D;AAEA,SAAS,cAAc,MAAc,MAAsB;AACzD,MAAI,CAAC,eAAe,IAAI,EAAG,OAAM,IAAI,MAAM,4CAA4C,IAAI,EAAE;AAC7F,QAAM,WAAWF,SAAQ,MAAM,IAAI;AACnC,MAAI,CAAC,SAAS,WAAW,GAAG,IAAI,GAAGU,IAAG,EAAE,GAAG;AACzC,UAAM,IAAI,MAAM,4CAA4C,IAAI,EAAE;AAAA,EACpE;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,eAAe,OAAiC;AACvD,SACE,OAAO,UAAU,YACjB,MAAM,SAAS,KACf,CAAC,MAAM,SAAS,IAAI,KACpB,CAAC,MAAM,WAAW,GAAG,KACrB,MAAM,UAAU,KAAK,MAAM,SAC3B,MAAM,MAAM,GAAG,EAAE,MAAM,CAAC,SAAS,KAAK,SAAS,KAAK,SAAS,OAAO,SAAS,IAAI;AAErF;AAEA,SAAS,YAAY,OAAiC;AACpD,SAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,SAAS;AAC9E;AAEA,SAAS,SAAS,OAAiC;AACjD,SAAO,OAAO,UAAU,YAAY,iBAAiB,KAAK,KAAK;AACjE;AAEA,SAAS,kBAAkB,OAAyB;AAClD,QAAM,OAAQ,MAAgC;AAC9C,SAAO,SAAS,YAAY,SAAS;AACvC;AAEA,eAAe,uBAAuB,MAA6B;AACjE,QAAMN,OAAM,MAAM,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAClD,QAAM,OAAO,MAAMK,OAAM,IAAI;AAC7B,MAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,GAAG;AAChD,UAAM,IAAI,MAAM,wCAAwC,IAAI,EAAE;AAAA,EAChE;AACA,MAAI,OAAO,QAAQ,WAAW,cAAc,KAAK,QAAQ,QAAQ,OAAO,GAAG;AACzE,UAAM,IAAI,MAAM,wBAAwB,IAAI,mCAAmC;AAAA,EACjF;AACA,QAAMH,OAAM,MAAM,GAAK;AACzB;AAEA,eAAe,WAAW,MAAgC;AACxD,MAAI;AACF,UAAMG,OAAM,IAAI;AAChB,WAAO;AAAA,EACT,SAAS,OAAgB;AACvB,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACF;;;AE/QA,SAAS,WAAAE,UAAS,cAAc;AAChC,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AASvB,SAAS,uBAAuB,MAAyB,QAAQ,KAAa;AACnF,SACE,IAAI,6BACH,QAAQ,aAAa,WAClBD,MAAKD,SAAQ,GAAG,WAAW,uBAAuB,YAAY,SAAS,IACvEC,MAAK,IAAI,iBAAiBA,MAAKD,SAAQ,GAAG,UAAU,OAAO,GAAG,UAAU;AAEhF;AAEO,SAAS,kBAAkB,MAAyB,QAAQ,KAAiB;AAClF,QAAM,eAAe,IAAI;AACzB,MAAI,iBAAiB,QAAW;AAC9B,UAAM,OAAOE,SAAQ,YAAY;AACjC,WAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW;AAAA,MACX,YAAYD,MAAK,MAAM,cAAc;AAAA,MACrC,cAAcA,MAAK,MAAM,cAAc;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,cAAcA;AAAA,IAClB,IAAI,mBAAmB,OAAO;AAAA,IAC9B,WAAW,QAAQ,SAAS,KAAK,MAAM;AAAA,EACzC;AACA,QAAM,YACJ,QAAQ,aAAa,WACjBA,MAAKD,SAAQ,GAAG,WAAW,uBAAuB,UAAU,IAC5DC,MAAK,IAAI,kBAAkBA,MAAKD,SAAQ,GAAG,UAAU,OAAO,GAAG,UAAU;AAC/E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAYC,MAAK,aAAa,cAAc;AAAA,IAC5C,cAAcA,MAAK,WAAW,cAAc;AAAA,EAC9C;AACF;;;ALhCA,IAAM,WAA4B;AAAA,EAChC,MAAM,IAAI,SAAS,MAAM;AACvB,UAAM,UAAU,QAAQ,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC;AAAA,EAC9C;AACF;AAEA,eAAsB,wBAAyC;AAC7D,SAAO,2BAA2B,kBAAkB,EAAE,SAAS;AACjE;AAEA,eAAsB,uBAAwC;AAC5D,SAAO,2BAA2B,QAAQ,IAAI,kBAAkB;AAClE;AAEA,eAAe,2BAA2B,WAAgD;AACxF,QAAM,aAAaE,SAAQA,SAAQ,cAAc,YAAY,GAAG,CAAC,CAAC;AAClE,QAAM,UAAU,MAAM,mBAAmB;AAAA,IACvC;AAAA,IACA,iBAAiB,uBAAuB;AAAA,EAC1C,CAAC;AACD,SAAO,mBAAmB;AAAA,IACxB,UAAU,QAAQ;AAAA,IAClB,YAAY;AAAA,MACV,UAAU,QAAQ;AAAA,MAClB,aAAaC,MAAK,SAAS,OAAO,qBAAqB;AAAA,MACvD,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,UAAU;AAAA,IACjD;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,0BAAyC;AAC7D,QAAM,qBAAqB,EAAE,UAAU,QAAQ,UAAU,SAAS,CAAC;AACrE;AAEA,eAAe,OAAsB;AACnC,QAAM,UAAU,QAAQ,KAAK,CAAC;AAC9B,MAAI,YAAY,WAAW;AACzB,YAAQ,OAAO,MAAM,GAAG,MAAM,sBAAsB,CAAC;AAAA,CAAI;AACzD;AAAA,EACF;AACA,MAAI,YAAY,UAAU;AACxB,YAAQ,OAAO,MAAM,GAAG,MAAM,qBAAqB,CAAC;AAAA,CAAI;AACxD;AAAA,EACF;AACA,MAAI,YAAY,aAAa;AAC3B,UAAM,wBAAwB;AAC9B;AAAA,EACF;AACA,QAAM,IAAI,MAAM,2DAA2D;AAC7E;AAEA,IAAI,QAAQ,KAAK,CAAC,MAAM,UAAa,YAAY,QAAQ,cAAc,QAAQ,KAAK,CAAC,CAAC,EAAE,MAAM;AAC5F,QAAM,KAAK;AACb;",
6
+ "names": ["dirname", "join", "createHash", "chmod", "lstat", "mkdir", "readFile", "rename", "rm", "join", "resolve", "sep", "readFile", "join", "resolve", "readFile", "join", "createHash", "mkdir", "destination", "chmod", "rename", "rm", "lstat", "sep", "homedir", "join", "resolve", "dirname", "join"]
7
7
  }
@@ -2,69 +2,69 @@
2
2
  "schemaVersion": 2,
3
3
  "package": {
4
4
  "name": "@elpapi42/pi-fleet",
5
- "version": "0.1.0-beta.1"
5
+ "version": "0.1.0-beta.2"
6
6
  },
7
7
  "managedPi": "@earendil-works/pi-coding-agent@0.80.10",
8
8
  "files": [
9
9
  {
10
10
  "path": "dist/cli-meta.json",
11
11
  "bytes": 21246,
12
- "sha256": "d41a7caf70bec4c461c8b5d4689b78432419e551bc4e3ae23f6e04cdd8e1d5ec"
12
+ "sha256": "a8ff1d8172d10a74c0a2b3a1c01b814551f8a32d28154cb44859a4af1cb6d361"
13
13
  },
14
14
  {
15
15
  "path": "dist/cli.mjs",
16
- "bytes": 139855,
17
- "sha256": "ed3af28bbad74119d2014d86aff8ac5d8ea024cb5fc4bae25c1261280a7e8a08"
16
+ "bytes": 144920,
17
+ "sha256": "e1ac407f4123f3990bb22d6978307d7f8a70f05faf41399c755a21929dc5d9f5"
18
18
  },
19
19
  {
20
20
  "path": "dist/cli.mjs.map",
21
- "bytes": 251175,
22
- "sha256": "17e4faa1d18a82df4ffda51414b978375dd7bb4570d590aec7aa65a2a4427322"
21
+ "bytes": 260315,
22
+ "sha256": "4d77fe125e07ce2d12b7b33ca60617860c57783363b882c2981f6926e244e945"
23
23
  },
24
24
  {
25
25
  "path": "dist/installer-meta.json",
26
26
  "bytes": 6625,
27
- "sha256": "6c887b0e0164d2f675874277e68c975e77fa361a3f7765039326d3306d87edd4"
27
+ "sha256": "c7570b9253ca9335bf5a074e2467eedac63ce5bef25296694f8faa50c381353d"
28
28
  },
29
29
  {
30
30
  "path": "dist/installer.mjs",
31
- "bytes": 15347,
32
- "sha256": "2528d0cf1a6d7a90c0f4c3f471b685b62008d8796bb80c0d832fd99790e8b16a"
31
+ "bytes": 19836,
32
+ "sha256": "8cbd21b9134c025581e2ae61105e35687b4ff008faa3ac50b40ba8f1865794fd"
33
33
  },
34
34
  {
35
35
  "path": "dist/installer.mjs.map",
36
- "bytes": 29042,
37
- "sha256": "3c460db2b0b3f50d8e4a8aea2baa13f0edc96212e4465dee17baf5ba848210dd"
36
+ "bytes": 37234,
37
+ "sha256": "1253445c252391fe33f5bfebbc31f6cf6167a21ec24c50b2423f5f7108d44812"
38
38
  },
39
39
  {
40
40
  "path": "dist/runtime-meta.json",
41
41
  "bytes": 241009,
42
- "sha256": "62031776fa029ae05e2d925a43cda066f7b8b6ea135ede9e95bc61cb44c33a25"
42
+ "sha256": "ddefdd94b406d38ccac9293b83718dd4ef52c3a0e8b7122ec5720d443167cbfb"
43
43
  },
44
44
  {
45
45
  "path": "dist/runtime.mjs",
46
- "bytes": 314366,
47
- "sha256": "6a80d6be325eb30d6affb6468ffa2ae13ab941e4727b301349893c6fd576411d"
46
+ "bytes": 318107,
47
+ "sha256": "cbd2a3de2b5e12cc55d478ddf60ba367a2d21faa24e6a4a658c6128607cfd5f7"
48
48
  },
49
49
  {
50
50
  "path": "dist/runtime.mjs.map",
51
- "bytes": 688792,
52
- "sha256": "c95ef83f57dc04c3320dd19f91c0d7b71ccf8b5ee164f3a28378d3918f93b3b7"
51
+ "bytes": 695723,
52
+ "sha256": "7ca452f3b15d7bd73de52c210591dbd8df14a6284902379306833dc6ad0b8010"
53
53
  },
54
54
  {
55
55
  "path": "dist/sqlite-worker-meta.json",
56
- "bytes": 1845,
57
- "sha256": "7adfe47be35263ff6027317a55f7d65b25c8cda5b553dfc22e0d78b908a793da"
56
+ "bytes": 1846,
57
+ "sha256": "24f692498f5620279d8e2c1ca995175ba01b02b9af87fcec27aaebe2190f962e"
58
58
  },
59
59
  {
60
60
  "path": "dist/sqlite-worker.mjs",
61
- "bytes": 12027,
62
- "sha256": "2b0bde4549c7964aa9d55a347f407ad73b1f80d325350e7567891dc8d75cf2da"
61
+ "bytes": 12446,
62
+ "sha256": "56abddba47342e493e36905e86224c506cf90ec8568977c402b177db288050aa"
63
63
  },
64
64
  {
65
65
  "path": "dist/sqlite-worker.mjs.map",
66
- "bytes": 20305,
67
- "sha256": "14806789f0d6bbb7b9407dedbb486239e4cd2478d0c34ba38c75e31d2080b395"
66
+ "bytes": 21104,
67
+ "sha256": "d4f9cce1fe557178fa914ca8314d67dd1463efdb09a2acfd7143f879a8c13459"
68
68
  },
69
69
  {
70
70
  "path": "bin/pifleet-runtime.mjs",
@@ -78,8 +78,8 @@
78
78
  },
79
79
  {
80
80
  "path": "package.json",
81
- "bytes": 2080,
82
- "sha256": "601bce0d13b3d6cef81419fd06ece5f6943040751347c707f7252ea176382923"
81
+ "bytes": 2375,
82
+ "sha256": "967198e3cf00529d57adbe818c27a800727f410552b17aa951e03666082b4c92"
83
83
  }
84
84
  ],
85
85
  "trees": [
@@ -6,7 +6,7 @@
6
6
  "format": "esm"
7
7
  },
8
8
  "src/pi/process.ts": {
9
- "bytes": 8031,
9
+ "bytes": 8414,
10
10
  "imports": [
11
11
  {
12
12
  "path": "node:crypto",
@@ -6177,7 +6177,7 @@
6177
6177
  "format": "esm"
6178
6178
  },
6179
6179
  "src/runtime/control-server.ts": {
6180
- "bytes": 10292,
6180
+ "bytes": 10823,
6181
6181
  "imports": [
6182
6182
  {
6183
6183
  "path": "node:events",
@@ -6244,7 +6244,7 @@
6244
6244
  "format": "esm"
6245
6245
  },
6246
6246
  "src/runtime/agent-coordinator.ts": {
6247
- "bytes": 7255,
6247
+ "bytes": 7786,
6248
6248
  "imports": [
6249
6249
  {
6250
6250
  "path": "src/platform/runtime/process-tree.ts",
@@ -6266,7 +6266,7 @@
6266
6266
  "format": "esm"
6267
6267
  },
6268
6268
  "src/runtime/fleet-service.ts": {
6269
- "bytes": 32413,
6269
+ "bytes": 34550,
6270
6270
  "imports": [
6271
6271
  {
6272
6272
  "path": "node:crypto",
@@ -6312,7 +6312,7 @@
6312
6312
  "format": "esm"
6313
6313
  },
6314
6314
  "src/store/worker-store.ts": {
6315
- "bytes": 3812,
6315
+ "bytes": 4643,
6316
6316
  "imports": [
6317
6317
  {
6318
6318
  "path": "node:crypto",
@@ -6379,7 +6379,7 @@
6379
6379
  "imports": [],
6380
6380
  "exports": [],
6381
6381
  "inputs": {},
6382
- "bytes": 688792
6382
+ "bytes": 695723
6383
6383
  },
6384
6384
  "dist/runtime.mjs": {
6385
6385
  "imports": [
@@ -6468,7 +6468,7 @@
6468
6468
  "bytesInOutput": 1362
6469
6469
  },
6470
6470
  "src/pi/process.ts": {
6471
- "bytesInOutput": 6331
6471
+ "bytesInOutput": 6698
6472
6472
  },
6473
6473
  "src/platform/runtime/process-tree.ts": {
6474
6474
  "bytesInOutput": 993
@@ -6483,7 +6483,7 @@
6483
6483
  "bytesInOutput": 878
6484
6484
  },
6485
6485
  "src/runtime/control-server.ts": {
6486
- "bytesInOutput": 8583
6486
+ "bytesInOutput": 8998
6487
6487
  },
6488
6488
  "node_modules/@sinclair/typebox/build/esm/value/guard/guard.mjs": {
6489
6489
  "bytesInOutput": 1876
@@ -6939,7 +6939,7 @@
6939
6939
  "bytesInOutput": 108
6940
6940
  },
6941
6941
  "src/runtime/fleet-service.ts": {
6942
- "bytesInOutput": 27913
6942
+ "bytesInOutput": 29674
6943
6943
  },
6944
6944
  "src/pi/session-selector.ts": {
6945
6945
  "bytesInOutput": 2113
@@ -6948,16 +6948,16 @@
6948
6948
  "bytesInOutput": 2665
6949
6949
  },
6950
6950
  "src/runtime/agent-coordinator.ts": {
6951
- "bytesInOutput": 6296
6951
+ "bytesInOutput": 6762
6952
6952
  },
6953
6953
  "src/runtime/session-tail-subscription.ts": {
6954
6954
  "bytesInOutput": 4844
6955
6955
  },
6956
6956
  "src/store/worker-store.ts": {
6957
- "bytesInOutput": 2811
6957
+ "bytesInOutput": 3543
6958
6958
  }
6959
6959
  },
6960
- "bytes": 314366
6960
+ "bytes": 318107
6961
6961
  }
6962
6962
  }
6963
6963
  }