@afromero/kin3o 0.2.3 → 0.2.4
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 +9 -0
- package/dist/index.js +4 -2
- package/dist/providers/claude.d.ts +1 -1
- package/dist/providers/claude.js +6 -3
- package/dist/providers/codex.d.ts +1 -1
- package/dist/providers/codex.js +7 -3
- package/dist/providers/registry.d.ts +3 -1
- package/dist/providers/registry.js +6 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
**Text to Motion. From your terminal.**
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@afromero/kin3o)
|
|
8
|
+
[](https://www.npmjs.com/package/@afromero/kin3o)
|
|
8
9
|
[](https://github.com/affromero/kin3o/actions/workflows/ci.yml)
|
|
9
10
|
[](https://www.typescriptlang.org/)
|
|
10
11
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -237,6 +238,14 @@ npm run ci # typecheck + test
|
|
|
237
238
|
npm run build # Compile to dist/
|
|
238
239
|
```
|
|
239
240
|
|
|
241
|
+
## Related Projects
|
|
242
|
+
|
|
243
|
+
| Project | Description |
|
|
244
|
+
|---------|-------------|
|
|
245
|
+
| [**Fairtrail**](https://github.com/affromero/fairtrail) | Flight price evolution tracker with natural language search |
|
|
246
|
+
| [**PriceToken**](https://github.com/affromero/pricetoken) | Real-time LLM pricing API, npm/PyPI packages, and live dashboard |
|
|
247
|
+
| [**gitpane**](https://github.com/affromero/gitpane) | Multi-repo Git workspace dashboard for the terminal |
|
|
248
|
+
|
|
240
249
|
## License
|
|
241
250
|
|
|
242
251
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,7 @@ program
|
|
|
28
28
|
.option('--no-preview', 'Skip opening preview in browser')
|
|
29
29
|
.option('-t, --tokens <path>', 'Path to design tokens JSON (or "sotto" for built-in)')
|
|
30
30
|
.option('-i, --interactive', 'Generate interactive state machine (.lottie output)', false)
|
|
31
|
+
.option('--timeout <ms>', 'CLI subprocess timeout in milliseconds (default: auto per model)', parseInt)
|
|
31
32
|
.action(async (prompt, options) => {
|
|
32
33
|
const mode = options.interactive ? 'interactive' : 'static';
|
|
33
34
|
console.log(`\nkin3o — Generating ${mode} animation...`);
|
|
@@ -56,7 +57,7 @@ program
|
|
|
56
57
|
: buildSystemPrompt(tokens);
|
|
57
58
|
// 4. Generate
|
|
58
59
|
try {
|
|
59
|
-
const result = await provider.generate(model, systemPrompt, prompt);
|
|
60
|
+
const result = await provider.generate(model, systemPrompt, prompt, options.timeout);
|
|
60
61
|
console.log(` ✓ Generated in ${(result.durationMs / 1000).toFixed(1)}s`);
|
|
61
62
|
if (options.interactive) {
|
|
62
63
|
await handleInteractiveOutput(result.content, prompt, options);
|
|
@@ -166,6 +167,7 @@ program
|
|
|
166
167
|
.option('-o, --output <path>', 'Output file path')
|
|
167
168
|
.option('--no-preview', 'Skip opening preview in browser')
|
|
168
169
|
.option('-t, --tokens <path>', 'Path to design tokens JSON (or "sotto" for built-in)')
|
|
170
|
+
.option('--timeout <ms>', 'CLI subprocess timeout in milliseconds (default: 600000)', parseInt)
|
|
169
171
|
.action(async (file, prompt, rawOptions) => {
|
|
170
172
|
const resolvedPath = resolve(file);
|
|
171
173
|
// 1. Validate input file
|
|
@@ -225,7 +227,7 @@ program
|
|
|
225
227
|
: buildRefinementUserPrompt(currentJson, prompt);
|
|
226
228
|
// 6. Generate refined output
|
|
227
229
|
try {
|
|
228
|
-
const result = await provider.generate(model, systemPrompt, userPrompt);
|
|
230
|
+
const result = await provider.generate(model, systemPrompt, userPrompt, rawOptions.timeout);
|
|
229
231
|
console.log(` ✓ Refined in ${(result.durationMs / 1000).toFixed(1)}s`);
|
|
230
232
|
// 7. Compute output path
|
|
231
233
|
const outputDir = ensureOutputDir();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { GenerationResult } from './registry.js';
|
|
2
|
-
export declare function generateWithClaude(model: string, systemPrompt: string, userPrompt: string): Promise<GenerationResult>;
|
|
2
|
+
export declare function generateWithClaude(model: string, systemPrompt: string, userPrompt: string, timeoutMs?: number): Promise<GenerationResult>;
|
package/dist/providers/claude.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { getTimeoutMs } from './registry.js';
|
|
2
3
|
import { filterCliStderr } from '../utils.js';
|
|
3
|
-
export async function generateWithClaude(model, systemPrompt, userPrompt) {
|
|
4
|
+
export async function generateWithClaude(model, systemPrompt, userPrompt, timeoutMs) {
|
|
4
5
|
const start = Date.now();
|
|
5
6
|
const fullPrompt = `${systemPrompt}\n\n${userPrompt}`;
|
|
7
|
+
const timeout = getTimeoutMs(timeoutMs);
|
|
6
8
|
const content = await new Promise((resolve, reject) => {
|
|
7
9
|
const env = { ...process.env };
|
|
8
10
|
delete env.ANTHROPIC_API_KEY;
|
|
9
11
|
const proc = spawn('claude', ['--print', '--model', model], {
|
|
10
|
-
timeout
|
|
12
|
+
timeout,
|
|
11
13
|
env,
|
|
12
14
|
});
|
|
13
15
|
let stdout = '';
|
|
@@ -21,7 +23,8 @@ export async function generateWithClaude(model, systemPrompt, userPrompt) {
|
|
|
21
23
|
proc.on('close', (code) => {
|
|
22
24
|
if (code !== 0) {
|
|
23
25
|
const filtered = filterCliStderr(stderr);
|
|
24
|
-
|
|
26
|
+
const hint = code === 143 ? ` (timed out after ${Math.round(timeout / 1000)}s — try --timeout <ms> or a faster model)` : '';
|
|
27
|
+
reject(new Error(`claude CLI exited ${code}: ${filtered}${hint}`));
|
|
25
28
|
}
|
|
26
29
|
else {
|
|
27
30
|
resolve(stdout.trim());
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { GenerationResult } from './registry.js';
|
|
2
|
-
export declare function generateWithCodex(model: string, systemPrompt: string, userPrompt: string): Promise<GenerationResult>;
|
|
2
|
+
export declare function generateWithCodex(model: string, systemPrompt: string, userPrompt: string, timeoutMs?: number): Promise<GenerationResult>;
|
package/dist/providers/codex.js
CHANGED
|
@@ -2,11 +2,13 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { mkdtempSync, readFileSync, unlinkSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
|
+
import { getTimeoutMs } from './registry.js';
|
|
5
6
|
import { filterCliStderr } from '../utils.js';
|
|
6
|
-
export async function generateWithCodex(model, systemPrompt, userPrompt) {
|
|
7
|
+
export async function generateWithCodex(model, systemPrompt, userPrompt, timeoutMs) {
|
|
7
8
|
const start = Date.now();
|
|
8
9
|
const fullPrompt = `${systemPrompt}\n\n${userPrompt}`;
|
|
9
10
|
const tmpFile = join(mkdtempSync(join(tmpdir(), 'codex-')), 'output.txt');
|
|
11
|
+
const timeout = getTimeoutMs(timeoutMs);
|
|
10
12
|
const args = [
|
|
11
13
|
'exec', '-',
|
|
12
14
|
'--skip-git-repo-check',
|
|
@@ -16,7 +18,7 @@ export async function generateWithCodex(model, systemPrompt, userPrompt) {
|
|
|
16
18
|
];
|
|
17
19
|
const content = await new Promise((resolve, reject) => {
|
|
18
20
|
const proc = spawn('codex', args, {
|
|
19
|
-
timeout
|
|
21
|
+
timeout,
|
|
20
22
|
env: { ...process.env },
|
|
21
23
|
});
|
|
22
24
|
let stderr = '';
|
|
@@ -27,7 +29,9 @@ export async function generateWithCodex(model, systemPrompt, userPrompt) {
|
|
|
27
29
|
const filtered = filterCliStderr(stderr);
|
|
28
30
|
const hint = filtered.includes('401') || filtered.includes('Unauthorized')
|
|
29
31
|
? ' (ensure codex is authenticated via `codex auth`)'
|
|
30
|
-
:
|
|
32
|
+
: code === 143
|
|
33
|
+
? ` (timed out after ${Math.round(timeout / 1000)}s — try --timeout <ms> or a faster model)`
|
|
34
|
+
: '';
|
|
31
35
|
try {
|
|
32
36
|
const output = readFileSync(tmpFile, 'utf-8').trim();
|
|
33
37
|
unlinkSync(tmpFile);
|
|
@@ -9,8 +9,10 @@ export interface ProviderConfig {
|
|
|
9
9
|
models: string[];
|
|
10
10
|
defaultModel: string;
|
|
11
11
|
isAvailable: () => Promise<boolean>;
|
|
12
|
-
generate: (model: string, systemPrompt: string, userPrompt: string) => Promise<GenerationResult>;
|
|
12
|
+
generate: (model: string, systemPrompt: string, userPrompt: string, timeoutMs?: number) => Promise<GenerationResult>;
|
|
13
13
|
}
|
|
14
|
+
/** Get timeout with optional user override */
|
|
15
|
+
export declare function getTimeoutMs(userOverride?: number): number;
|
|
14
16
|
export declare const PROVIDERS: Record<string, ProviderConfig>;
|
|
15
17
|
/** Detect which providers are available (binary + auth) */
|
|
16
18
|
export declare function detectAvailableProviders(): Promise<string[]>;
|
|
@@ -4,6 +4,12 @@ import { homedir } from 'node:os';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { generateWithClaude } from './claude.js';
|
|
6
6
|
import { generateWithCodex } from './codex.js';
|
|
7
|
+
/** Default CLI subprocess timeout: 10 minutes */
|
|
8
|
+
const DEFAULT_TIMEOUT_MS = 600_000;
|
|
9
|
+
/** Get timeout with optional user override */
|
|
10
|
+
export function getTimeoutMs(userOverride) {
|
|
11
|
+
return userOverride ?? DEFAULT_TIMEOUT_MS;
|
|
12
|
+
}
|
|
7
13
|
const home = homedir();
|
|
8
14
|
function hasBinary(name) {
|
|
9
15
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@afromero/kin3o",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "AI-powered Lottie animation generator — text to motion from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
42
42
|
"@dotlottie/dotlottie-js": "^1.6.3",
|
|
43
43
|
"commander": "^13.0.0",
|
|
44
|
+
"lottie-react": "^2.4.1",
|
|
44
45
|
"open": "^10.1.0",
|
|
45
46
|
"puppeteer-core": "^24.0.0"
|
|
46
47
|
},
|