@nebutra/image-pipeline 0.1.2 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @nebutra/image-pipeline
2
2
 
3
+ ## 0.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @nebutra/capability-kit@2.0.0
9
+ - @nebutra/errors@2.0.0
10
+ - @nebutra/generation-context@0.1.4
11
+
3
12
  ## 0.1.2
4
13
 
5
14
  ### Patch Changes
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@nebutra/image-pipeline",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "BrandContext-first image generation and workflow routing surface",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "nebutra": {
9
+ "graph": "labs",
9
10
  "status": "wip",
10
11
  "productionReady": false,
11
12
  "surface": "generation-capability",
@@ -22,15 +23,19 @@
22
23
  "category": "ai",
23
24
  "summary": "BrandContext-first image/logo/poster/icon generation surface"
24
25
  },
25
- "main": "./src/index.ts",
26
- "types": "./src/index.ts",
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
27
28
  "exports": {
28
- ".": "./src/index.ts"
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js",
32
+ "default": "./dist/index.js"
33
+ }
29
34
  },
30
35
  "dependencies": {
31
- "@nebutra/capability-kit": "0.2.2",
32
- "@nebutra/errors": "0.1.2",
33
- "@nebutra/generation-context": "0.1.2"
36
+ "@nebutra/capability-kit": "2.0.0",
37
+ "@nebutra/generation-context": "0.1.4",
38
+ "@nebutra/errors": "2.0.0"
34
39
  },
