@howells/envelope 0.1.0

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 ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Howells
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # envelope
2
+
3
+ Thin wrapper around the `claude` (Claude Code) CLI for **strict** Zod-validated input/output.
4
+
5
+ This is designed for local Node apps where you want "model calls" to look like a normal API:
6
+ - validate inputs with Zod
7
+ - request structured JSON output via `--json-schema`
8
+ - validate outputs with Zod again (defense in depth)
9
+
10
+ ## Requirements
11
+
12
+ - Node 20+
13
+ - `claude` CLI installed and authenticated (Claude Code)
14
+
15
+ ## Usage (Zod envelope)
16
+
17
+ ```ts
18
+ import { z } from "zod";
19
+ import { createEnvelope, createClaudeCodeClient, createCodexClient } from "envelope";
20
+
21
+ const summarizeClaude = createEnvelope({
22
+ client: createClaudeCodeClient({ model: "opus", maxBudgetUsd: 2 }),
23
+ input: z.object({ text: z.string().min(1) }),
24
+ output: z.object({ summary: z.string().min(1) }),
25
+ prompt: ({ text }) =>
26
+ `Summarize this in 1 sentence. Return JSON only: ${JSON.stringify({ text })}`,
27
+ });
28
+
29
+ const summarizeCodex = createEnvelope({
30
+ client: createCodexClient({ model: "gpt-5.3-codex" }),
31
+ input: z.object({ text: z.string().min(1) }),
32
+ output: z.object({ summary: z.string().min(1) }),
33
+ prompt: ({ text }) =>
34
+ `Summarize this in 1 sentence. Return JSON only: ${JSON.stringify({ text })}`,
35
+ });
36
+
37
+ const out = await summarizeClaude({ text: "..." });
38
+ console.log(out.summary);
39
+ ```
40
+
41
+ ## Usage (AI SDK 6)
42
+
43
+ ```ts
44
+ import { generateText } from "ai";
45
+ import { claudeCode, codex } from "envelope/ai-sdk";
46
+
47
+ const { text } = await generateText({
48
+ model: claudeCode("opus"),
49
+ prompt: "Write a haiku about camellias.",
50
+ });
51
+
52
+ const r2 = await generateText({
53
+ model: codex("gpt-5.3-codex"),
54
+ prompt: "Write a haiku about camellias.",
55
+ });
56
+ ```
57
+
58
+ Notes:
59
+ - Current adapter is text-only and uses a single-shot call under the hood.
60
+ - If we want true streaming, we can extend it to use `claude --output-format stream-json`.
@@ -0,0 +1,10 @@
1
+ import type { LanguageModelV1 } from "@ai-sdk/provider";
2
+ import { type CliTool } from "./client.js";
3
+ export declare function cliModel(args: {
4
+ tool: CliTool;
5
+ model: string;
6
+ clientOptions?: any;
7
+ }): LanguageModelV1;
8
+ export declare function claudeCode(model: string, clientOptions?: any): LanguageModelV1;
9
+ export declare function codex(model: string, clientOptions?: any): LanguageModelV1;
10
+ //# sourceMappingURL=ai-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk.d.ts","sourceRoot":"","sources":["../src/ai-sdk.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,eAAe,EAIhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAA6D,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAkCtG,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,GAAG,CAAC;CACrB,GAAG,eAAe,CAwDlB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,mBAE5D;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,mBAEvD"}
package/dist/ai-sdk.js ADDED
@@ -0,0 +1,91 @@
1
+ // AI SDK adapter for CLI tools (Claude Code, Codex).
2
+ //
3
+ // This is intentionally minimal and text-first. For `object-json` mode, it
4
+ // maps the JSON schema to the tool's structured output mode.
5
+ import { createClaudeCodeClient, createCodexClient } from "./client.js";
6
+ function v1PromptToText(prompt) {
7
+ const parts = [];
8
+ for (const m of prompt) {
9
+ const role = m.role;
10
+ const content = m.content;
11
+ if (!Array.isArray(content))
12
+ continue;
13
+ const text = content
14
+ .filter((p) => p && typeof p === "object" && p.type === "text" && typeof p.text === "string")
15
+ .map((p) => p.text)
16
+ .join("");
17
+ if (text) {
18
+ parts.push(`${role}: ${text}`);
19
+ }
20
+ }
21
+ return parts.join("\n");
22
+ }
23
+ function toSchema(schema) {
24
+ if (!schema)
25
+ return null;
26
+ return schema;
27
+ }
28
+ function makeClient(tool, model, opts) {
29
+ if (tool === "codex") {
30
+ return createCodexClient({ model, ...(opts ?? {}) });
31
+ }
32
+ return createClaudeCodeClient({ model, ...(opts ?? {}) });
33
+ }
34
+ export function cliModel(args) {
35
+ const client = makeClient(args.tool, args.model, args.clientOptions);
36
+ return {
37
+ specificationVersion: "v1",
38
+ provider: args.tool,
39
+ modelId: args.model,
40
+ defaultObjectGenerationMode: "json",
41
+ supportsStructuredOutputs: true,
42
+ async doGenerate(callOptions) {
43
+ const promptText = v1PromptToText(callOptions.prompt);
44
+ const finishReason = "stop";
45
+ if (callOptions.mode.type === "object-json") {
46
+ const schema = toSchema(callOptions.mode.schema);
47
+ if (!schema) {
48
+ throw new Error("object-json mode requires a JSON schema");
49
+ }
50
+ const res = await client.structured({ prompt: promptText, jsonSchema: schema });
51
+ return {
52
+ text: JSON.stringify(res.structured ?? null),
53
+ finishReason,
54
+ usage: { promptTokens: 0, completionTokens: 0 },
55
+ rawCall: { rawPrompt: promptText, rawSettings: { mode: callOptions.mode } },
56
+ };
57
+ }
58
+ const res = await client.text({ prompt: promptText });
59
+ return {
60
+ text: res.text,
61
+ finishReason,
62
+ usage: { promptTokens: 0, completionTokens: 0 },
63
+ rawCall: { rawPrompt: promptText, rawSettings: { mode: callOptions.mode } },
64
+ };
65
+ },
66
+ async doStream(callOptions) {
67
+ const res = await this.doGenerate(callOptions);
68
+ const stream = new ReadableStream({
69
+ start(controller) {
70
+ if (res.text) {
71
+ controller.enqueue({ type: "text-delta", textDelta: res.text });
72
+ }
73
+ controller.enqueue({
74
+ type: "finish",
75
+ finishReason: res.finishReason,
76
+ usage: res.usage,
77
+ });
78
+ controller.close();
79
+ },
80
+ });
81
+ return { stream, rawCall: res.rawCall, warnings: [] };
82
+ },
83
+ };
84
+ }
85
+ export function claudeCode(model, clientOptions) {
86
+ return cliModel({ tool: "claude-code", model, clientOptions });
87
+ }
88
+ export function codex(model, clientOptions) {
89
+ return cliModel({ tool: "codex", model, clientOptions });
90
+ }
91
+ //# sourceMappingURL=ai-sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk.js","sourceRoot":"","sources":["../src/ai-sdk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ export type ClaudeCodeModel = string;
2
+ export interface ClaudeCodeOptions {
3
+ claudePath?: string;
4
+ cwd?: string;
5
+ env?: NodeJS.ProcessEnv;
6
+ model?: ClaudeCodeModel;
7
+ maxBudgetUsd?: number;
8
+ timeoutMs?: number;
9
+ permissionMode?: "dontAsk" | "default" | "bypassPermissions";
10
+ tools?: string;
11
+ }
12
+ export interface ClaudeCodeEnvelope<TStructured> {
13
+ type?: string;
14
+ subtype?: string;
15
+ is_error?: boolean;
16
+ result?: string;
17
+ structured_output?: TStructured;
18
+ total_cost_usd?: number;
19
+ stop_reason?: string | null;
20
+ session_id?: string;
21
+ permission_denials?: unknown[];
22
+ }
23
+ export declare function claudeCodeStructured<TStructured>(args: {
24
+ prompt: string;
25
+ jsonSchema: string;
26
+ options?: ClaudeCodeOptions;
27
+ }): Promise<ClaudeCodeEnvelope<TStructured>>;
28
+ export declare function claudeCodeText(args: {
29
+ prompt: string;
30
+ options?: ClaudeCodeOptions;
31
+ }): Promise<{
32
+ text: string;
33
+ }>;
34
+ //# sourceMappingURL=claude-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../src/claude-code.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,mBAAmB,CAAC;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB,CAAC,WAAW;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,EAAE,CAAC;CAChC;AAyCD,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,IAAI,EAAE;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B,4CA4CA;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B;;GAuCA"}
@@ -0,0 +1,97 @@
1
+ import { execFile } from "node:child_process";
2
+ function isRecord(v) {
3
+ return typeof v === "object" && v !== null;
4
+ }
5
+ function execFileAsync(file, args, opts) {
6
+ return new Promise((resolve, reject) => {
7
+ execFile(file, args, {
8
+ cwd: opts.cwd,
9
+ env: opts.env,
10
+ timeout: opts.timeoutMs,
11
+ maxBuffer: 128 * 1024 * 1024,
12
+ encoding: "utf8",
13
+ }, (err, stdout, stderr) => {
14
+ if (err) {
15
+ const e = new Error(`claude CLI failed (code=${err.code ?? "?"}): ${stderr || stdout}`);
16
+ e.cause = err;
17
+ reject(e);
18
+ return;
19
+ }
20
+ resolve({ stdout, stderr });
21
+ });
22
+ });
23
+ }
24
+ function buildBaseArgs(opts) {
25
+ return ["--model", opts.model, "-p", "--permission-mode", opts.permissionMode, "--tools", opts.tools];
26
+ }
27
+ export async function claudeCodeStructured(args) {
28
+ const options = {
29
+ claudePath: args.options?.claudePath ?? "claude",
30
+ cwd: args.options?.cwd ?? process.cwd(),
31
+ env: args.options?.env ?? process.env,
32
+ model: args.options?.model ?? "opus",
33
+ maxBudgetUsd: args.options?.maxBudgetUsd ?? 5,
34
+ timeoutMs: args.options?.timeoutMs ?? 120_000,
35
+ permissionMode: args.options?.permissionMode ?? "dontAsk",
36
+ tools: args.options?.tools ?? "",
37
+ };
38
+ const { stdout } = await execFileAsync(options.claudePath, [
39
+ ...buildBaseArgs(options),
40
+ "--max-budget-usd",
41
+ String(options.maxBudgetUsd),
42
+ "--output-format",
43
+ "json",
44
+ "--json-schema",
45
+ args.jsonSchema,
46
+ args.prompt,
47
+ ], { cwd: options.cwd, env: options.env, timeoutMs: options.timeoutMs });
48
+ let envelopeUnknown;
49
+ try {
50
+ envelopeUnknown = JSON.parse(stdout);
51
+ }
52
+ catch {
53
+ throw new Error(`claude CLI returned non-JSON output. First 200 chars:\n${stdout.slice(0, 200)}`);
54
+ }
55
+ if (!isRecord(envelopeUnknown)) {
56
+ throw new Error("claude CLI returned non-object JSON envelope");
57
+ }
58
+ const envelope = envelopeUnknown;
59
+ if (envelope.is_error) {
60
+ throw new Error(`claude CLI error envelope: ${envelope.subtype ?? "unknown"}`);
61
+ }
62
+ return envelope;
63
+ }
64
+ export async function claudeCodeText(args) {
65
+ const options = {
66
+ claudePath: args.options?.claudePath ?? "claude",
67
+ cwd: args.options?.cwd ?? process.cwd(),
68
+ env: args.options?.env ?? process.env,
69
+ model: args.options?.model ?? "opus",
70
+ maxBudgetUsd: args.options?.maxBudgetUsd ?? 5,
71
+ timeoutMs: args.options?.timeoutMs ?? 120_000,
72
+ permissionMode: args.options?.permissionMode ?? "dontAsk",
73
+ tools: args.options?.tools ?? "",
74
+ };
75
+ const { stdout } = await execFileAsync(options.claudePath, [
76
+ ...buildBaseArgs(options),
77
+ "--max-budget-usd",
78
+ String(options.maxBudgetUsd),
79
+ "--output-format",
80
+ "json",
81
+ args.prompt,
82
+ ], { cwd: options.cwd, env: options.env, timeoutMs: options.timeoutMs });
83
+ let envelopeUnknown;
84
+ try {
85
+ envelopeUnknown = JSON.parse(stdout);
86
+ }
87
+ catch {
88
+ // fallback: if the user configured output-format defaults, just return the raw stdout
89
+ return { text: stdout };
90
+ }
91
+ if (!isRecord(envelopeUnknown)) {
92
+ return { text: stdout };
93
+ }
94
+ const envelope = envelopeUnknown;
95
+ return { text: envelope.result ?? "" };
96
+ }
97
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../src/claude-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ import type { JSONSchema7 } from "@ai-sdk/provider";
3
+ import { type ClaudeCodeOptions } from "./claude-code.js";
4
+ import { type CodexOptions } from "./codex-cli.js";
5
+ export type CliTool = "claude-code" | "codex";
6
+ export interface GenerateTextArgs {
7
+ prompt: string;
8
+ }
9
+ export interface GenerateStructuredArgs {
10
+ prompt: string;
11
+ jsonSchema: JSONSchema7;
12
+ }
13
+ export interface CliClient {
14
+ tool: CliTool;
15
+ model: string;
16
+ text(args: GenerateTextArgs): Promise<{
17
+ text: string;
18
+ }>;
19
+ structured<T>(args: GenerateStructuredArgs): Promise<{
20
+ structured: T;
21
+ }>;
22
+ }
23
+ export declare function jsonSchemaFromZod(schema: z.ZodTypeAny): JSONSchema7;
24
+ export declare function createClaudeCodeClient(args?: {
25
+ model?: string;
26
+ maxBudgetUsd?: number;
27
+ timeoutMs?: number;
28
+ options?: Omit<ClaudeCodeOptions, "model" | "maxBudgetUsd" | "timeoutMs">;
29
+ }): CliClient;
30
+ export declare function createCodexClient(args?: {
31
+ model?: string;
32
+ timeoutMs?: number;
33
+ options?: Omit<CodexOptions, "model" | "timeoutMs">;
34
+ }): CliClient;
35
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE/E,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,OAAO,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,WAAW,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CACzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,WAAW,CAOnE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,CAAC,EAAE;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,cAAc,GAAG,WAAW,CAAC,CAAC;CAC3E,GAAG,SAAS,CAyBZ;AAED,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,WAAW,CAAC,CAAC;CACrD,GAAG,SAAS,CAwBZ"}
package/dist/client.js ADDED
@@ -0,0 +1,61 @@
1
+ import { zodToJsonSchema } from "zod-to-json-schema";
2
+ import { claudeCodeStructured, claudeCodeText, } from "./claude-code.js";
3
+ import { codexStructured, codexText } from "./codex-cli.js";
4
+ export function jsonSchemaFromZod(schema) {
5
+ // Important: do NOT provide `name`. `zod-to-json-schema` wraps named schemas
6
+ // in a `$ref`/`definitions` envelope, which breaks Codex's `--output-schema`
7
+ // requirement that the root schema is `type: "object"`.
8
+ return zodToJsonSchema(schema, {
9
+ $refStrategy: "none",
10
+ });
11
+ }
12
+ export function createClaudeCodeClient(args) {
13
+ const cfg = args;
14
+ const model = cfg?.model ?? "opus";
15
+ const maxBudgetUsd = cfg?.maxBudgetUsd ?? 5;
16
+ const timeoutMs = cfg?.timeoutMs ?? 120_000;
17
+ return {
18
+ tool: "claude-code",
19
+ model,
20
+ async text(input) {
21
+ const res = await claudeCodeText({
22
+ prompt: input.prompt,
23
+ options: { ...cfg?.options, model, maxBudgetUsd, timeoutMs },
24
+ });
25
+ return { text: res.text };
26
+ },
27
+ async structured(input) {
28
+ const envelope = await claudeCodeStructured({
29
+ prompt: input.prompt,
30
+ jsonSchema: JSON.stringify(input.jsonSchema),
31
+ options: { ...cfg?.options, model, maxBudgetUsd, timeoutMs },
32
+ });
33
+ return { structured: envelope.structured_output };
34
+ },
35
+ };
36
+ }
37
+ export function createCodexClient(args) {
38
+ const cfg = args;
39
+ const model = cfg?.model ?? "gpt-5.3-codex";
40
+ const timeoutMs = cfg?.timeoutMs ?? 180_000;
41
+ return {
42
+ tool: "codex",
43
+ model,
44
+ async text(input) {
45
+ const res = await codexText({
46
+ prompt: input.prompt,
47
+ options: { ...cfg?.options, model, timeoutMs },
48
+ });
49
+ return { text: res.text };
50
+ },
51
+ async structured(input) {
52
+ const res = await codexStructured({
53
+ prompt: input.prompt,
54
+ jsonSchema: JSON.stringify(input.jsonSchema),
55
+ options: { ...cfg?.options, model, timeoutMs },
56
+ });
57
+ return { structured: res.structured };
58
+ },
59
+ };
60
+ }
61
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,oBAAoB,EACpB,cAAc,GAEf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,SAAS,EAAqB,MAAM,gBAAgB,CAAC;AAoB/E,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,6EAA6E;IAC7E,6EAA6E;IAC7E,wDAAwD;IACxD,OAAO,eAAe,CAAC,MAAM,EAAE;QAC7B,YAAY,EAAE,MAAM;KACrB,CAA2B,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAKtC;IACC,MAAM,GAAG,GAAG,IAAI,CAAC;IACjB,MAAM,KAAK,GAAG,GAAG,EAAE,KAAK,IAAI,MAAM,CAAC;IACnC,MAAM,YAAY,GAAG,GAAG,EAAE,YAAY,IAAI,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,GAAG,EAAE,SAAS,IAAI,OAAO,CAAC;IAE5C,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,KAAK;QACL,KAAK,CAAC,IAAI,CAAC,KAAuB;YAChC,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,OAAO,EAAE,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE;aAC7D,CAAC,CAAC;YACH,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,UAAU,CAAI,KAA6B;YAC/C,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAI;gBAC7C,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC;gBAC5C,OAAO,EAAE,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE;aAC7D,CAAC,CAAC;YACH,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,iBAAsB,EAAE,CAAC;QACzD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAIjC;IACC,MAAM,GAAG,GAAG,IAAI,CAAC;IACjB,MAAM,KAAK,GAAG,GAAG,EAAE,KAAK,IAAI,eAAe,CAAC;IAC5C,MAAM,SAAS,GAAG,GAAG,EAAE,SAAS,IAAI,OAAO,CAAC;IAE5C,OAAO;QACL,IAAI,EAAE,OAAO;QACb,KAAK;QACL,KAAK,CAAC,IAAI,CAAC,KAAuB;YAChC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC;gBAC1B,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,OAAO,EAAE,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;aAC/C,CAAC,CAAC;YACH,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,UAAU,CAAI,KAA6B;YAC/C,MAAM,GAAG,GAAG,MAAM,eAAe,CAAI;gBACnC,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC;gBAC5C,OAAO,EAAE,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;aAC/C,CAAC,CAAC;YACH,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ export type CodexModel = string;
2
+ export interface CodexOptions {
3
+ codexPath?: string;
4
+ cwd?: string;
5
+ env?: NodeJS.ProcessEnv;
6
+ model?: CodexModel;
7
+ timeoutMs?: number;
8
+ skipGitRepoCheck?: boolean;
9
+ sandbox?: "read-only" | "workspace-write" | "danger-full-access";
10
+ profile?: string;
11
+ config?: Array<string>;
12
+ jsonlEvents?: boolean;
13
+ }
14
+ export declare function codexText(args: {
15
+ prompt: string;
16
+ options?: CodexOptions;
17
+ }): Promise<{
18
+ text: string;
19
+ }>;
20
+ export declare function codexStructured<TStructured>(args: {
21
+ prompt: string;
22
+ jsonSchema: string;
23
+ options?: CodexOptions;
24
+ }): Promise<{
25
+ structured: TStructured;
26
+ raw: string;
27
+ }>;
28
+ //# sourceMappingURL=codex-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-cli.d.ts","sourceRoot":"","sources":["../src/codex-cli.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAEhC,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,WAAW,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA0ED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,YAAY,CAAA;CAAE;;GAwB/E;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,IAAI,EAAE;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;gBAiCkC,WAAW;;GAI7C"}
@@ -0,0 +1,120 @@
1
+ import { execFile } from "node:child_process";
2
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ function execFileAsync(file, args, opts) {
6
+ return new Promise((resolve, reject) => {
7
+ execFile(file, args, {
8
+ cwd: opts.cwd,
9
+ env: opts.env,
10
+ timeout: opts.timeoutMs,
11
+ maxBuffer: 128 * 1024 * 1024,
12
+ encoding: "utf8",
13
+ }, (err, stdout, stderr) => {
14
+ if (err) {
15
+ const e = new Error(`codex CLI failed (code=${err.code ?? "?"}): ${stderr || stdout}`);
16
+ e.cause = err;
17
+ reject(e);
18
+ return;
19
+ }
20
+ resolve({ stdout, stderr });
21
+ });
22
+ });
23
+ }
24
+ function baseArgs(options) {
25
+ const args = ["exec"];
26
+ if (options.skipGitRepoCheck) {
27
+ args.push("--skip-git-repo-check");
28
+ }
29
+ if (options.cwd) {
30
+ args.push("-C", options.cwd);
31
+ }
32
+ if (options.model) {
33
+ args.push("--model", options.model);
34
+ }
35
+ if (options.sandbox) {
36
+ args.push("--sandbox", options.sandbox);
37
+ }
38
+ if (options.profile) {
39
+ args.push("--profile", options.profile);
40
+ }
41
+ for (const c of options.config) {
42
+ args.push("--config", c);
43
+ }
44
+ if (options.jsonlEvents) {
45
+ args.push("--json");
46
+ }
47
+ return args;
48
+ }
49
+ function defaultOptions(opts) {
50
+ return {
51
+ codexPath: opts?.codexPath ?? "codex",
52
+ cwd: opts?.cwd ?? process.cwd(),
53
+ env: opts?.env ?? process.env,
54
+ model: opts?.model ?? "gpt-5.3-codex",
55
+ timeoutMs: opts?.timeoutMs ?? 180_000,
56
+ skipGitRepoCheck: opts?.skipGitRepoCheck ?? true,
57
+ sandbox: opts?.sandbox ?? "danger-full-access",
58
+ profile: opts?.profile ?? "",
59
+ config: opts?.config ?? [],
60
+ jsonlEvents: opts?.jsonlEvents ?? false,
61
+ };
62
+ }
63
+ export async function codexText(args) {
64
+ const options = defaultOptions(args.options);
65
+ const td = await mkdtemp(join(tmpdir(), "envelope-codex-"));
66
+ try {
67
+ const outPath = join(td, "last.txt");
68
+ const cliArgs = [
69
+ ...baseArgs(options),
70
+ "--output-last-message",
71
+ outPath,
72
+ args.prompt,
73
+ ];
74
+ await execFileAsync(options.codexPath, cliArgs, {
75
+ cwd: options.cwd,
76
+ env: options.env,
77
+ timeoutMs: options.timeoutMs,
78
+ });
79
+ const text = await readFile(outPath, "utf8");
80
+ return { text };
81
+ }
82
+ finally {
83
+ await rm(td, { recursive: true, force: true });
84
+ }
85
+ }
86
+ export async function codexStructured(args) {
87
+ const options = defaultOptions(args.options);
88
+ const td = await mkdtemp(join(tmpdir(), "envelope-codex-"));
89
+ try {
90
+ const schemaPath = join(td, "schema.json");
91
+ const outPath = join(td, "last.txt");
92
+ await writeFile(schemaPath, args.jsonSchema, "utf8");
93
+ const cliArgs = [
94
+ ...baseArgs(options),
95
+ "--output-schema",
96
+ schemaPath,
97
+ "--output-last-message",
98
+ outPath,
99
+ args.prompt,
100
+ ];
101
+ await execFileAsync(options.codexPath, cliArgs, {
102
+ cwd: options.cwd,
103
+ env: options.env,
104
+ timeoutMs: options.timeoutMs,
105
+ });
106
+ const raw = await readFile(outPath, "utf8");
107
+ let parsed;
108
+ try {
109
+ parsed = JSON.parse(raw);
110
+ }
111
+ catch {
112
+ throw new Error(`codex output was not JSON. First 200 chars:\n${raw.slice(0, 200)}`);
113
+ }
114
+ return { structured: parsed, raw };
115
+ }
116
+ finally {
117
+ await rm(td, { recursive: true, force: true });
118
+ }
119
+ }
120
+ //# sourceMappingURL=codex-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-cli.js","sourceRoot":"","sources":["../src/codex-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import type { CliClient } from "./client.js";
3
+ export declare class EnvelopeError extends Error {
4
+ name: string;
5
+ }
6
+ export interface CreateEnvelopeArgs<TIn extends z.ZodTypeAny, TOut extends z.ZodTypeAny> {
7
+ input: TIn;
8
+ output: TOut;
9
+ prompt: (input: z.infer<TIn>) => string;
10
+ client?: CliClient;
11
+ }
12
+ export declare function createEnvelope<TIn extends z.ZodTypeAny, TOut extends z.ZodTypeAny>(args: CreateEnvelopeArgs<TIn, TOut>): (inputRaw: unknown) => Promise<z.infer<TOut>>;
13
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,qBAAa,aAAc,SAAQ,KAAK;IAC7B,IAAI,SAAmB;CACjC;AAED,MAAM,WAAW,kBAAkB,CAAC,GAAG,SAAS,CAAC,CAAC,UAAU,EAAE,IAAI,SAAS,CAAC,CAAC,UAAU;IACrF,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC;IACxC,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,GAAG,SAAS,CAAC,CAAC,UAAU,EAAE,IAAI,SAAS,CAAC,CAAC,UAAU,EAChF,IAAI,EAAE,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,IAKrB,UAAU,OAAO,KAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAgBzD"}
@@ -0,0 +1,22 @@
1
+ import { createClaudeCodeClient, jsonSchemaFromZod } from "./client.js";
2
+ export class EnvelopeError extends Error {
3
+ name = "EnvelopeError";
4
+ }
5
+ export function createEnvelope(args) {
6
+ const jsonSchema = jsonSchemaFromZod(args.output);
7
+ const client = args.client ?? createClaudeCodeClient();
8
+ return async (inputRaw) => {
9
+ const input = args.input.safeParse(inputRaw);
10
+ if (!input.success) {
11
+ throw new EnvelopeError(input.error.message);
12
+ }
13
+ const prompt = args.prompt(input.data);
14
+ const res = await client.structured({ prompt, jsonSchema });
15
+ const output = args.output.safeParse(res.structured);
16
+ if (!output.success) {
17
+ throw new EnvelopeError(`Model returned invalid structured output:\n${output.error.message}`);
18
+ }
19
+ return output.data;
20
+ };
21
+ }
22
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,GAAG,eAAe,CAAC;CACjC;AASD,MAAM,UAAU,cAAc,CAC5B,IAAmC;IAEnC,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,sBAAsB,EAAE,CAAC;IAEvD,OAAO,KAAK,EAAE,QAAiB,EAA0B,EAAE;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,UAAU,CAAU,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,aAAa,CACrB,8CAA8C,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CACrE,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from "./claude-code.js";
2
+ export * from "./codex-cli.js";
3
+ export * from "./client.js";
4
+ export * from "./envelope.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./claude-code.js";
2
+ export * from "./codex-cli.js";
3
+ export * from "./client.js";
4
+ export * from "./envelope.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@howells/envelope",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Strict Zod IO wrapper around local CLI LLMs (Claude Code, Codex), plus an AI SDK adapter.",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "license": "MIT",
9
+ "author": "Daniel Howells",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+ssh://git@github.com/howells/envelope.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/howells/envelope/issues"
16
+ },
17
+ "homepage": "https://github.com/howells/envelope#readme",
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./ai-sdk": {
27
+ "types": "./dist/ai-sdk.d.ts",
28
+ "default": "./dist/ai-sdk.js"
29
+ }
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "engines": {
35
+ "node": ">=20"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json",
39
+ "dev": "tsc -p tsconfig.json -w",
40
+ "prepack": "pnpm build",
41
+ "typecheck": "tsc -p tsconfig.json --noEmit"
42
+ },
43
+ "keywords": [
44
+ "llm",
45
+ "cli",
46
+ "claude",
47
+ "codex",
48
+ "zod",
49
+ "ai-sdk"
50
+ ],
51
+ "dependencies": {
52
+ "@ai-sdk/provider": "^1.0.0",
53
+ "@ai-sdk/provider-utils": "^3.0.0",
54
+ "zod": "^3.25.0",
55
+ "zod-to-json-schema": "^3.24.6"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "^22.0.0",
59
+ "typescript": "^5.7.0"
60
+ },
61
+ "packageManager": "pnpm@10.12.4"
62
+ }