aiesthetic-cli 1.0.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/README.md +50 -0
- package/bin/cli.js +192 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# aiesthetic-cli
|
|
2
|
+
|
|
3
|
+
The official Asset Engine for the AIesthetic platform. Generate and download high-quality visual assets (12s cinematic videos and 360 animation frames) directly to your local workspace.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
You can run the CLI without installing it using `npx`:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx aiesthetic-cli "Design a luxury watch website" "cinematic"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
The CLI requires an API key to function. Create a `.env` file in your working directory:
|
|
16
|
+
|
|
17
|
+
```env
|
|
18
|
+
AIESTHETIC_API_KEY=your_live_key_here
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx aiesthetic-cli <prompt> [style]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **prompt**: Descriptive text for your scene (e.g., "A futuristic flying car")
|
|
28
|
+
- **style**: (Optional) Visual style. Options: `cinematic` (default), `modern`, `bold`, etc.
|
|
29
|
+
|
|
30
|
+
## Output
|
|
31
|
+
|
|
32
|
+
The CLI creates a structured directory:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
aiesthetic_assets/
|
|
36
|
+
└── req_abc123/
|
|
37
|
+
├── frames/ # 360 sequential frames (30fps)
|
|
38
|
+
├── video.mp4 # 12s high-resolution video
|
|
39
|
+
├── README.md # Implementation instructions
|
|
40
|
+
└── cursor-prompt.txt # Prompt for your AI coding agent
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
- **Multi-threaded Downloads**: Fast asset retrieval with parallel streams.
|
|
46
|
+
- **Zero-Config**: Works out of the box with `npx`.
|
|
47
|
+
- **AI Agent Ready**: Provides pre-formatted prompts for tools like Cursor and Windsurf.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
© 2024 AIesthetic Team. All rights reserved.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
|
|
8
|
+
// ─────────────────────────────────────────
|
|
9
|
+
// COLORS & UI UTILS
|
|
10
|
+
// ─────────────────────────────────────────
|
|
11
|
+
const color = {
|
|
12
|
+
reset: "\x1b[0m",
|
|
13
|
+
bold: "\x1b[1m",
|
|
14
|
+
cyan: "\x1b[36m",
|
|
15
|
+
green: "\x1b[32m",
|
|
16
|
+
yellow: "\x1b[33m",
|
|
17
|
+
red: "\x1b[31m",
|
|
18
|
+
dim: "\x1b[2m",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// ─────────────────────────────────────────
|
|
22
|
+
// ENV LOADER
|
|
23
|
+
// ─────────────────────────────────────────
|
|
24
|
+
function loadEnv() {
|
|
25
|
+
if (fs.existsSync('.env')) {
|
|
26
|
+
const envFile = fs.readFileSync('.env', 'utf-8');
|
|
27
|
+
envFile.split('\n').forEach(line => {
|
|
28
|
+
const parts = line.split('=');
|
|
29
|
+
if (parts.length >= 2) {
|
|
30
|
+
const key = parts[0].trim();
|
|
31
|
+
const value = parts.slice(1).join('=').trim().replace(/['"]/g, '');
|
|
32
|
+
process.env[key] = value;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
loadEnv();
|
|
39
|
+
|
|
40
|
+
const CONFIG = {
|
|
41
|
+
BASE_URL: 'https://us-central1-aiestheticbackend.cloudfunctions.net/api/v1',
|
|
42
|
+
API_KEY: process.env.AIESTHETIC_API_KEY,
|
|
43
|
+
PROMPT: process.argv[2],
|
|
44
|
+
STYLE: process.argv[3] || 'cinematic',
|
|
45
|
+
CONCURRENT_DOWNLOADS: 10,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// ─────────────────────────────────────────
|
|
49
|
+
// CORE UTILS
|
|
50
|
+
// ─────────────────────────────────────────
|
|
51
|
+
async function request(method, path, body = null) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const options = {
|
|
54
|
+
method,
|
|
55
|
+
hostname: 'us-central1-aiestheticbackend.cloudfunctions.net',
|
|
56
|
+
path: `/api${path}`,
|
|
57
|
+
headers: {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
'x-api-key': CONFIG.API_KEY,
|
|
60
|
+
},
|
|
61
|
+
timeout: 900000,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const req = https.request(options, (res) => {
|
|
65
|
+
let data = '';
|
|
66
|
+
res.on('data', (chunk) => data += chunk);
|
|
67
|
+
res.on('end', () => {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(data);
|
|
70
|
+
if (res.statusCode >= 400) reject(parsed);
|
|
71
|
+
else resolve(parsed);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
reject(new Error(`Failed to parse response: ${data}`));
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
req.on('error', reject);
|
|
79
|
+
if (body) req.write(JSON.stringify(body));
|
|
80
|
+
req.end();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function downloadFile(url, dest) {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
const file = fs.createWriteStream(dest);
|
|
87
|
+
https.get(url, (res) => {
|
|
88
|
+
if (res.statusCode !== 200) {
|
|
89
|
+
reject(new Error(`Failed to download: ${res.statusCode}`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
res.pipe(file);
|
|
93
|
+
file.on('finish', () => {
|
|
94
|
+
file.close();
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
}).on('error', (err) => {
|
|
98
|
+
fs.unlink(dest, () => {});
|
|
99
|
+
reject(err);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─────────────────────────────────────────
|
|
105
|
+
// MAIN ENGINE
|
|
106
|
+
// ─────────────────────────────────────────
|
|
107
|
+
async function main() {
|
|
108
|
+
console.log(`\n${color.bold}${color.cyan}╔══════════════════════════════════════════════════════════╗${color.reset}`);
|
|
109
|
+
console.log(`${color.bold}${color.cyan}║ AIesthetic CLI — Asset Engine ║${color.reset}`);
|
|
110
|
+
console.log(`${color.bold}${color.cyan}╚══════════════════════════════════════════════════════════╝${color.reset}\n`);
|
|
111
|
+
|
|
112
|
+
if (!CONFIG.API_KEY) {
|
|
113
|
+
console.log(`${color.red}❌ Error: AIESTHETIC_API_KEY not found.${color.reset}`);
|
|
114
|
+
console.log(`${color.dim}Please add it to your .env file or export it as an environment variable.${color.reset}\n`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!CONFIG.PROMPT) {
|
|
119
|
+
console.log(`${color.yellow}⚠️ Usage: npx aiesthetic "Your Prompt" "style"${color.reset}\n`);
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// 1. Check Credits
|
|
125
|
+
process.stdout.write(` ${color.dim}▶${color.reset} Verifying Account... `);
|
|
126
|
+
const creditInfo = await request('GET', '/v1/credits');
|
|
127
|
+
console.log(`${color.green}Balance: ${creditInfo.credits} credits${color.reset}`);
|
|
128
|
+
|
|
129
|
+
// 2. Generate
|
|
130
|
+
console.log(` ${color.dim}▶${color.reset} Generating Assets...`);
|
|
131
|
+
console.log(` ${color.dim}Prompt:${color.reset} "${CONFIG.PROMPT}"`);
|
|
132
|
+
console.log(` ${color.dim}Style: ${color.reset} ${CONFIG.STYLE}`);
|
|
133
|
+
console.log(` ${color.yellow}⏳ Processing through GPU cluster (2-5 mins)...${color.reset}`);
|
|
134
|
+
|
|
135
|
+
const startTime = Date.now();
|
|
136
|
+
const generation = await request('POST', '/v1/generate', {
|
|
137
|
+
prompt: CONFIG.PROMPT,
|
|
138
|
+
style: CONFIG.STYLE,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
142
|
+
console.log(` ${color.green}✓ Generation complete in ${duration}s${color.reset}\n`);
|
|
143
|
+
|
|
144
|
+
// 3. Download
|
|
145
|
+
const { assets, requestId } = generation;
|
|
146
|
+
const outputDir = path.join(process.cwd(), 'aiesthetic_assets', requestId);
|
|
147
|
+
const framesDir = path.join(outputDir, 'frames');
|
|
148
|
+
|
|
149
|
+
console.log(` ${color.dim}▶${color.reset} Downloading Assets...`);
|
|
150
|
+
console.log(` ${color.dim}Target:${color.reset} ${outputDir}`);
|
|
151
|
+
|
|
152
|
+
if (!fs.existsSync(framesDir)) fs.mkdirSync(framesDir, { recursive: true });
|
|
153
|
+
|
|
154
|
+
// Download Video
|
|
155
|
+
process.stdout.write(` ${color.dim}• Downloading video...${color.reset} `);
|
|
156
|
+
await downloadFile(assets.videoUrl, path.join(outputDir, 'video.mp4'));
|
|
157
|
+
console.log(`${color.green}done${color.reset}`);
|
|
158
|
+
|
|
159
|
+
// Download Frames (Parallel)
|
|
160
|
+
const frameUrls = assets.frames.urls;
|
|
161
|
+
process.stdout.write(` ${color.dim}• Downloading 360 frames...${color.reset} `);
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < frameUrls.length; i += CONFIG.CONCURRENT_DOWNLOADS) {
|
|
164
|
+
const chunk = frameUrls.slice(i, i + CONFIG.CONCURRENT_DOWNLOADS);
|
|
165
|
+
await Promise.all(chunk.map((url, idx) => {
|
|
166
|
+
const frameNum = (i + idx + 1).toString().padStart(4, '0');
|
|
167
|
+
return downloadFile(url, path.join(framesDir, `frame_${frameNum}.jpg`));
|
|
168
|
+
}));
|
|
169
|
+
const progress = Math.round(((i + chunk.length) / frameUrls.length) * 100);
|
|
170
|
+
process.stdout.write(`\r ${color.dim}• Downloading 360 frames...${color.reset} ${color.cyan}${progress}%${color.reset}`);
|
|
171
|
+
}
|
|
172
|
+
console.log(` ${color.green}complete${color.reset}`);
|
|
173
|
+
|
|
174
|
+
// Save Meta
|
|
175
|
+
fs.writeFileSync(path.join(outputDir, 'README.md'), assets.readme.markdown);
|
|
176
|
+
fs.writeFileSync(path.join(outputDir, 'cursor-prompt.txt'), assets.readme.cursorPrompt);
|
|
177
|
+
|
|
178
|
+
console.log(`\n ${color.bold}${color.green}✨ GENERATION SUCCESSFUL${color.reset}`);
|
|
179
|
+
console.log(` ${color.dim}══════════════════════════════════════════════════════${color.reset}`);
|
|
180
|
+
console.log(` ${color.bold} NEXT STEPS FOR YOUR AI AGENT:${color.reset}`);
|
|
181
|
+
console.log(` 1. Open the generated README: ${color.cyan}${path.join(outputDir, 'README.md')}${color.reset}`);
|
|
182
|
+
console.log(` 2. Follow the technical instructions to build the site.`);
|
|
183
|
+
console.log(` 3. The cursor-prompt.txt contains your ready-to-use system prompt.`);
|
|
184
|
+
console.log(` ${color.dim}══════════════════════════════════════════════════════${color.reset}\n`);
|
|
185
|
+
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.log(`\n${color.red}❌ Error:${color.reset}`, error.message || error.code || error);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aiesthetic-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AIesthetic Asset Engine — Generate and download high-quality visual components from your terminal.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"aiesthetic": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ai",
|
|
14
|
+
"generative",
|
|
15
|
+
"vfx",
|
|
16
|
+
"web-design",
|
|
17
|
+
"cli",
|
|
18
|
+
"aiesthetic"
|
|
19
|
+
],
|
|
20
|
+
"author": "AIesthetic Team",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"dotenv": "^16.4.5"
|
|
24
|
+
}
|
|
25
|
+
}
|