studio-lumiere-cli 0.1.4 → 0.1.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.
Files changed (59) hide show
  1. package/dist/config/constants.d.ts +1 -63
  2. package/dist/config/constants.js +3 -1004
  3. package/dist/config/constants.js.map +1 -1
  4. package/dist/pipelines/generateImages.js +5 -3
  5. package/dist/pipelines/generateImages.js.map +1 -1
  6. package/dist/storage/files.js +4 -1
  7. package/dist/storage/files.js.map +1 -1
  8. package/dist/studio/constants.d.ts +64 -0
  9. package/dist/studio/constants.js +1018 -0
  10. package/dist/studio/constants.js.map +1 -0
  11. package/dist/studio/promptBuilder.d.ts +4 -0
  12. package/{src/prompt/buildPrompt.ts → dist/studio/promptBuilder.js} +166 -221
  13. package/dist/studio/promptBuilder.js.map +1 -0
  14. package/dist/studio/types.d.ts +155 -0
  15. package/dist/studio/types.js +2 -0
  16. package/dist/studio/types.js.map +1 -0
  17. package/dist/types.d.ts +2 -49
  18. package/package.json +9 -3
  19. package/scripts/sync-studio.mjs +16 -0
  20. package/.agents/skills/generate-images/SKILL.md +0 -121
  21. package/.env.example +0 -2
  22. package/AGENTS.md +0 -66
  23. package/examples/generate.d.ts +0 -1
  24. package/examples/generate.js +0 -28
  25. package/examples/generate.js.map +0 -1
  26. package/examples/generate.ts +0 -30
  27. package/examples/muse.d.ts +0 -1
  28. package/examples/muse.js +0 -18
  29. package/examples/muse.js.map +0 -1
  30. package/examples/muse.ts +0 -20
  31. package/examples/video.d.ts +0 -1
  32. package/examples/video.js +0 -18
  33. package/examples/video.js.map +0 -1
  34. package/examples/video.ts +0 -20
  35. package/logo-round.png +0 -0
  36. package/logo.jpeg +0 -0
  37. package/skills/studio-lumiere-cli/SKILL.md +0 -212
  38. package/skills/studio-lumiere-cli/agents/openai.yaml +0 -4
  39. package/src/cli.ts +0 -259
  40. package/src/clients/geminiClient.ts +0 -168
  41. package/src/config/constants.ts +0 -1105
  42. package/src/config/options.ts +0 -15
  43. package/src/config/templates.ts +0 -4
  44. package/src/config/tiredGirl.ts +0 -11
  45. package/src/image/annotate.ts +0 -139
  46. package/src/image/grid.ts +0 -58
  47. package/src/index.ts +0 -27
  48. package/src/pipelines/createMuse.ts +0 -76
  49. package/src/pipelines/generateImages.ts +0 -203
  50. package/src/pipelines/generateTiredGirl.ts +0 -86
  51. package/src/pipelines/generateVideo.ts +0 -36
  52. package/src/pipelines/refineImage.ts +0 -36
  53. package/src/pipelines/resolve.ts +0 -88
  54. package/src/pipelines/upscaleImage.ts +0 -30
  55. package/src/prompt/tiredGirlPrompt.ts +0 -35
  56. package/src/storage/files.ts +0 -44
  57. package/src/storage/museStore.ts +0 -31
  58. package/src/types.ts +0 -198
  59. package/tsconfig.json +0 -15
