@percher/shared 0.1.2 → 0.1.4

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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Valid app name: 3–40 chars, lowercase, starts with a letter,
3
+ * [a-z0-9-] only. App names become subdomains (name.percher.run),
4
+ * so changing this changes what hostnames the platform accepts.
5
+ */
6
+ export declare const APP_NAME_RE: RegExp;
7
+ //# sourceMappingURL=app-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-name.d.ts","sourceRoot":"","sources":["../src/app-name.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,WAAW,QAA2B,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Valid app name: 3–40 chars, lowercase, starts with a letter,
3
+ * [a-z0-9-] only. App names become subdomains (name.percher.run),
4
+ * so changing this changes what hostnames the platform accepts.
5
+ */
6
+ export const APP_NAME_RE = /^[a-z][a-z0-9-]{2,39}$/;
7
+ //# sourceMappingURL=app-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-name.js","sourceRoot":"","sources":["../src/app-name.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,wBAAwB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
+ export { APP_NAME_RE } from "./app-name";
2
+ export { MAX_TARBALL_BYTES } from "./limits";
1
3
  export { MAX_LOG_SEARCH_LENGTH } from "./logs";
4
+ export { canonicalInjectedKey, isPercherInjected, PERCHER_INJECTED_ENV_KEYS, } from "./platform-env";
2
5
  export { DEFAULT_NODE_VERSION_LABEL, DEFAULT_NODE_VERSION_MAJOR } from "./runtime";
3
6
  export { TIMEOUTS, type TimeoutKey } from "./timeouts";
4
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
+ export { APP_NAME_RE } from "./app-name";
2
+ export { MAX_TARBALL_BYTES } from "./limits";
1
3
  export { MAX_LOG_SEARCH_LENGTH } from "./logs";
4
+ export { canonicalInjectedKey, isPercherInjected, PERCHER_INJECTED_ENV_KEYS, } from "./platform-env";
2
5
  export { DEFAULT_NODE_VERSION_LABEL, DEFAULT_NODE_VERSION_MAJOR } from "./runtime";
3
6
  export { TIMEOUTS } from "./timeouts";
