@wrenai/genbi 0.0.4 → 0.0.5
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/README.md +64 -2
- package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
- package/dist/assets/index-A_Wrq7Ey.css +1 -0
- package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
- package/dist/index.html +2 -2
- package/dist-server/harness/bundle/schema.d.ts +38 -0
- package/dist-server/harness/bundle/schema.js +18 -10
- package/dist-server/harness/bundle/version.js +1 -1
- package/dist-server/harness/cli-args.d.ts +10 -2
- package/dist-server/harness/cli-args.js +24 -8
- package/dist-server/harness/cli.js +75 -9
- package/dist-server/harness/compile/cache.js +4 -4
- package/dist-server/harness/compile/compose-profile.d.ts +51 -14
- package/dist-server/harness/compile/compose-profile.js +85 -19
- package/dist-server/harness/compile/context-loader.d.ts +56 -0
- package/dist-server/harness/compile/context-loader.js +237 -0
- package/dist-server/harness/compile/errors.d.ts +19 -0
- package/dist-server/harness/compile/errors.js +35 -0
- package/dist-server/harness/compile/index.d.ts +4 -3
- package/dist-server/harness/compile/index.js +4 -3
- package/dist-server/harness/compile/pipeline.d.ts +15 -7
- package/dist-server/harness/compile/pipeline.js +62 -14
- package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
- package/dist-server/harness/compile/resolve-binary.js +3 -3
- package/dist-server/harness/compile/types.d.ts +29 -0
- package/dist-server/harness/compile/warble-identity.d.ts +11 -0
- package/dist-server/harness/compile/warble-identity.js +13 -0
- package/dist-server/harness/components/ai-step.d.ts +4 -0
- package/dist-server/harness/components/ai-step.js +40 -0
- package/dist-server/harness/components/broker.d.ts +60 -0
- package/dist-server/harness/components/broker.js +117 -0
- package/dist-server/harness/components/capability-card.d.ts +98 -0
- package/dist-server/harness/components/capability-card.js +169 -0
- package/dist-server/harness/components/context.d.ts +2 -0
- package/dist-server/harness/components/context.js +30 -0
- package/dist-server/harness/components/display.d.ts +6 -0
- package/dist-server/harness/components/display.js +29 -0
- package/dist-server/harness/components/egress-judge.d.ts +12 -0
- package/dist-server/harness/components/egress-judge.js +27 -0
- package/dist-server/harness/components/egress.d.ts +142 -0
- package/dist-server/harness/components/egress.js +469 -0
- package/dist-server/harness/components/in-process.d.ts +7 -0
- package/dist-server/harness/components/in-process.js +245 -0
- package/dist-server/harness/components/normalize.d.ts +35 -0
- package/dist-server/harness/components/normalize.js +245 -0
- package/dist-server/harness/components/plan.d.ts +31 -0
- package/dist-server/harness/components/plan.js +184 -0
- package/dist-server/harness/components/report.d.ts +207 -0
- package/dist-server/harness/components/report.js +309 -0
- package/dist-server/harness/components/runner.d.ts +193 -0
- package/dist-server/harness/components/runner.js +492 -0
- package/dist-server/harness/components/semantic-guards.d.ts +20 -0
- package/dist-server/harness/components/semantic-guards.js +42 -0
- package/dist-server/harness/components/terminal-json.d.ts +18 -0
- package/dist-server/harness/components/terminal-json.js +82 -0
- package/dist-server/harness/components/usage.d.ts +14 -0
- package/dist-server/harness/components/usage.js +19 -0
- package/dist-server/harness/components/wren-access.d.ts +29 -0
- package/dist-server/harness/components/wren-access.js +233 -0
- package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
- package/dist-server/harness/components/zone-dry-run.js +107 -0
- package/dist-server/harness/components/zone-gate.d.ts +76 -0
- package/dist-server/harness/components/zone-gate.js +144 -0
- package/dist-server/harness/events/index.d.ts +1 -1
- package/dist-server/harness/events/types.d.ts +52 -0
- package/dist-server/harness/index.d.ts +9 -2
- package/dist-server/harness/index.js +5 -1
- package/dist-server/harness/loop/executor.d.ts +1 -0
- package/dist-server/harness/loop/executor.js +37 -15
- package/dist-server/harness/npm-package-resolve.d.ts +2 -0
- package/dist-server/harness/npm-package-resolve.js +16 -4
- package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
- package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
- package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
- package/dist-server/harness/providers/adapters/openai.js +6 -0
- package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
- package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
- package/dist-server/harness/providers/binding.d.ts +45 -4
- package/dist-server/harness/providers/binding.js +44 -8
- package/dist-server/harness/providers/index.d.ts +9 -3
- package/dist-server/harness/providers/index.js +10 -3
- package/dist-server/harness/providers/zone.d.ts +96 -0
- package/dist-server/harness/providers/zone.js +112 -0
- package/dist-server/harness/render/answering-query.d.ts +29 -0
- package/dist-server/harness/render/answering-query.js +62 -0
- package/dist-server/harness/render/envelope.d.ts +6 -0
- package/dist-server/harness/render/envelope.js +10 -3
- package/dist-server/harness/route/adapter-spec.d.ts +8 -9
- package/dist-server/harness/route/adapter-spec.js +13 -14
- package/dist-server/harness/route/codex-ask-events.js +2 -1
- package/dist-server/harness/route/codex-ask.d.ts +4 -0
- package/dist-server/harness/route/codex-ask.js +69 -30
- package/dist-server/harness/route/codex-host-query.d.ts +52 -0
- package/dist-server/harness/route/codex-host-query.js +191 -0
- package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
- package/dist-server/harness/route/codex-local-manifest.js +96 -15
- package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
- package/dist-server/harness/route/codex-wren-proxy.js +227 -0
- package/dist-server/harness/route/describe.js +45 -6
- package/dist-server/harness/route/in-process.d.ts +22 -1
- package/dist-server/harness/route/in-process.js +54 -4
- package/dist-server/harness/route/route.js +5 -2
- package/dist-server/harness/route/types.d.ts +30 -1
- package/dist-server/harness/session/run.js +30 -10
- package/dist-server/harness/setup/runner.d.ts +2 -0
- package/dist-server/harness/setup/runner.js +38 -17
- package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
- package/dist-server/harness/setup/yaml-scalar.js +62 -0
- package/dist-server/server/adopt.js +1 -17
- package/dist-server/server/app.d.ts +1 -0
- package/dist-server/server/app.js +163 -14
- package/dist-server/server/auth-choice.d.ts +13 -1
- package/dist-server/server/auth-choice.js +66 -1
- package/dist-server/server/bin.js +177 -25
- package/dist-server/server/codex-local-grounding.d.ts +18 -0
- package/dist-server/server/codex-local-grounding.js +92 -0
- package/dist-server/server/codex-native-session.d.ts +25 -0
- package/dist-server/server/codex-native-session.js +1 -0
- package/dist-server/server/codex-runtime-composition.d.ts +34 -0
- package/dist-server/server/codex-runtime-composition.js +250 -0
- package/dist-server/server/codex-structured-ask.d.ts +23 -0
- package/dist-server/server/codex-structured-ask.js +102 -0
- package/dist-server/server/conn-config.js +4 -16
- package/dist-server/server/conversation-socket.d.ts +16 -0
- package/dist-server/server/conversation-socket.js +65 -0
- package/dist-server/server/db.d.ts +46 -0
- package/dist-server/server/db.js +88 -9
- package/dist-server/server/env-detect.d.ts +6 -0
- package/dist-server/server/env-detect.js +7 -0
- package/dist-server/server/fold.d.ts +22 -4
- package/dist-server/server/fold.js +37 -5
- package/dist-server/server/harness.d.ts +2 -2
- package/dist-server/server/harness.js +33 -7
- package/dist-server/server/interactive-terminal.d.ts +10 -6
- package/dist-server/server/interactive-terminal.js +33 -8
- package/dist-server/server/managed-wren-runtime.d.ts +116 -0
- package/dist-server/server/managed-wren-runtime.js +670 -0
- package/dist-server/server/native-artifacts.d.ts +23 -7
- package/dist-server/server/native-artifacts.js +43 -12
- package/dist-server/server/native-component-context.d.ts +19 -0
- package/dist-server/server/native-component-context.js +76 -0
- package/dist-server/server/native-component-preparation.d.ts +20 -0
- package/dist-server/server/native-component-preparation.js +129 -0
- package/dist-server/server/native-component-runtime.d.ts +36 -0
- package/dist-server/server/native-component-runtime.js +92 -0
- package/dist-server/server/native-components.d.ts +84 -0
- package/dist-server/server/native-components.js +160 -0
- package/dist-server/server/native-dispatch-process.d.ts +8 -0
- package/dist-server/server/native-dispatch-process.js +65 -0
- package/dist-server/server/native-runtime-spec.d.ts +89 -0
- package/dist-server/server/native-runtime-spec.js +169 -0
- package/dist-server/server/native-sessions.d.ts +172 -19
- package/dist-server/server/native-sessions.js +839 -244
- package/dist-server/server/native-shutdown.d.ts +6 -0
- package/dist-server/server/native-shutdown.js +28 -0
- package/dist-server/server/native-wren-home.d.ts +18 -0
- package/dist-server/server/native-wren-home.js +262 -0
- package/dist-server/server/node-pty-host.d.ts +2 -1
- package/dist-server/server/node-pty-host.js +3 -2
- package/dist-server/server/profile-registry.d.ts +139 -0
- package/dist-server/server/profile-registry.js +620 -0
- package/dist-server/server/runtime-binding.d.ts +4 -1
- package/dist-server/server/runtime-binding.js +50 -2
- package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
- package/dist-server/server/runtime-host/claude-component-step.js +186 -0
- package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
- package/dist-server/server/runtime-host/codex-app-server.js +239 -0
- package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
- package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
- package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
- package/dist-server/server/runtime-host/codex-component-step.js +308 -0
- package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
- package/dist-server/server/runtime-host/codex-conversation.js +295 -0
- package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
- package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
- package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
- package/dist-server/server/runtime-host/codex-events.js +66 -0
- package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-policy.js +151 -0
- package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
- package/dist-server/server/runtime-host/codex-process.js +96 -0
- package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
- package/dist-server/server/runtime-host/codex-rpc.js +182 -0
- package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
- package/dist-server/server/runtime-host/codex-session.js +492 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
- package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-terminal.js +120 -0
- package/dist-server/server/runtime-host/index.d.ts +5 -0
- package/dist-server/server/runtime-host/index.js +3 -0
- package/dist-server/server/runtime-host/local.d.ts +29 -0
- package/dist-server/server/runtime-host/local.js +90 -0
- package/dist-server/server/runtime-host/policy.d.ts +8 -0
- package/dist-server/server/runtime-host/policy.js +78 -0
- package/dist-server/server/runtime-host/types.d.ts +52 -0
- package/dist-server/server/runtime-host/types.js +62 -0
- package/dist-server/server/structured-query-evidence.d.ts +7 -0
- package/dist-server/server/structured-query-evidence.js +47 -0
- package/dist-server/server/structured-runtime.d.ts +52 -0
- package/dist-server/server/structured-runtime.js +130 -0
- package/dist-server/server/structured-scope.d.ts +4 -0
- package/dist-server/server/structured-scope.js +12 -0
- package/dist-server/server/turn.d.ts +6 -1
- package/dist-server/server/turn.js +82 -25
- package/dist-server/server/wire-types.d.ts +67 -3
- package/dist-server/server/wren-profiles.js +1 -1
- package/managed-wren/README.md +60 -0
- package/managed-wren/dependency-wheels.json +282 -0
- package/managed-wren/manifest.json +373 -0
- package/managed-wren/release-inputs.json +45 -0
- package/managed-wren/runtime-tree.cjs +115 -0
- package/managed-wren/source-inputs.json +40 -0
- package/package.json +15 -6
- package/profiles/genbi-default/context/binding.yml +17 -7
- package/profiles/genbi-default/context/context.json +146 -0
- package/profiles/genbi-default/ir.golden.json +39 -43
- package/profiles/genbi-default/profile.yml +31 -0
- package/profiles/genbi-enrich-context/context/binding.yml +14 -3
- package/profiles/genbi-enrich-context/context/context.json +146 -0
- package/profiles/genbi-enrich-context/ir.golden.json +4 -5
- package/profiles/genbi-monitor/context/binding.yml +13 -5
- package/profiles/genbi-monitor/context/context.json +146 -0
- package/profiles/genbi-monitor/ir.golden.json +13 -7
- package/profiles/genbi-report/context/binding.yml +19 -0
- package/profiles/genbi-report/context/context.json +223 -0
- package/profiles/genbi-report/ir.golden.json +328 -0
- package/profiles/genbi-report/profile.yml +38 -0
- package/profiles/genbi-setup/ir.golden.json +3 -1
- package/dist/assets/index-Xs4q7pNo.css +0 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type ExecutionPlan, type RunnerHost } from "../harness/components/runner.js";
|
|
3
|
+
import type { ComponentInvocationResult } from "@warble/claude-agent-sdk";
|
|
4
|
+
export declare const NATIVE_COMPONENT_REQUEST_SCHEMA: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly additionalProperties: false;
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly request: {
|
|
9
|
+
readonly type: "string";
|
|
10
|
+
readonly minLength: 1;
|
|
11
|
+
readonly maxLength: 65536;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
readonly required: readonly ["request"];
|
|
15
|
+
};
|
|
16
|
+
declare const launchHost: z.ZodObject<{
|
|
17
|
+
protocol: z.ZodLiteral<"warble-component-host/1">;
|
|
18
|
+
execution: z.ZodLiteral<"host_owned_steps">;
|
|
19
|
+
session_id: z.ZodString;
|
|
20
|
+
vendor: z.ZodEnum<{
|
|
21
|
+
claude: "claude";
|
|
22
|
+
codex: "codex";
|
|
23
|
+
}>;
|
|
24
|
+
auth_identity: z.ZodString;
|
|
25
|
+
runtime_generation: z.ZodString;
|
|
26
|
+
binding: z.ZodObject<{
|
|
27
|
+
project_identity: z.ZodString;
|
|
28
|
+
generation: z.ZodString;
|
|
29
|
+
revision: z.ZodString;
|
|
30
|
+
}, z.core.$strict>;
|
|
31
|
+
host_plan_sha256: z.ZodString;
|
|
32
|
+
plan_path: z.ZodLiteral<".warble/component-plans.json">;
|
|
33
|
+
root_tools: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
34
|
+
}, z.core.$strict>;
|
|
35
|
+
export type NativeComponentIdentity = Pick<z.infer<typeof launchHost>, "session_id" | "vendor" | "auth_identity" | "runtime_generation" | "binding">;
|
|
36
|
+
export interface NativeComponentReceipt {
|
|
37
|
+
readonly identity: NativeComponentIdentity;
|
|
38
|
+
readonly irDocument: string;
|
|
39
|
+
readonly scopeDocument: string;
|
|
40
|
+
readonly hostRoots: Readonly<Record<string, unknown>>;
|
|
41
|
+
readonly contexts: Readonly<Record<string, unknown>>;
|
|
42
|
+
}
|
|
43
|
+
export interface NativeComponentPlans {
|
|
44
|
+
readonly identity: NativeComponentIdentity;
|
|
45
|
+
readonly digest: string;
|
|
46
|
+
readonly roots: Readonly<Record<string, ExecutionPlan>>;
|
|
47
|
+
readonly tools: Readonly<Record<string, string>>;
|
|
48
|
+
}
|
|
49
|
+
/** Supplied by a certified server-side provisioner, never browser/runtime settings JSON. */
|
|
50
|
+
export interface NativeComponentPreparation {
|
|
51
|
+
readonly identity: NativeComponentIdentity;
|
|
52
|
+
readonly hostRoots: Readonly<Record<string, unknown>>;
|
|
53
|
+
readonly contexts: Readonly<Record<string, unknown>>;
|
|
54
|
+
/** Revalidates the approved account and exact vendor execution generation. */
|
|
55
|
+
assertCurrent(): void;
|
|
56
|
+
host(root: string, plan: ExecutionPlan, signal: AbortSignal): Promise<RunnerHost>;
|
|
57
|
+
}
|
|
58
|
+
export declare function nativeComponentHostContract(prepared: NativeComponentPreparation): Readonly<Record<string, unknown>>;
|
|
59
|
+
/** Correlate emitted launch/plan data with host-owned inputs, never model arguments. */
|
|
60
|
+
export declare function readNativeComponentPlans(document: string, launch: unknown, receipt: NativeComponentReceipt): NativeComponentPlans;
|
|
61
|
+
/** Per-session fixed-root admission. A tool name selects a root, never an active step. */
|
|
62
|
+
export declare class NativeComponentAdmission {
|
|
63
|
+
private readonly current;
|
|
64
|
+
private readonly host;
|
|
65
|
+
private readonly controller;
|
|
66
|
+
private readonly requests;
|
|
67
|
+
private active;
|
|
68
|
+
private activeController;
|
|
69
|
+
private cleanupFailure;
|
|
70
|
+
private closing?;
|
|
71
|
+
private readonly plans;
|
|
72
|
+
constructor(plans: NativeComponentPlans, current: () => NativeComponentIdentity | undefined, host: (root: string, plan: ExecutionPlan, signal: AbortSignal) => Promise<RunnerHost>);
|
|
73
|
+
private check;
|
|
74
|
+
list(): readonly {
|
|
75
|
+
name: string;
|
|
76
|
+
description: string;
|
|
77
|
+
inputSchema: typeof NATIVE_COMPONENT_REQUEST_SCHEMA;
|
|
78
|
+
}[];
|
|
79
|
+
call(name: string, input: unknown, requestId: string | number, signal?: AbortSignal): Promise<ComponentInvocationResult>;
|
|
80
|
+
/** Browser detach cancels work while allowing the retained terminal to reconnect. */
|
|
81
|
+
cancelActive(): void;
|
|
82
|
+
close(): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { canonicalPlanJson, planDigest, readExecutionPlan } from "../harness/components/plan.js";
|
|
5
|
+
import { ComponentRunner } from "../harness/components/runner.js";
|
|
6
|
+
const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
7
|
+
const binding = z.object({ project_identity: z.string().min(1), generation: z.string().min(1), revision: z.string().min(1) }).strict();
|
|
8
|
+
const identity = {
|
|
9
|
+
protocol: z.literal("warble-component-host/1"), execution: z.literal("host_owned_steps"),
|
|
10
|
+
session_id: z.string().min(1), vendor: z.enum(["claude", "codex"]), auth_identity: z.string().min(1),
|
|
11
|
+
runtime_generation: z.string().min(1), binding,
|
|
12
|
+
};
|
|
13
|
+
export const NATIVE_COMPONENT_REQUEST_SCHEMA = { type: "object", additionalProperties: false,
|
|
14
|
+
properties: { request: { type: "string", minLength: 1, maxLength: 65_536 } }, required: ["request"],
|
|
15
|
+
};
|
|
16
|
+
const rootTools = z.record(z.string(), z.string().regex(/^warble_run_[a-f0-9]{32}$/));
|
|
17
|
+
const launchHost = z.object({ ...identity, host_plan_sha256: digest,
|
|
18
|
+
plan_path: z.literal(".warble/component-plans.json"), root_tools: rootTools,
|
|
19
|
+
}).strict();
|
|
20
|
+
const nativePlan = z.object({ ...identity, version: z.literal("1"), execution_status: z.literal("not_executed"),
|
|
21
|
+
host_plan_sha256: digest, input_ir_sha256: digest, scope_sha256: digest,
|
|
22
|
+
plans: z.record(z.string(), z.record(z.string(), z.unknown())), prepared_contexts: z.record(z.string(), digest),
|
|
23
|
+
root_tools: rootTools, request_schema: z.unknown(),
|
|
24
|
+
}).strict();
|
|
25
|
+
export function nativeComponentHostContract(prepared) {
|
|
26
|
+
prepared.assertCurrent();
|
|
27
|
+
return { version: "1", protocol: "warble-component-host/1", ...structuredClone(prepared.identity),
|
|
28
|
+
roots: structuredClone(prepared.hostRoots),
|
|
29
|
+
prepared_contexts: Object.fromEntries(Object.entries(prepared.contexts).map(([id, context]) => [id, planDigest(context)])),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function equal(left, right) {
|
|
33
|
+
if (!isDeepStrictEqual(left, right))
|
|
34
|
+
throw new Error("Native component receipt mismatch");
|
|
35
|
+
}
|
|
36
|
+
function bytesDigest(value) { return `sha256:${createHash("sha256").update(value).digest("hex")}`; }
|
|
37
|
+
/** Correlate emitted launch/plan data with host-owned inputs, never model arguments. */
|
|
38
|
+
export function readNativeComponentPlans(document, launch, receipt) {
|
|
39
|
+
if (Buffer.byteLength(document) > 4 * 1024 * 1024)
|
|
40
|
+
throw new Error("Native component plan exceeds byte limit");
|
|
41
|
+
const raw = JSON.parse(document);
|
|
42
|
+
const parsed = nativePlan.parse(raw);
|
|
43
|
+
const descriptor = launchHost.parse(launch);
|
|
44
|
+
const { host_plan_sha256: claimed, ...payload } = raw;
|
|
45
|
+
equal(claimed, planDigest(payload));
|
|
46
|
+
equal(descriptor.host_plan_sha256, claimed);
|
|
47
|
+
for (const key of ["session_id", "vendor", "auth_identity", "runtime_generation", "binding"]) {
|
|
48
|
+
equal(parsed[key], receipt.identity[key]);
|
|
49
|
+
equal(descriptor[key], receipt.identity[key]);
|
|
50
|
+
}
|
|
51
|
+
equal(parsed.input_ir_sha256, bytesDigest(receipt.irDocument));
|
|
52
|
+
const scope = JSON.parse(receipt.scopeDocument);
|
|
53
|
+
// The v3 scope contract defaults an omitted entry kind to the pinned agent form.
|
|
54
|
+
if (scope.entry?.kind === undefined && typeof scope.entry?.verb === "string")
|
|
55
|
+
scope.entry.kind = "agent";
|
|
56
|
+
equal(parsed.scope_sha256, planDigest(scope));
|
|
57
|
+
equal(parsed.root_tools, descriptor.root_tools);
|
|
58
|
+
equal(parsed.request_schema, NATIVE_COMPONENT_REQUEST_SCHEMA);
|
|
59
|
+
equal(Object.keys(parsed.plans).sort(), Object.keys(receipt.hostRoots).sort());
|
|
60
|
+
equal(Object.keys(parsed.root_tools).sort(), Object.keys(parsed.plans).sort());
|
|
61
|
+
if (new Set(Object.values(parsed.root_tools)).size !== Object.keys(parsed.root_tools).length)
|
|
62
|
+
throw new Error("Duplicate native admission tool");
|
|
63
|
+
equal(parsed.prepared_contexts, Object.fromEntries(Object.entries(receipt.contexts).map(([id, context]) => [id, planDigest(context)])));
|
|
64
|
+
const ir = z.object({ context_binding: z.record(z.string(), z.unknown()), components: z.array(z.object({ id: z.string() }).passthrough()) }).passthrough().parse(JSON.parse(receipt.irDocument));
|
|
65
|
+
const declarations = Object.fromEntries(ir.components.map((component) => [component.id, component]));
|
|
66
|
+
const roots = Object.fromEntries(Object.entries(parsed.plans).map(([root, plan]) => {
|
|
67
|
+
equal(plan.entry, root);
|
|
68
|
+
const executable = readExecutionPlan(JSON.stringify(plan), { digest: digest.parse(plan.plan_sha256), inputIrDigest: parsed.input_ir_sha256,
|
|
69
|
+
contextBinding: ir.context_binding, declarations, directHostDocument: canonicalPlanJson(receipt.hostRoots[root]),
|
|
70
|
+
});
|
|
71
|
+
for (const id of Object.keys(executable.components))
|
|
72
|
+
if (!Object.hasOwn(receipt.contexts, id))
|
|
73
|
+
throw new Error("Missing native component context");
|
|
74
|
+
return [root, executable];
|
|
75
|
+
}));
|
|
76
|
+
return { identity: structuredClone(receipt.identity), digest: parsed.host_plan_sha256, roots, tools: parsed.root_tools };
|
|
77
|
+
}
|
|
78
|
+
/** Per-session fixed-root admission. A tool name selects a root, never an active step. */
|
|
79
|
+
export class NativeComponentAdmission {
|
|
80
|
+
current;
|
|
81
|
+
host;
|
|
82
|
+
controller = new AbortController();
|
|
83
|
+
requests = new Set();
|
|
84
|
+
active;
|
|
85
|
+
activeController;
|
|
86
|
+
cleanupFailure = false;
|
|
87
|
+
closing;
|
|
88
|
+
plans;
|
|
89
|
+
constructor(plans, current, host) {
|
|
90
|
+
this.current = current;
|
|
91
|
+
this.host = host;
|
|
92
|
+
this.plans = structuredClone(plans);
|
|
93
|
+
}
|
|
94
|
+
check() {
|
|
95
|
+
this.controller.signal.throwIfAborted();
|
|
96
|
+
if (this.cleanupFailure)
|
|
97
|
+
throw new Error("Native component cleanup failed");
|
|
98
|
+
if (!isDeepStrictEqual(this.current(), this.plans.identity)) {
|
|
99
|
+
this.controller.abort();
|
|
100
|
+
throw new Error("Native component session expired");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
list() {
|
|
104
|
+
this.check();
|
|
105
|
+
return Object.entries(this.plans.tools).map(([root, name]) => ({ name,
|
|
106
|
+
description: String(this.plans.roots[root].components[root].declaration.description ?? `Run ${root}`), inputSchema: NATIVE_COMPONENT_REQUEST_SCHEMA, }));
|
|
107
|
+
}
|
|
108
|
+
async call(name, input, requestId, signal) {
|
|
109
|
+
this.check();
|
|
110
|
+
const root = Object.entries(this.plans.tools).find(([, tool]) => tool === name)?.[0];
|
|
111
|
+
const parsed = z.object({ request: z.string().trim().min(1).max(65_536) }).strict().parse(input);
|
|
112
|
+
if (!root || Buffer.byteLength(JSON.stringify(parsed)) > 65_536)
|
|
113
|
+
throw new Error("Invalid native component request");
|
|
114
|
+
const key = JSON.stringify(requestId);
|
|
115
|
+
if (this.active || this.requests.has(key) || this.requests.size >= 1024)
|
|
116
|
+
throw new Error("Native component request is busy or replayed");
|
|
117
|
+
this.requests.add(key);
|
|
118
|
+
const activeController = new AbortController();
|
|
119
|
+
this.activeController = activeController;
|
|
120
|
+
const combined = AbortSignal.any([this.controller.signal, activeController.signal, ...(signal ? [signal] : [])]);
|
|
121
|
+
const plan = this.plans.roots[root];
|
|
122
|
+
const run = (async () => {
|
|
123
|
+
const host = await this.host(root, plan, combined);
|
|
124
|
+
this.check();
|
|
125
|
+
combined.throwIfAborted();
|
|
126
|
+
const runner = new ComponentRunner(plan, host);
|
|
127
|
+
const result = await runner.run(root, parsed, combined);
|
|
128
|
+
this.cleanupFailure ||= runner.cleanupFailed;
|
|
129
|
+
this.check();
|
|
130
|
+
combined.throwIfAborted();
|
|
131
|
+
return result;
|
|
132
|
+
})();
|
|
133
|
+
this.active = run;
|
|
134
|
+
try {
|
|
135
|
+
return await run;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
this.active = undefined;
|
|
139
|
+
this.activeController = undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Browser detach cancels work while allowing the retained terminal to reconnect. */
|
|
143
|
+
cancelActive() { this.activeController?.abort(); }
|
|
144
|
+
close() {
|
|
145
|
+
this.controller.abort();
|
|
146
|
+
return this.closing ??= (async () => {
|
|
147
|
+
let timer;
|
|
148
|
+
try {
|
|
149
|
+
await Promise.race([this.active?.catch(() => { }), new Promise((_, reject) => {
|
|
150
|
+
timer = setTimeout(() => reject(new Error("Native component cleanup timed out")), 5_000);
|
|
151
|
+
})]);
|
|
152
|
+
if (this.cleanupFailure)
|
|
153
|
+
throw new Error("Native component cleanup failed");
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
clearTimeout(timer);
|
|
157
|
+
}
|
|
158
|
+
})();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Bounded compiler process group; cancellation also reaps descendants before returning. */
|
|
2
|
+
export declare function runNativeDispatchProcess(input: {
|
|
3
|
+
executable: string;
|
|
4
|
+
args: readonly string[];
|
|
5
|
+
cwd: string;
|
|
6
|
+
env: NodeJS.ProcessEnv;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/** Bounded compiler process group; cancellation also reaps descendants before returning. */
|
|
3
|
+
export async function runNativeDispatchProcess(input) {
|
|
4
|
+
input.signal?.throwIfAborted();
|
|
5
|
+
if (process.platform === "win32")
|
|
6
|
+
throw new Error("Native dispatch cleanup is unavailable");
|
|
7
|
+
const child = spawn(input.executable, [...input.args], { cwd: input.cwd, env: input.env, detached: true, stdio: "ignore" });
|
|
8
|
+
let aborted = false;
|
|
9
|
+
const failure = () => new Error("Native dispatch process failed");
|
|
10
|
+
const alive = () => {
|
|
11
|
+
if (!child.pid)
|
|
12
|
+
return false;
|
|
13
|
+
try {
|
|
14
|
+
process.kill(-child.pid, 0);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return error.code !== "ESRCH";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const kill = (signal) => {
|
|
22
|
+
if (!child.pid)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
process.kill(-child.pid, signal);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code !== "ESRCH")
|
|
29
|
+
throw failure();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const gone = async (ms) => {
|
|
33
|
+
const deadline = performance.now() + ms;
|
|
34
|
+
while (alive() && performance.now() < deadline)
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
36
|
+
return !alive();
|
|
37
|
+
};
|
|
38
|
+
let cancel;
|
|
39
|
+
const completion = new Promise((resolve, reject) => {
|
|
40
|
+
child.once("error", () => reject(failure()));
|
|
41
|
+
child.once("exit", resolve);
|
|
42
|
+
cancel = () => { aborted = true; reject(failure()); };
|
|
43
|
+
});
|
|
44
|
+
const timer = setTimeout(cancel, 30_000);
|
|
45
|
+
input.signal?.addEventListener("abort", cancel, { once: true });
|
|
46
|
+
if (input.signal?.aborted)
|
|
47
|
+
cancel();
|
|
48
|
+
try {
|
|
49
|
+
const code = await completion;
|
|
50
|
+
if (code !== 0 || aborted)
|
|
51
|
+
throw failure();
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
input.signal?.removeEventListener("abort", cancel);
|
|
56
|
+
if (!(await gone(100))) {
|
|
57
|
+
kill("SIGTERM");
|
|
58
|
+
if (!(await gone(300))) {
|
|
59
|
+
kill("SIGKILL");
|
|
60
|
+
if (!(await gone(2_000)))
|
|
61
|
+
throw new Error("Native dispatch cleanup failed");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { EnrichmentBinding } from "./enrichment.js";
|
|
2
|
+
import type { RuntimeBackendId } from "./runtime-host/types.js";
|
|
3
|
+
import type { NativeVendor } from "./native-dispatch-registry.js";
|
|
4
|
+
export type NativeExecutableName = "node" | "producer" | "vendor" | "wren" | "python";
|
|
5
|
+
export type NativeSha256 = `sha256:${string}`;
|
|
6
|
+
/** Server-attested executable identity. Paths and digests never cross the API boundary. */
|
|
7
|
+
export interface NativeExecutableIdentity {
|
|
8
|
+
readonly name: NativeExecutableName;
|
|
9
|
+
readonly executable: string;
|
|
10
|
+
readonly identity: NativeSha256;
|
|
11
|
+
readonly digest: NativeSha256;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The complete environment admitted at a native-session child boundary.
|
|
15
|
+
* An index signature is intentionally absent: callers cannot append an
|
|
16
|
+
* operator or browser-provided variable without first widening this contract.
|
|
17
|
+
*/
|
|
18
|
+
export interface NativeChildEnvironment {
|
|
19
|
+
readonly PATH: string;
|
|
20
|
+
readonly HOME: string;
|
|
21
|
+
readonly USER?: string;
|
|
22
|
+
readonly LOGNAME?: string;
|
|
23
|
+
readonly TERM: "xterm-256color";
|
|
24
|
+
readonly COLORTERM: "truecolor";
|
|
25
|
+
readonly WREN_PROJECT_HOME?: string;
|
|
26
|
+
readonly WREN_HOME?: string;
|
|
27
|
+
readonly CODEX_HOME?: string;
|
|
28
|
+
readonly WARBLE_MCP_CONNECTION_CREDENTIAL?: string;
|
|
29
|
+
readonly WARBLE_SETUP_BOOTSTRAP_ROOT?: string;
|
|
30
|
+
readonly PYTHONNOUSERSITE?: "1";
|
|
31
|
+
readonly PYTHONUTF8?: "1";
|
|
32
|
+
}
|
|
33
|
+
/** Adapt the exact allowlist to Node's index-signature API at the spawn seam. */
|
|
34
|
+
export declare function nativeProcessEnvironment(environment: NativeChildEnvironment): NodeJS.ProcessEnv;
|
|
35
|
+
export interface NativeRuntimeSpec {
|
|
36
|
+
readonly version: "1";
|
|
37
|
+
readonly backend: RuntimeBackendId;
|
|
38
|
+
readonly vendor: NativeVendor;
|
|
39
|
+
readonly executables: Readonly<Partial<Record<NativeExecutableName, NativeExecutableIdentity>>>;
|
|
40
|
+
readonly toolDirectories: readonly string[];
|
|
41
|
+
readonly workspace: string;
|
|
42
|
+
readonly project?: {
|
|
43
|
+
readonly identity: string;
|
|
44
|
+
readonly path: string;
|
|
45
|
+
readonly generation: number;
|
|
46
|
+
readonly revision: string;
|
|
47
|
+
};
|
|
48
|
+
readonly sessionWrenHome?: string;
|
|
49
|
+
readonly mcp?: {
|
|
50
|
+
readonly credentialEnvironmentVariable: "WARBLE_MCP_CONNECTION_CREDENTIAL";
|
|
51
|
+
readonly credential: string;
|
|
52
|
+
};
|
|
53
|
+
readonly terminal: {
|
|
54
|
+
readonly TERM: "xterm-256color";
|
|
55
|
+
readonly COLORTERM: "truecolor";
|
|
56
|
+
};
|
|
57
|
+
readonly childEnvironment: NativeChildEnvironment;
|
|
58
|
+
}
|
|
59
|
+
export declare function attestNativeExecutable(name: NativeExecutableName, executable: string): NativeExecutableIdentity;
|
|
60
|
+
export declare function assertNativeExecutableIdentity(identity: NativeExecutableIdentity): void;
|
|
61
|
+
export declare function resolveNativeExecutable(name: NativeExecutableName, executable: string, pathValue: string | undefined): NativeExecutableIdentity | undefined;
|
|
62
|
+
export interface NativeChildEnvironmentInput {
|
|
63
|
+
readonly toolDirectories: readonly string[];
|
|
64
|
+
readonly home: string;
|
|
65
|
+
readonly projectPath?: string;
|
|
66
|
+
readonly wrenHome?: string;
|
|
67
|
+
readonly codexHome?: string;
|
|
68
|
+
readonly mcpCredential?: string;
|
|
69
|
+
readonly setupBootstrapRoot?: string;
|
|
70
|
+
readonly python?: boolean;
|
|
71
|
+
}
|
|
72
|
+
export declare function buildNativeChildEnvironment(input: NativeChildEnvironmentInput): NativeChildEnvironment;
|
|
73
|
+
export interface NativeRuntimeSpecInput {
|
|
74
|
+
readonly backend: RuntimeBackendId;
|
|
75
|
+
readonly vendor: NativeVendor;
|
|
76
|
+
readonly executables: readonly NativeExecutableIdentity[];
|
|
77
|
+
readonly toolDirectories: readonly string[];
|
|
78
|
+
readonly workspace: string;
|
|
79
|
+
readonly home: string;
|
|
80
|
+
readonly binding?: EnrichmentBinding;
|
|
81
|
+
readonly sessionWrenHome?: string;
|
|
82
|
+
readonly codexHome?: string;
|
|
83
|
+
readonly mcpCredential?: string;
|
|
84
|
+
readonly setupBootstrapRoot?: string;
|
|
85
|
+
}
|
|
86
|
+
/** Build the sealed launch object exclusively from server-owned inputs. */
|
|
87
|
+
export declare function buildNativeRuntimeSpec(input: NativeRuntimeSpecInput): NativeRuntimeSpec;
|
|
88
|
+
/** Revalidate identities and the derived environment immediately before spawn. */
|
|
89
|
+
export declare function assertNativeRuntimeSpec(spec: NativeRuntimeSpec): void;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { userInfo } from "node:os";
|
|
5
|
+
import { InteractiveLaunchError } from "./native-session-workspace.js";
|
|
6
|
+
/** Adapt the exact allowlist to Node's index-signature API at the spawn seam. */
|
|
7
|
+
export function nativeProcessEnvironment(environment) {
|
|
8
|
+
return { ...environment };
|
|
9
|
+
}
|
|
10
|
+
function unavailable() {
|
|
11
|
+
throw new InteractiveLaunchError("native child environment is invalid");
|
|
12
|
+
}
|
|
13
|
+
function canonicalDirectory(directory) {
|
|
14
|
+
if (!path.isAbsolute(directory))
|
|
15
|
+
return unavailable();
|
|
16
|
+
try {
|
|
17
|
+
// Parent aliases such as macOS /var -> /private/var are acceptable, but
|
|
18
|
+
// the admitted directory itself must not be replaceable through a final
|
|
19
|
+
// symlink component.
|
|
20
|
+
if (lstatSync(path.resolve(directory)).isSymbolicLink())
|
|
21
|
+
return unavailable();
|
|
22
|
+
const canonical = realpathSync(directory);
|
|
23
|
+
if (!statSync(canonical).isDirectory())
|
|
24
|
+
return unavailable();
|
|
25
|
+
return canonical;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return unavailable();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function canonicalExecutable(executable) {
|
|
32
|
+
if (!path.isAbsolute(executable))
|
|
33
|
+
return unavailable();
|
|
34
|
+
try {
|
|
35
|
+
const canonical = realpathSync(executable);
|
|
36
|
+
const metadata = statSync(canonical);
|
|
37
|
+
if (!metadata.isFile() || (metadata.mode & 0o111) === 0)
|
|
38
|
+
return unavailable();
|
|
39
|
+
return canonical;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return unavailable();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function digestExecutable(executable) {
|
|
46
|
+
return `sha256:${createHash("sha256").update(readFileSync(executable)).digest("hex")}`;
|
|
47
|
+
}
|
|
48
|
+
export function attestNativeExecutable(name, executable) {
|
|
49
|
+
const canonical = canonicalExecutable(executable);
|
|
50
|
+
const digest = digestExecutable(canonical);
|
|
51
|
+
return Object.freeze({ name, executable: canonical, identity: digest, digest });
|
|
52
|
+
}
|
|
53
|
+
export function assertNativeExecutableIdentity(identity) {
|
|
54
|
+
const current = attestNativeExecutable(identity.name, identity.executable);
|
|
55
|
+
if (current.executable !== identity.executable || current.identity !== identity.identity || current.digest !== identity.digest)
|
|
56
|
+
unavailable();
|
|
57
|
+
}
|
|
58
|
+
export function resolveNativeExecutable(name, executable, pathValue) {
|
|
59
|
+
const candidates = path.isAbsolute(executable) || executable.includes(path.sep)
|
|
60
|
+
? [path.resolve(executable)]
|
|
61
|
+
: (pathValue ?? "").split(path.delimiter).filter(Boolean).map((directory) => path.join(directory, executable));
|
|
62
|
+
for (const candidate of candidates) {
|
|
63
|
+
try {
|
|
64
|
+
return attestNativeExecutable(name, candidate);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Discovery happens once at server composition. Launches consume only
|
|
68
|
+
// the returned canonical identity and never repeat an ambient PATH lookup.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
export function buildNativeChildEnvironment(input) {
|
|
74
|
+
const toolDirectories = [...new Set(input.toolDirectories.map(canonicalDirectory))];
|
|
75
|
+
if (toolDirectories.length === 0)
|
|
76
|
+
return unavailable();
|
|
77
|
+
const environment = {
|
|
78
|
+
PATH: toolDirectories.join(path.delimiter),
|
|
79
|
+
HOME: canonicalDirectory(input.home),
|
|
80
|
+
// Claude locates the macOS login keychain account by username. Derive it
|
|
81
|
+
// from the OS, never from mutable shell or browser-provided environment.
|
|
82
|
+
...(process.platform === "darwin" ? { USER: userInfo().username, LOGNAME: userInfo().username } : {}),
|
|
83
|
+
TERM: "xterm-256color",
|
|
84
|
+
COLORTERM: "truecolor",
|
|
85
|
+
...(input.projectPath ? { WREN_PROJECT_HOME: canonicalDirectory(input.projectPath) } : {}),
|
|
86
|
+
...(input.wrenHome ? { WREN_HOME: canonicalDirectory(input.wrenHome) } : {}),
|
|
87
|
+
...(input.codexHome ? { CODEX_HOME: canonicalDirectory(input.codexHome) } : {}),
|
|
88
|
+
...(input.mcpCredential ? { WARBLE_MCP_CONNECTION_CREDENTIAL: input.mcpCredential } : {}),
|
|
89
|
+
...(input.setupBootstrapRoot ? { WARBLE_SETUP_BOOTSTRAP_ROOT: canonicalDirectory(input.setupBootstrapRoot) } : {}),
|
|
90
|
+
...(input.python ? { PYTHONNOUSERSITE: "1", PYTHONUTF8: "1" } : {}),
|
|
91
|
+
};
|
|
92
|
+
return Object.freeze(environment);
|
|
93
|
+
}
|
|
94
|
+
/** Build the sealed launch object exclusively from server-owned inputs. */
|
|
95
|
+
export function buildNativeRuntimeSpec(input) {
|
|
96
|
+
if (input.executables.length === 0)
|
|
97
|
+
return unavailable();
|
|
98
|
+
const executables = {};
|
|
99
|
+
for (const executable of input.executables) {
|
|
100
|
+
assertNativeExecutableIdentity(executable);
|
|
101
|
+
if (executables[executable.name])
|
|
102
|
+
return unavailable();
|
|
103
|
+
executables[executable.name] = executable;
|
|
104
|
+
}
|
|
105
|
+
if (!executables.vendor || !executables.producer)
|
|
106
|
+
return unavailable();
|
|
107
|
+
const toolDirectories = [...new Set(input.toolDirectories.map(canonicalDirectory))];
|
|
108
|
+
const workspace = canonicalDirectory(input.workspace);
|
|
109
|
+
const sessionWrenHome = input.sessionWrenHome ? canonicalDirectory(input.sessionWrenHome) : undefined;
|
|
110
|
+
const childEnvironment = buildNativeChildEnvironment({
|
|
111
|
+
toolDirectories,
|
|
112
|
+
home: input.home,
|
|
113
|
+
...(input.binding ? { projectPath: input.binding.path } : {}),
|
|
114
|
+
...(sessionWrenHome ? { wrenHome: sessionWrenHome } : {}),
|
|
115
|
+
...(input.codexHome ? { codexHome: input.codexHome } : {}),
|
|
116
|
+
...(input.mcpCredential ? { mcpCredential: input.mcpCredential } : {}),
|
|
117
|
+
...(input.setupBootstrapRoot ? { setupBootstrapRoot: input.setupBootstrapRoot } : {}),
|
|
118
|
+
});
|
|
119
|
+
const spec = {
|
|
120
|
+
version: "1",
|
|
121
|
+
backend: input.backend,
|
|
122
|
+
vendor: input.vendor,
|
|
123
|
+
executables: Object.freeze(executables),
|
|
124
|
+
toolDirectories: Object.freeze(toolDirectories),
|
|
125
|
+
workspace,
|
|
126
|
+
...(input.binding ? {
|
|
127
|
+
project: Object.freeze({
|
|
128
|
+
identity: input.binding.identity,
|
|
129
|
+
path: canonicalDirectory(input.binding.path),
|
|
130
|
+
generation: input.binding.generation,
|
|
131
|
+
revision: input.binding.revision,
|
|
132
|
+
}),
|
|
133
|
+
} : {}),
|
|
134
|
+
...(sessionWrenHome ? { sessionWrenHome } : {}),
|
|
135
|
+
...(input.mcpCredential ? {
|
|
136
|
+
mcp: Object.freeze({
|
|
137
|
+
credentialEnvironmentVariable: "WARBLE_MCP_CONNECTION_CREDENTIAL",
|
|
138
|
+
credential: input.mcpCredential,
|
|
139
|
+
}),
|
|
140
|
+
} : {}),
|
|
141
|
+
terminal: Object.freeze({ TERM: "xterm-256color", COLORTERM: "truecolor" }),
|
|
142
|
+
childEnvironment,
|
|
143
|
+
};
|
|
144
|
+
return Object.freeze(spec);
|
|
145
|
+
}
|
|
146
|
+
/** Revalidate identities and the derived environment immediately before spawn. */
|
|
147
|
+
export function assertNativeRuntimeSpec(spec) {
|
|
148
|
+
for (const identity of Object.values(spec.executables)) {
|
|
149
|
+
if (identity)
|
|
150
|
+
assertNativeExecutableIdentity(identity);
|
|
151
|
+
}
|
|
152
|
+
const rebuilt = buildNativeRuntimeSpec({
|
|
153
|
+
backend: spec.backend,
|
|
154
|
+
vendor: spec.vendor,
|
|
155
|
+
executables: Object.values(spec.executables).filter((value) => value !== undefined),
|
|
156
|
+
toolDirectories: spec.toolDirectories,
|
|
157
|
+
workspace: spec.workspace,
|
|
158
|
+
home: spec.childEnvironment.HOME,
|
|
159
|
+
...(spec.project ? { binding: { ...spec.project } } : {}),
|
|
160
|
+
...(spec.sessionWrenHome ? { sessionWrenHome: spec.sessionWrenHome } : {}),
|
|
161
|
+
...(spec.childEnvironment.CODEX_HOME ? { codexHome: spec.childEnvironment.CODEX_HOME } : {}),
|
|
162
|
+
...(spec.mcp ? { mcpCredential: spec.mcp.credential } : {}),
|
|
163
|
+
...(spec.childEnvironment.WARBLE_SETUP_BOOTSTRAP_ROOT ? { setupBootstrapRoot: spec.childEnvironment.WARBLE_SETUP_BOOTSTRAP_ROOT } : {}),
|
|
164
|
+
});
|
|
165
|
+
if (JSON.stringify(rebuilt) !== JSON.stringify(spec))
|
|
166
|
+
return unavailable();
|
|
167
|
+
if (lstatSync(spec.workspace).isSymbolicLink())
|
|
168
|
+
return unavailable();
|
|
169
|
+
}
|