gin-skills 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.
Files changed (114) hide show
  1. package/DEVELOPMENT.md +510 -0
  2. package/README.md +103 -0
  3. package/agents/developer.md +56 -0
  4. package/agents/frontend-design.md +69 -0
  5. package/agents/mobile-reviewer.md +36 -0
  6. package/agents/review-code.md +49 -0
  7. package/agents/security-scanner.md +50 -0
  8. package/agents/tester.md +72 -0
  9. package/bin/cli.js +460 -0
  10. package/landing/ai-build-ai.png +0 -0
  11. package/landing/index.html +1524 -0
  12. package/landing/logo.png +0 -0
  13. package/package.json +37 -0
  14. package/skills/active-life-dev/SKILL.md +157 -0
  15. package/skills/active-life-dev/docs/auth.md +187 -0
  16. package/skills/active-life-dev/docs/customers.md +216 -0
  17. package/skills/active-life-dev/docs/integrations.md +209 -0
  18. package/skills/active-life-dev/docs/inventory.md +192 -0
  19. package/skills/active-life-dev/docs/modules.md +181 -0
  20. package/skills/active-life-dev/docs/orders.md +180 -0
  21. package/skills/active-life-dev/docs/patterns.md +319 -0
  22. package/skills/active-life-dev/docs/products.md +216 -0
  23. package/skills/active-life-dev/docs/schema.md +502 -0
  24. package/skills/active-life-dev/docs/setup.md +169 -0
  25. package/skills/active-life-dev/docs/vouchers.md +144 -0
  26. package/skills/ai-asset-generator/SKILL.md +247 -0
  27. package/skills/ai-asset-generator/docs/gen-image.md +274 -0
  28. package/skills/ai-asset-generator/docs/genvideo.md +341 -0
  29. package/skills/ai-asset-generator/docs/remove-background.md +19 -0
  30. package/skills/ai-asset-generator/lib/bg-remove.mjs +54 -0
  31. package/skills/ai-asset-generator/lib/env.mjs +48 -0
  32. package/skills/ai-asset-generator/lib/kie-client.mjs +111 -0
  33. package/skills/ai-asset-generator/output/logo/logo.png +0 -0
  34. package/skills/ai-build-ai/SKILL.md +127 -0
  35. package/skills/ai-build-ai/docs/agent-teams.md +293 -0
  36. package/skills/ai-build-ai/docs/checkpointing.md +161 -0
  37. package/skills/ai-build-ai/docs/create-agent.md +399 -0
  38. package/skills/ai-build-ai/docs/create-mcp.md +395 -0
  39. package/skills/ai-build-ai/docs/create-skill.md +299 -0
  40. package/skills/ai-build-ai/docs/headless-mode.md +614 -0
  41. package/skills/ai-build-ai/docs/hooks.md +578 -0
  42. package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
  43. package/skills/ai-build-ai/docs/output-styles.md +208 -0
  44. package/skills/ai-build-ai/docs/overview.md +162 -0
  45. package/skills/ai-build-ai/docs/permissions.md +391 -0
  46. package/skills/ai-build-ai/docs/plugins.md +396 -0
  47. package/skills/ai-build-ai/docs/sandbox.md +262 -0
  48. package/skills/ai-build-ai/docs/team-lead-workflow.md +648 -0
  49. package/skills/ant-design/SKILL.md +323 -0
  50. package/skills/ant-design/docs/components.md +160 -0
  51. package/skills/ant-design/docs/data-entry.md +406 -0
  52. package/skills/ant-design/docs/display.md +594 -0
  53. package/skills/ant-design/docs/feedback.md +451 -0
  54. package/skills/ant-design/docs/key-components.md +414 -0
  55. package/skills/ant-design/docs/navigation.md +310 -0
  56. package/skills/ant-design/docs/pro-components.md +543 -0
  57. package/skills/ant-design/docs/setup.md +213 -0
  58. package/skills/ant-design/docs/theme.md +265 -0
  59. package/skills/flutter-performance/SKILL.md +803 -0
  60. package/skills/flutter-performance/references/flutter-patterns.md +595 -0
  61. package/skills/icon-generator/SKILL.md +270 -0
  62. package/skills/mobile-app-review/SKILL.md +321 -0
  63. package/skills/mobile-app-review/references/apple-review.md +132 -0
  64. package/skills/mobile-app-review/references/google-play-review.md +203 -0
  65. package/skills/mongodb/SKILL.md +667 -0
  66. package/skills/mongodb/references/mongoose-patterns.md +368 -0
  67. package/skills/nestjs-architecture/SKILL.md +1086 -0
  68. package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
  69. package/skills/performance/SKILL.md +509 -0
  70. package/skills/react-fsd-architecture/SKILL.md +693 -0
  71. package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
  72. package/skills/react-native-expo/SKILL.md +128 -0
  73. package/skills/react-native-expo/references/data-layer.md +252 -0
  74. package/skills/react-native-expo/references/design-system.md +252 -0
  75. package/skills/react-native-expo/references/navigation.md +199 -0
  76. package/skills/react-native-expo/references/performance.md +229 -0
  77. package/skills/react-native-expo/references/platform-services.md +179 -0
  78. package/skills/react-native-expo/references/state-management.md +209 -0
  79. package/skills/react-native-expo/references/ui-patterns.md +301 -0
  80. package/skills/react-query/SKILL.md +685 -0
  81. package/skills/react-query/references/query-patterns.md +365 -0
  82. package/skills/review-code/SKILL.md +374 -0
  83. package/skills/review-code/references/clean-code-principles.md +395 -0
  84. package/skills/review-code/references/frontend-patterns.md +136 -0
  85. package/skills/review-code/references/nestjs-patterns.md +184 -0
  86. package/skills/security-scanner/SKILL.md +366 -0
  87. package/skills/security-scanner/references/nestjs-security.md +260 -0
  88. package/skills/security-scanner/references/nextjs-security.md +201 -0
  89. package/skills/security-scanner/references/react-native-security.md +199 -0
  90. package/skills/ui-ux-pro-max/SKILL.md +377 -0
  91. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  92. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  93. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  94. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  95. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  96. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  97. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  98. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  99. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  100. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  101. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  102. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  103. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  104. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  105. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  106. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  107. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  108. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  109. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  110. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  111. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  112. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  113. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  114. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