4
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAAE,QAAQ,EAAmB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAAE,QAAQ,EAAmB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Max accepted compressed app-tarball size in bytes (500 MB). Enforced
3
+ * server-side on upload (Content-Length pre-check + streamed byte cap)
4
+ * and on GitHub/Forgejo repo imports; the CLI pre-checks before upload.
5
+ * Distinct from the decompressed extraction cap in the API's tarball
6
+ * extractor, which guards against zip bombs.
7
+ */
8
+ export declare const MAX_TARBALL_BYTES: number;
9
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,QAAoB,CAAC"}
package/dist/limits.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Max accepted compressed app-tarball size in bytes (500 MB). Enforced
3
+ * server-side on upload (Content-Length pre-check + streamed byte cap)
4
+ * and on GitHub/Forgejo repo imports; the CLI pre-checks before upload.
5
+ * Distinct from the decompressed extraction cap in the API's tarball
6
+ * extractor, which guards against zip bombs.
7
+ */
8
+ export const MAX_TARBALL_BYTES = 500 * 1024 * 1024;
9
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Env keys Percher injects into every user container at runtime. Treating
3
+ * these as "missing from your private settings" or "undeclared in
4
+ * percher.toml" produces false positives — the user can't (and shouldn't)
5
+ * set them; the platform does. Three consumers must stay locked together:
6
+ *
7
+ * - the source-scan classifier (apps/dashboard env-scan UI hides these)
8
+ * - the env gate (packages/api/src/pipeline/env-gate.ts auto-passes them)
9
+ * - the AI agent contract (docs/CLAUDE.md "Do NOT include" list)
10
+ *
11
+ * Pre-2026-05-29 each consumer kept its own list and they drifted — the
12
+ * env gate had {NODE_ENV, PORT, DATABASE_URL, POCKETBASE_*, PERCHER_APP_*}
13
+ * but the dashboard "Scan code" UI had nothing, so it surfaced every
14
+ * single one as "Referenced in code but missing from your private
15
+ * settings" and panicked users into setting bogus values.
16
+ *
17
+ * Keep this list in lockstep with docs/CLAUDE.md → "Do NOT include
18
+ * (Percher sets these automatically)" and the egress-proxy wiring
19
+ * documented under "Outbound HTTPS".
20
+ */
21
+ export declare const PERCHER_INJECTED_ENV_KEYS: ReadonlySet<string>;
22
+ /**
23
+ * True iff Percher injects this env key into every user container.
24
+ *
25
+ * Case-aware for proxy variants only — POSIX env vars are case-sensitive
26
+ * in general, but HTTP-proxy libraries by convention accept both cases.
27
+ * We do NOT case-fold arbitrary keys (PATH vs path are genuinely
28
+ * different envs to most software).
29
+ */
30
+ export declare function isPercherInjected(key: string): boolean;
31
+ /**
32
+ * Canonical (uppercase) form of an injected proxy variant, or the key
33
+ * unchanged. Used by the scan classifier to collapse `http_proxy` +
34
+ * `HTTP_PROXY` into a single entry.
35
+ */
36
+ export declare function canonicalInjectedKey(key: string): string;
37
+ //# sourceMappingURL=platform-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-env.d.ts","sourceRoot":"","sources":["../src/platform-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAqBxD,CAAC;AAcH;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAItD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGxD"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Env keys Percher injects into every user container at runtime. Treating
3
+ * these as "missing from your private settings" or "undeclared in
4
+ * percher.toml" produces false positives — the user can't (and shouldn't)
5
+ * set them; the platform does. Three consumers must stay locked together:
6
+ *
7
+ * - the source-scan classifier (apps/dashboard env-scan UI hides these)
8
+ * - the env gate (packages/api/src/pipeline/env-gate.ts auto-passes them)
9
+ * - the AI agent contract (docs/CLAUDE.md "Do NOT include" list)
10
+ *
11
+ * Pre-2026-05-29 each consumer kept its own list and they drifted — the
12
+ * env gate had {NODE_ENV, PORT, DATABASE_URL, POCKETBASE_*, PERCHER_APP_*}
13
+ * but the dashboard "Scan code" UI had nothing, so it surfaced every
14
+ * single one as "Referenced in code but missing from your private
15
+ * settings" and panicked users into setting bogus values.
16
+ *
17
+ * Keep this list in lockstep with docs/CLAUDE.md → "Do NOT include
18
+ * (Percher sets these automatically)" and the egress-proxy wiring
19
+ * documented under "Outbound HTTPS".
20
+ */
21
+ export const PERCHER_INJECTED_ENV_KEYS = new Set([
22
+ // Container fundamentals
23
+ "NODE_ENV",
24
+ "PORT",
25
+ // Data backends (mode-specific but always one or the other; both safe
26
+ // to list — a key that's never set in the container still doesn't
27
+ // belong in the user's "private settings" UI)
28
+ "DATABASE_URL",
29
+ "POCKETBASE_URL",
30
+ "POCKETBASE_ADMIN_EMAIL",
31
+ "POCKETBASE_ADMIN_PASSWORD",
32
+ // App identity
33
+ "PERCHER_APP_NAME",
34
+ "PERCHER_APP_URL",
35
+ // Egress proxy (Percher injects these so app code can reach the
36
+ // public internet through the forward proxy — see docs/CLAUDE.md
37
+ // "Outbound HTTPS")
38
+ "HTTP_PROXY",
39
+ "HTTPS_PROXY",
40
+ "NO_PROXY",
41
+ "NODE_USE_ENV_PROXY",
42
+ ]);
43
+ /**
44
+ * Lowercase proxy variants that libraries (curl, requests, undici-via-
45
+ * NODE_USE_ENV_PROXY) accept interchangeably with the uppercase form.
46
+ * Treated as the same env at classification time so the scan UI doesn't
47
+ * show duplicate "missing" rows for `HTTP_PROXY` and `http_proxy`.
48
+ */
49
+ const PROXY_LOWERCASE_ALIASES = new Set([
50
+ "http_proxy",
51
+ "https_proxy",
52
+ "no_proxy",
53
+ ]);
54
+ /**
55
+ * True iff Percher injects this env key into every user container.
56
+ *
57
+ * Case-aware for proxy variants only — POSIX env vars are case-sensitive
58
+ * in general, but HTTP-proxy libraries by convention accept both cases.
59
+ * We do NOT case-fold arbitrary keys (PATH vs path are genuinely
60
+ * different envs to most software).
61
+ */
62
+ export function isPercherInjected(key) {
63
+ if (PERCHER_INJECTED_ENV_KEYS.has(key))
64
+ return true;
65
+ if (PROXY_LOWERCASE_ALIASES.has(key))
66
+ return true;
67
+ return false;
68
+ }
69
+ /**
70
+ * Canonical (uppercase) form of an injected proxy variant, or the key
71
+ * unchanged. Used by the scan classifier to collapse `http_proxy` +
72
+ * `HTTP_PROXY` into a single entry.
73
+ */
74
+ export function canonicalInjectedKey(key) {
75
+ if (PROXY_LOWERCASE_ALIASES.has(key))
76
+ return key.toUpperCase();
77
+ return key;
78
+ }
79
+ //# sourceMappingURL=platform-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-env.js","sourceRoot":"","sources":["../src/platform-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAwB,IAAI,GAAG,CAAS;IAC5E,yBAAyB;IACzB,UAAU;IACV,MAAM;IACN,sEAAsE;IACtE,kEAAkE;IAClE,8CAA8C;IAC9C,cAAc;IACd,gBAAgB;IAChB,wBAAwB;IACxB,2BAA2B;IAC3B,eAAe;IACf,kBAAkB;IAClB,iBAAiB;IACjB,gEAAgE;IAChE,iEAAiE;IACjE,oBAAoB;IACpB,YAAY;IACZ,aAAa;IACb,UAAU;IACV,oBAAoB;CACrB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAS;IACnE,YAAY;IACZ,aAAa;IACb,UAAU;CACX,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;IAC/D,OAAO,GAAG,CAAC;AACb,CAAC"}
package/dist/pow.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Proof-of-work for the headless anonymous-deploy gate (FUTURE23 Spår A
3
+ * hardening). A solution is any string such that
4
+ * `sha256(`${nonce}:${solution}`)` has at least `difficulty` leading zero
5
+ * bits. Lives in @percher/shared so the API verifies with the EXACT algorithm
6
+ * the CLI/MCP client solves — a drift between the two would silently reject
7
+ * every legitimate publish or accept unsolved ones.
8
+ *
9
+ * A browser CAPTCHA can't gate this endpoint: the caller is a headless agent
10
+ * or CLI. PoW is the headless equivalent — the client spends CPU instead of a
11
+ * human solving a puzzle, so a botnet pays a real cost per app regardless of
12
+ * how many IPs it controls.
13
+ */
14
+ export declare const POW_ALGORITHM = "sha256-leading-zero-bits";
15
+ /** Count leading zero bits in a hex digest. */
16
+ export declare function leadingZeroBits(hexDigest: string): number;
17
+ /** True if `solution` meets `difficulty` for `nonce`. difficulty<=0 is a no-op. */
18
+ export declare function verifyPow(nonce: string, solution: string, difficulty: number): boolean;
19
+ /**
20
+ * Brute-force a solution. Returns the first hit, or null if `maxIterations` is
21
+ * exhausted (guards against a pathological difficulty hanging the client).
22
+ */
23
+ export declare function solvePow(nonce: string, difficulty: number, maxIterations?: number): string | null;
24
+ //# sourceMappingURL=pow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pow.d.ts","sourceRoot":"","sources":["../src/pow.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,6BAA6B,CAAC;AAMxD,+CAA+C;AAC/C,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAazD;AAED,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAItF;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,aAAa,SAAa,GACzB,MAAM,GAAG,IAAI,CAOf"}
package/dist/pow.js ADDED
@@ -0,0 +1,56 @@
1
+ import { createHash } from "node:crypto";
2
+ /**
3
+ * Proof-of-work for the headless anonymous-deploy gate (FUTURE23 Spår A
4
+ * hardening). A solution is any string such that
5
+ * `sha256(`${nonce}:${solution}`)` has at least `difficulty` leading zero
6
+ * bits. Lives in @percher/shared so the API verifies with the EXACT algorithm
7
+ * the CLI/MCP client solves — a drift between the two would silently reject
8
+ * every legitimate publish or accept unsolved ones.
9
+ *
10
+ * A browser CAPTCHA can't gate this endpoint: the caller is a headless agent
11
+ * or CLI. PoW is the headless equivalent — the client spends CPU instead of a
12
+ * human solving a puzzle, so a botnet pays a real cost per app regardless of
13
+ * how many IPs it controls.
14
+ */
15
+ export const POW_ALGORITHM = "sha256-leading-zero-bits";
16
+ function digest(nonce, solution) {
17
+ return createHash("sha256").update(`${nonce}:${solution}`).digest("hex");
18
+ }
19
+ /** Count leading zero bits in a hex digest. */
20
+ export function leadingZeroBits(hexDigest) {
21
+ let bits = 0;
22
+ for (const ch of hexDigest) {
23
+ const nibble = Number.parseInt(ch, 16);
24
+ if (nibble === 0) {
25
+ bits += 4;
26
+ continue;
27
+ }
28
+ // clz32(n) - 28 = leading zeros of a 4-bit value (1..15).
29
+ bits += Math.clz32(nibble) - 28;
30
+ break;
31
+ }
32
+ return bits;
33
+ }
34
+ /** True if `solution` meets `difficulty` for `nonce`. difficulty<=0 is a no-op. */
35
+ export function verifyPow(nonce, solution, difficulty) {
36
+ if (difficulty <= 0)
37
+ return true;
38
+ if (!solution)
39
+ return false;
40
+ return leadingZeroBits(digest(nonce, solution)) >= difficulty;
41
+ }
42
+ /**
43
+ * Brute-force a solution. Returns the first hit, or null if `maxIterations` is
44
+ * exhausted (guards against a pathological difficulty hanging the client).
45
+ */
46
+ export function solvePow(nonce, difficulty, maxIterations = 50_000_000) {
47
+ if (difficulty <= 0)
48
+ return "0";
49
+ for (let i = 0; i < maxIterations; i++) {
50
+ const solution = i.toString(36);
51
+ if (leadingZeroBits(digest(nonce, solution)) >= difficulty)
52
+ return solution;
53
+ }
54
+ return null;
55
+ }
56
+ //# sourceMappingURL=pow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pow.js","sourceRoot":"","sources":["../src/pow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAExD,SAAS,MAAM,CAAC,KAAa,EAAE,QAAgB;IAC7C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,CAAC;YACV,SAAS;QACX,CAAC;QACD,0DAA0D;QAC1D,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM;IACR,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,QAAgB,EAAE,UAAkB;IAC3E,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,IAAI,UAAU,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAa,EACb,UAAkB,EAClB,aAAa,GAAG,UAAU;IAE1B,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,IAAI,UAAU;YAAE,OAAO,QAAQ,CAAC;IAC9E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percher/shared",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Shared constants and contracts (timeouts, etc.) used across the Percher CLI, API, MCP, and worker. Internal — values change in lockstep across packages.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,6 +20,16 @@
20
20
  "types": "./dist/runtime.d.ts",
21
21
  "import": "./dist/runtime.js",
22
22
  "default": "./dist/runtime.js"
23
+ },
24
+ "./platform-env": {
25
+ "types": "./dist/platform-env.d.ts",
26
+ "import": "./dist/platform-env.js",
27
+ "default": "./dist/platform-env.js"
28
+ },
29
+ "./pow": {
30
+ "types": "./dist/pow.d.ts",
31
+ "import": "./dist/pow.js",
32
+ "default": "./dist/pow.js"
23
33
  }
24
34
  },
25
35
  "files": [