35
40
  "devDependencies": {
36
41
  "@types/node": "^25.9.1",
@@ -51,6 +56,12 @@
51
56
  "publishConfig": {
52
57
  "access": "public"
53
58
  },
59
+ "files": [
60
+ "dist",
61
+ "README.md",
62
+ "LICENSE",
63
+ "CHANGELOG.md"
64
+ ],
54
65
  "scripts": {
55
66
  "build": "tsup",
56
67
  "test": "vitest run",
@@ -1,22 +0,0 @@
1
-
2
- > @nebutra/image-pipeline@0.1.2 build /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/image-pipeline
3
- > tsup
4
-
5
- CLI Building entry: src/cli.ts, src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Using tsup config: /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/image-pipeline/tsup.config.ts
9
- CLI Target: es2022
10
- CLI Cleaning output folder
11
- ESM Build start
12
- ESM dist/chunk-CYXB6GKX.js 4.76 KB
13
- ESM dist/index.js 152.00 B
14
- ESM dist/cli.js 1.29 KB
15
- ESM dist/index.js.map 71.00 B
16
- ESM dist/cli.js.map 2.31 KB
17
- ESM dist/chunk-CYXB6GKX.js.map 9.26 KB
18
- ESM ⚡️ Build success in 22ms
19
- DTS Build start
20
- DTS ⚡️ Build success in 2047ms
21
- DTS dist/cli.d.ts 13.00 B
22
- DTS dist/index.d.ts 1.47 KB
@@ -1,14 +0,0 @@
1
-
2
- > @nebutra/image-pipeline@0.1.1 test /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/image-pipeline
3
- > vitest run
4
-
5
-
6
- RUN v4.1.4 /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/image-pipeline
7
-
8
- ✓ src/index.test.ts (2 tests) 31ms
9
-
10
- Test Files 1 passed (1)
11
- Tests 2 passed (2)
12
- Start at 09:38:39
13
- Duration 1.42s (transform 374ms, setup 0ms, import 524ms, tests 31ms, environment 0ms)
14
-
@@ -1,4 +0,0 @@
1
-
2
- > @nebutra/image-pipeline@0.1.2 typecheck /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/image-pipeline
3
- > tsc --noEmit
4
-
package/examples/logo.ts DELETED
@@ -1,9 +0,0 @@
1
- import { createDemoBrandContext } from "@nebutra/generation-context";
2
- import { ImagePipeline } from "../src/index";
3
-
4
- const asset = await new ImagePipeline().generate(
5
- { type: "logo", company: "Loop", oneLiner: "AI debugging for indie devs" },
6
- createDemoBrandContext(),
7
- );
8
-
9
- process.stdout.write(`${asset.path}\n`);
@@ -1,9 +0,0 @@
1
- import { createDemoBrandContext } from "@nebutra/generation-context";
2
- import { ImagePipeline } from "../src/index";
3
-
4
- const asset = await new ImagePipeline().generate(
5
- { type: "poster", title: "Ship with context", subtitle: "A founder operating system" },
6
- createDemoBrandContext(),
7
- );
8
-
9
- process.stdout.write(`${asset.path}\n`);
@@ -1,10 +0,0 @@
1
- import { createDemoBrandContext } from "@nebutra/generation-context";
2
- import { ImagePipeline } from "../src/index";
3
-
4
- const asset = await new ImagePipeline().runWorkflow(
5
- "hero",
6
- { headline: "Debug the loop" },
7
- createDemoBrandContext(),
8
- );
9
-
10
- process.stdout.write(`${asset.path}\n`);
package/src/cli.ts DELETED
@@ -1,32 +0,0 @@
1
- import { createDemoBrandContext } from "@nebutra/generation-context";
2
- import { ImagePipeline, readImageDebug } from "./index";
3
-
4
- const command = process.argv[2] ?? "doctor";
5
- const pipeline = new ImagePipeline();
6
-
7
- if (command === "doctor") {
8
- process.stdout.write(
9
- `${JSON.stringify({ capability: "image-pipeline", results: await pipeline.doctor() }, null, 2)}\n`,
10
- );
11
- } else if (command === "debug") {
12
- process.stdout.write(
13
- `${JSON.stringify({ capability: "image-pipeline", entries: await readImageDebug() }, null, 2)}\n`,
14
- );
15
- } else if (command === "warmup") {
16
- process.stdout.write(
17
- `${JSON.stringify({ capability: "image-pipeline", result: await pipeline.warmup() }, null, 2)}\n`,
18
- );
19
- } else if (command === "workflows") {
20
- process.stdout.write(
21
- `${JSON.stringify({ capability: "image-pipeline", workflows: await pipeline.workflows() }, null, 2)}\n`,
22
- );
23
- } else if (command === "quickstart") {
24
- const asset = await pipeline.generate(
25
- { type: "logo", company: "Loop", oneLiner: "AI debugging for indie devs" },
26
- createDemoBrandContext(),
27
- );
28
- process.stdout.write(`${JSON.stringify({ capability: "image-pipeline", asset }, null, 2)}\n`);
29
- } else {
30
- process.stderr.write(`Unknown image-pipeline command: ${command}\n`);
31
- process.exitCode = 1;
32
- }
package/src/index.test.ts DELETED
@@ -1,33 +0,0 @@
1
- import { mkdtemp, readFile, rm } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { createDemoBrandContext } from "@nebutra/generation-context";
5
- import { afterEach, describe, expect, it } from "vitest";
6
- import { ImagePipeline, readImageDebug } from "./index";
7
-
8
- let root: string | undefined;
9
-
10
- afterEach(async () => {
11
- if (root) await rm(root, { recursive: true, force: true });
12
- root = undefined;
13
- });
14
-
15
- describe("ImagePipeline", () => {
16
- it("requires BrandContext", async () => {
17
- const pipeline = new ImagePipeline();
18
- await expect(
19
- pipeline.generate({ type: "icon", subject: "debugger" }, undefined),
20
- ).rejects.toMatchObject({ capability: "image-pipeline", suggestion: expect.any(String) });
21
- });
22
-
23
- it("writes a deterministic SVG asset with brand metadata", async () => {
24
- root = await mkdtemp(join(tmpdir(), "image-pipeline-"));
25
- const asset = await new ImagePipeline({ root }).generate(
26
- { type: "logo", company: "Loop", oneLiner: "AI debugging" },
27
- createDemoBrandContext(),
28
- );
29
- expect(asset.format).toBe("svg");
30
- expect(await readFile(asset.path, "utf8")).toContain("Loop");
31
- expect(await readImageDebug(root)).toHaveLength(1);
32
- });
33
- });
package/src/index.ts DELETED
@@ -1,177 +0,0 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
2
- import { dirname, join } from "node:path";
3
- import { appendCapabilityDebug, readCapabilityDebug } from "@nebutra/capability-kit/debug";
4
- import { CapabilityError } from "@nebutra/errors";
5
- import {
6
- assetId,
7
- type BrandContext,
8
- type GeneratedAsset,
9
- requireBrandContext,
10
- summarizeBrandContext,
11
- } from "@nebutra/generation-context";
12
-
13
- export type ImageIntent =
14
- | { type: "logo"; company: string; oneLiner: string }
15
- | { type: "poster"; title: string; subtitle?: string }
16
- | { type: "icon"; subject: string }
17
- | { type: "hero"; headline: string }
18
- | { type: "style-transfer"; sourcePath: string; instruction: string };
19
-
20
- export interface ImageAsset extends GeneratedAsset {
21
- readonly kind: "image";
22
- readonly width: number;
23
- readonly height: number;
24
- readonly format: "svg" | "png" | "jpg" | "webp";
25
- }
26
-
27
- export interface ImageHealth {
28
- readonly provider: string;
29
- readonly ok: boolean;
30
- readonly suggestion?: string;
31
- }
32
-
33
- export interface ImagePipelineOptions {
34
- readonly root?: string;
35
- readonly provider?: "svg-local" | "comfy" | "remote";
36
- readonly comfyUrl?: string;
37
- }
38
-
39
- export async function readImageDebug(root = process.cwd(), limit = 10): Promise<unknown[]> {
40
- return readCapabilityDebug("image-pipeline", { root, limit });
41
- }
42
-
43
- function intentLabel(intent: ImageIntent): string {
44
- switch (intent.type) {
45
- case "logo":
46
- return `${intent.company}: ${intent.oneLiner}`;
47
- case "poster":
48
- return `${intent.title}${intent.subtitle ? ` — ${intent.subtitle}` : ""}`;
49
- case "icon":
50
- return intent.subject;
51
- case "hero":
52
- return intent.headline;
53
- case "style-transfer":
54
- return `${intent.sourcePath}: ${intent.instruction}`;
55
- }
56
- }
57
-
58
- function escapeXml(value: string): string {
59
- return value
60
- .replaceAll("&", "&amp;")
61
- .replaceAll("<", "&lt;")
62
- .replaceAll(">", "&gt;")
63
- .replaceAll('"', "&quot;");
64
- }
65
-
66
- function svgFor(intent: ImageIntent, brand: BrandContext): string {
67
- const primary = brand.palette[0]?.hex ?? "#111111";
68
- const accent = brand.palette.find((color) => color.role === "accent")?.hex ?? "#F3C14B";
69
- const label = intentLabel(intent);
70
- return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="800" viewBox="0 0 1200 800">
71
- <rect width="1200" height="800" fill="${escapeXml(primary)}"/>
72
- <circle cx="960" cy="160" r="96" fill="${escapeXml(accent)}" opacity="0.9"/>
73
- <text x="80" y="360" fill="#ffffff" font-family="${escapeXml(brand.typography.heading)}" font-size="78" font-weight="700">${escapeXml(brand.name)}</text>
74
- <text x="84" y="440" fill="#ffffff" font-family="${escapeXml(brand.typography.body)}" font-size="34">${escapeXml(label)}</text>
75
- <text x="84" y="520" fill="#ffffff" opacity="0.72" font-family="${escapeXml(brand.typography.body)}" font-size="24">${escapeXml(summarizeBrandContext(brand))}</text>
76
- </svg>`;
77
- }
78
-
79
- export class ImagePipeline {
80
- readonly #root: string;
81
- readonly #provider: "svg-local" | "comfy" | "remote";
82
- readonly #comfyUrl: string;
83
-
84
- constructor(options: ImagePipelineOptions = {}) {
85
- this.#root = options.root ?? process.cwd();
86
- this.#provider = options.provider ?? "svg-local";
87
- this.#comfyUrl = options.comfyUrl ?? process.env.COMFYUI_URL ?? "http://127.0.0.1:8188";
88
- }
89
-
90
- async generate(intent: ImageIntent, brandInput: BrandContext | undefined): Promise<ImageAsset> {
91
- const brand = requireBrandContext(brandInput, "image-pipeline");
92
- if (this.#provider !== "svg-local") {
93
- throw new CapabilityError("image-pipeline", `${this.#provider} provider is not configured`, {
94
- suggestion:
95
- "Use the zero-config svg-local provider or configure a sidecar adapter before selecting model-backed image generation.",
96
- statusCode: 503,
97
- });
98
- }
99
-
100
- const id = assetId("image", `${intent.type}_${intentLabel(intent)}`);
101
- const path = join(this.#root, ".nebutra", "generated", "image-pipeline", `${id}.svg`);
102
- await mkdir(dirname(path), { recursive: true });
103
- await writeFile(path, svgFor(intent, brand), "utf8");
104
-
105
- const asset: ImageAsset = {
106
- id,
107
- tenantId: brand.tenantId,
108
- kind: "image",
109
- path,
110
- brandId: brand.brandId,
111
- provider: "svg-local",
112
- model: "brand-context-svg-v1",
113
- createdAt: new Date().toISOString(),
114
- license: { status: "commercial-ok", source: "deterministic local renderer" },
115
- width: 1200,
116
- height: 800,
117
- format: "svg",
118
- metadata: { intent, brandSource: brand.sourcePath },
119
- };
120
- await appendCapabilityDebug(
121
- "image-pipeline",
122
- { type: "generate", asset },
123
- { root: this.#root },
124
- );
125
- return asset;
126
- }
127
-
128
- async runWorkflow(
129
- workflowName: string,
130
- inputs: Record<string, unknown>,
131
- brand: BrandContext | undefined,
132
- ): Promise<ImageAsset> {
133
- requireBrandContext(brand, "image-pipeline");
134
- return this.generate(
135
- {
136
- type: "poster",
137
- title: workflowName,
138
- subtitle: Object.keys(inputs).join(", ") || "workflow",
139
- },
140
- brand,
141
- );
142
- }
143
-
144
- async doctor(): Promise<ImageHealth[]> {
145
- const comfyConfigured = Boolean(process.env.COMFYUI_URL);
146
- return [
147
- { provider: "svg-local", ok: true },
148
- comfyConfigured
149
- ? { provider: "comfy", ok: true }
150
- : {
151
- provider: "comfy",
152
- ok: false,
153
- suggestion: `Set COMFYUI_URL to enable model-backed workflow execution. Default probe target is ${this.#comfyUrl}.`,
154
- },
155
- {
156
- provider: "remote",
157
- ok: Boolean(process.env.IMAGE_REMOTE_API_KEY),
158
- suggestion: "Set IMAGE_REMOTE_API_KEY to enable remote image fallback.",
159
- },
160
- ];
161
- }
162
-
163
- async workflows(): Promise<readonly string[]> {
164
- return ["logo", "poster", "icon", "hero", "style-transfer"];
165
- }
166
-
167
- async warmup(): Promise<ImageHealth> {
168
- if (this.#provider === "svg-local") {
169
- return { provider: this.#provider, ok: true };
170
- }
171
- return {
172
- provider: this.#provider,
173
- ok: false,
174
- suggestion: "Start the image sidecar before warming model weights.",
175
- };
176
- }
177
- }
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "target": "esnext",
7
- "types": ["node"],
8
- "incremental": false
9
- },
10
- "include": ["src", "examples"],
11
- "exclude": ["node_modules", "dist"]
12
- }
package/tsup.config.ts DELETED
@@ -1,11 +0,0 @@
1
- import { defineConfig } from "tsup";
2
-
3
- export default defineConfig({
4
- entry: ["src/index.ts", "src/cli.ts"],
5
- format: ["esm"],
6
- dts: true,
7
- sourcemap: true,
8
- clean: true,
9
- target: "es2022",
10
- external: ["@nebutra/capability-kit/debug", "@nebutra/errors", "@nebutra/generation-context"],
11
- });