@@ -1,36 +0,0 @@
1
- import path from "node:path";
2
- import { GeminiClient } from "../clients/geminiClient.js";
3
- import { LumiereConfig, RefineRequest } from "../types.js";
4
- import { readImageAsPart, resolveOutputDir, saveBase64Image, writeJson } from "../storage/files.js";
5
-
6
- export const refineImage = async (config: LumiereConfig, request: RefineRequest): Promise<string> => {
7
- const client = new GeminiClient(config);
8
- const outputDir = resolveOutputDir(request.outputDir ?? config.outputDir, "refinements");
9
-
10
- const sizeAdjustment = request.sizeAdjustment;
11
- let prompt = `You are an expert high-end jewelry photo retoucher. Refine this image according to: "${request.instruction}". Preserve jewelry design and materials. Maintain photorealism.`;
12
-
13
- if (sizeAdjustment !== undefined && sizeAdjustment !== 100) {
14
- const direction = sizeAdjustment < 100 ? "smaller" : "larger";
15
- prompt = `Adjust the size of all jewelry pieces to ${sizeAdjustment}% of current size (${direction}). Preserve all details, lighting, and composition. ${request.instruction}`;
16
- }
17
-
18
- const imagePart = await readImageAsPart(request.inputImage);
19
- const images = await client.generateImage({
20
- parts: [{ text: prompt }, imagePart],
21
- imageSize: "2K"
22
- });
23
-
24
- const outputPath = path.join(outputDir, "refined.png");
25
- await saveBase64Image(images[0], outputPath);
26
-
27
- await writeJson(path.join(outputDir, "refine.json"), {
28
- inputImage: request.inputImage,
29
- instruction: request.instruction,
30
- sizeAdjustment,
31
- outputPath
32
- });
33
-
34
- return outputPath;
35
- };
36
-
@@ -1,88 +0,0 @@
1
- import { Option, OccasionTheme, Resolution, Template } from "../types.js";
2
- import { TEMPLATE_MAP } from "../config/templates.js";
3
- import {
4
- BACKGROUNDS,
5
- BACKGROUND_TYPES,
6
- ETHNICITIES,
7
- HAIR_COLORS,
8
- OCCASIONS,
9
- RESOLUTIONS,
10
- SKIN_TONES,
11
- VIBES
12
- } from "../config/options.js";
13
-
14
- const optionMap = (options: Option[]): Map<string, Option> => {
15
- return new Map(options.map((opt) => [opt.id, opt]));
16
- };
17
-
18
- const ETHNICITY_MAP = optionMap(ETHNICITIES);
19
- const SKIN_TONE_MAP = optionMap(SKIN_TONES);
20
- const HAIR_COLOR_MAP = optionMap(HAIR_COLORS);
21
- const BACKGROUND_MAP = optionMap(BACKGROUNDS);
22
- const BACKGROUND_TYPE_MAP = optionMap(BACKGROUND_TYPES);
23
- const VIBE_MAP = optionMap(VIBES);
24
- const OCCASION_MAP = new Map(OCCASIONS.map((o) => [o.id, o]));
25
- const RESOLUTION_MAP = new Map(RESOLUTIONS.map((r) => [r.id, r]));
26
-
27
- export const getTemplate = (id: string): Template => {
28
- const template = TEMPLATE_MAP.get(id);
29
- if (!template) {
30
- throw new Error(`Unknown template: ${id}`);
31
- }
32
- return template;
33
- };
34
-
35
- export const resolveTemplateOption = (template: Template, id?: string, which?: "primary" | "secondary" | "tertiary"): Option | undefined => {
36
- if (!id) return undefined;
37
- const options =
38
- which === "secondary"
39
- ? template.secondaryCustomizationOptions
40
- : which === "tertiary"
41
- ? template.tertiaryCustomizationOptions
42
- : template.customizationOptions;
43
-
44
- return options?.find((opt: Option) => opt.id === id) ?? { id, name: id, value: id };
45
- };
46
-
47
- export const resolveEthnicity = (id?: string): Option | undefined => {
48
- if (!id) return undefined;
49
- return ETHNICITY_MAP.get(id) ?? { id, name: id, value: id };
50
- };
51
-
52
- export const resolveSkinTone = (id?: string): Option | undefined => {
53
- if (!id) return undefined;
54
- return SKIN_TONE_MAP.get(id) ?? { id, name: id, value: id };
55
- };
56
-
57
- export const resolveHairColor = (id?: string): Option | undefined => {
58
- if (!id) return undefined;
59
- return HAIR_COLOR_MAP.get(id) ?? { id, name: id, value: id };
60
- };
61
-
62
- export const resolveBackground = (id?: string): Option | undefined => {
63
- if (!id) return undefined;
64
- return BACKGROUND_MAP.get(id) ?? { id, name: id, value: id };
65
- };
66
-
67
- export const resolveBackgroundType = (id?: string): Option | undefined => {
68
- if (!id) return undefined;
69
- return BACKGROUND_TYPE_MAP.get(id) ?? { id, name: id, value: id };
70
- };
71
-
72
- export const resolveVibe = (id?: string): Option | undefined => {
73
- if (!id) return undefined;
74
- return VIBE_MAP.get(id) ?? { id, name: id, value: id };
75
- };
76
-
77
- export const resolveOccasion = (id?: string): OccasionTheme | undefined => {
78
- if (!id) return undefined;
79
- return OCCASION_MAP.get(id);
80
- };
81
-
82
- export const resolveResolution = (id?: string): Resolution => {
83
- const fallback = RESOLUTION_MAP.get("portrait") ?? Array.from(RESOLUTION_MAP.values())[0];
84
- if (!fallback) throw new Error("No resolutions configured.");
85
- if (!id) return fallback;
86
- return RESOLUTION_MAP.get(id) ?? fallback;
87
- };
88
-
@@ -1,30 +0,0 @@
1
- import path from "node:path";
2
- import { GeminiClient } from "../clients/geminiClient.js";
3
- import { LumiereConfig, UpscaleRequest } from "../types.js";
4
- import { readImageAsPart, resolveOutputDir, saveBase64Image, writeJson } from "../storage/files.js";
5
-
6
- export const upscaleImage = async (config: LumiereConfig, request: UpscaleRequest): Promise<string> => {
7
- const client = new GeminiClient(config);
8
- const outputDir = resolveOutputDir(request.outputDir ?? config.outputDir, "upscales");
9
-
10
- const scale = request.scale ?? 2;
11
- const prompt = `Upscale this image to ${scale}x resolution. Preserve all details, composition, and color. Do not change jewelry design, lighting, or background.`;
12
-
13
- const imagePart = await readImageAsPart(request.inputImage);
14
- const images = await client.generateImage({
15
- parts: [{ text: prompt }, imagePart],
16
- imageSize: scale >= 4 ? "4K" : "2K"
17
- });
18
-
19
- const outputPath = path.join(outputDir, "upscaled.png");
20
- await saveBase64Image(images[0], outputPath);
21
-
22
- await writeJson(path.join(outputDir, "upscale.json"), {
23
- inputImage: request.inputImage,
24
- scale,
25
- outputPath
26
- });
27
-
28
- return outputPath;
29
- };
30
-
@@ -1,35 +0,0 @@
1
- import { Option } from "../types.js";
2
- import { TIRED_GIRL_STYLE_MAP, TIRED_GIRL_STYLES } from "../config/tiredGirl.js";
3
-
4
- export const resolveTiredGirlStyles = (styleIds?: string[], quantity: number = 1): Option[] => {
5
- if (styleIds && styleIds.length > 0) {
6
- const resolved = styleIds
7
- .map((id) => TIRED_GIRL_STYLE_MAP.get(id))
8
- .filter((style): style is Option => !!style);
9
- if (resolved.length > 0) return resolved;
10
- }
11
-
12
- // Default cycling through predefined styles
13
- const styles: Option[] = [];
14
- for (let i = 0; i < quantity; i += 1) {
15
- styles.push(TIRED_GIRL_STYLES[i % TIRED_GIRL_STYLES.length]);
16
- }
17
- return styles;
18
- };
19
-
20
- export const buildTiredGirlPrompt = (style: Option, hasMuse: boolean): string => {
21
- return `You are a world-renowned fashion photographer.
22
- Your task is to write a prompt for an AI image generator to create a photorealistic portrait of a model in a "before" look.
23
-
24
- CRITICAL GUIDELINES:
25
- 1. JEWELRY REMOVAL: The final image must contain NO jewelry whatsoever (no rings, earrings, necklaces, bracelets, watches).
26
- - If jewelry appears in reference images, REMOVE it.
27
- 2. IDENTITY CONSISTENCY: The model must match the reference images exactly (same face, features, skin tone).
28
- ${hasMuse ? "3. The reference images are of the same model (Muse). Use them to preserve identity." : "3. Use the reference image to preserve identity."}
29
- 4. STYLE: ${style.value}
30
- 5. WARDROBE: casual, at-home, or relaxed styling (if not explicitly specified in style).
31
- 6. LIGHTING: naturalistic, soft, real-world light.
32
- 7. REALISM: no stylized effects, no glamour retouching.
33
-
34
- OUTPUT: A single, dense prompt describing the scene. Do not include any jewelry.`;
35
- };
@@ -1,44 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import fsSync from "node:fs";
3
- import path from "node:path";
4
-
5
- export const ensureDir = async (dir: string): Promise<void> => {
6
- await fs.mkdir(dir, { recursive: true });
7
- };
8
-
9
- export const readFileAsBase64 = async (filePath: string): Promise<{ data: string; mimeType: string }> => {
10
- const buffer = await fs.readFile(filePath);
11
- const ext = path.extname(filePath).toLowerCase();
12
- const mimeType = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
13
- return { data: buffer.toString("base64"), mimeType };
14
- };
15
-
16
- export const readImageAsPart = async (filePath: string): Promise<{ inlineData: { mimeType: string; data: string } }> => {
17
- const { data, mimeType } = await readFileAsBase64(filePath);
18
- return { inlineData: { data, mimeType } };
19
- };
20
-
21
- export const saveBase64Image = async (base64DataUrl: string, outputPath: string): Promise<void> => {
22
- const base64 = base64DataUrl.includes(",") ? base64DataUrl.split(",")[1] : base64DataUrl;
23
- const buffer = Buffer.from(base64, "base64");
24
- await ensureDir(path.dirname(outputPath));
25
- await fs.writeFile(outputPath, buffer);
26
- };
27
-
28
- export const saveBinary = async (bytes: Uint8Array, outputPath: string): Promise<void> => {
29
- await ensureDir(path.dirname(outputPath));
30
- await fs.writeFile(outputPath, Buffer.from(bytes));
31
- };
32
-
33
- export const writeJson = async (outputPath: string, payload: unknown): Promise<void> => {
34
- await ensureDir(path.dirname(outputPath));
35
- await fs.writeFile(outputPath, JSON.stringify(payload, null, 2));
36
- };
37
-
38
- export const resolveOutputDir = (baseDir: string, subdir: string): string => {
39
- const stamp = new Date().toISOString().replace(/[:.]/g, "-");
40
- const outputDir = path.join(baseDir, subdir, stamp);
41
- fsSync.mkdirSync(outputDir, { recursive: true });
42
- return outputDir;
43
- };
44
-
@@ -1,31 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { MuseRecord } from "../types.js";
4
-
5
- const museIndexPath = (baseDir: string) => path.join(baseDir, "muses.json");
6
-
7
- export const loadMuses = async (baseDir: string): Promise<MuseRecord[]> => {
8
- try {
9
- const raw = await fs.readFile(museIndexPath(baseDir), "utf-8");
10
- return JSON.parse(raw) as MuseRecord[];
11
- } catch {
12
- return [];
13
- }
14
- };
15
-
16
- export const saveMuses = async (baseDir: string, muses: MuseRecord[]): Promise<void> => {
17
- await fs.mkdir(baseDir, { recursive: true });
18
- await fs.writeFile(museIndexPath(baseDir), JSON.stringify(muses, null, 2));
19
- };
20
-
21
- export const addMuse = async (baseDir: string, muse: MuseRecord): Promise<void> => {
22
- const muses = await loadMuses(baseDir);
23
- muses.push(muse);
24
- await saveMuses(baseDir, muses);
25
- };
26
-
27
- export const getMuseById = async (baseDir: string, id: string): Promise<MuseRecord | undefined> => {
28
- const muses = await loadMuses(baseDir);
29
- return muses.find((m) => m.id === id);
30
- };
31
-
package/src/types.ts DELETED
@@ -1,198 +0,0 @@
1
- export type AspectRatio = "1:1" | "3:4" | "4:3" | "9:16" | "16:9";
2
-
3
- export interface Option {
4
- id: string;
5
- name: string;
6
- value: string;
7
- visualColor?: string;
8
- visualImage?: string;
9
- }
10
-
11
- export interface Template {
12
- id: string;
13
- name: string;
14
- description: string;
15
- visualUrl?: string;
16
- basePrompt: string;
17
- customizationLabel?: string;
18
- customizationOptions?: Option[];
19
- secondaryCustomizationLabel?: string;
20
- secondaryCustomizationOptions?: Option[];
21
- tertiaryCustomizationLabel?: string;
22
- tertiaryCustomizationOptions?: Option[];
23
- isAdminOnly?: boolean;
24
- museEnabled?: boolean;
25
- videoEnabled?: boolean;
26
- }
27
-
28
- export interface Resolution {
29
- id: string;
30
- label: string;
31
- aspectRatio: AspectRatio;
32
- icon?: string;
33
- description: string;
34
- instagramUse: string;
35
- }
36
-
37
- export interface OccasionTheme {
38
- id: string;
39
- name: string;
40
- icon: string;
41
- description: string;
42
- autoConfig: {
43
- vibe: string;
44
- background: string;
45
- backgroundType: string;
46
- };
47
- promptDetails?: {
48
- setting: string;
49
- atmosphere: string;
50
- colorPalette: string;
51
- culturalElements: string;
52
- styling: string;
53
- };
54
- }
55
-
56
- export interface GenerationSelections {
57
- templateId: string;
58
- detailId?: string;
59
- secondaryDetailId?: string;
60
- tertiaryDetailId?: string;
61
- ethnicityId?: string;
62
- skinToneId?: string;
63
- hairColorId?: string;
64
- backgroundId?: string;
65
- backgroundTypeId?: string;
66
- vibeId?: string;
67
- resolutionId?: string;
68
- occasionId?: string;
69
- }
70
-
71
- export interface GenerationRequest {
72
- inputImages: string[];
73
- quantity?: number;
74
- selections: GenerationSelections;
75
- styleHint?: string;
76
- museId?: string;
77
- museImagePaths?: string[];
78
- outputDir?: string;
79
- enhancePrompt?: boolean;
80
- }
81
-
82
- export interface GenerationResult {
83
- prompt: string;
84
- enhancedPrompt?: string;
85
- outputImages: string[];
86
- logPath: string;
87
- }
88
-
89
- export interface RefineRequest {
90
- inputImage: string;
91
- instruction: string;
92
- sizeAdjustment?: number;
93
- outputDir?: string;
94
- }
95
-
96
- export interface UpscaleRequest {
97
- inputImage: string;
98
- scale?: number;
99
- outputDir?: string;
100
- }
101
-
102
- export interface MuseRecord {
103
- id: string;
104
- name: string;
105
- imagePaths: string[];
106
- createdAt: string;
107
- }
108
-
109
- export interface CreateMuseRequest {
110
- name: string;
111
- sourceImage: string;
112
- variations?: number;
113
- outputDir?: string;
114
- }
115
-
116
- export interface CreateMuseResult {
117
- muse: MuseRecord;
118
- variationPaths: string[];
119
- logPath: string;
120
- }
121
-
122
- export interface VideoRequest {
123
- prompt: string;
124
- imageInput?: string;
125
- durationSeconds?: number;
126
- aspectRatio?: AspectRatio;
127
- outputDir?: string;
128
- }
129
-
130
- export interface VideoResult {
131
- operationName?: string;
132
- videoPath?: string;
133
- logPath: string;
134
- }
135
-
136
- export interface TiredGirlRequest {
137
- inputImage?: string;
138
- museId?: string;
139
- styleIds?: string[];
140
- quantity?: number;
141
- outputDir?: string;
142
- }
143
-
144
- export interface TiredGirlResult {
145
- outputImages: string[];
146
- logPath: string;
147
- }
148
-
149
- export type TextPosition =
150
- | "top-center"
151
- | "top-left"
152
- | "top-right"
153
- | "bottom-center"
154
- | "bottom-center-high"
155
- | "bottom-left"
156
- | "bottom-right"
157
- | "center";
158
-
159
- export interface TextOverlayOptions {
160
- fontFamily?: string;
161
- fontSize?: number;
162
- fontWeight?: number;
163
- color?: string;
164
- strokeColor?: string;
165
- strokeWidth?: number;
166
- banner?: boolean;
167
- bannerColor?: string;
168
- bannerPadding?: number;
169
- bannerRadius?: number;
170
- position?: TextPosition;
171
- padding?: number;
172
- maxWidthRatio?: number;
173
- }
174
-
175
- export interface GridOptions {
176
- columns: number;
177
- rows: number;
178
- padding?: number;
179
- background?: string;
180
- tileWidth?: number;
181
- tileHeight?: number;
182
- }
183
-
184
- export interface LumiereConfig {
185
- apiKey: string;
186
- outputDir: string;
187
- models?: {
188
- prompt?: string;
189
- image?: string;
190
- video?: string;
191
- };
192
- retry?: {
193
- maxRetries?: number;
194
- baseDelayMs?: number;
195
- maxDelayMs?: number;
196
- };
197
- }
198
-
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "dist",
7
- "rootDir": "src",
8
- "declaration": true,
9
- "sourceMap": true,
10
- "strict": true,
11
- "esModuleInterop": true,
12
- "skipLibCheck": true
13
- },
14
- "include": ["src"]
15
- }