@@ -0,0 +1,341 @@
1
+ POST
2
+ /api/v1/jobs/createTask
3
+ Create Task
4
+ Create a new generation task
5
+
6
+ Request Parameters
7
+ The API accepts a JSON payload with the following structure:
8
+
9
+ Request Body Structure
10
+ {
11
+ "model": "string",
12
+ "callBackUrl": "string (optional)",
13
+ "input": {
14
+ // Input parameters based on form configuration
15
+ }
16
+ }
17
+ Root Level Parameters
18
+ model
19
+ Required
20
+ string
21
+ The model name to use for generation
22
+
23
+ Example:
24
+
25
+ "bytedance/v1-pro-image-to-video"
26
+ callBackUrl
27
+ Optional
28
+ string
29
+ Callback URL for task completion notifications. Optional parameter. If provided, the system will send POST requests to this URL when the task completes (success or failure). If not provided, no callback notifications will be sent.
30
+
31
+ Example:
32
+
33
+ "https://your-domain.com/api/callback"
34
+ Input Object Parameters
35
+ The input object contains the following parameters based on the form configuration:
36
+
37
+ input.prompt
38
+ Required
39
+ string
40
+ The text prompt used to generate the video
41
+
42
+ Max length: 10000 characters
43
+ Example:
44
+
45
+ "A golden retriever dashing through shallow surf at the beach, back angle camera low near waterline, splashes frozen in time, blur trails in waves and paws, afternoon sun glinting off wet fur, overcast day, dramatic clouds"
46
+ input.image_url
47
+ Required
48
+ string(URL)
49
+ The URL of the image used to generate video
50
+
51
+ Please provide the URL of the uploaded file; Accepted types: image/jpeg, image/png, image/webp; Max size: 10.0MB
52
+ Example:
53
+
54
+ "https://file.aiquickdraw.com/custom-page/akr/section-images/1755179021328w1nhip18.webp"
55
+ input.resolution
56
+ Optional
57
+ string
58
+ Video resolution - 480p for faster generation, 720p for balance, 1080p for higher quality
59
+
60
+ Available options:
61
+
62
+ 480p
63
+ -
64
+ 480p
65
+ 720p
66
+ -
67
+ 720p
68
+ 1080p
69
+ -
70
+ 1080p
71
+ Example:
72
+
73
+ "720p"
74
+ input.duration
75
+ Optional
76
+ string
77
+ Duration of the video in seconds
78
+
79
+ Available options:
80
+
81
+ 5
82
+ -
83
+ 5s
84
+ 10
85
+ -
86
+ 10s
87
+ Example:
88
+
89
+ "5"
90
+ input.camera_fixed
91
+ Optional
92
+ boolean
93
+ Whether to fix the camera position
94
+
95
+ Boolean value (true/false)
96
+ Example:
97
+
98
+ false
99
+ input.seed
100
+ Optional
101
+ number
102
+ Random seed to control video generation. Use -1 for random.
103
+
104
+ Min: -1, Max: 2147483647, Step: 1
105
+ Example:
106
+
107
+ -1
108
+ input.enable_safety_checker
109
+ Optional
110
+ boolean
111
+ The safety checker is always enabled in Playground. It can only be disabled by setting false through the API.
112
+
113
+ Boolean value (true/false)
114
+ Example:
115
+
116
+ true
117
+ Request Example
118
+
119
+ cURL
120
+
121
+ JavaScript
122
+
123
+ Python
124
+ curl -X POST "https://api.kie.ai/api/v1/jobs/createTask" \
125
+ -H "Content-Type: application/json" \
126
+ -H "Authorization: Bearer YOUR_API_KEY" \
127
+ -d '{
128
+ "model": "bytedance/v1-pro-image-to-video",
129
+ "callBackUrl": "https://your-domain.com/api/callback",
130
+ "input": {
131
+ "prompt": "A golden retriever dashing through shallow surf at the beach, back angle camera low near waterline, splashes frozen in time, blur trails in waves and paws, afternoon sun glinting off wet fur, overcast day, dramatic clouds",
132
+ "image_url": "https://file.aiquickdraw.com/custom-page/akr/section-images/1755179021328w1nhip18.webp",
133
+ "resolution": "720p",
134
+ "duration": "5",
135
+ "camera_fixed": false,
136
+ "seed": -1,
137
+ "enable_safety_checker": true
138
+ }
139
+ }'
140
+ Response Example
141
+ {
142
+ "code": 200,
143
+ "message": "success",
144
+ "data": {
145
+ "taskId": "task_12345678"
146
+ }
147
+ }
148
+ Response Fields
149
+ code
150
+ Status code, 200 for success, others for failure
151
+ message
152
+ Response message, error description when failed
153
+ data.taskId
154
+ Task ID for querying task status
155
+ Callback Notifications
156
+ When you provide the callBackUrl parameter when creating a task, the system will send POST requests to the specified URL upon task completion (success or failure).
157
+
158
+ Success Callback Example
159
+ {
160
+ "code": 200,
161
+ "data": {
162
+ "completeTime": 1755599644000,
163
+ "costTime": 8,
164
+ "createTime": 1755599634000,
165
+ "model": "bytedance/v1-pro-image-to-video",
166
+ "param": "{\"callBackUrl\":\"https://your-domain.com/api/callback\",\"model\":\"bytedance/v1-pro-image-to-video\",\"input\":{\"prompt\":\"A golden retriever dashing through shallow surf at the beach, back angle camera low near waterline, splashes frozen in time, blur trails in waves and paws, afternoon sun glinting off wet fur, overcast day, dramatic clouds\",\"image_url\":\"https://file.aiquickdraw.com/custom-page/akr/section-images/1755179021328w1nhip18.webp\",\"resolution\":\"720p\",\"duration\":\"5\",\"camera_fixed\":false,\"seed\":-1,\"enable_safety_checker\":true}}",
167
+ "resultJson": "{\"resultUrls\":[\"https://example.com/generated-image.jpg\"]}",
168
+ "state": "success",
169
+ "taskId": "e989621f54392584b05867f87b160672",
170
+ "failCode": null,
171
+ "failMsg": null,
172
+ },
173
+ "msg": "Playground task completed successfully."
174
+ }
175
+ Failure Callback Example
176
+ {
177
+ "code": 501,
178
+ "data": {
179
+ "completeTime": 1755597081000,
180
+ "costTime": 0,
181
+ "createTime": 1755596341000,
182
+ "failCode": "500",
183
+ "failMsg": "Internal server error",
184
+ "model": "bytedance/v1-pro-image-to-video",
185
+ "param": "{\"callBackUrl\":\"https://your-domain.com/api/callback\",\"model\":\"bytedance/v1-pro-image-to-video\",\"input\":{\"prompt\":\"A golden retriever dashing through shallow surf at the beach, back angle camera low near waterline, splashes frozen in time, blur trails in waves and paws, afternoon sun glinting off wet fur, overcast day, dramatic clouds\",\"image_url\":\"https://file.aiquickdraw.com/custom-page/akr/section-images/1755179021328w1nhip18.webp\",\"resolution\":\"720p\",\"duration\":\"5\",\"camera_fixed\":false,\"seed\":-1,\"enable_safety_checker\":true}}",
186
+ "state": "fail",
187
+ "taskId": "bd3a37c523149e4adf45a3ddb5faf1a8",
188
+ "resultJson": null,
189
+ },
190
+ "msg": "Playground task failed."
191
+ }
192
+ Important Notes
193
+ The callback content structure is identical to the Query Task API response
194
+ The param field contains the complete Create Task request parameters, not just the input section
195
+ If callBackUrl is not provided, no callback notifications will be sent
196
+
197
+ GET
198
+ /api/v1/jobs/recordInfo
199
+ Query Task
200
+ Query task status and results by task ID
201
+
202
+ Request Example
203
+
204
+ cURL
205
+
206
+ JavaScript
207
+
208
+ Python
209
+ curl -X GET "https://api.kie.ai/api/v1/jobs/recordInfo?taskId=task_12345678" \
210
+ -H "Authorization: Bearer YOUR_API_KEY"
211
+ Response Example
212
+ {
213
+ "code": 200,
214
+ "message": "success",
215
+ "data": {
216
+ "taskId": "task_12345678",
217
+ "model": "bytedance/v1-pro-image-to-video",
218
+ "state": "success",
219
+ "param": "{\"model\":\"bytedance/v1-pro-image-to-video\",\"callBackUrl\":\"https://your-domain.com/api/callback\",\"input\":{\"prompt\":\"A golden retriever dashing through shallow surf at the beach, back angle camera low near waterline, splashes frozen in time, blur trails in waves and paws, afternoon sun glinting off wet fur, overcast day, dramatic clouds\",\"image_url\":\"https://file.aiquickdraw.com/custom-page/akr/section-images/1755179021328w1nhip18.webp\",\"resolution\":\"720p\",\"duration\":\"5\",\"camera_fixed\":false,\"seed\":-1,\"enable_safety_checker\":true}}",
220
+ "resultJson": "{\"resultUrls\":[\"https://example.com/generated-image.jpg\"]}",
221
+ "failCode": "",
222
+ "failMsg": "",
223
+ "costTime": 0,
224
+ "completeTime": 1698765432000,
225
+ "createTime": 1698765400000
226
+ }
227
+ }
228
+ Response Fields
229
+ code
230
+ Status code, 200 for success, others for failure
231
+ message
232
+ Response message, error description when failed
233
+ data.taskId
234
+ Task ID
235
+ data.model
236
+ Model used for generation
237
+ data.state
238
+ Generation state
239
+ data.param
240
+ Complete Create Task request parameters as JSON string (includes model, callBackUrl, input and all other parameters)
241
+ data.resultJson
242
+ Result JSON string containing generated media URLs
243
+ data.failCode
244
+ Error code (when generation failed)
245
+ data.failMsg
246
+ Error message (when generation failed)
247
+ data.completeTime
248
+ Completion timestamp
249
+ data.createTime
250
+ Creation timestamp
251
+ data.costTime
252
+ Cost time in milliseconds
253
+ State Values
254
+ waiting
255
+ Waiting for generation
256
+ queuing
257
+ In queue
258
+ generating
259
+ Generating
260
+ success
261
+ Generation successful
262
+ fail
263
+ Generation failed
264
+
265
+
266
+ example input
267
+ GET
268
+ /api/v1/jobs/recordInfo
269
+ Query Task
270
+ Query task status and results by task ID
271
+
272
+ Request Example
273
+
274
+ cURL
275
+
276
+ JavaScript
277
+
278
+ Python
279
+ curl -X GET "https://api.kie.ai/api/v1/jobs/recordInfo?taskId=task_12345678" \
280
+ -H "Authorization: Bearer YOUR_API_KEY"
281
+ Response Example
282
+ {
283
+ "code": 200,
284
+ "message": "success",
285
+ "data": {
286
+ "taskId": "task_12345678",
287
+ "model": "bytedance/v1-pro-image-to-video",
288
+ "state": "success",
289
+ "param": "{\"model\":\"bytedance/v1-pro-image-to-video\",\"callBackUrl\":\"https://your-domain.com/api/callback\",\"input\":{\"prompt\":\"A golden retriever dashing through shallow surf at the beach, back angle camera low near waterline, splashes frozen in time, blur trails in waves and paws, afternoon sun glinting off wet fur, overcast day, dramatic clouds\",\"image_url\":\"https://file.aiquickdraw.com/custom-page/akr/section-images/1755179021328w1nhip18.webp\",\"resolution\":\"720p\",\"duration\":\"5\",\"camera_fixed\":false,\"seed\":-1,\"enable_safety_checker\":true}}",
290
+ "resultJson": "{\"resultUrls\":[\"https://example.com/generated-image.jpg\"]}",
291
+ "failCode": "",
292
+ "failMsg": "",
293
+ "costTime": 0,
294
+ "completeTime": 1698765432000,
295
+ "createTime": 1698765400000
296
+ }
297
+ }
298
+ Response Fields
299
+ code
300
+ Status code, 200 for success, others for failure
301
+ message
302
+ Response message, error description when failed
303
+ data.taskId
304
+ Task ID
305
+ data.model
306
+ Model used for generation
307
+ data.state
308
+ Generation state
309
+ data.param
310
+ Complete Create Task request parameters as JSON string (includes model, callBackUrl, input and all other parameters)
311
+ data.resultJson
312
+ Result JSON string containing generated media URLs
313
+ data.failCode
314
+ Error code (when generation failed)
315
+ data.failMsg
316
+ Error message (when generation failed)
317
+ data.completeTime
318
+ Completion timestamp
319
+ data.createTime
320
+ Creation timestamp
321
+ data.costTime
322
+ Cost time in milliseconds
323
+ State Values
324
+ waiting
325
+ Waiting for generation
326
+ queuing
327
+ In queue
328
+ generating
329
+ Generating
330
+ success
331
+ Generation successful
332
+ fail
333
+ Generation failed
334
+
335
+
336
+ ouput
337
+ {
338
+ "resultUrls": [
339
+ "https://file.aiquickdraw.com/custom-page/akr/section-images/17551796948046brblmi1.mp4"
340
+ ]
341
+ }
@@ -0,0 +1,19 @@
1
+ curl -X 'POST' \
2
+ 'https://api.styai.app/api/v1/media/remove-background' \
3
+ -H 'accept: application/json' \
4
+ -H 'X-API-Key: YOUR_STY_AI_API_KEY' \
5
+ -H 'Content-Type: multipart/form-data' \
6
+ -F 'file=@intro-step1.png;type=image/png' \
7
+ -F 'cropToForeground=false' \
8
+ -F 'targetSize=1024 768' \
9
+ -F 'outputFormat=png'
10
+
11
+
12
+ output
13
+ {
14
+ buffer: "base64",
15
+ contentType: "image/png",
16
+ size: 300000,
17
+ processingTime: 1000,
18
+ sucess: true
19
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared background removal via Sty AI API.
3
+ */
4
+
5
+ import { writeFile, readFile } from "fs/promises";
6
+ import { resolve, dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { log } from "./kie-client.mjs";
9
+
10
+ const __lib = dirname(fileURLToPath(import.meta.url));
11
+ const OUTPUT_BASE = resolve(join(__lib, "..", "output"));
12
+
13
+ function assertSafePath(p, base) {
14
+ const resolved = resolve(p);
15
+ if (!resolved.startsWith(base)) {
16
+ throw new Error(`Path traversal blocked: ${p} escapes ${base}`);
17
+ }
18
+ return resolved;
19
+ }
20
+
21
+ export async function removeBackground(inputPath, outputPath) {
22
+ const safeOutput = assertSafePath(outputPath, OUTPUT_BASE);
23
+
24
+ const apiKey = process.env.STY_AI_API_KEY;
25
+ if (!apiKey) {
26
+ throw new Error("STY_AI_API_KEY environment variable is not set");
27
+ }
28
+
29
+ const fileBuffer = await readFile(inputPath);
30
+ const blob = new Blob([fileBuffer], { type: "image/png" });
31
+
32
+ const formData = new FormData();
33
+ formData.append("file", blob, "image.png");
34
+ formData.append("cropToForeground", "true");
35
+ formData.append("outputFormat", "png");
36
+
37
+ const res = await fetch("https://api.styai.app/api/v1/media/remove-background", {
38
+ method: "POST",
39
+ headers: {
40
+ accept: "application/json",
41
+ "X-API-Key": apiKey,
42
+ },
43
+ body: formData,
44
+ });
45
+
46
+ const json = await res.json();
47
+ if (!json.sucess && !json.success) {
48
+ throw new Error("Background removal failed");
49
+ }
50
+
51
+ const buffer = Buffer.from(json.buffer, "base64");
52
+ await writeFile(safeOutput, buffer);
53
+ log(` [bg-removed] ${safeOutput}`);
54
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Shared .env loader for ai-asset-generator scripts.
3
+ *
4
+ * Search order:
5
+ * 1. Project root (skills/) — the single source of truth for all env vars
6
+ * 2. ai-asset-generator/ dir — legacy fallback
7
+ * 3. Monorepo root (skills/../../) — fallback for monorepo setups
8
+ *
9
+ * Files checked: .env, .env.local (values from earlier files win).
10
+ */
11
+
12
+ import { readFile } from "fs/promises";
13
+ import { join, dirname } from "path";
14
+ import { fileURLToPath } from "url";
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const GENERATOR_DIR = join(__dirname, "..");
18
+ const PROJECT_ROOT = join(__dirname, "..", "..", ".."); // skills/
19
+ const MONOREPO_ROOT = join(__dirname, "..", "..", "..", ".."); // one above skills/
20
+
21
+ export async function loadEnv() {
22
+ const searchPaths = [PROJECT_ROOT, GENERATOR_DIR, MONOREPO_ROOT];
23
+ const envFiles = [".env", ".env.local"];
24
+
25
+ for (const dir of searchPaths) {
26
+ for (const envFile of envFiles) {
27
+ try {
28
+ const raw = await readFile(join(dir, envFile), "utf8");
29
+ let loaded = 0;
30
+ for (const line of raw.split("\n")) {
31
+ const trimmed = line.trim();
32
+ if (!trimmed || trimmed.startsWith("#")) continue;
33
+ const eq = trimmed.indexOf("=");
34
+ if (eq === -1) continue;
35
+ const key = trimmed.slice(0, eq).trim();
36
+ const val = trimmed.slice(eq + 1).trim();
37
+ if (!process.env[key]) {
38
+ process.env[key] = val;
39
+ loaded++;
40
+ }
41
+ }
42
+ if (loaded > 0) console.log(` [env] Loaded ${loaded} vars from ${dir}/${envFile}`);
43
+ } catch {
44
+ // File doesn't exist, continue
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Shared KIE AI API client.
3
+ *
4
+ * Provides: createTask, queryTask, pollUntilDone, downloadFile
5
+ * All scripts use the same API base and auth pattern.
6
+ */
7
+
8
+ import { writeFile, mkdir } from "fs/promises";
9
+ import { existsSync } from "fs";
10
+ import { dirname, join, resolve } from "path";
11
+ import { fileURLToPath } from "url";
12
+
13
+ const __lib = dirname(fileURLToPath(import.meta.url));
14
+ const SKILL_DIR = join(__lib, "..");
15
+
16
+ // ─── Helpers ────────────────────────────────────────────────
17
+ export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
18
+ export const log = (msg) => console.log(msg);
19
+
20
+ /**
21
+ * Default output directory: ai-asset-generator/output/<name>/
22
+ * Each generator gets its own subfolder under the skill's output/ dir.
23
+ */
24
+ export function outputDir(name) {
25
+ return join(SKILL_DIR, "output", name);
26
+ }
27
+
28
+ // ─── API Client ─────────────────────────────────────────────
29
+ const API = "https://api.kie.ai/api/v1/jobs";
30
+
31
+ function headers() {
32
+ return {
33
+ "Content-Type": "application/json",
34
+ Authorization: `Bearer ${process.env.KIE_AI_API_KEY}`,
35
+ };
36
+ }
37
+
38
+ export async function createTask(payload) {
39
+ const res = await fetch(`${API}/createTask`, {
40
+ method: "POST",
41
+ headers: headers(),
42
+ body: JSON.stringify(payload),
43
+ });
44
+ const json = await res.json();
45
+ if (json.code !== 200) {
46
+ throw new Error(`createTask failed (${json.code}): ${json.message}`);
47
+ }
48
+ return json.data.taskId;
49
+ }
50
+
51
+ export async function queryTask(taskId) {
52
+ const res = await fetch(`${API}/recordInfo?taskId=${taskId}`, {
53
+ headers: { Authorization: `Bearer ${process.env.KIE_AI_API_KEY}` },
54
+ });
55
+ const json = await res.json();
56
+ if (json.code !== 200) {
57
+ throw new Error(`queryTask failed (${json.code}): ${json.message}`);
58
+ }
59
+ return json.data;
60
+ }
61
+
62
+ /**
63
+ * Poll a task until it reaches success/fail.
64
+ * Uses increasing delay: 5s → 8s → 12s → … capped at 30s.
65
+ */
66
+ export async function pollUntilDone(taskId, label, timeoutMs = 300_000) {
67
+ const t0 = Date.now();
68
+ let delay = 5_000;
69
+
70
+ while (Date.now() - t0 < timeoutMs) {
71
+ const task = await queryTask(taskId);
72
+
73
+ if (task.state === "success") {
74
+ const urls = JSON.parse(task.resultJson).resultUrls;
75
+ log(` [done] ${label}`);
76
+ return urls[0];
77
+ }
78
+
79
+ if (task.state === "fail") {
80
+ throw new Error(`${label} failed: ${task.failMsg || "unknown"}`);
81
+ }
82
+
83
+ const elapsed = Math.round((Date.now() - t0) / 1000);
84
+ log(` [${task.state}] ${label} (${elapsed}s, next poll in ${Math.round(delay / 1000)}s)`);
85
+ await sleep(delay);
86
+ delay = Math.min(Math.round(delay * 1.5), 30_000);
87
+ }
88
+
89
+ throw new Error(`${label} timed out after ${timeoutMs / 1000}s`);
90
+ }
91
+
92
+ export async function downloadFile(url, dest) {
93
+ const parsed = new URL(url);
94
+ if (!['https:'].includes(parsed.protocol)) {
95
+ throw new Error(`Disallowed URL scheme: ${parsed.protocol}`);
96
+ }
97
+
98
+ const safeDest = resolve(dest);
99
+ const safeBase = resolve(join(SKILL_DIR, "output"));
100
+ if (!safeDest.startsWith(safeBase)) {
101
+ throw new Error(`Path traversal blocked: ${dest} escapes output directory`);
102
+ }
103
+
104
+ const dir = dirname(safeDest);
105
+ if (!existsSync(dir)) await mkdir(dir, { recursive: true });
106
+ const res = await fetch(url);
107
+ if (!res.ok) throw new Error(`Download failed: ${res.status} ${url}`);
108
+ const buf = Buffer.from(await res.arrayBuffer());
109
+ await writeFile(safeDest, buf);
110
+ log(` [saved] ${safeDest}`);
111
+ }