@wrenai/genbi 0.0.1
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/LICENSE +201 -0
- package/README.md +141 -0
- package/bin/genbi.mjs +31 -0
- package/dist/assets/echarts-LTFC-ysd.js +40 -0
- package/dist/assets/index-Bo--QMUN.js +441 -0
- package/dist/assets/index-Xs4q7pNo.css +1 -0
- package/dist/index.html +14 -0
- package/dist/wrenai_logo.png +0 -0
- package/dist/wrenai_logo_white.png +0 -0
- package/dist-server/harness/auth/detect.d.ts +18 -0
- package/dist-server/harness/auth/detect.js +37 -0
- package/dist-server/harness/auth/index.d.ts +4 -0
- package/dist-server/harness/auth/index.js +2 -0
- package/dist-server/harness/auth/probe.d.ts +18 -0
- package/dist-server/harness/auth/probe.js +52 -0
- package/dist-server/harness/auth/types.d.ts +42 -0
- package/dist-server/harness/auth/types.js +9 -0
- package/dist-server/harness/bundle/loader.d.ts +32 -0
- package/dist-server/harness/bundle/loader.js +43 -0
- package/dist-server/harness/bundle/schema.d.ts +199 -0
- package/dist-server/harness/bundle/schema.js +127 -0
- package/dist-server/harness/bundle/version.d.ts +10 -0
- package/dist-server/harness/bundle/version.js +43 -0
- package/dist-server/harness/capability/gate.d.ts +6 -0
- package/dist-server/harness/capability/gate.js +29 -0
- package/dist-server/harness/capability/registry.d.ts +6 -0
- package/dist-server/harness/capability/registry.js +17 -0
- package/dist-server/harness/cli-args.d.ts +123 -0
- package/dist-server/harness/cli-args.js +247 -0
- package/dist-server/harness/cli.d.ts +2 -0
- package/dist-server/harness/cli.js +151 -0
- package/dist-server/harness/compile/cache.d.ts +23 -0
- package/dist-server/harness/compile/cache.js +109 -0
- package/dist-server/harness/compile/compose-profile.d.ts +34 -0
- package/dist-server/harness/compile/compose-profile.js +95 -0
- package/dist-server/harness/compile/errors.d.ts +22 -0
- package/dist-server/harness/compile/errors.js +41 -0
- package/dist-server/harness/compile/fingerprint.d.ts +22 -0
- package/dist-server/harness/compile/fingerprint.js +84 -0
- package/dist-server/harness/compile/index.d.ts +8 -0
- package/dist-server/harness/compile/index.js +7 -0
- package/dist-server/harness/compile/pipeline.d.ts +66 -0
- package/dist-server/harness/compile/pipeline.js +176 -0
- package/dist-server/harness/compile/resolve-binary.d.ts +55 -0
- package/dist-server/harness/compile/resolve-binary.js +133 -0
- package/dist-server/harness/compile/types.d.ts +107 -0
- package/dist-server/harness/compile/types.js +1 -0
- package/dist-server/harness/compile/warble-identity.d.ts +12 -0
- package/dist-server/harness/compile/warble-identity.js +33 -0
- package/dist-server/harness/compliance/gate.d.ts +35 -0
- package/dist-server/harness/compliance/gate.js +48 -0
- package/dist-server/harness/compliance/index.d.ts +2 -0
- package/dist-server/harness/compliance/index.js +1 -0
- package/dist-server/harness/compliance/types.d.ts +16 -0
- package/dist-server/harness/compliance/types.js +1 -0
- package/dist-server/harness/events/emitter.d.ts +34 -0
- package/dist-server/harness/events/emitter.js +25 -0
- package/dist-server/harness/events/index.d.ts +4 -0
- package/dist-server/harness/events/index.js +2 -0
- package/dist-server/harness/events/sse.d.ts +20 -0
- package/dist-server/harness/events/sse.js +48 -0
- package/dist-server/harness/events/types.d.ts +131 -0
- package/dist-server/harness/events/types.js +1 -0
- package/dist-server/harness/exec/errors.d.ts +16 -0
- package/dist-server/harness/exec/errors.js +29 -0
- package/dist-server/harness/exec/index.d.ts +5 -0
- package/dist-server/harness/exec/index.js +2 -0
- package/dist-server/harness/exec/local.d.ts +32 -0
- package/dist-server/harness/exec/local.js +134 -0
- package/dist-server/harness/exec/policy.d.ts +12 -0
- package/dist-server/harness/exec/policy.js +1 -0
- package/dist-server/harness/exec/types.d.ts +63 -0
- package/dist-server/harness/exec/types.js +1 -0
- package/dist-server/harness/guardrails/index.d.ts +2 -0
- package/dist-server/harness/guardrails/index.js +1 -0
- package/dist-server/harness/guardrails/policy.d.ts +45 -0
- package/dist-server/harness/guardrails/policy.js +57 -0
- package/dist-server/harness/index.d.ts +37 -0
- package/dist-server/harness/index.js +19 -0
- package/dist-server/harness/loop/errors.d.ts +53 -0
- package/dist-server/harness/loop/errors.js +72 -0
- package/dist-server/harness/loop/executor.d.ts +123 -0
- package/dist-server/harness/loop/executor.js +380 -0
- package/dist-server/harness/loop/guard.d.ts +16 -0
- package/dist-server/harness/loop/guard.js +1 -0
- package/dist-server/harness/loop/index.d.ts +5 -0
- package/dist-server/harness/loop/index.js +3 -0
- package/dist-server/harness/native-producer/index.d.ts +109 -0
- package/dist-server/harness/native-producer/index.js +302 -0
- package/dist-server/harness/npm-package-resolve.d.ts +28 -0
- package/dist-server/harness/npm-package-resolve.js +47 -0
- package/dist-server/harness/providers/adapters/anthropic.d.ts +7 -0
- package/dist-server/harness/providers/adapters/anthropic.js +6 -0
- package/dist-server/harness/providers/adapters/mock.d.ts +4 -0
- package/dist-server/harness/providers/adapters/mock.js +5 -0
- package/dist-server/harness/providers/adapters/openai-compatible.d.ts +10 -0
- package/dist-server/harness/providers/adapters/openai-compatible.js +10 -0
- package/dist-server/harness/providers/binding.d.ts +23 -0
- package/dist-server/harness/providers/binding.js +13 -0
- package/dist-server/harness/providers/errors.d.ts +6 -0
- package/dist-server/harness/providers/errors.js +12 -0
- package/dist-server/harness/providers/index.d.ts +18 -0
- package/dist-server/harness/providers/index.js +22 -0
- package/dist-server/harness/providers/registry.d.ts +16 -0
- package/dist-server/harness/providers/registry.js +24 -0
- package/dist-server/harness/render/envelope.d.ts +145 -0
- package/dist-server/harness/render/envelope.js +428 -0
- package/dist-server/harness/render/errors.d.ts +33 -0
- package/dist-server/harness/render/errors.js +46 -0
- package/dist-server/harness/render/index.d.ts +5 -0
- package/dist-server/harness/render/index.js +3 -0
- package/dist-server/harness/render/validate.d.ts +13 -0
- package/dist-server/harness/render/validate.js +88 -0
- package/dist-server/harness/route/adapter-spec.d.ts +40 -0
- package/dist-server/harness/route/adapter-spec.js +61 -0
- package/dist-server/harness/route/agent-sdk-cli.d.ts +30 -0
- package/dist-server/harness/route/agent-sdk-cli.js +86 -0
- package/dist-server/harness/route/agent-sdk-manifest.d.ts +31 -0
- package/dist-server/harness/route/agent-sdk-manifest.js +56 -0
- package/dist-server/harness/route/artifact-content.d.ts +53 -0
- package/dist-server/harness/route/artifact-content.js +83 -0
- package/dist-server/harness/route/chat-event-mapper.d.ts +95 -0
- package/dist-server/harness/route/chat-event-mapper.js +164 -0
- package/dist-server/harness/route/codex-ask-events.d.ts +25 -0
- package/dist-server/harness/route/codex-ask-events.js +395 -0
- package/dist-server/harness/route/codex-ask.d.ts +15 -0
- package/dist-server/harness/route/codex-ask.js +242 -0
- package/dist-server/harness/route/codex-local-cli.d.ts +20 -0
- package/dist-server/harness/route/codex-local-cli.js +78 -0
- package/dist-server/harness/route/codex-local-manifest.d.ts +22 -0
- package/dist-server/harness/route/codex-local-manifest.js +180 -0
- package/dist-server/harness/route/describe.d.ts +54 -0
- package/dist-server/harness/route/describe.js +84 -0
- package/dist-server/harness/route/dispatched.d.ts +138 -0
- package/dist-server/harness/route/dispatched.js +339 -0
- package/dist-server/harness/route/in-process.d.ts +43 -0
- package/dist-server/harness/route/in-process.js +106 -0
- package/dist-server/harness/route/index.d.ts +19 -0
- package/dist-server/harness/route/index.js +12 -0
- package/dist-server/harness/route/profile-source.d.ts +37 -0
- package/dist-server/harness/route/profile-source.js +76 -0
- package/dist-server/harness/route/route.d.ts +33 -0
- package/dist-server/harness/route/route.js +114 -0
- package/dist-server/harness/route/tier-binding.d.ts +36 -0
- package/dist-server/harness/route/tier-binding.js +63 -0
- package/dist-server/harness/route/types.d.ts +286 -0
- package/dist-server/harness/route/types.js +1 -0
- package/dist-server/harness/session/errors.d.ts +4 -0
- package/dist-server/harness/session/errors.js +7 -0
- package/dist-server/harness/session/gate.d.ts +11 -0
- package/dist-server/harness/session/gate.js +13 -0
- package/dist-server/harness/session/index.d.ts +5 -0
- package/dist-server/harness/session/index.js +3 -0
- package/dist-server/harness/session/run.d.ts +60 -0
- package/dist-server/harness/session/run.js +333 -0
- package/dist-server/harness/session/types.d.ts +29 -0
- package/dist-server/harness/session/types.js +1 -0
- package/dist-server/harness/setup/codex-events.d.ts +12 -0
- package/dist-server/harness/setup/codex-events.js +108 -0
- package/dist-server/harness/setup/runner.d.ts +497 -0
- package/dist-server/harness/setup/runner.js +1314 -0
- package/dist-server/harness/tools/errors.d.ts +107 -0
- package/dist-server/harness/tools/errors.js +141 -0
- package/dist-server/harness/tools/index.d.ts +13 -0
- package/dist-server/harness/tools/index.js +8 -0
- package/dist-server/harness/tools/mcp.d.ts +26 -0
- package/dist-server/harness/tools/mcp.js +15 -0
- package/dist-server/harness/tools/native.d.ts +179 -0
- package/dist-server/harness/tools/native.js +322 -0
- package/dist-server/harness/tools/parse.d.ts +14 -0
- package/dist-server/harness/tools/parse.js +18 -0
- package/dist-server/harness/tools/resolve-wren-binary.d.ts +12 -0
- package/dist-server/harness/tools/resolve-wren-binary.js +21 -0
- package/dist-server/harness/tools/resolve.d.ts +51 -0
- package/dist-server/harness/tools/resolve.js +79 -0
- package/dist-server/harness/tools/setup-native.d.ts +84 -0
- package/dist-server/harness/tools/setup-native.js +353 -0
- package/dist-server/harness/tools/wiring.d.ts +10 -0
- package/dist-server/harness/tools/wiring.js +16 -0
- package/dist-server/providers/setup.provider.yaml +31 -0
- package/dist-server/providers/wren.provider.yaml +33 -0
- package/dist-server/server/adopt.d.ts +87 -0
- package/dist-server/server/adopt.js +263 -0
- package/dist-server/server/app.d.ts +6 -0
- package/dist-server/server/app.js +2521 -0
- package/dist-server/server/auth-choice.d.ts +16 -0
- package/dist-server/server/auth-choice.js +36 -0
- package/dist-server/server/bin.d.ts +2 -0
- package/dist-server/server/bin.js +601 -0
- package/dist-server/server/bootstrap-project-binding.d.ts +31 -0
- package/dist-server/server/bootstrap-project-binding.js +71 -0
- package/dist-server/server/clarify.d.ts +10 -0
- package/dist-server/server/clarify.js +17 -0
- package/dist-server/server/codex-setup-mcp.d.ts +2 -0
- package/dist-server/server/codex-setup-mcp.js +147 -0
- package/dist-server/server/compose.d.ts +89 -0
- package/dist-server/server/compose.js +271 -0
- package/dist-server/server/conn-config.d.ts +30 -0
- package/dist-server/server/conn-config.js +253 -0
- package/dist-server/server/context-files.d.ts +12 -0
- package/dist-server/server/context-files.js +117 -0
- package/dist-server/server/context-map.d.ts +30 -0
- package/dist-server/server/context-map.js +95 -0
- package/dist-server/server/context-source.d.ts +68 -0
- package/dist-server/server/context-source.js +85 -0
- package/dist-server/server/db.d.ts +862 -0
- package/dist-server/server/db.js +1839 -0
- package/dist-server/server/enrichment-runner.d.ts +184 -0
- package/dist-server/server/enrichment-runner.js +213 -0
- package/dist-server/server/enrichment-verify.d.ts +73 -0
- package/dist-server/server/enrichment-verify.js +290 -0
- package/dist-server/server/enrichment.d.ts +232 -0
- package/dist-server/server/enrichment.js +199 -0
- package/dist-server/server/env-detect.d.ts +8 -0
- package/dist-server/server/env-detect.js +17 -0
- package/dist-server/server/env-field-hints.d.ts +46 -0
- package/dist-server/server/env-field-hints.js +48 -0
- package/dist-server/server/fold.d.ts +133 -0
- package/dist-server/server/fold.js +407 -0
- package/dist-server/server/harness-profile-sources.d.ts +3 -0
- package/dist-server/server/harness-profile-sources.js +24 -0
- package/dist-server/server/harness.d.ts +19 -0
- package/dist-server/server/harness.js +439 -0
- package/dist-server/server/impact.d.ts +18 -0
- package/dist-server/server/impact.js +85 -0
- package/dist-server/server/interactive-terminal.d.ts +175 -0
- package/dist-server/server/interactive-terminal.js +336 -0
- package/dist-server/server/native-artifacts.d.ts +1004 -0
- package/dist-server/server/native-artifacts.js +564 -0
- package/dist-server/server/native-dispatch-registry.d.ts +42 -0
- package/dist-server/server/native-dispatch-registry.js +45 -0
- package/dist-server/server/native-producer-fence.d.ts +14 -0
- package/dist-server/server/native-producer-fence.js +48 -0
- package/dist-server/server/native-session-resume.d.ts +6 -0
- package/dist-server/server/native-session-resume.js +103 -0
- package/dist-server/server/native-session-workspace.d.ts +40 -0
- package/dist-server/server/native-session-workspace.js +175 -0
- package/dist-server/server/native-sessions.d.ts +374 -0
- package/dist-server/server/native-sessions.js +1517 -0
- package/dist-server/server/native-wren-runtime.d.ts +20 -0
- package/dist-server/server/native-wren-runtime.js +259 -0
- package/dist-server/server/node-pty-host.d.ts +31 -0
- package/dist-server/server/node-pty-host.js +100 -0
- package/dist-server/server/route-intent.d.ts +40 -0
- package/dist-server/server/route-intent.js +23 -0
- package/dist-server/server/runtime-binding.d.ts +52 -0
- package/dist-server/server/runtime-binding.js +258 -0
- package/dist-server/server/source-catalog.d.ts +84 -0
- package/dist-server/server/source-catalog.js +221 -0
- package/dist-server/server/source-driver.d.ts +35 -0
- package/dist-server/server/source-driver.js +124 -0
- package/dist-server/server/spa.d.ts +2 -0
- package/dist-server/server/spa.js +51 -0
- package/dist-server/server/subscription-model-catalog.d.ts +27 -0
- package/dist-server/server/subscription-model-catalog.js +203 -0
- package/dist-server/server/turn.d.ts +249 -0
- package/dist-server/server/turn.js +1074 -0
- package/dist-server/server/wire-types.d.ts +799 -0
- package/dist-server/server/wire-types.js +1 -0
- package/dist-server/server/wren-home.d.ts +19 -0
- package/dist-server/server/wren-home.js +33 -0
- package/dist-server/server/wren-profiles.d.ts +43 -0
- package/dist-server/server/wren-profiles.js +123 -0
- package/package.json +93 -0
- package/profiles/genbi-default/context/binding.yml +9 -0
- package/profiles/genbi-default/ir.golden.json +506 -0
- package/profiles/genbi-default/profile.yml +16 -0
- package/profiles/genbi-enrich-context/components/apply_enrichment/component.yml +42 -0
- package/profiles/genbi-enrich-context/components/draft_enrichment/component.yml +30 -0
- package/profiles/genbi-enrich-context/components/draft_enrichment/steps/draft.md +34 -0
- package/profiles/genbi-enrich-context/components/inspect_context/component.yml +32 -0
- package/profiles/genbi-enrich-context/components/inspect_context/steps/inspect.md +13 -0
- package/profiles/genbi-enrich-context/context/binding.yml +4 -0
- package/profiles/genbi-enrich-context/ir.golden.json +302 -0
- package/profiles/genbi-enrich-context/profile.yml +15 -0
- package/profiles/genbi-monitor/context/binding.yml +7 -0
- package/profiles/genbi-monitor/ir.golden.json +188 -0
- package/profiles/genbi-monitor/profile.yml +27 -0
- package/profiles/genbi-setup/components/build_context/component.yml +44 -0
- package/profiles/genbi-setup/components/build_context/steps/build.md +14 -0
- package/profiles/genbi-setup/components/connect_source/component.yml +51 -0
- package/profiles/genbi-setup/components/connect_source/steps/connect.md +23 -0
- package/profiles/genbi-setup/context/binding.yml +8 -0
- package/profiles/genbi-setup/ir.golden.json +140 -0
- package/profiles/genbi-setup/profile.yml +18 -0
- package/profiles/genbi-setup/raw/schema.json +5 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CompileCache } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The default cache root, rooted under the *current user's* home rather than a world-shared
|
|
4
|
+
* fixed path like `os.tmpdir()` — on a multi-user host, a predictable shared tmpdir path lets
|
|
5
|
+
* another local account pre-create the dir and plant a slot for a computable cache key.
|
|
6
|
+
* Respects `XDG_CACHE_HOME` when set, matching the XDG base-dir convention.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveDefaultCacheDir(): string;
|
|
9
|
+
/**
|
|
10
|
+
* The v1 {@link CompileCache} backend: one subdirectory per cache key under `cacheDir`, each
|
|
11
|
+
* holding a copy of `ir.json` and (in-process only) `bundle/bundle.json`. `get` treats a slot whose
|
|
12
|
+
* `ir.json` has since been deleted as a miss rather than throwing, so an external cleanup of
|
|
13
|
+
* `cacheDir` degrades to "just recompile" instead of a hard failure.
|
|
14
|
+
*
|
|
15
|
+
* No TTL/eviction and no crash-consistency guarantee on `set` (a process killed mid-copy can leave
|
|
16
|
+
* a partial slot that a later `get` may treat as a hit) — both are deferred; simple/documented per
|
|
17
|
+
* the task's scope guidance rather than a production-grade cache.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createFileSystemCompileCache(options?: {
|
|
20
|
+
readonly cacheDir?: string;
|
|
21
|
+
}): CompileCache;
|
|
22
|
+
/** A non-persistent {@link CompileCache} for tests/short-lived processes — no filesystem I/O. */
|
|
23
|
+
export declare function createInMemoryCompileCache(): CompileCache;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { cp, mkdir, stat } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
/**
|
|
7
|
+
* Deterministic on-disk slot name for a cache key — order-independent, one directory per (profile,
|
|
8
|
+
* context, mode, provider-fragment-hash, warble-identity, hub-dir). An absent `hubDir` (no
|
|
9
|
+
* `--hub-dir` passed, so warble's compiled-in default applied) hashes as the empty string, which is
|
|
10
|
+
* a distinct value from any real root rather than an alias for one.
|
|
11
|
+
*/
|
|
12
|
+
function keyDigest(key) {
|
|
13
|
+
return createHash("sha256")
|
|
14
|
+
.update(`${key.profileHash}:${key.contextFingerprint}:${key.mode}:${key.providerFragmentHash}:${key.warbleIdentity}:${key.hubDir ?? ""}`)
|
|
15
|
+
.digest("hex");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The default cache root, rooted under the *current user's* home rather than a world-shared
|
|
19
|
+
* fixed path like `os.tmpdir()` — on a multi-user host, a predictable shared tmpdir path lets
|
|
20
|
+
* another local account pre-create the dir and plant a slot for a computable cache key.
|
|
21
|
+
* Respects `XDG_CACHE_HOME` when set, matching the XDG base-dir convention.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveDefaultCacheDir() {
|
|
24
|
+
const xdgCacheHome = process.env.XDG_CACHE_HOME;
|
|
25
|
+
const base = xdgCacheHome !== undefined && xdgCacheHome.trim().length > 0 ? xdgCacheHome : path.join(os.homedir(), ".cache");
|
|
26
|
+
return path.join(base, "wren-harness", "compile");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates the default cache dir (if missing) restricted to the owner (`0700`) so another local
|
|
30
|
+
* account can't read or even traverse into it. `mkdirSync`'s `mode` is subject to the process
|
|
31
|
+
* umask, so `chmodSync` afterwards guarantees `0700` regardless of umask or a looser pre-existing
|
|
32
|
+
* mode. Only applied to the *default* dir — an explicit `cacheDir` override is left exactly as
|
|
33
|
+
* the caller passed it (see `createFileSystemCompileCache`).
|
|
34
|
+
*/
|
|
35
|
+
function createSecureDefaultCacheDir() {
|
|
36
|
+
const dir = resolveDefaultCacheDir();
|
|
37
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
38
|
+
if (process.platform !== "win32") {
|
|
39
|
+
chmodSync(dir, 0o700);
|
|
40
|
+
}
|
|
41
|
+
return dir;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Defense in depth against a planted slot (see `resolveDefaultCacheDir`'s doc comment above): if a slot directory exists but isn't
|
|
45
|
+
* owned by the current process's uid, treat it as untrustworthy and miss rather than trusting its
|
|
46
|
+
* contents. A slot that doesn't exist at all (the common miss case) is NOT a security decision —
|
|
47
|
+
* `stat` failing for any reason falls through to `true` so the normal `existsSync(irPath)` miss
|
|
48
|
+
* path below still runs. Windows has no POSIX uid model, so this is skipped there.
|
|
49
|
+
*/
|
|
50
|
+
async function isOwnedByCurrentUser(slot) {
|
|
51
|
+
if (process.platform === "win32")
|
|
52
|
+
return true;
|
|
53
|
+
const uid = process.getuid?.();
|
|
54
|
+
if (uid === undefined)
|
|
55
|
+
return true;
|
|
56
|
+
try {
|
|
57
|
+
const stats = await stat(slot);
|
|
58
|
+
return stats.uid === uid;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The v1 {@link CompileCache} backend: one subdirectory per cache key under `cacheDir`, each
|
|
66
|
+
* holding a copy of `ir.json` and (in-process only) `bundle/bundle.json`. `get` treats a slot whose
|
|
67
|
+
* `ir.json` has since been deleted as a miss rather than throwing, so an external cleanup of
|
|
68
|
+
* `cacheDir` degrades to "just recompile" instead of a hard failure.
|
|
69
|
+
*
|
|
70
|
+
* No TTL/eviction and no crash-consistency guarantee on `set` (a process killed mid-copy can leave
|
|
71
|
+
* a partial slot that a later `get` may treat as a hit) — both are deferred; simple/documented per
|
|
72
|
+
* the task's scope guidance rather than a production-grade cache.
|
|
73
|
+
*/
|
|
74
|
+
export function createFileSystemCompileCache(options) {
|
|
75
|
+
const cacheDir = options?.cacheDir ?? createSecureDefaultCacheDir();
|
|
76
|
+
return {
|
|
77
|
+
async get(key) {
|
|
78
|
+
const slot = path.join(cacheDir, keyDigest(key));
|
|
79
|
+
if (!(await isOwnedByCurrentUser(slot)))
|
|
80
|
+
return undefined;
|
|
81
|
+
const irPath = path.join(slot, "ir.json");
|
|
82
|
+
if (!existsSync(irPath))
|
|
83
|
+
return undefined;
|
|
84
|
+
const bundlePath = path.join(slot, "bundle", "bundle.json");
|
|
85
|
+
return { irPath, ...(existsSync(bundlePath) ? { bundlePath } : {}) };
|
|
86
|
+
},
|
|
87
|
+
async set(key, entry) {
|
|
88
|
+
const slot = path.join(cacheDir, keyDigest(key));
|
|
89
|
+
await mkdir(slot, { recursive: true });
|
|
90
|
+
await cp(entry.irPath, path.join(slot, "ir.json"));
|
|
91
|
+
if (entry.bundlePath !== undefined) {
|
|
92
|
+
await mkdir(path.join(slot, "bundle"), { recursive: true });
|
|
93
|
+
await cp(entry.bundlePath, path.join(slot, "bundle", "bundle.json"));
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** A non-persistent {@link CompileCache} for tests/short-lived processes — no filesystem I/O. */
|
|
99
|
+
export function createInMemoryCompileCache() {
|
|
100
|
+
const store = new Map();
|
|
101
|
+
return {
|
|
102
|
+
async get(key) {
|
|
103
|
+
return store.get(keyDigest(key));
|
|
104
|
+
},
|
|
105
|
+
async set(key, entry) {
|
|
106
|
+
store.set(keyDigest(key), entry);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copies `profileSource` into `destDir`, then rewrites its context binding's `project:` field to
|
|
3
|
+
* point at `userProject` (absolute path) instead of the profile's own fixture binding. Returns the
|
|
4
|
+
* composed profile's directory (safe to pass straight to `warble compile`).
|
|
5
|
+
*
|
|
6
|
+
* The binding file to rewrite is *not* hardcoded to `context/binding.yml` — it's read from the
|
|
7
|
+
* copied `profile.yml`'s own `context: project: <path>` pointer (the documented Warble mechanism:
|
|
8
|
+
* "profile mounts context via `context: project: <path-to-binding.yml>`"), so this works for any
|
|
9
|
+
* profile following that shape, not just `genbi-default`. The resolved binding path is confined to
|
|
10
|
+
* the composed directory: a `../`-laden pointer that escapes it is a loud {@link PathTraversalError}
|
|
11
|
+
* (reusing the exec module's shared traversal error rather than reading/overwriting an arbitrary
|
|
12
|
+
* host file).
|
|
13
|
+
*
|
|
14
|
+
* Both `profile.yml` and the binding file are small, flat YAML where only a single scalar matters
|
|
15
|
+
* here — regex extraction/rewrite is used deliberately instead of pulling in a full YAML parser
|
|
16
|
+
* dependency for that. Both the pointer lookup and the `project:` rewrite assert *exactly one*
|
|
17
|
+
* match and loud-fail on zero or multiple (a duplicate top-level `project:` would otherwise leave a
|
|
18
|
+
* stale line that last-key-wins YAML parsing could silently bind to the wrong project). If a Warble
|
|
19
|
+
* profile ever grows a more elaborate `context:` block (e.g. multiple context sources), this needs
|
|
20
|
+
* revisiting.
|
|
21
|
+
*/
|
|
22
|
+
export declare function composeUserProfile(options: {
|
|
23
|
+
readonly profileSource: string;
|
|
24
|
+
readonly userProject: string;
|
|
25
|
+
readonly destDir: string;
|
|
26
|
+
}): Promise<string>;
|
|
27
|
+
/** Extracts the single `context: project: <path>` pointer from a profile.yml's text. */
|
|
28
|
+
export declare function extractContextBindingPath(profileYamlText: string, sourcePath: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Replaces the single top-level `project:` line in a context-binding YAML's text, preserving
|
|
31
|
+
* surrounding comments. Asserts exactly one top-level `project:` — a second one would survive the
|
|
32
|
+
* rewrite as a stale line and could win under last-key-wins YAML parsing (silent misbind).
|
|
33
|
+
*/
|
|
34
|
+
export declare function rewriteContextBindingProject(bindingText: string, newProjectPath: string, sourcePath: string): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { cp, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { PathTraversalError } from "../exec/index.js";
|
|
4
|
+
import { InvalidProfileShapeError } from "./errors.js";
|
|
5
|
+
/**
|
|
6
|
+
* Copies `profileSource` into `destDir`, then rewrites its context binding's `project:` field to
|
|
7
|
+
* point at `userProject` (absolute path) instead of the profile's own fixture binding. Returns the
|
|
8
|
+
* composed profile's directory (safe to pass straight to `warble compile`).
|
|
9
|
+
*
|
|
10
|
+
* The binding file to rewrite is *not* hardcoded to `context/binding.yml` — it's read from the
|
|
11
|
+
* copied `profile.yml`'s own `context: project: <path>` pointer (the documented Warble mechanism:
|
|
12
|
+
* "profile mounts context via `context: project: <path-to-binding.yml>`"), so this works for any
|
|
13
|
+
* profile following that shape, not just `genbi-default`. The resolved binding path is confined to
|
|
14
|
+
* the composed directory: a `../`-laden pointer that escapes it is a loud {@link PathTraversalError}
|
|
15
|
+
* (reusing the exec module's shared traversal error rather than reading/overwriting an arbitrary
|
|
16
|
+
* host file).
|
|
17
|
+
*
|
|
18
|
+
* Both `profile.yml` and the binding file are small, flat YAML where only a single scalar matters
|
|
19
|
+
* here — regex extraction/rewrite is used deliberately instead of pulling in a full YAML parser
|
|
20
|
+
* dependency for that. Both the pointer lookup and the `project:` rewrite assert *exactly one*
|
|
21
|
+
* match and loud-fail on zero or multiple (a duplicate top-level `project:` would otherwise leave a
|
|
22
|
+
* stale line that last-key-wins YAML parsing could silently bind to the wrong project). If a Warble
|
|
23
|
+
* profile ever grows a more elaborate `context:` block (e.g. multiple context sources), this needs
|
|
24
|
+
* revisiting.
|
|
25
|
+
*/
|
|
26
|
+
export async function composeUserProfile(options) {
|
|
27
|
+
const profileSourceAbs = path.resolve(options.profileSource);
|
|
28
|
+
const userProjectAbs = path.resolve(options.userProject);
|
|
29
|
+
const composedDir = path.join(options.destDir, "profile");
|
|
30
|
+
await cp(profileSourceAbs, composedDir, { recursive: true });
|
|
31
|
+
const profileYamlPath = path.join(composedDir, "profile.yml");
|
|
32
|
+
const profileYamlText = await readFile(profileYamlPath, "utf-8");
|
|
33
|
+
const bindingRelPath = extractContextBindingPath(profileYamlText, profileYamlPath);
|
|
34
|
+
const bindingPath = resolveWithinComposedDir(composedDir, bindingRelPath);
|
|
35
|
+
const bindingText = await readFile(bindingPath, "utf-8");
|
|
36
|
+
const rewritten = rewriteContextBindingProject(bindingText, userProjectAbs, bindingPath);
|
|
37
|
+
await writeFile(bindingPath, rewritten, "utf-8");
|
|
38
|
+
return composedDir;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolves `bindingRelPath` against `composedDir` and rejects anything that escapes it. Mirrors the
|
|
42
|
+
* containment check `harness/exec/local.ts`'s (private) `resolveWithinScope` performs, reusing its
|
|
43
|
+
* exported {@link PathTraversalError} — kept as a local check here so the fix stays within this
|
|
44
|
+
* module rather than widening the exec module's public surface.
|
|
45
|
+
*/
|
|
46
|
+
function resolveWithinComposedDir(composedDir, bindingRelPath) {
|
|
47
|
+
const targetAbs = path.resolve(composedDir, bindingRelPath);
|
|
48
|
+
const relative = path.relative(composedDir, targetAbs);
|
|
49
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
50
|
+
throw new PathTraversalError(bindingRelPath, composedDir);
|
|
51
|
+
}
|
|
52
|
+
return targetAbs;
|
|
53
|
+
}
|
|
54
|
+
/** Extracts the single `context: project: <path>` pointer from a profile.yml's text. */
|
|
55
|
+
export function extractContextBindingPath(profileYamlText, sourcePath) {
|
|
56
|
+
const pattern = /^context:[ \t]*\r?\n[ \t]+project:[ \t]*(\S+)[ \t]*$/gm;
|
|
57
|
+
const matches = [...profileYamlText.matchAll(pattern)];
|
|
58
|
+
if (matches.length === 0) {
|
|
59
|
+
throw new InvalidProfileShapeError(`"${sourcePath}" has no "context:\\n project: <path>" block — cannot locate its context binding file`);
|
|
60
|
+
}
|
|
61
|
+
if (matches.length > 1) {
|
|
62
|
+
throw new InvalidProfileShapeError(`"${sourcePath}" has ${matches.length} "context:\\n project:" blocks — expected exactly one`);
|
|
63
|
+
}
|
|
64
|
+
const captured = matches[0]?.[1];
|
|
65
|
+
if (captured === undefined) {
|
|
66
|
+
throw new InvalidProfileShapeError(`"${sourcePath}" has a malformed "context: project:" pointer`);
|
|
67
|
+
}
|
|
68
|
+
const unquoted = stripSurroundingQuotes(captured);
|
|
69
|
+
if (unquoted.length === 0) {
|
|
70
|
+
throw new InvalidProfileShapeError(`"${sourcePath}" has an empty "context: project:" pointer`);
|
|
71
|
+
}
|
|
72
|
+
return unquoted;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Replaces the single top-level `project:` line in a context-binding YAML's text, preserving
|
|
76
|
+
* surrounding comments. Asserts exactly one top-level `project:` — a second one would survive the
|
|
77
|
+
* rewrite as a stale line and could win under last-key-wins YAML parsing (silent misbind).
|
|
78
|
+
*/
|
|
79
|
+
export function rewriteContextBindingProject(bindingText, newProjectPath, sourcePath) {
|
|
80
|
+
const pattern = /^project:[ \t]*.*$/gm;
|
|
81
|
+
const matches = [...bindingText.matchAll(pattern)];
|
|
82
|
+
if (matches.length === 0) {
|
|
83
|
+
throw new InvalidProfileShapeError(`"${sourcePath}" has no top-level "project:" field to rewrite`);
|
|
84
|
+
}
|
|
85
|
+
if (matches.length > 1) {
|
|
86
|
+
throw new InvalidProfileShapeError(`"${sourcePath}" has ${matches.length} top-level "project:" fields — expected exactly one (ambiguous binding)`);
|
|
87
|
+
}
|
|
88
|
+
// Function replacement so a project path containing `$` isn't interpreted as a replacement token.
|
|
89
|
+
return bindingText.replace(/^project:[ \t]*.*$/m, () => `project: ${newProjectPath}`);
|
|
90
|
+
}
|
|
91
|
+
/** Strips a single matching pair of surrounding single/double quotes, if present. */
|
|
92
|
+
function stripSurroundingQuotes(value) {
|
|
93
|
+
const match = value.match(/^(['"])(.*)\1$/);
|
|
94
|
+
return match?.[2] ?? value;
|
|
95
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by {@link resolveWarbleBinary} (see `./resolve-binary.js`) when no `warble` binary can be
|
|
3
|
+
* found via any of its four resolution tiers (explicit arg, installed `@warble/cli` package,
|
|
4
|
+
* `PATH`, opt-in sibling repo release build). Deliberately loud — this pipeline never silently
|
|
5
|
+
* degrades to "skip compiling".
|
|
6
|
+
*/
|
|
7
|
+
export declare class WarbleBinaryNotFoundError extends Error {
|
|
8
|
+
constructor(attempts: readonly string[]);
|
|
9
|
+
}
|
|
10
|
+
/** Thrown when a `warble` subprocess (compile or dispatch) exits non-zero. */
|
|
11
|
+
export declare class WarbleCommandFailedError extends Error {
|
|
12
|
+
readonly command: string;
|
|
13
|
+
readonly args: readonly string[];
|
|
14
|
+
readonly exitCode: number;
|
|
15
|
+
readonly stderr: string;
|
|
16
|
+
readonly stdout: string;
|
|
17
|
+
constructor(command: string, args: readonly string[], exitCode: number, stderr: string, stdout?: string);
|
|
18
|
+
}
|
|
19
|
+
/** Thrown when a Warble profile directory doesn't have the expected `profile.yml` / context-binding shape. */
|
|
20
|
+
export declare class InvalidProfileShapeError extends Error {
|
|
21
|
+
constructor(message: string);
|
|
22
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by {@link resolveWarbleBinary} (see `./resolve-binary.js`) when no `warble` binary can be
|
|
3
|
+
* found via any of its four resolution tiers (explicit arg, installed `@warble/cli` package,
|
|
4
|
+
* `PATH`, opt-in sibling repo release build). Deliberately loud — this pipeline never silently
|
|
5
|
+
* degrades to "skip compiling".
|
|
6
|
+
*/
|
|
7
|
+
export class WarbleBinaryNotFoundError extends Error {
|
|
8
|
+
constructor(attempts) {
|
|
9
|
+
super(`could not resolve a "warble" binary:\n` +
|
|
10
|
+
attempts.map((attempt) => ` - ${attempt}`).join("\n") +
|
|
11
|
+
`\nfix: pass "warbleBin" explicitly, run "pnpm install" so "@warble/cli" is installed, or ` +
|
|
12
|
+
`put "warble" on PATH.`);
|
|
13
|
+
this.name = "WarbleBinaryNotFoundError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Thrown when a `warble` subprocess (compile or dispatch) exits non-zero. */
|
|
17
|
+
export class WarbleCommandFailedError extends Error {
|
|
18
|
+
command;
|
|
19
|
+
args;
|
|
20
|
+
exitCode;
|
|
21
|
+
stderr;
|
|
22
|
+
stdout;
|
|
23
|
+
constructor(command, args, exitCode, stderr, stdout = "") {
|
|
24
|
+
// warble reports diagnostics on stderr, but capture stdout too so nothing useful is dropped.
|
|
25
|
+
const diagnostics = [stderr.trim(), stdout.trim()].filter((chunk) => chunk.length > 0).join("\n");
|
|
26
|
+
super(`"${command} ${args.join(" ")}" exited ${exitCode}:\n${diagnostics || "(no output)"}`);
|
|
27
|
+
this.command = command;
|
|
28
|
+
this.args = args;
|
|
29
|
+
this.exitCode = exitCode;
|
|
30
|
+
this.stderr = stderr;
|
|
31
|
+
this.stdout = stdout;
|
|
32
|
+
this.name = "WarbleCommandFailedError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Thrown when a Warble profile directory doesn't have the expected `profile.yml` / context-binding shape. */
|
|
36
|
+
export class InvalidProfileShapeError extends Error {
|
|
37
|
+
constructor(message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "InvalidProfileShapeError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A content fingerprint for a directory tree: every included file's relative path plus a sha256 of
|
|
3
|
+
* its *contents*, combined into a single sha256. This is the identity {@link CompileCache} keys on
|
|
4
|
+
* for both the source profile and the user's wren project.
|
|
5
|
+
*
|
|
6
|
+
* Content (not size+mtime) is hashed deliberately: mtimes are unreliable (a fresh `git checkout`
|
|
7
|
+
* normalizes them) and same-size edits (e.g. a column rename) collide on size — either would yield
|
|
8
|
+
* a false cache HIT and silently serve a stale bundle, defeating the point of binding each user to
|
|
9
|
+
* THEIR current semantic layer. Bulk data files are skipped (see {@link EXCLUDED_DATA_SUFFIXES}).
|
|
10
|
+
* Symlinks are followed (via `stat`, not the `readdir` dirent type) so a symlinked semantic-layer
|
|
11
|
+
* file is hashed rather than silently skipped; broken symlinks are ignored.
|
|
12
|
+
*/
|
|
13
|
+
export declare function hashDirectory(rootDir: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* A content fingerprint for an ordered list of individual files (e.g. `--provider` fragment
|
|
16
|
+
* paths) — order-sensitive, since dispatch merges multiple provider fragments and `[a, b]` can
|
|
17
|
+
* compile to a different artifact than `[b, a]`. Path strings themselves are NOT part of the
|
|
18
|
+
* hash — only each file's content — so a fragment copied to a different path with identical
|
|
19
|
+
* bytes doesn't cause an unnecessary cache miss. An empty list hashes deterministically too, so
|
|
20
|
+
* passing `[]` (no providers) is distinguishable from any non-empty set, including the default.
|
|
21
|
+
*/
|
|
22
|
+
export declare function hashFiles(filePaths: readonly string[]): Promise<string>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/** Directories never worth fingerprinting: VCS metadata, package installs, prior compiler output. */
|
|
5
|
+
const IGNORED_DIR_NAMES = new Set([".git", "node_modules", ".warble"]);
|
|
6
|
+
/**
|
|
7
|
+
* Bulk data files excluded from the fingerprint: their bytes are NOT compiled into the IR/bundle
|
|
8
|
+
* (the context binding is a *path* to a wren project; row data is read at query time, not compile
|
|
9
|
+
* time), so a data-only change doesn't invalidate the compiled artifact. Everything that DOES feed
|
|
10
|
+
* the compiled artifact — profile.yml, components, and the user project's semantic-layer defs
|
|
11
|
+
* (`wren_project.yml`, `mdl` under `target/`, `cubes.yml`, `relationships.yml`, `instructions.md`,
|
|
12
|
+
* `models/`, `views/`) — is content-hashed. When in doubt a file is included, not excluded.
|
|
13
|
+
*/
|
|
14
|
+
const EXCLUDED_DATA_SUFFIXES = [".duckdb", ".duckdb.wal", ".wal", ".parquet", ".sqlite", ".db"];
|
|
15
|
+
/**
|
|
16
|
+
* A content fingerprint for a directory tree: every included file's relative path plus a sha256 of
|
|
17
|
+
* its *contents*, combined into a single sha256. This is the identity {@link CompileCache} keys on
|
|
18
|
+
* for both the source profile and the user's wren project.
|
|
19
|
+
*
|
|
20
|
+
* Content (not size+mtime) is hashed deliberately: mtimes are unreliable (a fresh `git checkout`
|
|
21
|
+
* normalizes them) and same-size edits (e.g. a column rename) collide on size — either would yield
|
|
22
|
+
* a false cache HIT and silently serve a stale bundle, defeating the point of binding each user to
|
|
23
|
+
* THEIR current semantic layer. Bulk data files are skipped (see {@link EXCLUDED_DATA_SUFFIXES}).
|
|
24
|
+
* Symlinks are followed (via `stat`, not the `readdir` dirent type) so a symlinked semantic-layer
|
|
25
|
+
* file is hashed rather than silently skipped; broken symlinks are ignored.
|
|
26
|
+
*/
|
|
27
|
+
export async function hashDirectory(rootDir) {
|
|
28
|
+
const entries = [];
|
|
29
|
+
await walk(rootDir, rootDir, entries);
|
|
30
|
+
entries.sort();
|
|
31
|
+
const hash = createHash("sha256");
|
|
32
|
+
for (const entry of entries)
|
|
33
|
+
hash.update(entry).update("\n");
|
|
34
|
+
return hash.digest("hex");
|
|
35
|
+
}
|
|
36
|
+
async function walk(rootDir, dir, out) {
|
|
37
|
+
const dirents = await readdir(dir, { withFileTypes: true });
|
|
38
|
+
for (const dirent of dirents) {
|
|
39
|
+
const fullPath = path.join(dir, dirent.name);
|
|
40
|
+
// `stat` follows symlinks, so a symlink is classified by its target (not skipped the way the
|
|
41
|
+
// `readdir` dirent's isDirectory()/isFile() would). A broken symlink / TOCTOU race throws → skip.
|
|
42
|
+
let stats;
|
|
43
|
+
try {
|
|
44
|
+
stats = await stat(fullPath);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (stats.isDirectory()) {
|
|
50
|
+
if (IGNORED_DIR_NAMES.has(dirent.name))
|
|
51
|
+
continue;
|
|
52
|
+
await walk(rootDir, fullPath, out);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!stats.isFile())
|
|
56
|
+
continue;
|
|
57
|
+
if (isExcludedDataFile(dirent.name))
|
|
58
|
+
continue;
|
|
59
|
+
const content = await readFile(fullPath);
|
|
60
|
+
const fileHash = createHash("sha256").update(content).digest("hex");
|
|
61
|
+
const relPath = path.relative(rootDir, fullPath);
|
|
62
|
+
out.push(`${relPath}:${fileHash}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function isExcludedDataFile(name) {
|
|
66
|
+
const lower = name.toLowerCase();
|
|
67
|
+
return EXCLUDED_DATA_SUFFIXES.some((suffix) => lower.endsWith(suffix));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A content fingerprint for an ordered list of individual files (e.g. `--provider` fragment
|
|
71
|
+
* paths) — order-sensitive, since dispatch merges multiple provider fragments and `[a, b]` can
|
|
72
|
+
* compile to a different artifact than `[b, a]`. Path strings themselves are NOT part of the
|
|
73
|
+
* hash — only each file's content — so a fragment copied to a different path with identical
|
|
74
|
+
* bytes doesn't cause an unnecessary cache miss. An empty list hashes deterministically too, so
|
|
75
|
+
* passing `[]` (no providers) is distinguishable from any non-empty set, including the default.
|
|
76
|
+
*/
|
|
77
|
+
export async function hashFiles(filePaths) {
|
|
78
|
+
const hash = createHash("sha256");
|
|
79
|
+
for (const filePath of filePaths) {
|
|
80
|
+
const content = await readFile(path.resolve(filePath));
|
|
81
|
+
hash.update(createHash("sha256").update(content).digest("hex")).update("\n");
|
|
82
|
+
}
|
|
83
|
+
return hash.digest("hex");
|
|
84
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createFileSystemCompileCache, createInMemoryCompileCache, resolveDefaultCacheDir } from "./cache.js";
|
|
2
|
+
export { composeUserProfile, extractContextBindingPath, rewriteContextBindingProject } from "./compose-profile.js";
|
|
3
|
+
export { InvalidProfileShapeError, WarbleBinaryNotFoundError, WarbleCommandFailedError, } from "./errors.js";
|
|
4
|
+
export { hashDirectory, hashFiles } from "./fingerprint.js";
|
|
5
|
+
export { compileProfile, compileRawProfile, runWarble } from "./pipeline.js";
|
|
6
|
+
export { resolveHubDir, resolveWarbleBinary } from "./resolve-binary.js";
|
|
7
|
+
export type { CompileCache, CompileCacheEntry, CompileCacheKey, CompileMode, CompileProfileOptions, CompileProfileResult, CompileRawProfileOptions, } from "./types.js";
|
|
8
|
+
export { getWarbleIdentity } from "./warble-identity.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createFileSystemCompileCache, createInMemoryCompileCache, resolveDefaultCacheDir } from "./cache.js";
|
|
2
|
+
export { composeUserProfile, extractContextBindingPath, rewriteContextBindingProject } from "./compose-profile.js";
|
|
3
|
+
export { InvalidProfileShapeError, WarbleBinaryNotFoundError, WarbleCommandFailedError, } from "./errors.js";
|
|
4
|
+
export { hashDirectory, hashFiles } from "./fingerprint.js";
|
|
5
|
+
export { compileProfile, compileRawProfile, runWarble } from "./pipeline.js";
|
|
6
|
+
export { resolveHubDir, resolveWarbleBinary } from "./resolve-binary.js";
|
|
7
|
+
export { getWarbleIdentity } from "./warble-identity.js";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CompileProfileOptions, CompileProfileResult, CompileRawProfileOptions } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* This package's bundled wren capability-provider fragment (`providers/wren.provider.yaml`) — the
|
|
4
|
+
* default `--provider` for in-process dispatch, matching how `fixtures/genbi-default.native.bundle.json`
|
|
5
|
+
* was produced (see `test/e2e-wren-native.test.ts`'s comment).
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_WREN_PROVIDER_PATH: string;
|
|
8
|
+
/**
|
|
9
|
+
* Compiles a Warble profile bound to a specific user's wren project (the per-user compile
|
|
10
|
+
* pipeline): composes a copy of `profileSource` with its context binding rebound to `userProject`
|
|
11
|
+
* (see `composeUserProfile`), shells out to `warble compile` for the IR, and — in `"agnostic"`
|
|
12
|
+
* mode — additionally `warble dispatch --target vercel` for the vercel bundle this package's
|
|
13
|
+
* `runAgent()` consumes.
|
|
14
|
+
*
|
|
15
|
+
* A cache hit (see below) never *executes* `warble`, but by default DOES need to resolve + read
|
|
16
|
+
* the binary — its content hash and the Hub root derived from its location are both cache-key
|
|
17
|
+
* inputs (see `warbleIdentity` and `hubDir` below), so a hit can't be trusted without knowing which
|
|
18
|
+
* binary is current and which component library it reads. Pass both `options.warbleIdentity` and
|
|
19
|
+
* `options.hubDir` explicitly to skip touching the binary at all even on a hit.
|
|
20
|
+
*
|
|
21
|
+
* Deliberately shells via plain `node:child_process` (`execFile`, no shell) rather than this
|
|
22
|
+
* package's `ExecutionEnv` seam (`harness/exec/`): `ExecutionEnv` gates *runtime* native-tool side
|
|
23
|
+
* effects against a bundle's guardrail-derived `EnforcementPolicy` (read-only / write-scope /
|
|
24
|
+
* egress) — there is no bundle or agent yet at this point, compiling happens before any of that
|
|
25
|
+
* exists, so synthesizing a policy for it would be a semantic mismatch, not a reuse. This mirrors
|
|
26
|
+
* the same no-shell, structured-result `execFile` convention `harness/exec/local.ts` already uses.
|
|
27
|
+
*
|
|
28
|
+
* Results are cached on (profile content hash x user-context fingerprint x mode x resolved
|
|
29
|
+
* provider-fragment content hash x warble binary identity x resolved Hub root) — see `./cache.js` —
|
|
30
|
+
* so an unchanged profile/context/providers/compiler/Hub tuple returns the previous artifact
|
|
31
|
+
* instead of recompiling. Folding in the provider-fragment hash, warble identity and Hub root
|
|
32
|
+
* (rather than just profile+context+mode) matters: otherwise a custom `options.providers` and this
|
|
33
|
+
* package's default fragment collide on the same key, and editing a provider fragment, rebuilding
|
|
34
|
+
* `warble`, or compiling against a different component library would keep serving a stale artifact
|
|
35
|
+
* forever, since there's no TTL/eviction.
|
|
36
|
+
*
|
|
37
|
+
* The `warble` invocations run inside a temporary scratch directory (the composed profile + raw
|
|
38
|
+
* compiler output). When the cache relocates the artifacts to a durable home outside that scratch
|
|
39
|
+
* dir — as the default persistent {@link createFileSystemCompileCache} does — the scratch dir is
|
|
40
|
+
* removed and the durable cache paths are returned. When the cache does NOT relocate them (e.g. a
|
|
41
|
+
* non-persistent in-memory cache that just stores the paths), the scratch dir is retained so the
|
|
42
|
+
* returned paths stay valid. Either way the returned `irPath`/`bundlePath` are always readable, and
|
|
43
|
+
* a caller-supplied `workDir` is never deleted.
|
|
44
|
+
*/
|
|
45
|
+
export declare function compileProfile(options: CompileProfileOptions): Promise<CompileProfileResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Compiles a profile's authored context without composing it against a bound
|
|
48
|
+
* project. The fixed context fingerprint keeps raw-profile artifacts distinct
|
|
49
|
+
* from every user-project-bound compile while `profileHash` still invalidates
|
|
50
|
+
* the entry whenever the source profile changes.
|
|
51
|
+
*/
|
|
52
|
+
export declare function compileRawProfile(options: CompileRawProfileOptions): Promise<CompileProfileResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Shells a `warble` subcommand (no shell interpolation — `execFile`, argv
|
|
55
|
+
* array only). Exported for `InProcessSetupRunner` (`harness/setup/runner.ts`),
|
|
56
|
+
* which dispatches the pre-committed `genbi-setup/ir.golden.json` straight to
|
|
57
|
+
* `warble dispatch --target vercel`, bypassing `compileProfile`'s
|
|
58
|
+
* `composeUserProfile` rewrite entirely (there is no user project to bind
|
|
59
|
+
* context against at setup time — see that runner's doc comment). Reusing
|
|
60
|
+
* this rather than duplicating an `execFile` wrapper keeps the two callers'
|
|
61
|
+
* timeout/max-buffer/error-shaping behavior identical.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runWarble(bin: string, args: readonly string[]): Promise<{
|
|
64
|
+
stdout: string;
|
|
65
|
+
stderr: string;
|
|
66
|
+
}>;
|