sintetica-cli 0.1.0 → 0.3.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.
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const brandCommand: Command;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.brandCommand = void 0;
4
+ const commander_1 = require("commander");
5
+ const api_js_1 = require("../lib/api.js");
6
+ const getCommand = new commander_1.Command('get')
7
+ .description('View current brand settings')
8
+ .action(async () => {
9
+ try {
10
+ const data = await (0, api_js_1.api)('/api/cli/brand');
11
+ console.log(JSON.stringify(data));
12
+ }
13
+ catch (err) {
14
+ console.error(JSON.stringify({ error: err instanceof Error ? err.message : 'Failed' }));
15
+ process.exit(1);
16
+ }
17
+ });
18
+ const setCommand = new commander_1.Command('set')
19
+ .description('Update brand settings')
20
+ .option('--name <name>', 'Brand/company name')
21
+ .option('--website <url>', 'Brand website')
22
+ .option('--industry <industry>', 'Industry (e.g. wellness, beauty, fitness, fashion, food, tech)')
23
+ .option('--colors <colors>', 'Brand colors, comma-separated hex (e.g. "#1a1a1a,#d4af37,#ffffff")')
24
+ .option('--voice <voice>', 'Brand voice (e.g. premium, playful, clinical, bold, professional, eco-conscious)')
25
+ .option('--archetype <archetype>', 'Brand archetype (e.g. hero, sage, rebel, lover, creator, explorer, ruler, magician, caregiver, everyman, jester, innocent)')
26
+ .option('--target-audience <audience>', 'Target audience description')
27
+ .option('--tagline <tagline>', 'Brand tagline')
28
+ .option('--keywords <words>', 'Brand keywords, comma-separated')
29
+ .option('--logo-url <url>', 'Logo URL')
30
+ .option('--background-style <style>', 'Default background style (e.g. "clean white", "dark moody", "natural outdoor")')
31
+ .action(async (opts) => {
32
+ const body = {};
33
+ if (opts.name)
34
+ body.name = opts.name;
35
+ if (opts.website)
36
+ body.website = opts.website;
37
+ if (opts.industry)
38
+ body.industry = opts.industry;
39
+ if (opts.colors)
40
+ body.colors = opts.colors.split(',').map(c => c.trim());
41
+ if (opts.voice)
42
+ body.voice = opts.voice;
43
+ if (opts.archetype)
44
+ body.archetype = opts.archetype;
45
+ if (opts.targetAudience)
46
+ body.targetAudience = opts.targetAudience;
47
+ if (opts.tagline)
48
+ body.tagline = opts.tagline;
49
+ if (opts.keywords)
50
+ body.keywords = opts.keywords.split(',').map(k => k.trim());
51
+ if (opts.logoUrl)
52
+ body.logoUrl = opts.logoUrl;
53
+ if (opts.backgroundStyle)
54
+ body.backgroundStyle = opts.backgroundStyle;
55
+ if (Object.keys(body).length === 0) {
56
+ console.error(JSON.stringify({ error: 'Provide at least one setting to update' }));
57
+ process.exit(1);
58
+ }
59
+ try {
60
+ const data = await (0, api_js_1.api)('/api/cli/brand', { method: 'PATCH', body });
61
+ console.log(JSON.stringify(data));
62
+ }
63
+ catch (err) {
64
+ console.error(JSON.stringify({ error: err instanceof Error ? err.message : 'Failed' }));
65
+ process.exit(1);
66
+ }
67
+ });
68
+ const analyzeCommand = new commander_1.Command('analyze')
69
+ .description('Analyze a website to auto-detect brand identity (colors, voice, industry)')
70
+ .requiredOption('--url <url>', 'Website URL to analyze')
71
+ .option('--apply', 'Automatically apply detected settings to your profile')
72
+ .action(async (opts) => {
73
+ try {
74
+ const data = await (0, api_js_1.api)('/api/cli/brand/analyze', {
75
+ body: { url: opts.url },
76
+ });
77
+ console.log(JSON.stringify(data));
78
+ // Auto-apply if flag is set
79
+ if (opts.apply) {
80
+ const applyBody = {};
81
+ if (data.name)
82
+ applyBody.name = data.name;
83
+ if (data.colors && data.colors.length > 0)
84
+ applyBody.colors = data.colors;
85
+ if (data.voice)
86
+ applyBody.voice = data.voice;
87
+ if (data.industry)
88
+ applyBody.industry = data.industry;
89
+ if (data.logoUrl)
90
+ applyBody.logoUrl = data.logoUrl;
91
+ if (data.keywords && data.keywords.length > 0)
92
+ applyBody.keywords = data.keywords;
93
+ if (Object.keys(applyBody).length > 0) {
94
+ const result = await (0, api_js_1.api)('/api/cli/brand', { method: 'PATCH', body: applyBody });
95
+ process.stderr.write(' Brand settings applied from analysis\n');
96
+ console.log(JSON.stringify(result));
97
+ }
98
+ }
99
+ }
100
+ catch (err) {
101
+ console.error(JSON.stringify({ error: err instanceof Error ? err.message : 'Analysis failed' }));
102
+ process.exit(1);
103
+ }
104
+ });
105
+ exports.brandCommand = new commander_1.Command('brand')
106
+ .description('Manage brand identity (colors, voice, audience, style)')
107
+ .addCommand(getCommand)
108
+ .addCommand(setCommand)
109
+ .addCommand(analyzeCommand);
@@ -85,10 +85,43 @@ exports.generateCommand
85
85
  .option('--style <style>', 'Style preset', 'minimal')
86
86
  .option('--format <format>', 'Image format', 'instagram_square')
