@principles/host-runtime 0.5.0
“Shared host-neutral orchestration for Principles Disciple MVP-Core hook paths.”
Calls spawnSync() to run a child processExample uses a placeholder target or callback
SHA-256e641c845d30e1370ad469fe53f8ae7d6b02086ef914099596c2552e475bfff0a
MaleculeH₂(CmPo₂)
Evidence
6:29… (SPEC §34): codes only, never messages, stacks,
7 * or response bodies. The injectable fetchFn keeps unit tests offline.
8 */
9export const DEFAULT_PRODUCT_TELEMETRY_ENDPOINT = 'https://principles-website.pages.dev/api/product-telemetry/snapshot';
10export const PRODUCT_TELEMETRY_TIMEOUT_MS = 8000;
11export const PRODUCT_TELEMETRY_MAX_BODY_BYTES = 4096;
12function defaultFetchFn(url, init) {
13 // fetch must be invoked against globalThis inside workers/gateway runti …
18:30… throws; every failure maps to a coarse code
19 * with a retryable hint the service uses for backoff.
20 */
21export async function exportSnapshot(args) {
22 const endpoint = args.endpoint ?? DEFAULT_PRODUCT_TELEMETRY_ENDPOINT;
23 const timeoutMs = args.timeoutMs ?? PRODUCT_TELEMETRY_TIMEOUT_MS;
24 const fetchFn = args.fetchFn ?? defaultFetchFn;
25 const body = JSON.stringify(args.snapshot);
26 if (Buffer.byteLength(body, 'utf8') > PRODUCT_TELEMETRY_MAX_BODY_BYTES) {
27 // Safety net: the strict …
31 const controller = new AbortController();
32 const timer = setTimeout(() => controller.abort(), timeoutMs);
33 try {
34 const response = await fetchFn(endpoint, {
35 method: 'POST',
36 headers: { 'content-type': 'application/json' },
37 body,
38 signal: controller.signal,
39 });
40 const { status } = response;
41 if (statu …
45:19… { ok: false, code: 'http_400', retryable: false };
46 if (status === 429)
47 return { ok: false, code: 'http_429', retryable: true };
48 if (status >= 500)
49 return { ok: false …
1import { spawnSync } from 'node:child_process';
2export const RULE_BATCH_TIMEOUT_MS = 2_500;
3export const RULE_BATCH_OUTPUT_BYTES = 64 * 1024;
4export const RULE_SOURCE_BYTES = 64 * 1024;
5export const RULE_BATCH_SOURCE_BYTES = 256 * 1024;
6export const MAX_ACTIVE_RULES = 32;
7const EVALUATION_PROCESS_SOURCE = String.raw `
8const vm = require('node:vm');
9const fs = require('node:fs');
10function normalize(sourceCode) {
11 const withoutExports = sourceCode
12 .replace(/export\s+const\s+meta\s*=/, 'const meta =')
13 .replace(/export\s+function\s+evaluate\s*\(/, 'function evaluate(');
14 return withoutExports + '\nglobalThis.__pdRuleModule = { meta: typeof meta === "undefined" ? undefined : meta, evaluate: typeof evaluate === "undefined" ? undefined : evaluate };';
15}
16function preview(error) { return String(error).slice(0, 500); }
17try {
18 const workerData = JSON.parse(fs.readFileSync(0, 'utf8'));
19 const results = [];
20 for (const rule of workerData.rules) {
21 try {
22 const context = vm.createContext(Object.create(null));
23 new vm.Script(normalize(rule.source), { filename: rule.filename }).runInContext(context, { timeout: 1000 });
24 if (!context.__pdRuleModule || typeof context.__pdRuleModule.evaluate !== 'function') throw new Error('compiled module has no evaluate function');
25 // Sandbox boundary: hand the context ONLY a primitive JSON string and
26 // build the call input + helpers with the context's own intrinsics.
27 // Passing host-realm objects (the parsed input, a host-built helpers
28 // object) would let rule source walk .constructor.constructor back to
29 // the host realm's Function constructor and reach the child process
30 // and require — a full sandbox escape with user privileges.
31 context.__pdInputJson = JSON.stringify(workerData.input);
32 new vm.Script(
33 'const __pdCallInput = JSON.parse(globalThis.__pdInputJson);' +
34 'delete globalThis.__pdInputJson;' +
35 'const __pdCallHelpers = Object.freeze({' +
36 ' isRiskPath: () => __pdCallInput.workspace.isRiskPath,' +
⋯4 lines
41 '});', { filename: rule.filename + '.bootstrap' }
42 ).runInContext(context, { timeout: 1000 });
43 const result = new vm.Script('__pdRuleModule.evaluate(__pdCallInput, __pdCallHelpers)', { filename: rule.filename + '.call' })
44 .runInContext(context, { timeout: 1000 });
45 results.push({ ok: true, result });
46 } catch (error) {
47 results.push({ ok: false, error: preview(error) });
48 }
No evidence locations were recorded for this file. Raw result