@clubmatto/ai-kit 0.0.1
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/CHANGELOG.md +15 -0
- package/README.md +65 -0
- package/dist/scripts/fetch-playwright-skills.js +63 -0
- package/dist/src/cmd/sync.js +109 -0
- package/dist/src/commands/sync.js +111 -0
- package/dist/src/content.js +99 -0
- package/dist/src/index.js +19 -0
- package/dist/src/logger.js +2 -0
- package/dist/src/manifest.js +24 -0
- package/dist/src/output.js +46 -0
- package/dist/src/reader.js +99 -0
- package/dist/src/template.js +10 -0
- package/dist/tests/content.test.js +141 -0
- package/dist/tests/integration/cli.test.js +43 -0
- package/dist/tests/output.js +36 -0
- package/dist/tests/reader.test.js +141 -0
- package/dist/tests/sync.test.js +90 -0
- package/dist/tests/utils.js +20 -0
- package/dist/vitest.config.js +9 -0
- package/docs/roadmap.md +16 -0
- package/eslint.config.mjs +38 -0
- package/package.json +78 -0
- package/scripts/fetch-playwright-skills.ts +79 -0
- package/src/agents/monorepo.md +30 -0
- package/src/agents/opencode.json +31 -0
- package/src/cmd/sync.ts +158 -0
- package/src/commands/commit.md +43 -0
- package/src/commands/interview.md +92 -0
- package/src/commands/synth.md +45 -0
- package/src/index.ts +24 -0
- package/src/logger.ts +10 -0
- package/src/manifest.ts +29 -0
- package/src/output.ts +66 -0
- package/src/reader.ts +114 -0
- package/src/rules/go.md +306 -0
- package/src/rules/kotlin.md +177 -0
- package/src/rules/plan-mode.md +7 -0
- package/src/rules/spring-boot.md +549 -0
- package/src/rules/typescript.md +302 -0
- package/src/rules/unsure.md +9 -0
- package/src/skills/image-gen/SKILL.md +50 -0
- package/src/skills/image-gen/scripts/generate.js +166 -0
- package/src/skills/playwright-cli/SKILL.md +279 -0
- package/src/skills/playwright-cli/references/request-mocking.md +87 -0
- package/src/skills/playwright-cli/references/running-code.md +232 -0
- package/src/skills/playwright-cli/references/session-management.md +170 -0
- package/src/skills/playwright-cli/references/storage-state.md +275 -0
- package/src/skills/playwright-cli/references/test-generation.md +88 -0
- package/src/skills/playwright-cli/references/tracing.md +142 -0
- package/src/skills/playwright-cli/references/video-recording.md +43 -0
- package/src/template.ts +14 -0
- package/tests/fixtures/agents/another.json +4 -0
- package/tests/fixtures/agents/monorepo.md +5 -0
- package/tests/fixtures/agents/opencode.json +4 -0
- package/tests/fixtures/commands/another.md +5 -0
- package/tests/fixtures/commands/commit.md +7 -0
- package/tests/fixtures/commands/test.md +13 -0
- package/tests/fixtures/rules/nested/nested-rule.md +3 -0
- package/tests/fixtures/rules/test-rule.md +5 -0
- package/tests/fixtures/rules/typescript.md +5 -0
- package/tests/fixtures/skills/test-skill/SKILL.md +7 -0
- package/tests/fixtures/skills/test-skill/nested-refs/doc.md +3 -0
- package/tests/fixtures/skills/test-skill/skill-details.md +7 -0
- package/tests/integration/cli.test.ts +55 -0
- package/tests/output.ts +37 -0
- package/tests/reader.test.ts +193 -0
- package/tests/sync.test.ts +136 -0
- package/tests/utils.ts +17 -0
- package/tsconfig.json +23 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# 🟦 TypeScript Specialist Agent Rules
|
|
2
|
+
|
|
3
|
+
## 🎯 Your TypeScript Persona
|
|
4
|
+
|
|
5
|
+
You are a senior TypeScript engineer with expertise in:
|
|
6
|
+
|
|
7
|
+
- Modern TypeScript idioms and best practices (strict mode, advanced types)
|
|
8
|
+
- Functional programming with TypeScript's rich type system
|
|
9
|
+
- Async/await and Promise patterns for concurrent operations
|
|
10
|
+
- Building type-safe, scalable applications (Node.js, backend services, libraries)
|
|
11
|
+
- Performance optimization and bundle size reduction
|
|
12
|
+
|
|
13
|
+
**Your primary values**: Type safety, developer experience, and pragmatic functional programming.
|
|
14
|
+
|
|
15
|
+
## 📁 TypeScript Project Structure
|
|
16
|
+
|
|
17
|
+
Follow this structure for TypeScript projects:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
[project-name]/
|
|
21
|
+
├── src/ # Source code
|
|
22
|
+
│ ├── modules/ # Feature modules
|
|
23
|
+
│ ├── utils/ # Utility functions
|
|
24
|
+
│ ├── types/ # Type definitions (.d.ts, .ts)
|
|
25
|
+
│ ├── services/ # API clients, external integrations
|
|
26
|
+
│ └── index.ts # Main entry point
|
|
27
|
+
├── tests/ # Test files
|
|
28
|
+
│ ├── unit/ # Unit tests
|
|
29
|
+
│ ├── integration/ # Integration tests
|
|
30
|
+
│ └── fixtures/ # Test data
|
|
31
|
+
├── dist/ # Compiled output (generated)
|
|
32
|
+
├── node_modules/ # Dependencies (generated)
|
|
33
|
+
├── package.json # Project metadata and scripts
|
|
34
|
+
├── tsconfig.json # TypeScript configuration (MUST be present)
|
|
35
|
+
├── .eslintrc.js # ESLint configuration
|
|
36
|
+
├── .prettierrc # Prettier configuration (if used)
|
|
37
|
+
└── README.md # Project documentation
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 🛠️ Development Commands
|
|
41
|
+
|
|
42
|
+
### Essential Workflow Commands
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Install dependencies
|
|
46
|
+
npm install
|
|
47
|
+
# or with pnpm
|
|
48
|
+
pnpm install
|
|
49
|
+
|
|
50
|
+
# Run development server (if applicable)
|
|
51
|
+
npm run dev
|
|
52
|
+
|
|
53
|
+
# Build for production
|
|
54
|
+
npm run build
|
|
55
|
+
|
|
56
|
+
# Type checking (MUST PASS)
|
|
57
|
+
npm run type-check
|
|
58
|
+
# or tsc --noEmit
|
|
59
|
+
|
|
60
|
+
# Linting (MUST PASS)
|
|
61
|
+
npm run lint
|
|
62
|
+
|
|
63
|
+
# Testing
|
|
64
|
+
npm test # Run all tests
|
|
65
|
+
npm run test:watch # Watch mode
|
|
66
|
+
npm run test:coverage # Coverage report
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Common Scripts in package.json
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"scripts": {
|
|
74
|
+
"dev": "vite dev",
|
|
75
|
+
"build": "tsc && vite build",
|
|
76
|
+
"preview": "vite preview",
|
|
77
|
+
"type-check": "tsc --noEmit",
|
|
78
|
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
79
|
+
"test": "vitest",
|
|
80
|
+
"test:coverage": "vitest --coverage"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 📝 TypeScript Code Standards
|
|
86
|
+
|
|
87
|
+
### Type Safety (CRITICAL)
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// ✅ GOOD: Explicit types, avoid `any`
|
|
91
|
+
function calculateTotal(items: Item[]): number {
|
|
92
|
+
return items.reduce((sum, item) => sum + item.price, 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ✅ GOOD: Use `unknown` for type-safe dynamic values
|
|
96
|
+
function parseUser(input: unknown): User {
|
|
97
|
+
if (isUser(input)) {
|
|
98
|
+
return input;
|
|
99
|
+
}
|
|
100
|
+
throw new Error("Invalid user");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ✅ GOOD: Generic types for reusable functions
|
|
104
|
+
function identity<T>(value: T): T {
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ❌ BAD: Using `any` defeats type safety
|
|
109
|
+
function dangerous(data: any) {
|
|
110
|
+
return data.unknownProperty; // No type checking!
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ❌ BAD: Non-null assertions (`!`) without justification
|
|
114
|
+
const element = document.getElementById("myId")!; // Risky
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Strict Mode Configuration
|
|
118
|
+
|
|
119
|
+
Always enable strict mode in `tsconfig.json`:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"compilerOptions": {
|
|
124
|
+
"strict": true,
|
|
125
|
+
"noImplicitAny": true,
|
|
126
|
+
"strictNullChecks": true,
|
|
127
|
+
"strictFunctionTypes": true,
|
|
128
|
+
"strictBindCallApply": true,
|
|
129
|
+
"strictPropertyInitialization": true,
|
|
130
|
+
"noImplicitThis": true,
|
|
131
|
+
"alwaysStrict": true
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Immutability & Readonly
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// ✅ GOOD: Prefer `const` and readonly properties
|
|
140
|
+
interface User {
|
|
141
|
+
readonly id: string;
|
|
142
|
+
name: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const user: Readonly<User> = { id: "1", name: "Alice" };
|
|
146
|
+
// user.id = '2' // Compile error
|
|
147
|
+
|
|
148
|
+
// ✅ GOOD: Immutable arrays and objects
|
|
149
|
+
const items: readonly Item[] = [item1, item2];
|
|
150
|
+
const config = { port: 3000 } as const;
|
|
151
|
+
|
|
152
|
+
// ✅ GOOD: Use functional array methods
|
|
153
|
+
const activeUsers = users.filter((user) => user.isActive);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Async/Await & Error Handling
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// ✅ GOOD: Async/await with try/catch
|
|
160
|
+
async function fetchUser(id: string): Promise<User> {
|
|
161
|
+
try {
|
|
162
|
+
const response = await fetch(`/api/users/${id}`);
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
throw new Error(`HTTP ${response.status}`);
|
|
165
|
+
}
|
|
166
|
+
return await response.json();
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("Failed to fetch user:", error);
|
|
169
|
+
throw new Error("User fetch failed", { cause: error });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ✅ GOOD: Promise utilities
|
|
174
|
+
const results = await Promise.allSettled(promises);
|
|
175
|
+
const fastest = await Promise.race([fetch1, fetch2]);
|
|
176
|
+
|
|
177
|
+
// ❌ BAD: Unhandled promise rejections
|
|
178
|
+
fetchUser("123"); // Missing await or catch
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Error Boundaries & Resilience
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// ✅ GOOD: Discriminated unions for error states
|
|
185
|
+
type Result<T> = { type: "success"; data: T } | { type: "error"; error: Error };
|
|
186
|
+
|
|
187
|
+
// ✅ GOOD: Assertion functions
|
|
188
|
+
function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
|
|
189
|
+
if (value === undefined || value === null) {
|
|
190
|
+
throw new Error("Value is undefined or null");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 🧪 Testing Standards
|
|
196
|
+
|
|
197
|
+
### Test Framework (Vitest)
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// ✅ GOOD: Descriptive test suites
|
|
201
|
+
describe("calculateTotal", () => {
|
|
202
|
+
it("returns sum of item prices", () => {
|
|
203
|
+
const items = [{ price: 10 }, { price: 20 }];
|
|
204
|
+
expect(calculateTotal(items)).toBe(30);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("returns 0 for empty array", () => {
|
|
208
|
+
expect(calculateTotal([])).toBe(0);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ✅ GOOD: Mocking external dependencies
|
|
213
|
+
import { fetchUser } from "./api";
|
|
214
|
+
import { vi } from "vitest";
|
|
215
|
+
|
|
216
|
+
vi.mock("./api");
|
|
217
|
+
|
|
218
|
+
it("fetches user", async () => {
|
|
219
|
+
const mockUser = { id: "1", name: "Alice" };
|
|
220
|
+
vi.mocked(fetchUser).mockResolvedValue(mockUser);
|
|
221
|
+
|
|
222
|
+
const result = await getUser("1");
|
|
223
|
+
expect(result).toEqual(mockUser);
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Test Structure
|
|
228
|
+
|
|
229
|
+
- Place test files next to source files (e.g., `src/utils/calc.ts` → `src/utils/calc.test.ts`)
|
|
230
|
+
- Use `describe` for grouping, `it` or `test` for individual cases
|
|
231
|
+
- Prefer `toBe` for primitives, `toEqual` for objects
|
|
232
|
+
- Clean up after each test (`afterEach`, `afterAll`)
|
|
233
|
+
|
|
234
|
+
## 📦 Dependency Management
|
|
235
|
+
|
|
236
|
+
### package.json Rules
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"dependencies": {
|
|
241
|
+
// Pin exact versions for production dependencies
|
|
242
|
+
"express": "4.18.0"
|
|
243
|
+
},
|
|
244
|
+
"devDependencies": {
|
|
245
|
+
// Development tools can use caret ranges
|
|
246
|
+
"typescript": "^5.0.0",
|
|
247
|
+
"vitest": "^1.0.0"
|
|
248
|
+
},
|
|
249
|
+
"scripts": {
|
|
250
|
+
"audit": "npm audit --audit-level=high"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Security & Updates
|
|
256
|
+
|
|
257
|
+
- Run `npm audit` regularly
|
|
258
|
+
- Use `npm outdated` to check for updates
|
|
259
|
+
- Update dependencies systematically, test thoroughly after each update
|
|
260
|
+
- Prefer smaller, focused libraries over monolithic frameworks
|
|
261
|
+
|
|
262
|
+
## 🚫 TypeScript-Specific Restrictions
|
|
263
|
+
|
|
264
|
+
### Never Do These:
|
|
265
|
+
|
|
266
|
+
- ❌ **Never use `any`** unless in a `.d.ts` file for external JavaScript libraries. Use `unknown` instead.
|
|
267
|
+
- ❌ **Never use non-null assertions (`!`)** without explicit justification and safety checks.
|
|
268
|
+
- ❌ **Never ignore TypeScript errors** with `@ts-ignore` or `@ts-expect-error` without explanation.
|
|
269
|
+
- ❌ **Never commit code that fails type checking** (`npm run type-check` must pass).
|
|
270
|
+
- ❌ **Never commit code that fails linting** (`npm run lint` must pass).
|
|
271
|
+
- ❌ **Never use `eval` or `Function` constructor** (security risk).
|
|
272
|
+
- ❌ **Never use `var`** – always use `const` or `let`.
|
|
273
|
+
- ❌ **Never use default exports** unless required by framework convention. Prefer named exports.
|
|
274
|
+
|
|
275
|
+
### Avoid These When Possible:
|
|
276
|
+
|
|
277
|
+
- ⚠️ Avoid `as` type assertions – prefer type guards or proper typing.
|
|
278
|
+
- ⚠️ Avoid `enum` – use union types or const objects.
|
|
279
|
+
- ⚠️ Avoid complex inheritance – prefer composition and functional patterns.
|
|
280
|
+
- ⚠️ Avoid long files – split into smaller modules (< 300 lines).
|
|
281
|
+
|
|
282
|
+
## 🔍 Tooling Configuration
|
|
283
|
+
|
|
284
|
+
### ESLint & Prettier
|
|
285
|
+
|
|
286
|
+
Always configure linting and formatting:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
// .eslintrc.js
|
|
290
|
+
module.exports = {
|
|
291
|
+
extends: [
|
|
292
|
+
"eslint:recommended",
|
|
293
|
+
"plugin:@typescript-eslint/recommended",
|
|
294
|
+
"plugin:prettier/recommended",
|
|
295
|
+
],
|
|
296
|
+
rules: {
|
|
297
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
{{FOOTER}}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# When You're Unsure
|
|
2
|
+
|
|
3
|
+
If instructions are ambiguous or you encounter an edge case:
|
|
4
|
+
|
|
5
|
+
1. **Acknowledge the ambiguity** in your response.
|
|
6
|
+
2. **Propose 2–3 specific options** with pros/cons.
|
|
7
|
+
3. **Ask a clarifying question** rather than guessing.
|
|
8
|
+
|
|
9
|
+
\_{{FOOTER}}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: image-gen
|
|
3
|
+
description: Generates simple images, placeholders, and backgrounds using MiniMax AI. Use when the user needs to create images from text prompts.
|
|
4
|
+
allowed-tools: Bash(image-gen:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Image Generation
|
|
8
|
+
|
|
9
|
+
Generate simple images, placeholders, and backgrounds using MiniMax AI.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
Requires `MINIMAX_API_KEY` environment variable set with your MiniMax API key.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
node scripts/generate.js "a blue sky with clouds" --output-dir=output/
|
|
19
|
+
|
|
20
|
+
node scripts/generate.js "a warm sunset" --output-dir=output/ --aspect-ratio=16:9
|
|
21
|
+
|
|
22
|
+
node scripts/generate.js "a solid red background" --output-dir=output/ --aspect-ratio=1:1
|
|
23
|
+
|
|
24
|
+
node scripts/generate.js "a portrait photo" --output-dir=output/ --width=1024 --height=1024
|
|
25
|
+
|
|
26
|
+
node scripts/generate.js "a forest" --output-dir=output/ --seed=42 --n=2
|
|
27
|
+
|
|
28
|
+
node scripts/generate.js "a modern building" --output-dir=output/ --prompt-optimizer
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Arguments
|
|
32
|
+
|
|
33
|
+
| Argument | Required | Description |
|
|
34
|
+
| --------------------- | -------- | --------------------------------------------------- |
|
|
35
|
+
| `prompt` | Yes | Text description of the image (max 1500 characters) |
|
|
36
|
+
| `--output-dir=<path>` | Yes | Output directory for the generated image |
|
|
37
|
+
| `--aspect-ratio` | No | Aspect ratio (default: 16:9) |
|
|
38
|
+
| `--width=<px>` | No | Image width in pixels (512-2048, divisible by 8) |
|
|
39
|
+
| `--height=<px>` | No | Image height in pixels (512-2048, divisible by 8) |
|
|
40
|
+
| `--seed=<number>` | No | Random seed for reproducible results |
|
|
41
|
+
| `--n=<count>` | No | Number of images to generate (1-9, default: 1) |
|
|
42
|
+
| `--prompt-optimizer` | No | Enable automatic prompt optimization |
|
|
43
|
+
|
|
44
|
+
## Aspect Ratio Options
|
|
45
|
+
|
|
46
|
+
`1:1`, `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9`
|
|
47
|
+
|
|
48
|
+
## Output
|
|
49
|
+
|
|
50
|
+
Images are saved to the specified directory with auto-generated filenames based on the prompt. Multiple images get numbered suffixes (e.g., `a-blue-sky.jpeg`, `a-blue-sky-1.jpeg`).
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const https = require("https");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
function generateFilename(prompt, index = 0) {
|
|
6
|
+
const base = prompt
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.split(/\s+/)
|
|
9
|
+
.join("-")
|
|
10
|
+
.replace(/[^a-z0-9-]/g, "");
|
|
11
|
+
const truncated = base.length > 50 ? base.slice(0, 50) : base;
|
|
12
|
+
const ext = ".jpeg";
|
|
13
|
+
const suffix = index > 0 ? `-${index}` : "";
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(truncated + suffix + ext)) {
|
|
16
|
+
return truncated + suffix + ext;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (let i = 1; ; i++) {
|
|
20
|
+
const name = `${truncated}-${i}`;
|
|
21
|
+
if (!fs.existsSync(name + ext)) {
|
|
22
|
+
return name + ext;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseArgs() {
|
|
28
|
+
const args = process.argv.slice(2);
|
|
29
|
+
let prompt = "";
|
|
30
|
+
let outputDir = "";
|
|
31
|
+
let options = {
|
|
32
|
+
aspectRatio: "16:9",
|
|
33
|
+
width: null,
|
|
34
|
+
height: null,
|
|
35
|
+
seed: null,
|
|
36
|
+
n: 1,
|
|
37
|
+
promptOptimizer: false,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
for (const arg of args) {
|
|
41
|
+
if (arg.startsWith("--aspect-ratio=")) {
|
|
42
|
+
options.aspectRatio = arg.split("=")[1];
|
|
43
|
+
} else if (arg.startsWith("--width=")) {
|
|
44
|
+
options.width = parseInt(arg.split("=")[1], 10);
|
|
45
|
+
} else if (arg.startsWith("--height=")) {
|
|
46
|
+
options.height = parseInt(arg.split("=")[1], 10);
|
|
47
|
+
} else if (arg.startsWith("--seed=")) {
|
|
48
|
+
options.seed = parseInt(arg.split("=")[1], 10);
|
|
49
|
+
} else if (arg.startsWith("--n=")) {
|
|
50
|
+
options.n = parseInt(arg.split("=")[1], 10);
|
|
51
|
+
} else if (arg === "--prompt-optimizer") {
|
|
52
|
+
options.promptOptimizer = true;
|
|
53
|
+
} else if (arg.startsWith("--output-dir=")) {
|
|
54
|
+
outputDir = arg.split("=")[1];
|
|
55
|
+
} else if (!arg.startsWith("--")) {
|
|
56
|
+
prompt = arg;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!prompt) {
|
|
61
|
+
console.error(
|
|
62
|
+
"usage: generate.js <prompt> --output-dir=<path> [--aspect-ratio=16:9] [--width=1024] [--height=1024] [--seed=<number>] [--n=1] [--prompt-optimizer]",
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!outputDir) {
|
|
68
|
+
console.error(
|
|
69
|
+
"usage: generate.js <prompt> --output-dir=<path> [--aspect-ratio=16:9] [--width=1024] [--height=1024] [--seed=<number>] [--n=1] [--prompt-optimizer]",
|
|
70
|
+
);
|
|
71
|
+
console.error("error: --output-dir is required");
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { prompt, outputDir, options };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildPayload(prompt, options) {
|
|
79
|
+
const payload = {
|
|
80
|
+
model: "image-01",
|
|
81
|
+
prompt,
|
|
82
|
+
response_format: "base64",
|
|
83
|
+
n: options.n,
|
|
84
|
+
prompt_optimizer: options.promptOptimizer,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (options.width && options.height) {
|
|
88
|
+
payload.width = options.width;
|
|
89
|
+
payload.height = options.height;
|
|
90
|
+
} else {
|
|
91
|
+
payload.aspect_ratio = options.aspectRatio;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (options.seed !== null) {
|
|
95
|
+
payload.seed = options.seed;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return payload;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function main() {
|
|
102
|
+
const { prompt, outputDir, options } = parseArgs();
|
|
103
|
+
|
|
104
|
+
const apiKey = process.env.MINIMAX_API_KEY;
|
|
105
|
+
if (!apiKey) {
|
|
106
|
+
console.error("MINIMAX_API_KEY not set");
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!fs.existsSync(outputDir)) {
|
|
111
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const payload = JSON.stringify(buildPayload(prompt, options));
|
|
115
|
+
|
|
116
|
+
const req = https.request(
|
|
117
|
+
{
|
|
118
|
+
hostname: "api.minimax.io",
|
|
119
|
+
port: 443,
|
|
120
|
+
path: "/v1/image_generation",
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
Authorization: `Bearer ${apiKey}`,
|
|
124
|
+
"Content-Type": "application/json",
|
|
125
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
(res) => {
|
|
129
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
130
|
+
let body = "";
|
|
131
|
+
res.on("data", (chunk) => (body += chunk));
|
|
132
|
+
res.on("end", () => {
|
|
133
|
+
console.error(
|
|
134
|
+
`request failed with status ${res.statusCode}: ${body}`,
|
|
135
|
+
);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let body = "";
|
|
142
|
+
res.on("data", (chunk) => (body += chunk));
|
|
143
|
+
res.on("end", () => {
|
|
144
|
+
const result = JSON.parse(body);
|
|
145
|
+
const images = result.data?.image_base64 || [];
|
|
146
|
+
|
|
147
|
+
for (let i = 0; i < images.length; i++) {
|
|
148
|
+
const decoded = Buffer.from(images[i], "base64");
|
|
149
|
+
const filename = generateFilename(prompt, i);
|
|
150
|
+
const outputPath = path.join(outputDir, filename);
|
|
151
|
+
fs.writeFileSync(outputPath, decoded);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
req.on("error", (err) => {
|
|
158
|
+
console.error(`request failed: ${err}`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
req.write(payload);
|
|
163
|
+
req.end();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main();
|