87
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")')
93
+ .option('--keywords <words>', 'Brand keywords, comma-separated')
88
94
  .option('--wait', 'Wait for completion')
89
95
  .action(async (opts) => {
90
96
  if (opts.product) {
91
- await generateAction.parseAsync(['', '', '--product', opts.product, '--style', opts.style, '--format', opts.format, '--count', opts.count, ...(opts.wait ? ['--wait'] : [])]);
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));
114
+ return;
115
+ }
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));
120
+ }
121
+ catch (err) {
122
+ console.error(JSON.stringify({ error: err instanceof Error ? err.message : 'Generation failed' }));
123
+ process.exit(1);
124
+ }
92
125
  }
93
126
  else {
94
127
  exports.generateCommand.help();
@@ -19,18 +19,31 @@ const listCommand = new commander_1.Command('list')
19
19
  }
20
20
  });
21
21
  const createCommand = new commander_1.Command('create')
22
- .description('Create a product from an image URL')
22
+ .description('Create a product from a local image or URL')
23
23
  .requiredOption('--name <name>', 'Product name')
24
- .requiredOption('--image-url <url>', 'URL to product image')
24
+ .option('--image <path>', 'Path to local product image')
25
+ .option('--image-url <url>', 'URL to product image')
25
26
  .option('--description <desc>', 'Product description')
26
27
  .option('--category <cat>', 'Category (beauty, wellness, pharma, apparel, etc.)')
27
28
  .option('--brand-name <brand>', 'Brand name')
28
29
  .action(async (opts) => {
30
+ if (!opts.image && !opts.imageUrl) {
31
+ console.error(JSON.stringify({ error: 'Provide either --image (local file) or --image-url (URL)' }));
32
+ process.exit(1);
33
+ }
29
34
  try {
35
+ let imageUrl = opts.imageUrl;
36
+ // Upload local file first if provided
37
+ if (opts.image) {
38
+ process.stderr.write(' Uploading image...\n');
39
+ const upload = await (0, api_js_1.uploadFile)(opts.image);
40
+ imageUrl = upload.url;
41
+ process.stderr.write(` Uploaded (${(upload.size / 1024).toFixed(0)}KB)\n`);
42
+ }
30
43
  const data = await (0, api_js_1.api)('/api/cli/products', {
31
44
  body: {
32
45
  name: opts.name,
33
- imageUrl: opts.imageUrl,
46
+ imageUrl,
34
47
  description: opts.description,
35
48
  category: opts.category,
36
49
  brandName: opts.brandName,
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ const commander_1 = require("commander");
5
5
  const login_js_1 = require("./commands/login.js");
6
6
  const credits_js_1 = require("./commands/credits.js");
7
7
  const topup_js_1 = require("./commands/topup.js");
8
+ const brand_js_1 = require("./commands/brand.js");
8
9
  const products_js_1 = require("./commands/products.js");
9
10
  const generate_js_1 = require("./commands/generate.js");
10
11
  const assets_js_1 = require("./commands/assets.js");
@@ -17,6 +18,7 @@ program
17
18
  program.addCommand(login_js_1.loginCommand);
18
19
  program.addCommand(credits_js_1.creditsCommand);
19
20
  program.addCommand(topup_js_1.topupCommand);
21
+ program.addCommand(brand_js_1.brandCommand);
20
22
  program.addCommand(products_js_1.productsCommand);
21
23
  program.addCommand(generate_js_1.generateCommand);
22
24
  program.addCommand(assets_js_1.assetsCommand);
package/dist/lib/api.d.ts CHANGED
@@ -14,4 +14,12 @@ export declare function downloadBinary(path: string, body?: Record<string, unkno
14
14
  buffer: Buffer;
15
15
  fileName: string;
16
16
  }>;
17
+ /**
18
+ * Upload a local file to the server. Returns the CDN URL.
19
+ */
20
+ export declare function uploadFile(filePath: string): Promise<{
21
+ url: string;
22
+ fileName: string;
23
+ size: number;
24
+ }>;
17
25
  export {};
package/dist/lib/api.js CHANGED
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.api = api;
4
4
  exports.downloadBinary = downloadBinary;
5
+ exports.uploadFile = uploadFile;
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
5
8
  const config_js_1 = require("./config.js");
6
9
  /**
7
10
  * Make an authenticated API call to the Sintetica server.
@@ -53,3 +56,26 @@ async function downloadBinary(path, body) {
53
56
  const buffer = Buffer.from(await res.arrayBuffer());
54
57
  return { buffer, fileName };
55
58
  }
59
+ /**
60
+ * Upload a local file to the server. Returns the CDN URL.
61
+ */
62
+ async function uploadFile(filePath) {
63
+ const baseUrl = (0, config_js_1.getBaseUrl)();
64
+ const apiKey = (0, config_js_1.getApiKey)();
65
+ const fileBuffer = (0, fs_1.readFileSync)(filePath);
66
+ const fileName = (0, path_1.basename)(filePath);
67
+ const formData = new FormData();
68
+ formData.append('file', new Blob([fileBuffer]), fileName);
69
+ const res = await fetch(`${baseUrl}/api/cli/upload`, {
70
+ method: 'POST',
71
+ headers: {
72
+ 'Authorization': `Bearer ${apiKey}`,
73
+ },
74
+ body: formData,
75
+ });
76
+ if (!res.ok) {
77
+ const errorBody = await res.json().catch(() => ({ error: res.statusText }));
78
+ throw new Error(errorBody.error || `HTTP ${res.status}`);
79
+ }
80
+ return res.json();
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sintetica-cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.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>",