@danypops/pi-packed 0.21.6 → 0.21.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-packed",
3
- "version": "0.21.6",
3
+ "version": "0.21.8",
4
4
  "description": "Pi package lifecycle, validation, daemon, tools, profiles, and TUI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,8 @@ import {
15
15
  import { createLogger } from "../shared/log.ts";
16
16
 
17
17
  const log = createLogger("registry");
18
+ const README_MAX_CHARS = 50_000;
19
+ const README_DOCUMENT_MAX_BYTES = 1024 * 1024;
18
20
 
19
21
  /** Upstream etiquette: honor Retry-After on 429, exponential backoff
20
22
  * otherwise, give up after RETRY_MAX_ATTEMPTS. */
@@ -128,6 +130,7 @@ export class HttpRegistry implements Registry {
128
130
  dist?: { unpackedSize?: number; integrity?: string; attestations?: { url?: string; provenance?: unknown } };
129
131
  };
130
132
  const manifestFields = v.pi ? Object.keys(v.pi).filter((key) => ["extensions", "skills", "prompts", "themes"].includes(key)) : [];
133
+ const readme = await this.publishedReadme(encoded);
131
134
  return {
132
135
  name: boundedString(v.name, 214) ?? boundedString(name, 214)!,
133
136
  version: boundedString(v.version, 128) ?? "",
@@ -144,7 +147,8 @@ export class HttpRegistry implements Registry {
144
147
  pi: boundedPiManifest(v.pi),
145
148
  peerDependencies: boundedDependencies(v.peerDependencies),
146
149
  scripts: boundedDependencies(v.scripts),
147
- readmeAvailable: false,
150
+ readme,
151
+ readmeAvailable: readme !== undefined && readme.trim().length > 0,
148
152
  unpackedSize: v.dist?.unpackedSize,
149
153
  packageEvidence:
150
154
  manifestFields.length > 0
@@ -162,6 +166,22 @@ export class HttpRegistry implements Registry {
162
166
  };
163
167
  }
164
168
 
169
+ private async publishedReadme(encodedName: string): Promise<string | undefined> {
170
+ try {
171
+ const response = await fetchWithRetry(
172
+ `${this.base}/${encodedName}`,
173
+ { headers: { accept: "application/json" } },
174
+ this.retryBaseDelayMs,
175
+ );
176
+ if (!response.ok) return undefined;
177
+ const document = (await boundedJson(response, README_DOCUMENT_MAX_BYTES)) as { readme?: unknown };
178
+ return boundedString(document.readme, README_MAX_CHARS);
179
+ } catch (error) {
180
+ log.debug("published README unavailable", { error: error instanceof Error ? error.message : String(error) });
181
+ return undefined;
182
+ }
183
+ }
184
+
165
185
  /** Uses npm's abbreviated multi-version doc, not `/latest` (which carries
166
186
  * no `time`/`modified` field at all -- confirmed against the live
167
187
  * registry). Still bounded well under the full unabbreviated document's
@@ -76,6 +76,7 @@ describe("HttpRegistry", () => {
76
76
  },
77
77
  });
78
78
  }
79
+ if (url.pathname === "/pi-lsp") return Response.json({ readme: `# pi-lsp\n\n${"r".repeat(60_000)}` });
79
80
  return new Response("nf", { status: 404 });
80
81
  },
81
82
  });
@@ -103,8 +104,10 @@ describe("HttpRegistry", () => {
103
104
  repository: "git+https://github.com/x/pi-lsp.git",
104
105
  license: "MIT",
105
106
  unpackedSize: 12345,
106
- readmeAvailable: false,
107
+ readmeAvailable: true,
107
108
  });
109
+ expect(info.readme).toHaveLength(50_000);
110
+ expect(info.readme).toStartWith("# pi-lsp");
108
111
  expect(info.pi?.extensions).toBeDefined();
109
112
  expect(info.packageEvidence).toMatchObject({ shape: "manifest", verified: false });
110
113
  expect(info.publication).toEqual({
@@ -0,0 +1,102 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from "bun:test";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import type { Server } from "bun";
6
+ import type { Installer, UpdateOutcome } from "../src/packages/package.ts";
7
+ import { PackedClient } from "../src/public/client.ts";
8
+ import { HttpRegistry } from "../src/registry/registry.ts";
9
+ import { createApp } from "../src/daemon/service.ts";
10
+
11
+ const TOKEN = "e".repeat(64);
12
+ const PUBLISHED_README = `# @danypops/enigma
13
+
14
+ Encrypted credential vault and supervisor daemon. Holds delegated OAuth/API
15
+ credentials for other daemons and injects them as environment variables into
16
+ spawned child processes.
17
+
18
+ ## Why this exists
19
+
20
+ Consumers receive only their scoped credentials.`;
21
+
22
+ class NoopInstaller implements Installer {
23
+ async install(): Promise<string> {
24
+ throw new Error("not exercised");
25
+ }
26
+ async remove(): Promise<string> {
27
+ throw new Error("not exercised");
28
+ }
29
+ async update(): Promise<UpdateOutcome> {
30
+ throw new Error("not exercised");
31
+ }
32
+ }
33
+
34
+ describe("published npm metadata E2E", () => {
35
+ let npmServer: Server<undefined>;
36
+ let daemonServer: Server<undefined>;
37
+ let stateDir: string;
38
+ const npmRequests: Array<{ path: string; accept: string | null }> = [];
39
+
40
+ beforeAll(() => {
41
+ stateDir = mkdtempSync(join(tmpdir(), "packed-npm-metadata-"));
42
+ npmServer = Bun.serve({
43
+ hostname: "127.0.0.1",
44
+ port: 0,
45
+ fetch(request) {
46
+ const url = new URL(request.url);
47
+ npmRequests.push({ path: url.pathname, accept: request.headers.get("accept") });
48
+ if (url.pathname.endsWith("/latest")) {
49
+ return Response.json({
50
+ name: "@danypops/enigma",
51
+ version: "0.22.1",
52
+ description: "Encrypted credential vault daemon",
53
+ license: "MIT",
54
+ repository: { type: "git", url: "git+https://github.com/DanyPops/enigma.git" },
55
+ dist: { integrity: "sha512-published-fixture" },
56
+ });
57
+ }
58
+ if (url.pathname === "/%40danypops/enigma") {
59
+ return Response.json({
60
+ name: "@danypops/enigma",
61
+ "dist-tags": { latest: "0.22.1" },
62
+ readme: PUBLISHED_README,
63
+ });
64
+ }
65
+ return new Response("not found", { status: 404 });
66
+ },
67
+ });
68
+ const app = createApp({
69
+ reg: new HttpRegistry(`http://127.0.0.1:${npmServer.port}`, 20, 0, 1),
70
+ inst: new NoopInstaller(),
71
+ token: TOKEN,
72
+ stateDir,
73
+ });
74
+ daemonServer = Bun.serve({ hostname: "127.0.0.1", port: 0, fetch: app.fetch });
75
+ });
76
+
77
+ afterAll(() => {
78
+ daemonServer.stop(true);
79
+ npmServer.stop(true);
80
+ rmSync(stateDir, { recursive: true, force: true });
81
+ });
82
+
83
+ it("preserves an npm-published README through HttpRegistry, daemon RPC, and PackedClient", async () => {
84
+ const client = new PackedClient(`http://127.0.0.1:${daemonServer.port}`, TOKEN);
85
+ const info = await client.info("@danypops/enigma");
86
+
87
+ expect(npmRequests).toHaveLength(2);
88
+ expect(npmRequests).toEqual(
89
+ expect.arrayContaining([
90
+ { path: "/%40danypops/enigma/latest", accept: "application/json" },
91
+ { path: "/%40danypops/enigma", accept: "application/json" },
92
+ ]),
93
+ );
94
+ expect(info).toMatchObject({
95
+ name: "@danypops/enigma",
96
+ version: "0.22.1",
97
+ license: "MIT",
98
+ readmeAvailable: true,
99
+ readme: PUBLISHED_README,
100
+ });
101
+ });
102
+ });