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.
- package/dist/commands/brand.d.ts +2 -0
- package/dist/commands/brand.js +109 -0
- package/dist/commands/generate.js +34 -1
- package/dist/commands/products.js +16 -3
- package/dist/index.js +2 -0
- package/dist/lib/api.d.ts +8 -0
- package/dist/lib/api.js +26 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
22
|
+
.description('Create a product from a local image or URL')
|
|
23
23
|
.requiredOption('--name <name>', 'Product name')
|
|
24
|
-
.
|
|
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
|
|
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.
|
|
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>",
|