@felores/kie-cli 0.1.2 → 0.2.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/index.js +171 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -624,6 +624,12 @@ var ListTasksSchema = z.object({
|
|
|
624
624
|
limit: z.number().int().max(100).default(20).describe("Maximum number of tasks to return"),
|
|
625
625
|
status: z.enum(["pending", "processing", "completed", "failed"]).optional().describe("Filter by status")
|
|
626
626
|
});
|
|
627
|
+
var WaitForTaskSchema = z.object({
|
|
628
|
+
task_id: z.string().min(1).describe("Task ID returned by a generation tool to wait for"),
|
|
629
|
+
timeout_seconds: z.number().int().min(5).max(600).default(180).describe("Max seconds to wait before giving up"),
|
|
630
|
+
interval_seconds: z.number().int().min(1).max(60).default(5).describe("Seconds between status checks while waiting"),
|
|
631
|
+
rendezvous_url: z.string().url().optional().describe("Optional callback rendezvous result base URL (e.g. https://felo-workers.felo.workers.dev/kie/result). Omit to poll the Kie API directly (the default). When set, or when KIE_AI_RESULT_URL / a KIE_AI_CALLBACK_URL ending in /kie/callback is configured, it waits on the rendezvous instead")
|
|
632
|
+
});
|
|
627
633
|
var Veo3Get1080pVideoSchema = z.object({
|
|
628
634
|
task_id: z.string().min(1).describe("Veo3 task ID to get 1080p video for"),
|
|
629
635
|
index: z.number().int().min(0).optional().describe("Video index (optional, for multiple video results)")
|
|
@@ -3433,6 +3439,170 @@ var veo3Get1080pVideoTool = {
|
|
|
3433
3439
|
}
|
|
3434
3440
|
};
|
|
3435
3441
|
|
|
3442
|
+
// ../core/dist/tools/wait_for_task.js
|
|
3443
|
+
function resolveResultBase(explicit, ctx) {
|
|
3444
|
+
const strip = (u) => u.replace(/\/+$/, "");
|
|
3445
|
+
if (explicit)
|
|
3446
|
+
return strip(explicit);
|
|
3447
|
+
if (process.env.KIE_AI_RESULT_URL)
|
|
3448
|
+
return strip(process.env.KIE_AI_RESULT_URL);
|
|
3449
|
+
const callback = ctx.getCallbackUrl();
|
|
3450
|
+
if (callback.endsWith("/kie/callback")) {
|
|
3451
|
+
return callback.slice(0, -"/kie/callback".length) + "/kie/result";
|
|
3452
|
+
}
|
|
3453
|
+
return null;
|
|
3454
|
+
}
|
|
3455
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3456
|
+
async function pollOnce(ctx, task_id) {
|
|
3457
|
+
try {
|
|
3458
|
+
const res = await getTaskStatusTool.run({ task_id }, ctx);
|
|
3459
|
+
return JSON.parse(res.content[0].text);
|
|
3460
|
+
} catch {
|
|
3461
|
+
return null;
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
var waitForTaskTool = {
|
|
3465
|
+
name: "wait_for_task",
|
|
3466
|
+
description: "Wait for a generation task to complete in a single call, so you don't have to poll get_task_status repeatedly. Pass the task_id returned by any generation tool: it blocks until the result is ready (or the timeout) and returns the final URLs, streaming progress meanwhile. By default it polls the Kie API directly (no setup); if a callback rendezvous is configured (KIE_AI_RESULT_URL, rendezvous_url, or a KIE_AI_CALLBACK_URL ending in /kie/callback) it waits on that instead. Tip for long jobs: clients should enable resetTimeoutOnProgress with a generous maxTotalTimeout.",
|
|
3467
|
+
category: "utility",
|
|
3468
|
+
schema: WaitForTaskSchema,
|
|
3469
|
+
async run(args, ctx) {
|
|
3470
|
+
try {
|
|
3471
|
+
const { task_id, timeout_seconds = 180, interval_seconds = 5, rendezvous_url } = WaitForTaskSchema.parse(args);
|
|
3472
|
+
const base = resolveResultBase(rendezvous_url, ctx);
|
|
3473
|
+
const start = Date.now();
|
|
3474
|
+
const deadline = start + timeout_seconds * 1e3;
|
|
3475
|
+
const totalTicks = Math.max(1, Math.ceil(timeout_seconds / interval_seconds));
|
|
3476
|
+
let tick = 0;
|
|
3477
|
+
const elapsed = () => Math.round((Date.now() - start) / 1e3);
|
|
3478
|
+
if (base) {
|
|
3479
|
+
const url = `${base}/${encodeURIComponent(task_id)}`;
|
|
3480
|
+
while (Date.now() < deadline) {
|
|
3481
|
+
tick++;
|
|
3482
|
+
try {
|
|
3483
|
+
const res = await fetch(url);
|
|
3484
|
+
if (res.status === 200) {
|
|
3485
|
+
const data = await res.json();
|
|
3486
|
+
return {
|
|
3487
|
+
content: [
|
|
3488
|
+
{
|
|
3489
|
+
type: "text",
|
|
3490
|
+
text: JSON.stringify({
|
|
3491
|
+
success: true,
|
|
3492
|
+
task_id,
|
|
3493
|
+
status: data.status ?? "completed",
|
|
3494
|
+
elapsed_seconds: elapsed(),
|
|
3495
|
+
result_urls: data.urls ?? [],
|
|
3496
|
+
model: data.model ?? null,
|
|
3497
|
+
message: "Result received from rendezvous"
|
|
3498
|
+
}, null, 2)
|
|
3499
|
+
}
|
|
3500
|
+
]
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
} catch {
|
|
3504
|
+
}
|
|
3505
|
+
await ctx.onProgress?.({
|
|
3506
|
+
progress: tick,
|
|
3507
|
+
total: totalTicks,
|
|
3508
|
+
message: `Waiting on rendezvous\u2026 ${elapsed()}s elapsed`
|
|
3509
|
+
});
|
|
3510
|
+
await sleep(interval_seconds * 1e3);
|
|
3511
|
+
}
|
|
3512
|
+
return {
|
|
3513
|
+
isError: true,
|
|
3514
|
+
content: [
|
|
3515
|
+
{
|
|
3516
|
+
type: "text",
|
|
3517
|
+
text: JSON.stringify({
|
|
3518
|
+
success: false,
|
|
3519
|
+
task_id,
|
|
3520
|
+
status: "timed_out",
|
|
3521
|
+
error: "timeout",
|
|
3522
|
+
elapsed_seconds: elapsed(),
|
|
3523
|
+
message: `No result after ${timeout_seconds}s. The task may still be running; retry wait_for_task or check get_task_status.`
|
|
3524
|
+
}, null, 2)
|
|
3525
|
+
}
|
|
3526
|
+
]
|
|
3527
|
+
};
|
|
3528
|
+
}
|
|
3529
|
+
while (Date.now() < deadline) {
|
|
3530
|
+
tick++;
|
|
3531
|
+
const details = await pollOnce(ctx, task_id);
|
|
3532
|
+
const task = await ctx.db.getTask(task_id);
|
|
3533
|
+
if (task?.status === "completed") {
|
|
3534
|
+
const fromDetails = details?.result_urls;
|
|
3535
|
+
const result_urls = fromDetails && fromDetails.length > 0 ? fromDetails : task.result_url ? [task.result_url] : [];
|
|
3536
|
+
return {
|
|
3537
|
+
content: [
|
|
3538
|
+
{
|
|
3539
|
+
type: "text",
|
|
3540
|
+
text: JSON.stringify({
|
|
3541
|
+
success: true,
|
|
3542
|
+
task_id,
|
|
3543
|
+
status: "completed",
|
|
3544
|
+
elapsed_seconds: elapsed(),
|
|
3545
|
+
result_urls,
|
|
3546
|
+
details,
|
|
3547
|
+
message: "Generation completed"
|
|
3548
|
+
}, null, 2)
|
|
3549
|
+
}
|
|
3550
|
+
]
|
|
3551
|
+
};
|
|
3552
|
+
}
|
|
3553
|
+
if (task?.status === "failed") {
|
|
3554
|
+
return {
|
|
3555
|
+
isError: true,
|
|
3556
|
+
content: [
|
|
3557
|
+
{
|
|
3558
|
+
type: "text",
|
|
3559
|
+
text: JSON.stringify({
|
|
3560
|
+
success: false,
|
|
3561
|
+
task_id,
|
|
3562
|
+
status: "failed",
|
|
3563
|
+
elapsed_seconds: elapsed(),
|
|
3564
|
+
error: task.error_message ?? "Generation failed",
|
|
3565
|
+
details,
|
|
3566
|
+
message: "Generation failed"
|
|
3567
|
+
}, null, 2)
|
|
3568
|
+
}
|
|
3569
|
+
]
|
|
3570
|
+
};
|
|
3571
|
+
}
|
|
3572
|
+
await ctx.onProgress?.({
|
|
3573
|
+
progress: tick,
|
|
3574
|
+
total: totalTicks,
|
|
3575
|
+
message: `Generating\u2026 ${elapsed()}s elapsed (status: ${task?.status ?? "pending"})`
|
|
3576
|
+
});
|
|
3577
|
+
await sleep(interval_seconds * 1e3);
|
|
3578
|
+
}
|
|
3579
|
+
return {
|
|
3580
|
+
isError: true,
|
|
3581
|
+
content: [
|
|
3582
|
+
{
|
|
3583
|
+
type: "text",
|
|
3584
|
+
text: JSON.stringify({
|
|
3585
|
+
success: false,
|
|
3586
|
+
task_id,
|
|
3587
|
+
status: "timed_out",
|
|
3588
|
+
error: "timeout",
|
|
3589
|
+
elapsed_seconds: elapsed(),
|
|
3590
|
+
message: `Still running after ${timeout_seconds}s. Call wait_for_task again with the same task_id, or check get_task_status.`
|
|
3591
|
+
}, null, 2)
|
|
3592
|
+
}
|
|
3593
|
+
]
|
|
3594
|
+
};
|
|
3595
|
+
} catch (error) {
|
|
3596
|
+
return ctx.formatError("wait_for_task", error, {
|
|
3597
|
+
task_id: "Required: task ID returned by a generation tool",
|
|
3598
|
+
timeout_seconds: "Optional: max seconds to wait (5-600, default: 180)",
|
|
3599
|
+
interval_seconds: "Optional: seconds between status checks (1-60, default: 5)",
|
|
3600
|
+
rendezvous_url: "Optional: rendezvous result base URL (e.g. https://host/kie/result); omit to poll the Kie API directly"
|
|
3601
|
+
});
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
};
|
|
3605
|
+
|
|
3436
3606
|
// ../core/dist/tools/wan_animate.js
|
|
3437
3607
|
import { z as z21 } from "zod";
|
|
3438
3608
|
var wanAnimateTool = {
|
|
@@ -3651,6 +3821,7 @@ var TOOL_REGISTRY = [
|
|
|
3651
3821
|
topazUpscaleImageTool,
|
|
3652
3822
|
veo3GenerateVideoTool,
|
|
3653
3823
|
veo3Get1080pVideoTool,
|
|
3824
|
+
waitForTaskTool,
|
|
3654
3825
|
wanAnimateTool,
|
|
3655
3826
|
wanVideoTool,
|
|
3656
3827
|
zImageTool
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@felores/kie-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Standalone CLI for Kie.ai APIs: generate images, video, music and speech from the terminal. Same models as the Kie.ai MCP server, no MCP client required.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|