sintetica-cli 0.3.0 → 0.4.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.
@@ -84,47 +84,89 @@ exports.generateCommand
84
84
  .option('--product <id>', 'Product ID')
85
85
  .option('--style <style>', 'Style preset', 'minimal')
86
86
  .option('--format <format>', 'Image format', 'instagram_square')
87
- .option('--count <n>', 'Number of variations', '4')
88
- .option('--prompt <text>', 'Custom creative direction (e.g. "zen spa setting with candles")')
89
- .option('--brand-colors <colors>', 'Brand colors, comma-separated (e.g. "#1a1a1a,#d4af37")')
90
- .option('--brand-voice <voice>', 'Brand voice (e.g. "premium", "playful", "clinical")')
91
- .option('--target-audience <audience>', 'Target audience (e.g. "health-conscious millennials")')
92
- .option('--background <bg>', 'Background style (e.g. "marble surface", "gym setting", "kitchen counter")')
87
+ .option('--count <n>', 'Number of images per job', '1')
88
+ .option('--prompt <text>', 'Custom creative direction')
89
+ .option('--shots <json>', 'Multiple shots with different prompts (JSON array of strings)')
90
+ .option('--brand-colors <colors>', 'Brand colors, comma-separated')
91
+ .option('--brand-voice <voice>', 'Brand voice')
92
+ .option('--target-audience <audience>', 'Target audience')
93
+ .option('--background <bg>', 'Background style')
93
94
  .option('--keywords <words>', 'Brand keywords, comma-separated')
94
95
  .option('--wait', 'Wait for completion')
95
96
  .action(async (opts) => {
96
- if (opts.product) {
97
- try {
98
- const result = await (0, api_js_1.api)('/api/cli/generate', {
99
- body: {
100
- productId: opts.product,
101
- style: opts.style,
102
- format: opts.format,
103
- count: parseInt(opts.count),
104
- prompt: opts.prompt,
105
- brandColors: opts.brandColors?.split(',').map(c => c.trim()),
106
- brandVoice: opts.brandVoice,
107
- targetAudience: opts.targetAudience,
108
- background: opts.background,
109
- keywords: opts.keywords?.split(',').map(k => k.trim()),
110
- },
111
- });
112
- if (!opts.wait) {
113
- console.log(JSON.stringify(result));
97
+ if (!opts.product) {
98
+ exports.generateCommand.help();
99
+ return;
100
+ }
101
+ try {
102
+ const baseBody = {
103
+ productId: opts.product,
104
+ style: opts.style,
105
+ format: opts.format,
106
+ brandColors: opts.brandColors?.split(',').map(c => c.trim()),
107
+ brandVoice: opts.brandVoice,
108
+ targetAudience: opts.targetAudience,
109
+ background: opts.background,
110
+ keywords: opts.keywords?.split(',').map(k => k.trim()),
111
+ };
112
+ // Multi-shot mode: separate jobs with different prompts
113
+ if (opts.shots) {
114
+ let shotPrompts;
115
+ try {
116
+ shotPrompts = JSON.parse(opts.shots);
117
+ if (!Array.isArray(shotPrompts))
118
+ throw new Error('not an array');
119
+ }
120
+ catch {
121
+ console.error(JSON.stringify({ error: '--shots must be a JSON array of strings, e.g. \'["prompt 1","prompt 2"]\'' }));
122
+ process.exit(1);
114
123
  return;
115
124
  }
116
- process.stderr.write(` Job ${result.jobId} started (${result.creditsCost} credits)\n`);
117
- const finalStatus = await pollUntilDone(result.jobId);
118
- process.stderr.write('\n');
119
- console.log(JSON.stringify(finalStatus));
125
+ const allAssets = [];
126
+ const jobs = [];
127
+ for (let i = 0; i < shotPrompts.length; i++) {
128
+ const shotPrompt = shotPrompts[i];
129
+ process.stderr.write(` Shot ${i + 1}/${shotPrompts.length}: submitting...\n`);
130
+ const result = await (0, api_js_1.api)('/api/cli/generate', {
131
+ body: { ...baseBody, count: 1, prompt: shotPrompt },
132
+ });
133
+ jobs.push({ jobId: result.jobId, prompt: shotPrompt });
134
+ process.stderr.write(` Shot ${i + 1}: job ${result.jobId} (${result.creditsCost} credits)\n`);
135
+ }
136
+ if (opts.wait) {
137
+ for (let i = 0; i < jobs.length; i++) {
138
+ process.stderr.write(` Waiting for shot ${i + 1}/${jobs.length}...`);
139
+ const status = await pollUntilDone(jobs[i].jobId);
140
+ process.stderr.write('\n');
141
+ allAssets.push(...status.assets);
142
+ }
143
+ console.log(JSON.stringify({
144
+ shots: jobs.length,
145
+ assets: allAssets,
146
+ jobIds: jobs.map(j => j.jobId),
147
+ }));
148
+ }
149
+ else {
150
+ console.log(JSON.stringify({ jobs }));
151
+ }
152
+ return;
120
153
  }
121
- catch (err) {
122
- console.error(JSON.stringify({ error: err instanceof Error ? err.message : 'Generation failed' }));
123
- process.exit(1);
154
+ // Single shot mode
155
+ const result = await (0, api_js_1.api)('/api/cli/generate', {
156
+ body: { ...baseBody, count: parseInt(opts.count), prompt: opts.prompt },
157
+ });
158
+ if (!opts.wait) {
159
+ console.log(JSON.stringify(result));
160
+ return;
124
161
  }
162
+ process.stderr.write(` Job ${result.jobId} started (${result.creditsCost} credits)\n`);
163
+ const finalStatus = await pollUntilDone(result.jobId);
164
+ process.stderr.write('\n');
165
+ console.log(JSON.stringify(finalStatus));
125
166
  }
126
- else {
127
- exports.generateCommand.help();
167
+ catch (err) {
168
+ console.error(JSON.stringify({ error: err instanceof Error ? err.message : 'Generation failed' }));
169
+ process.exit(1);
128
170
  }
129
171
  });
130
172
  exports.generateCommand.addCommand(statusCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sintetica-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Sintetica CLI — AI product photography from your terminal. Generate professional product photos with one command.",
5
5
  "keywords": ["sintetica", "ai", "product-photography", "ecommerce", "shopify", "cli", "claude-code"],
6
6
  "author": "Sintetica <hello@sintetica.ro>",