@crewhaus/tool-image-generation 0.1.1 → 0.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewhaus/tool-image-generation",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "M4.1 — image-generation tool (DALL-E 3, Replicate, future Flux/SD). Calls remote API; no host writes; non-destructive.",
6
6
  "main": "src/index.ts",
@@ -12,16 +12,16 @@
12
12
  "test": "bun test src"
13
13
  },
14
14
  "dependencies": {
15
- "@crewhaus/errors": "0.1.1",
16
- "@crewhaus/tool-builder": "0.1.1",
17
- "@crewhaus/tool-catalog": "0.1.1",
15
+ "@crewhaus/errors": "0.1.2",
16
+ "@crewhaus/tool-builder": "0.1.2",
17
+ "@crewhaus/tool-catalog": "0.1.2",
18
18
  "zod": "^3.23.8"
19
19
  },
20
20
  "license": "Apache-2.0",
21
21
  "author": {
22
22
  "name": "Max Meier",
23
- "email": "max@studiomax.io",
24
- "url": "https://studiomax.io"
23
+ "email": "max@crewhaus.ai",
24
+ "url": "https://crewhaus.ai"
25
25
  },
26
26
  "repository": {
27
27
  "type": "git",
@@ -33,12 +33,7 @@
33
33
  "url": "https://github.com/crewhaus/factory/issues"
34
34
  },
35
35
  "publishConfig": {
36
- "access": "restricted"
36
+ "access": "public"
37
37
  },
38
- "files": [
39
- "src",
40
- "README.md",
41
- "LICENSE",
42
- "NOTICE"
43
- ]
38
+ "files": ["src", "README.md", "LICENSE", "NOTICE"]
44
39
  }
package/src/index.test.ts CHANGED
@@ -100,6 +100,25 @@ describe("imageGenerate — openai provider", () => {
100
100
  await expect(imageGenerate.execute({ prompt: "x" })).rejects.toThrow(ImageGenerationError);
101
101
  });
102
102
 
103
+ test("error body read failure is swallowed by .catch(() => '')", async () => {
104
+ process.env["OPENAI_API_KEY"] = "sk-test";
105
+ // A non-ok response whose .text() itself rejects exercises the
106
+ // `.catch(() => "")` fallback on the error path (line ~145).
107
+ const badResponse = {
108
+ ok: false,
109
+ status: 500,
110
+ statusText: "Internal Server Error",
111
+ text: async () => {
112
+ throw new Error("stream already consumed");
113
+ },
114
+ } as unknown as Response;
115
+ const mockFetch: typeof globalThis.fetch = async () => badResponse;
116
+ registerImageGenerationConfig({ provider: "openai", fetch: mockFetch });
117
+ await expect(imageGenerate.execute({ prompt: "x" })).rejects.toThrow(
118
+ /OpenAI image-generation request failed \(500/,
119
+ );
120
+ });
121
+
103
122
  test("b64_json response format returns a data URI preview", async () => {
104
123
  process.env["OPENAI_API_KEY"] = "sk-test";
105
124
  const longB64 = "A".repeat(500);
@@ -111,3 +130,40 @@ describe("imageGenerate — openai provider", () => {
111
130
  expect(out).toContain("[truncated 500 bytes total]");
112
131
  });
113
132
  });
133
+
134
+ describe("imageGenerate — defaultProvider env routing (no explicit provider)", () => {
135
+ test("OPENAI_API_KEY present → routes to openai (uses injected fetch)", async () => {
136
+ // No `provider` in config forces execute() through defaultProvider(env).
137
+ process.env["OPENAI_API_KEY"] = "sk-test";
138
+ process.env["CREWHAUS_IMAGE_PROVIDER"] = undefined;
139
+ let called = false;
140
+ const mockFetch: typeof globalThis.fetch = async () => {
141
+ called = true;
142
+ return new Response(JSON.stringify({ data: [{ url: "https://example.com/auto.png" }] }), {
143
+ status: 200,
144
+ });
145
+ };
146
+ registerImageGenerationConfig({ fetch: mockFetch });
147
+ const out = await imageGenerate.execute({ prompt: "auto-routed" });
148
+ expect(called).toBe(true);
149
+ expect(out).toContain("https://example.com/auto.png");
150
+ });
151
+
152
+ test("no API key but CREWHAUS_IMAGE_PROVIDER=mock → routes to mock", async () => {
153
+ process.env["OPENAI_API_KEY"] = undefined;
154
+ process.env["CREWHAUS_IMAGE_PROVIDER"] = "mock";
155
+ registerImageGenerationConfig({});
156
+ const out = await imageGenerate.execute({ prompt: "offline lobster" });
157
+ expect(out).toContain("mock image generated");
158
+ expect(out).toContain("offline lobster");
159
+ });
160
+
161
+ test("neither env var set → falls through to openai, surfacing missing-key error", async () => {
162
+ process.env["OPENAI_API_KEY"] = undefined;
163
+ process.env["CREWHAUS_IMAGE_PROVIDER"] = undefined;
164
+ registerImageGenerationConfig({});
165
+ // defaultProvider returns "openai" (the documented fall-through); execute
166
+ // then throws the clear missing-key error from generateOpenAI.
167
+ await expect(imageGenerate.execute({ prompt: "x" })).rejects.toThrow(ImageGenerationError);
168
+ });
169
+ });
package/src/index.ts CHANGED
@@ -87,6 +87,8 @@ export const imageGenerate: RegisteredTool = buildTool({
87
87
  // Pillar 3 sink-side: the prompt is sent to a remote provider; lineage
88
88
  // exfiltration via prompt smuggling is real.
89
89
  scope: "external",
90
+ // FR-002 — declare the io-capability fact (remote image-gen API call).
91
+ ioCapability: "network",
90
92
  execute: async (input) => {
91
93
  const cfg = registeredConfig ?? {};
92
94
  const provider = cfg.provider ?? defaultProvider(process.env);