maoda-commander-tt 0.0.19 → 0.0.20
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/main.js +396 -502
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2455,277 +2455,26 @@ user_prompt:
|
|
|
2455
2455
|
}
|
|
2456
2456
|
};
|
|
2457
2457
|
|
|
2458
|
-
// src/commands/ai/ai-image-command.ts
|
|
2459
|
-
import
|
|
2460
|
-
import * as path7 from "path";
|
|
2461
|
-
import { Option as Option8 } from "commander";
|
|
2462
|
-
|
|
2463
|
-
// src/commands/ai/ai-image-reference.ts
|
|
2458
|
+
// src/commands/ai/ai-image-command-v2.ts
|
|
2459
|
+
import { spawn as spawn3 } from "child_process";
|
|
2464
2460
|
import * as fs5 from "fs/promises";
|
|
2465
|
-
import
|
|
2461
|
+
import { homedir as homedir7 } from "os";
|
|
2466
2462
|
import * as path6 from "path";
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
var
|
|
2470
|
-
var
|
|
2471
|
-
var MAX_REFERENCE_IMAGE_COUNT = 14;
|
|
2472
|
-
var API_SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
2473
|
-
"image/jpeg",
|
|
2474
|
-
"image/png",
|
|
2475
|
-
"image/webp",
|
|
2476
|
-
"image/bmp",
|
|
2477
|
-
"image/tiff",
|
|
2478
|
-
"image/gif"
|
|
2479
|
-
]);
|
|
2480
|
-
async function prepareReferenceImages(filePaths) {
|
|
2481
|
-
if (filePaths.length > MAX_REFERENCE_IMAGE_COUNT) {
|
|
2482
|
-
return makeError(
|
|
2483
|
-
1,
|
|
2484
|
-
`\u53C2\u8003\u56FE\u6700\u591A\u652F\u6301 ${MAX_REFERENCE_IMAGE_COUNT} \u5F20\uFF0C\u5F53\u524D\u4F20\u5165 ${filePaths.length} \u5F20\u3002`
|
|
2485
|
-
);
|
|
2486
|
-
}
|
|
2487
|
-
const preparedImages = [];
|
|
2488
|
-
for (const filePath of filePaths) {
|
|
2489
|
-
const prepared = await prepareReferenceImage(filePath);
|
|
2490
|
-
if (!prepared.ok) {
|
|
2491
|
-
return prepared;
|
|
2492
|
-
}
|
|
2493
|
-
preparedImages.push(prepared.value);
|
|
2494
|
-
}
|
|
2495
|
-
return makeOkWith(preparedImages);
|
|
2496
|
-
}
|
|
2497
|
-
async function prepareReferenceImage(filePath) {
|
|
2498
|
-
const resolvedPath = path6.resolve(process.cwd(), filePath);
|
|
2499
|
-
let stats;
|
|
2500
|
-
try {
|
|
2501
|
-
stats = await fs5.stat(resolvedPath);
|
|
2502
|
-
} catch (error) {
|
|
2503
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2504
|
-
return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
2505
|
-
}
|
|
2506
|
-
if (!stats.isFile()) {
|
|
2507
|
-
return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u6587\u4EF6: ${resolvedPath}`);
|
|
2508
|
-
}
|
|
2509
|
-
let fileBytes;
|
|
2510
|
-
try {
|
|
2511
|
-
fileBytes = await fs5.readFile(resolvedPath);
|
|
2512
|
-
} catch (error) {
|
|
2513
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2514
|
-
return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5185\u5BB9\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
2515
|
-
}
|
|
2516
|
-
const detectedType = detectMimeType(fileBytes, resolvedPath);
|
|
2517
|
-
if (!detectedType) {
|
|
2518
|
-
return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${resolvedPath}`);
|
|
2519
|
-
}
|
|
2520
|
-
const needsTranscode = !API_SUPPORTED_MIME_TYPES.has(detectedType.mimeType) || fileBytes.byteLength > MAX_REFERENCE_IMAGE_BYTES;
|
|
2521
|
-
if (!needsTranscode) {
|
|
2522
|
-
return makeOkWith({
|
|
2523
|
-
inputPath: filePath,
|
|
2524
|
-
resolvedPath,
|
|
2525
|
-
sourceMimeType: detectedType.mimeType,
|
|
2526
|
-
requestMimeType: detectedType.mimeType,
|
|
2527
|
-
originalBytes: fileBytes.byteLength,
|
|
2528
|
-
finalBytes: fileBytes.byteLength,
|
|
2529
|
-
compressed: false,
|
|
2530
|
-
dataUrl: toDataUrl(detectedType.mimeType, fileBytes)
|
|
2531
|
-
});
|
|
2532
|
-
}
|
|
2533
|
-
const compressed = await compressImageForReference(resolvedPath);
|
|
2534
|
-
if (!compressed.ok) {
|
|
2535
|
-
return compressed;
|
|
2536
|
-
}
|
|
2537
|
-
return makeOkWith({
|
|
2538
|
-
inputPath: filePath,
|
|
2539
|
-
resolvedPath,
|
|
2540
|
-
sourceMimeType: detectedType.mimeType,
|
|
2541
|
-
requestMimeType: compressed.value.mimeType,
|
|
2542
|
-
originalBytes: fileBytes.byteLength,
|
|
2543
|
-
finalBytes: compressed.value.bytes.byteLength,
|
|
2544
|
-
compressed: true,
|
|
2545
|
-
dataUrl: toDataUrl(compressed.value.mimeType, compressed.value.bytes)
|
|
2546
|
-
});
|
|
2547
|
-
}
|
|
2548
|
-
function detectMimeType(fileBytes, filePath) {
|
|
2549
|
-
if (fileBytes.length >= 3 && fileBytes[0] === 255 && fileBytes[1] === 216 && fileBytes[2] === 255) {
|
|
2550
|
-
return { mimeType: "image/jpeg", extension: "jpeg" };
|
|
2551
|
-
}
|
|
2552
|
-
if (fileBytes.length >= 8 && fileBytes[0] === 137 && fileBytes[1] === 80 && fileBytes[2] === 78 && fileBytes[3] === 71 && fileBytes[4] === 13 && fileBytes[5] === 10 && fileBytes[6] === 26 && fileBytes[7] === 10) {
|
|
2553
|
-
return { mimeType: "image/png", extension: "png" };
|
|
2554
|
-
}
|
|
2555
|
-
if (fileBytes.length >= 6) {
|
|
2556
|
-
const gifHeader = fileBytes.subarray(0, 6).toString("ascii");
|
|
2557
|
-
if (gifHeader === "GIF87a" || gifHeader === "GIF89a") {
|
|
2558
|
-
return { mimeType: "image/gif", extension: "gif" };
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
if (fileBytes.length >= 12 && fileBytes.subarray(0, 4).toString("ascii") === "RIFF" && fileBytes.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
2562
|
-
return { mimeType: "image/webp", extension: "webp" };
|
|
2563
|
-
}
|
|
2564
|
-
if (fileBytes.length >= 2 && fileBytes.subarray(0, 2).toString("ascii") === "BM") {
|
|
2565
|
-
return { mimeType: "image/bmp", extension: "bmp" };
|
|
2566
|
-
}
|
|
2567
|
-
if (fileBytes.length >= 4 && (fileBytes[0] === 73 && fileBytes[1] === 73 && fileBytes[2] === 42 && fileBytes[3] === 0 || fileBytes[0] === 77 && fileBytes[1] === 77 && fileBytes[2] === 0 && fileBytes[3] === 42)) {
|
|
2568
|
-
return { mimeType: "image/tiff", extension: "tiff" };
|
|
2569
|
-
}
|
|
2570
|
-
if (fileBytes.length >= 12 && fileBytes.subarray(4, 8).toString("ascii") === "ftyp") {
|
|
2571
|
-
const brand = fileBytes.subarray(8, 12).toString("ascii").trim();
|
|
2572
|
-
if (brand.startsWith("avif") || brand === "avis") {
|
|
2573
|
-
return { mimeType: "image/avif", extension: "avif" };
|
|
2574
|
-
}
|
|
2575
|
-
if (brand.startsWith("hei") || brand.startsWith("hev") || brand === "mif1" || brand === "msf1") {
|
|
2576
|
-
return { mimeType: "image/heic", extension: "heic" };
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
const ext = path6.extname(filePath).toLowerCase();
|
|
2580
|
-
switch (ext) {
|
|
2581
|
-
case ".jpg":
|
|
2582
|
-
case ".jpeg":
|
|
2583
|
-
return { mimeType: "image/jpeg", extension: "jpeg" };
|
|
2584
|
-
case ".png":
|
|
2585
|
-
return { mimeType: "image/png", extension: "png" };
|
|
2586
|
-
case ".gif":
|
|
2587
|
-
return { mimeType: "image/gif", extension: "gif" };
|
|
2588
|
-
case ".webp":
|
|
2589
|
-
return { mimeType: "image/webp", extension: "webp" };
|
|
2590
|
-
case ".bmp":
|
|
2591
|
-
return { mimeType: "image/bmp", extension: "bmp" };
|
|
2592
|
-
case ".tif":
|
|
2593
|
-
case ".tiff":
|
|
2594
|
-
return { mimeType: "image/tiff", extension: "tiff" };
|
|
2595
|
-
case ".heic":
|
|
2596
|
-
case ".heif":
|
|
2597
|
-
return { mimeType: "image/heic", extension: "heic" };
|
|
2598
|
-
case ".avif":
|
|
2599
|
-
return { mimeType: "image/avif", extension: "avif" };
|
|
2600
|
-
default:
|
|
2601
|
-
return void 0;
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2604
|
-
async function compressImageForReference(sourcePath) {
|
|
2605
|
-
if (process.platform !== "darwin") {
|
|
2606
|
-
return makeError(
|
|
2607
|
-
1,
|
|
2608
|
-
`\u53C2\u8003\u56FE\u9700\u8981\u538B\u7F29\u6216\u8F6C\u7801\uFF0C\u4F46\u5F53\u524D\u5E73\u53F0 ${process.platform} \u672A\u5B9E\u73B0\u81EA\u52A8\u5904\u7406\u3002`
|
|
2609
|
-
);
|
|
2610
|
-
}
|
|
2611
|
-
const tempDir = await fs5.mkdtemp(path6.join(os2.tmpdir(), "tt-ai-image-"));
|
|
2612
|
-
try {
|
|
2613
|
-
const dimensions = await readImageDimensions(sourcePath);
|
|
2614
|
-
const maxDimension = Math.max(dimensions.width, dimensions.height);
|
|
2615
|
-
const resizeLimits = uniqueNumbers([
|
|
2616
|
-
maxDimension,
|
|
2617
|
-
Math.floor(maxDimension * 0.85),
|
|
2618
|
-
Math.floor(maxDimension * 0.7),
|
|
2619
|
-
Math.floor(maxDimension * 0.55),
|
|
2620
|
-
Math.floor(maxDimension * 0.4)
|
|
2621
|
-
]).filter((value) => value >= 64);
|
|
2622
|
-
const qualityLevels = [85, 75, 65, 55, 45, 35];
|
|
2623
|
-
let attemptIndex = 0;
|
|
2624
|
-
for (const resizeLimit of resizeLimits) {
|
|
2625
|
-
for (const quality of qualityLevels) {
|
|
2626
|
-
const targetPath = path6.join(tempDir, `compressed-${attemptIndex}.jpeg`);
|
|
2627
|
-
attemptIndex += 1;
|
|
2628
|
-
await runSips([
|
|
2629
|
-
"--setProperty",
|
|
2630
|
-
"format",
|
|
2631
|
-
"jpeg",
|
|
2632
|
-
"--setProperty",
|
|
2633
|
-
"formatOptions",
|
|
2634
|
-
String(quality),
|
|
2635
|
-
sourcePath,
|
|
2636
|
-
"--out",
|
|
2637
|
-
targetPath
|
|
2638
|
-
]);
|
|
2639
|
-
if (resizeLimit < maxDimension) {
|
|
2640
|
-
await runSips(["-Z", String(resizeLimit), targetPath]);
|
|
2641
|
-
}
|
|
2642
|
-
const compressedBytes = await fs5.readFile(targetPath);
|
|
2643
|
-
if (compressedBytes.byteLength <= MAX_REFERENCE_IMAGE_BYTES) {
|
|
2644
|
-
return makeOkWith({
|
|
2645
|
-
mimeType: "image/jpeg",
|
|
2646
|
-
bytes: compressedBytes
|
|
2647
|
-
});
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
return makeError(1, `\u53C2\u8003\u56FE\u538B\u7F29\u5931\u8D25\uFF0C\u65E0\u6CD5\u538B\u7F29\u5230 2MB \u4EE5\u5185: ${sourcePath}`);
|
|
2652
|
-
} catch (error) {
|
|
2653
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2654
|
-
return makeError(1, `\u5904\u7406\u53C2\u8003\u56FE\u5931\u8D25: ${sourcePath}: ${message}`);
|
|
2655
|
-
} finally {
|
|
2656
|
-
await fs5.rm(tempDir, { recursive: true, force: true });
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
async function readImageDimensions(sourcePath) {
|
|
2660
|
-
const { stdout } = await runSips([
|
|
2661
|
-
"-g",
|
|
2662
|
-
"pixelWidth",
|
|
2663
|
-
"-g",
|
|
2664
|
-
"pixelHeight",
|
|
2665
|
-
sourcePath
|
|
2666
|
-
]);
|
|
2667
|
-
const width = readSipsMetric(stdout, "pixelWidth");
|
|
2668
|
-
const height = readSipsMetric(stdout, "pixelHeight");
|
|
2669
|
-
if (!width || !height) {
|
|
2670
|
-
throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247\u5C3A\u5BF8");
|
|
2671
|
-
}
|
|
2672
|
-
return { width, height };
|
|
2673
|
-
}
|
|
2674
|
-
async function runSips(args) {
|
|
2675
|
-
return execFileAsync("/usr/bin/sips", [...args]);
|
|
2676
|
-
}
|
|
2677
|
-
function readSipsMetric(output, key) {
|
|
2678
|
-
const matcher = new RegExp(`${key}:\\s*(\\d+)`);
|
|
2679
|
-
const matched = output.match(matcher);
|
|
2680
|
-
if (!matched) {
|
|
2681
|
-
return void 0;
|
|
2682
|
-
}
|
|
2683
|
-
const value = Number.parseInt(matched[1], 10);
|
|
2684
|
-
return Number.isFinite(value) ? value : void 0;
|
|
2685
|
-
}
|
|
2686
|
-
function uniqueNumbers(values) {
|
|
2687
|
-
return [...new Set(values)];
|
|
2688
|
-
}
|
|
2689
|
-
function toDataUrl(mimeType, fileBytes) {
|
|
2690
|
-
const suffix = mimeType.replace("image/", "");
|
|
2691
|
-
return `data:image/${suffix};base64,${fileBytes.toString("base64")}`;
|
|
2692
|
-
}
|
|
2693
|
-
|
|
2694
|
-
// src/commands/ai/ai-image-command.ts
|
|
2695
|
-
var IMAGE_GENERATION_URL = "https://ark.cn-beijing.volces.com/api/v3/images/generations";
|
|
2696
|
-
var IMAGE_MODEL = "doubao-seedream-5-0-260128";
|
|
2697
|
-
var AiImageCommand = class extends AiBaseCommand {
|
|
2463
|
+
var GENERATED_IMAGES_DIR = path6.join(homedir7(), ".codex", "generated_images");
|
|
2464
|
+
var CODEX_EMPTY_WORKDIR = path6.join(homedir7(), ".codex-empty");
|
|
2465
|
+
var CREATED_AT_TOLERANCE_MS = 1e3;
|
|
2466
|
+
var AiImageCommandV2 = class extends AiBaseCommand {
|
|
2698
2467
|
_meta() {
|
|
2699
2468
|
return {
|
|
2700
2469
|
name: "image",
|
|
2701
|
-
description: "\
|
|
2470
|
+
description: "\u901A\u8FC7 codex exec \u751F\u6210\u56FE\u7247\uFF0C\u5E76\u8FD4\u56DE\u672C\u5730\u751F\u6210\u56FE\u7247\u8DEF\u5F84"
|
|
2702
2471
|
};
|
|
2703
2472
|
}
|
|
2704
|
-
_configureOptions(cmd) {
|
|
2705
|
-
cmd.addOption(
|
|
2706
|
-
new Option8(
|
|
2707
|
-
"-r, --reference <path>",
|
|
2708
|
-
"\u53C2\u8003\u56FE\u8DEF\u5F84\uFF0C\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4E5F\u652F\u6301\u7528\u82F1\u6587\u9017\u53F7\u5206\u9694\u591A\u4E2A\u8DEF\u5F84"
|
|
2709
|
-
).argParser(parseReferencePaths).default([])
|
|
2710
|
-
);
|
|
2711
|
-
cmd.addOption(
|
|
2712
|
-
new Option8(
|
|
2713
|
-
"-s, --size <size>",
|
|
2714
|
-
"\u8F93\u51FA\u5C3A\u5BF8\uFF0C\u4F8B\u5982 1024x1024\u30012048x2048\u30012K"
|
|
2715
|
-
).default("2K")
|
|
2716
|
-
);
|
|
2717
|
-
cmd.addOption(
|
|
2718
|
-
new Option8("--output-format <format>", "\u751F\u6210\u56FE\u7247\u7F16\u7801\u683C\u5F0F").choices(["jpeg", "png", "webp"]).default("jpeg")
|
|
2719
|
-
);
|
|
2720
|
-
cmd.addOption(
|
|
2721
|
-
new Option8(
|
|
2722
|
-
"-o, --output <path>",
|
|
2723
|
-
"\u8F93\u51FA\u8DEF\u5F84\uFF0C\u53EF\u4F20\u6587\u4EF6\u8DEF\u5F84\u6216\u76EE\u5F55\u8DEF\u5F84\uFF1B\u672A\u4F20\u65F6\u9ED8\u8BA4\u5199\u5165\u5F53\u524D\u76EE\u5F55\u5E76\u81EA\u52A8\u547D\u540D"
|
|
2724
|
-
)
|
|
2725
|
-
);
|
|
2726
|
-
}
|
|
2727
2473
|
_configureArguments(cmd) {
|
|
2728
|
-
cmd.argument(
|
|
2474
|
+
cmd.argument(
|
|
2475
|
+
"[prompt...]",
|
|
2476
|
+
"\u751F\u56FE\u63D0\u793A\u8BCD\uFF1B\u5982\u6709\u53C2\u8003\u56FE\uFF0C\u9700\u5728 prompt \u4E2D\u7ED9\u51FA\u53C2\u8003\u56FE\u7684\u7EDD\u5BF9\u5730\u5740"
|
|
2477
|
+
);
|
|
2729
2478
|
}
|
|
2730
2479
|
async _execute(ctx) {
|
|
2731
2480
|
const prompt = this._readPrompt(ctx);
|
|
@@ -2733,250 +2482,162 @@ var AiImageCommand = class extends AiBaseCommand {
|
|
|
2733
2482
|
this._outputJsonError(1, "\u8BF7\u8F93\u5165\u751F\u56FE\u63D0\u793A\u8BCD");
|
|
2734
2483
|
return this._makeOk();
|
|
2735
2484
|
}
|
|
2736
|
-
const
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
}
|
|
2741
|
-
const requestBody = {
|
|
2742
|
-
model: IMAGE_MODEL,
|
|
2743
|
-
prompt,
|
|
2744
|
-
size: ctx.options.size ?? "2K",
|
|
2745
|
-
response_format: "url",
|
|
2746
|
-
output_format: ctx.options.outputFormat,
|
|
2747
|
-
watermark: false,
|
|
2748
|
-
...referenceResult.value.length > 0 ? {
|
|
2749
|
-
image: referenceResult.value.map((item) => item.dataUrl)
|
|
2750
|
-
} : {}
|
|
2751
|
-
};
|
|
2752
|
-
const responseResult = await this._requestImage(requestBody);
|
|
2753
|
-
if (!responseResult.ok) {
|
|
2754
|
-
this._outputJsonError(responseResult.code, responseResult.msg);
|
|
2755
|
-
return this._makeOk();
|
|
2756
|
-
}
|
|
2757
|
-
const payload = responseResult.value;
|
|
2758
|
-
const remoteUrl = payload.data?.[0]?.url?.trim();
|
|
2759
|
-
if (!remoteUrl) {
|
|
2760
|
-
this._outputJsonError(1, "\u63A5\u53E3\u8C03\u7528\u6210\u529F\uFF0C\u4F46 data.[0].url \u4E3A\u7A7A");
|
|
2761
|
-
return this._makeOk();
|
|
2762
|
-
}
|
|
2763
|
-
const outputTargetResult = await resolveOutputTarget(
|
|
2764
|
-
ctx.options.output,
|
|
2765
|
-
ctx.options.outputFormat
|
|
2766
|
-
);
|
|
2767
|
-
if (!outputTargetResult.ok) {
|
|
2768
|
-
this._outputJsonError(outputTargetResult.code, outputTargetResult.msg);
|
|
2485
|
+
const startedAt = Date.now();
|
|
2486
|
+
const runResult = await this._runCodexImagePrompt(prompt);
|
|
2487
|
+
if (!runResult.ok) {
|
|
2488
|
+
this._outputJsonError(runResult.code, runResult.msg);
|
|
2769
2489
|
return this._makeOk();
|
|
2770
2490
|
}
|
|
2771
|
-
const
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
);
|
|
2775
|
-
if (!saveResult.ok) {
|
|
2776
|
-
this._outputJsonError(saveResult.code, saveResult.msg);
|
|
2491
|
+
const imagePathResult = await findLatestGeneratedImageFile(startedAt);
|
|
2492
|
+
if (!imagePathResult.ok) {
|
|
2493
|
+
this._outputJsonError(imagePathResult.code, imagePathResult.msg);
|
|
2777
2494
|
return this._makeOk();
|
|
2778
2495
|
}
|
|
2779
2496
|
this._outputJsonOk({
|
|
2780
|
-
image_path:
|
|
2497
|
+
image_path: imagePathResult.value
|
|
2781
2498
|
});
|
|
2782
2499
|
return this._makeOk();
|
|
2783
2500
|
}
|
|
2784
|
-
async
|
|
2785
|
-
const token = process.env.ARK_AK?.trim() || ARK_AK;
|
|
2786
|
-
if (!token) {
|
|
2787
|
-
return makeError(1, "\u7F3A\u5C11 ARK_AK\uFF0C\u8BF7\u5148\u914D\u7F6E\u53EF\u7528\u7684 API Key");
|
|
2788
|
-
}
|
|
2789
|
-
const controller = new AbortController();
|
|
2790
|
-
const timer = setTimeout(() => controller.abort(), 12e4);
|
|
2501
|
+
async _runCodexImagePrompt(prompt) {
|
|
2791
2502
|
try {
|
|
2792
|
-
|
|
2793
|
-
method: "POST",
|
|
2794
|
-
headers: {
|
|
2795
|
-
Authorization: `Bearer ${token}`,
|
|
2796
|
-
"Content-Type": "application/json"
|
|
2797
|
-
},
|
|
2798
|
-
body: JSON.stringify(requestBody),
|
|
2799
|
-
signal: controller.signal
|
|
2800
|
-
});
|
|
2801
|
-
const bodyText = await response.text();
|
|
2802
|
-
const payload = parseJsonSafely(bodyText);
|
|
2803
|
-
if (!response.ok) {
|
|
2804
|
-
const suffix = extractErrorMessage(payload) ?? bodyText.trim();
|
|
2805
|
-
return makeError(
|
|
2806
|
-
response.status,
|
|
2807
|
-
suffix ? `\u751F\u56FE\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}: ${suffix}` : `\u751F\u56FE\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}`
|
|
2808
|
-
);
|
|
2809
|
-
}
|
|
2810
|
-
if (!payload) {
|
|
2811
|
-
return makeError(1, "\u751F\u56FE\u63A5\u53E3\u8FD4\u56DE\u975E JSON \u54CD\u5E94");
|
|
2812
|
-
}
|
|
2813
|
-
if (hasFailedCode(payload.code)) {
|
|
2814
|
-
return makeError(
|
|
2815
|
-
1,
|
|
2816
|
-
extractErrorMessage(payload) ?? "\u751F\u56FE\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF"
|
|
2817
|
-
);
|
|
2818
|
-
}
|
|
2819
|
-
return makeOkWith(payload);
|
|
2503
|
+
await fs5.mkdir(CODEX_EMPTY_WORKDIR, { recursive: true });
|
|
2820
2504
|
} catch (error) {
|
|
2821
|
-
const message = error instanceof Error
|
|
2822
|
-
return
|
|
2823
|
-
|
|
2824
|
-
|
|
2505
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2506
|
+
return this._makeError(
|
|
2507
|
+
1,
|
|
2508
|
+
`\u521B\u5EFA codex \u7A7A\u5DE5\u4F5C\u76EE\u5F55\u5931\u8D25: ${CODEX_EMPTY_WORKDIR}: ${message}`
|
|
2509
|
+
);
|
|
2825
2510
|
}
|
|
2826
|
-
|
|
2827
|
-
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2511
|
+
const codexPrompt = `\u751F\u6210\u56FE\u7247
|
|
2512
|
+
${prompt}`;
|
|
2513
|
+
const args = [
|
|
2514
|
+
"exec",
|
|
2515
|
+
"-C",
|
|
2516
|
+
CODEX_EMPTY_WORKDIR,
|
|
2517
|
+
"--skip-git-repo-check",
|
|
2518
|
+
"--ephemeral",
|
|
2519
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
2520
|
+
codexPrompt
|
|
2521
|
+
];
|
|
2522
|
+
const child = spawn3("codex", args, {
|
|
2523
|
+
cwd: process.cwd(),
|
|
2524
|
+
stdio: "ignore"
|
|
2836
2525
|
});
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
autoNamed: true
|
|
2526
|
+
return new Promise((resolve8) => {
|
|
2527
|
+
child.on("error", (error) => {
|
|
2528
|
+
resolve8(
|
|
2529
|
+
this._makeError(
|
|
2530
|
+
1,
|
|
2531
|
+
`\u6267\u884C codex ${args.join(" ")} \u5931\u8D25: ${error.message}`
|
|
2532
|
+
)
|
|
2533
|
+
);
|
|
2846
2534
|
});
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
if (path7.extname(resolvedPath)) {
|
|
2860
|
-
return makeOkWith({
|
|
2861
|
-
directoryPath: path7.dirname(resolvedPath),
|
|
2862
|
-
filePath: resolvedPath,
|
|
2863
|
-
autoNamed: false
|
|
2535
|
+
child.on("close", (code, signal) => {
|
|
2536
|
+
if (code === 0) {
|
|
2537
|
+
resolve8(this._makeOk());
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
const signalText = signal ? `, signal: ${signal}` : "";
|
|
2541
|
+
resolve8(
|
|
2542
|
+
this._makeError(
|
|
2543
|
+
code ?? 1,
|
|
2544
|
+
`\u6267\u884C codex ${args.join(" ")} \u5931\u8D25${signalText}`
|
|
2545
|
+
)
|
|
2546
|
+
);
|
|
2864
2547
|
});
|
|
2865
|
-
}
|
|
2866
|
-
return makeOkWith({
|
|
2867
|
-
directoryPath: resolvedPath,
|
|
2868
|
-
filePath: path7.join(resolvedPath, buildAutoFileName(outputFormat)),
|
|
2869
|
-
autoNamed: true
|
|
2870
2548
|
});
|
|
2871
2549
|
}
|
|
2872
|
-
}
|
|
2873
|
-
async function
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2878
|
-
return makeError(
|
|
2879
|
-
1,
|
|
2880
|
-
`\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55\u5931\u8D25: ${target.directoryPath}: ${message}`
|
|
2881
|
-
);
|
|
2882
|
-
}
|
|
2883
|
-
let response;
|
|
2884
|
-
try {
|
|
2885
|
-
response = await fetch(remoteUrl);
|
|
2886
|
-
} catch (error) {
|
|
2887
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2888
|
-
return makeError(1, `\u4E0B\u8F7D\u56FE\u7247\u5931\u8D25: ${message}`);
|
|
2889
|
-
}
|
|
2890
|
-
if (!response.ok) {
|
|
2891
|
-
return makeError(response.status, `\u4E0B\u8F7D\u56FE\u7247\u5931\u8D25: HTTP ${response.status}`);
|
|
2892
|
-
}
|
|
2893
|
-
const bytes = Buffer.from(await response.arrayBuffer());
|
|
2894
|
-
const targetPath = target.autoNamed ? replaceFileExtension(
|
|
2895
|
-
target.filePath,
|
|
2896
|
-
inferImageExtension(remoteUrl, response)
|
|
2897
|
-
) : target.filePath;
|
|
2898
|
-
try {
|
|
2899
|
-
await fs6.writeFile(targetPath, bytes);
|
|
2900
|
-
} catch (error) {
|
|
2901
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2902
|
-
return makeError(1, `\u5199\u5165\u56FE\u7247\u5931\u8D25: ${targetPath}: ${message}`);
|
|
2903
|
-
}
|
|
2904
|
-
return makeOkWith(path7.resolve(targetPath));
|
|
2905
|
-
}
|
|
2906
|
-
function parseReferencePaths(value, previous) {
|
|
2907
|
-
return previous.concat(
|
|
2908
|
-
value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
|
|
2550
|
+
};
|
|
2551
|
+
async function findLatestGeneratedImageFile(minCreatedAt) {
|
|
2552
|
+
const latestDirectoryResult = await findLatestCreatedDirectory(
|
|
2553
|
+
GENERATED_IMAGES_DIR,
|
|
2554
|
+
minCreatedAt
|
|
2909
2555
|
);
|
|
2556
|
+
if (!latestDirectoryResult.ok) {
|
|
2557
|
+
return latestDirectoryResult;
|
|
2558
|
+
}
|
|
2559
|
+
return findLatestCreatedFile(latestDirectoryResult.value);
|
|
2910
2560
|
}
|
|
2911
|
-
function
|
|
2912
|
-
|
|
2913
|
-
|
|
2561
|
+
async function findLatestCreatedDirectory(directoryPath, minCreatedAt) {
|
|
2562
|
+
const entriesResult = await readDirectoryEntries(directoryPath);
|
|
2563
|
+
if (!entriesResult.ok) {
|
|
2564
|
+
return entriesResult;
|
|
2914
2565
|
}
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2566
|
+
const directories = await collectCreatedEntries(
|
|
2567
|
+
directoryPath,
|
|
2568
|
+
entriesResult.value.filter((entry) => entry.isDirectory()).map((entry) => entry.name)
|
|
2569
|
+
);
|
|
2570
|
+
if (!directories.ok) {
|
|
2571
|
+
return directories;
|
|
2572
|
+
}
|
|
2573
|
+
const latest = directories.value.filter(
|
|
2574
|
+
(entry) => entry.createdAt >= minCreatedAt - CREATED_AT_TOLERANCE_MS
|
|
2575
|
+
).sort(compareCreatedEntryDesc)[0];
|
|
2576
|
+
if (!latest) {
|
|
2577
|
+
return makeErrorForPath(
|
|
2578
|
+
1,
|
|
2579
|
+
"\u672A\u627E\u5230\u672C\u6B21 codex \u751F\u56FE\u8F93\u51FA\u5B50\u76EE\u5F55",
|
|
2580
|
+
directoryPath
|
|
2581
|
+
);
|
|
2919
2582
|
}
|
|
2583
|
+
return makeOkWith(latest.filePath);
|
|
2920
2584
|
}
|
|
2921
|
-
function
|
|
2922
|
-
|
|
2923
|
-
|
|
2585
|
+
async function findLatestCreatedFile(directoryPath) {
|
|
2586
|
+
const entriesResult = await readDirectoryEntries(directoryPath);
|
|
2587
|
+
if (!entriesResult.ok) {
|
|
2588
|
+
return entriesResult;
|
|
2924
2589
|
}
|
|
2925
|
-
|
|
2926
|
-
|
|
2590
|
+
const files = await collectCreatedEntries(
|
|
2591
|
+
directoryPath,
|
|
2592
|
+
entriesResult.value.filter((entry) => entry.isFile()).map((entry) => entry.name)
|
|
2593
|
+
);
|
|
2594
|
+
if (!files.ok) {
|
|
2595
|
+
return files;
|
|
2927
2596
|
}
|
|
2928
|
-
const
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
function extractErrorMessage(payload) {
|
|
2932
|
-
if (!payload) {
|
|
2933
|
-
return void 0;
|
|
2597
|
+
const latest = files.value.sort(compareCreatedEntryDesc)[0];
|
|
2598
|
+
if (!latest) {
|
|
2599
|
+
return makeErrorForPath(1, "\u6700\u65B0\u751F\u56FE\u8F93\u51FA\u76EE\u5F55\u4E2D\u672A\u627E\u5230\u6587\u4EF6", directoryPath);
|
|
2934
2600
|
}
|
|
2935
|
-
|
|
2936
|
-
return message || void 0;
|
|
2937
|
-
}
|
|
2938
|
-
function buildAutoFileName(outputFormat) {
|
|
2939
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2940
|
-
return `ai-image-${timestamp}.${outputFormat}`;
|
|
2601
|
+
return makeOkWith(path6.resolve(latest.filePath));
|
|
2941
2602
|
}
|
|
2942
|
-
function
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2603
|
+
async function readDirectoryEntries(directoryPath) {
|
|
2604
|
+
try {
|
|
2605
|
+
return makeOkWith(await fs5.readdir(directoryPath, { withFileTypes: true }));
|
|
2606
|
+
} catch (error) {
|
|
2607
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2608
|
+
return makeErrorForPath(1, `\u8BFB\u53D6\u76EE\u5F55\u5931\u8D25: ${message}`, directoryPath);
|
|
2946
2609
|
}
|
|
2947
|
-
return `${filePath.slice(0, -ext.length)}.${extension}`;
|
|
2948
2610
|
}
|
|
2949
|
-
function
|
|
2950
|
-
const
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
const ext = path7.extname(url.pathname).toLowerCase();
|
|
2963
|
-
if (ext === ".png") {
|
|
2964
|
-
return "png";
|
|
2965
|
-
}
|
|
2966
|
-
if (ext === ".webp") {
|
|
2967
|
-
return "webp";
|
|
2968
|
-
}
|
|
2969
|
-
if (ext === ".jpg" || ext === ".jpeg") {
|
|
2970
|
-
return "jpeg";
|
|
2611
|
+
async function collectCreatedEntries(parentPath, names) {
|
|
2612
|
+
const entries = [];
|
|
2613
|
+
for (const name of names) {
|
|
2614
|
+
const filePath = path6.join(parentPath, name);
|
|
2615
|
+
try {
|
|
2616
|
+
const stats = await fs5.stat(filePath);
|
|
2617
|
+
entries.push({
|
|
2618
|
+
filePath,
|
|
2619
|
+
createdAt: getCreatedAt(stats)
|
|
2620
|
+
});
|
|
2621
|
+
} catch (error) {
|
|
2622
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2623
|
+
return makeErrorForPath(1, `\u8BFB\u53D6\u8DEF\u5F84\u72B6\u6001\u5931\u8D25: ${message}`, filePath);
|
|
2971
2624
|
}
|
|
2972
|
-
} catch {
|
|
2973
2625
|
}
|
|
2974
|
-
return
|
|
2626
|
+
return makeOkWith(entries);
|
|
2627
|
+
}
|
|
2628
|
+
function getCreatedAt(stats) {
|
|
2629
|
+
return stats.birthtimeMs || stats.ctimeMs || stats.mtimeMs;
|
|
2630
|
+
}
|
|
2631
|
+
function compareCreatedEntryDesc(a, b) {
|
|
2632
|
+
return b.createdAt - a.createdAt || b.filePath.localeCompare(a.filePath);
|
|
2633
|
+
}
|
|
2634
|
+
function makeErrorForPath(code, message, filePath) {
|
|
2635
|
+
return makeError(code, `${message}: ${filePath}`);
|
|
2975
2636
|
}
|
|
2976
2637
|
|
|
2977
2638
|
// src/commands/ai/ai-video-command.ts
|
|
2978
|
-
import * as
|
|
2979
|
-
import { Option as
|
|
2639
|
+
import * as path7 from "path";
|
|
2640
|
+
import { Option as Option8 } from "commander";
|
|
2980
2641
|
|
|
2981
2642
|
// src/bedrock/uuid/uuid.ts
|
|
2982
2643
|
var generateUuid = (() => {
|
|
@@ -3168,10 +2829,10 @@ var AiVideoCommand = class extends AiBaseCommand {
|
|
|
3168
2829
|
}
|
|
3169
2830
|
_configureOptions(cmd) {
|
|
3170
2831
|
cmd.addOption(
|
|
3171
|
-
new
|
|
2832
|
+
new Option8(
|
|
3172
2833
|
"-r, --reference <path>",
|
|
3173
2834
|
"\u53C2\u8003\u56FE\u7247\u6216\u89C6\u9891\u8DEF\u5F84\uFF0C\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4E5F\u652F\u6301\u7528\u82F1\u6587\u9017\u53F7\u5206\u9694\u591A\u4E2A\u8DEF\u5F84"
|
|
3174
|
-
).argParser(
|
|
2835
|
+
).argParser(parseReferencePaths).default([])
|
|
3175
2836
|
);
|
|
3176
2837
|
}
|
|
3177
2838
|
_configureArguments(cmd) {
|
|
@@ -3219,7 +2880,7 @@ var AiVideoCommand = class extends AiBaseCommand {
|
|
|
3219
2880
|
return makeOkWith({
|
|
3220
2881
|
asset_id: result.value.assetId,
|
|
3221
2882
|
asset_type: result.value.fileInfo.assetType,
|
|
3222
|
-
file_path:
|
|
2883
|
+
file_path: path7.resolve(process.cwd(), referencePath),
|
|
3223
2884
|
region: result.value.region
|
|
3224
2885
|
});
|
|
3225
2886
|
})
|
|
@@ -3239,7 +2900,7 @@ var AiVideoCommand = class extends AiBaseCommand {
|
|
|
3239
2900
|
});
|
|
3240
2901
|
}
|
|
3241
2902
|
};
|
|
3242
|
-
function
|
|
2903
|
+
function parseReferencePaths(value, previous) {
|
|
3243
2904
|
return previous.concat(
|
|
3244
2905
|
value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
|
|
3245
2906
|
);
|
|
@@ -3248,7 +2909,240 @@ function parseReferencePaths2(value, previous) {
|
|
|
3248
2909
|
// src/commands/ai/ai-vid-command.ts
|
|
3249
2910
|
import * as fs7 from "fs/promises";
|
|
3250
2911
|
import * as path9 from "path";
|
|
3251
|
-
import { Option as
|
|
2912
|
+
import { Option as Option9 } from "commander";
|
|
2913
|
+
|
|
2914
|
+
// src/commands/ai/ai-image-reference.ts
|
|
2915
|
+
import * as fs6 from "fs/promises";
|
|
2916
|
+
import * as os2 from "os";
|
|
2917
|
+
import * as path8 from "path";
|
|
2918
|
+
import { execFile } from "child_process";
|
|
2919
|
+
import { promisify } from "util";
|
|
2920
|
+
var execFileAsync = promisify(execFile);
|
|
2921
|
+
var MAX_REFERENCE_IMAGE_BYTES = 2 * 1024 * 1024;
|
|
2922
|
+
var MAX_REFERENCE_IMAGE_COUNT = 14;
|
|
2923
|
+
var API_SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
2924
|
+
"image/jpeg",
|
|
2925
|
+
"image/png",
|
|
2926
|
+
"image/webp",
|
|
2927
|
+
"image/bmp",
|
|
2928
|
+
"image/tiff",
|
|
2929
|
+
"image/gif"
|
|
2930
|
+
]);
|
|
2931
|
+
async function prepareReferenceImages(filePaths) {
|
|
2932
|
+
if (filePaths.length > MAX_REFERENCE_IMAGE_COUNT) {
|
|
2933
|
+
return makeError(
|
|
2934
|
+
1,
|
|
2935
|
+
`\u53C2\u8003\u56FE\u6700\u591A\u652F\u6301 ${MAX_REFERENCE_IMAGE_COUNT} \u5F20\uFF0C\u5F53\u524D\u4F20\u5165 ${filePaths.length} \u5F20\u3002`
|
|
2936
|
+
);
|
|
2937
|
+
}
|
|
2938
|
+
const preparedImages = [];
|
|
2939
|
+
for (const filePath of filePaths) {
|
|
2940
|
+
const prepared = await prepareReferenceImage(filePath);
|
|
2941
|
+
if (!prepared.ok) {
|
|
2942
|
+
return prepared;
|
|
2943
|
+
}
|
|
2944
|
+
preparedImages.push(prepared.value);
|
|
2945
|
+
}
|
|
2946
|
+
return makeOkWith(preparedImages);
|
|
2947
|
+
}
|
|
2948
|
+
async function prepareReferenceImage(filePath) {
|
|
2949
|
+
const resolvedPath = path8.resolve(process.cwd(), filePath);
|
|
2950
|
+
let stats;
|
|
2951
|
+
try {
|
|
2952
|
+
stats = await fs6.stat(resolvedPath);
|
|
2953
|
+
} catch (error) {
|
|
2954
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2955
|
+
return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
2956
|
+
}
|
|
2957
|
+
if (!stats.isFile()) {
|
|
2958
|
+
return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u6587\u4EF6: ${resolvedPath}`);
|
|
2959
|
+
}
|
|
2960
|
+
let fileBytes;
|
|
2961
|
+
try {
|
|
2962
|
+
fileBytes = await fs6.readFile(resolvedPath);
|
|
2963
|
+
} catch (error) {
|
|
2964
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2965
|
+
return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5185\u5BB9\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
2966
|
+
}
|
|
2967
|
+
const detectedType = detectMimeType(fileBytes, resolvedPath);
|
|
2968
|
+
if (!detectedType) {
|
|
2969
|
+
return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${resolvedPath}`);
|
|
2970
|
+
}
|
|
2971
|
+
const needsTranscode = !API_SUPPORTED_MIME_TYPES.has(detectedType.mimeType) || fileBytes.byteLength > MAX_REFERENCE_IMAGE_BYTES;
|
|
2972
|
+
if (!needsTranscode) {
|
|
2973
|
+
return makeOkWith({
|
|
2974
|
+
inputPath: filePath,
|
|
2975
|
+
resolvedPath,
|
|
2976
|
+
sourceMimeType: detectedType.mimeType,
|
|
2977
|
+
requestMimeType: detectedType.mimeType,
|
|
2978
|
+
originalBytes: fileBytes.byteLength,
|
|
2979
|
+
finalBytes: fileBytes.byteLength,
|
|
2980
|
+
compressed: false,
|
|
2981
|
+
dataUrl: toDataUrl(detectedType.mimeType, fileBytes)
|
|
2982
|
+
});
|
|
2983
|
+
}
|
|
2984
|
+
const compressed = await compressImageForReference(resolvedPath);
|
|
2985
|
+
if (!compressed.ok) {
|
|
2986
|
+
return compressed;
|
|
2987
|
+
}
|
|
2988
|
+
return makeOkWith({
|
|
2989
|
+
inputPath: filePath,
|
|
2990
|
+
resolvedPath,
|
|
2991
|
+
sourceMimeType: detectedType.mimeType,
|
|
2992
|
+
requestMimeType: compressed.value.mimeType,
|
|
2993
|
+
originalBytes: fileBytes.byteLength,
|
|
2994
|
+
finalBytes: compressed.value.bytes.byteLength,
|
|
2995
|
+
compressed: true,
|
|
2996
|
+
dataUrl: toDataUrl(compressed.value.mimeType, compressed.value.bytes)
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2999
|
+
function detectMimeType(fileBytes, filePath) {
|
|
3000
|
+
if (fileBytes.length >= 3 && fileBytes[0] === 255 && fileBytes[1] === 216 && fileBytes[2] === 255) {
|
|
3001
|
+
return { mimeType: "image/jpeg", extension: "jpeg" };
|
|
3002
|
+
}
|
|
3003
|
+
if (fileBytes.length >= 8 && fileBytes[0] === 137 && fileBytes[1] === 80 && fileBytes[2] === 78 && fileBytes[3] === 71 && fileBytes[4] === 13 && fileBytes[5] === 10 && fileBytes[6] === 26 && fileBytes[7] === 10) {
|
|
3004
|
+
return { mimeType: "image/png", extension: "png" };
|
|
3005
|
+
}
|
|
3006
|
+
if (fileBytes.length >= 6) {
|
|
3007
|
+
const gifHeader = fileBytes.subarray(0, 6).toString("ascii");
|
|
3008
|
+
if (gifHeader === "GIF87a" || gifHeader === "GIF89a") {
|
|
3009
|
+
return { mimeType: "image/gif", extension: "gif" };
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
if (fileBytes.length >= 12 && fileBytes.subarray(0, 4).toString("ascii") === "RIFF" && fileBytes.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
3013
|
+
return { mimeType: "image/webp", extension: "webp" };
|
|
3014
|
+
}
|
|
3015
|
+
if (fileBytes.length >= 2 && fileBytes.subarray(0, 2).toString("ascii") === "BM") {
|
|
3016
|
+
return { mimeType: "image/bmp", extension: "bmp" };
|
|
3017
|
+
}
|
|
3018
|
+
if (fileBytes.length >= 4 && (fileBytes[0] === 73 && fileBytes[1] === 73 && fileBytes[2] === 42 && fileBytes[3] === 0 || fileBytes[0] === 77 && fileBytes[1] === 77 && fileBytes[2] === 0 && fileBytes[3] === 42)) {
|
|
3019
|
+
return { mimeType: "image/tiff", extension: "tiff" };
|
|
3020
|
+
}
|
|
3021
|
+
if (fileBytes.length >= 12 && fileBytes.subarray(4, 8).toString("ascii") === "ftyp") {
|
|
3022
|
+
const brand = fileBytes.subarray(8, 12).toString("ascii").trim();
|
|
3023
|
+
if (brand.startsWith("avif") || brand === "avis") {
|
|
3024
|
+
return { mimeType: "image/avif", extension: "avif" };
|
|
3025
|
+
}
|
|
3026
|
+
if (brand.startsWith("hei") || brand.startsWith("hev") || brand === "mif1" || brand === "msf1") {
|
|
3027
|
+
return { mimeType: "image/heic", extension: "heic" };
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
const ext = path8.extname(filePath).toLowerCase();
|
|
3031
|
+
switch (ext) {
|
|
3032
|
+
case ".jpg":
|
|
3033
|
+
case ".jpeg":
|
|
3034
|
+
return { mimeType: "image/jpeg", extension: "jpeg" };
|
|
3035
|
+
case ".png":
|
|
3036
|
+
return { mimeType: "image/png", extension: "png" };
|
|
3037
|
+
case ".gif":
|
|
3038
|
+
return { mimeType: "image/gif", extension: "gif" };
|
|
3039
|
+
case ".webp":
|
|
3040
|
+
return { mimeType: "image/webp", extension: "webp" };
|
|
3041
|
+
case ".bmp":
|
|
3042
|
+
return { mimeType: "image/bmp", extension: "bmp" };
|
|
3043
|
+
case ".tif":
|
|
3044
|
+
case ".tiff":
|
|
3045
|
+
return { mimeType: "image/tiff", extension: "tiff" };
|
|
3046
|
+
case ".heic":
|
|
3047
|
+
case ".heif":
|
|
3048
|
+
return { mimeType: "image/heic", extension: "heic" };
|
|
3049
|
+
case ".avif":
|
|
3050
|
+
return { mimeType: "image/avif", extension: "avif" };
|
|
3051
|
+
default:
|
|
3052
|
+
return void 0;
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
async function compressImageForReference(sourcePath) {
|
|
3056
|
+
if (process.platform !== "darwin") {
|
|
3057
|
+
return makeError(
|
|
3058
|
+
1,
|
|
3059
|
+
`\u53C2\u8003\u56FE\u9700\u8981\u538B\u7F29\u6216\u8F6C\u7801\uFF0C\u4F46\u5F53\u524D\u5E73\u53F0 ${process.platform} \u672A\u5B9E\u73B0\u81EA\u52A8\u5904\u7406\u3002`
|
|
3060
|
+
);
|
|
3061
|
+
}
|
|
3062
|
+
const tempDir = await fs6.mkdtemp(path8.join(os2.tmpdir(), "tt-ai-image-"));
|
|
3063
|
+
try {
|
|
3064
|
+
const dimensions = await readImageDimensions(sourcePath);
|
|
3065
|
+
const maxDimension = Math.max(dimensions.width, dimensions.height);
|
|
3066
|
+
const resizeLimits = uniqueNumbers([
|
|
3067
|
+
maxDimension,
|
|
3068
|
+
Math.floor(maxDimension * 0.85),
|
|
3069
|
+
Math.floor(maxDimension * 0.7),
|
|
3070
|
+
Math.floor(maxDimension * 0.55),
|
|
3071
|
+
Math.floor(maxDimension * 0.4)
|
|
3072
|
+
]).filter((value) => value >= 64);
|
|
3073
|
+
const qualityLevels = [85, 75, 65, 55, 45, 35];
|
|
3074
|
+
let attemptIndex = 0;
|
|
3075
|
+
for (const resizeLimit of resizeLimits) {
|
|
3076
|
+
for (const quality of qualityLevels) {
|
|
3077
|
+
const targetPath = path8.join(tempDir, `compressed-${attemptIndex}.jpeg`);
|
|
3078
|
+
attemptIndex += 1;
|
|
3079
|
+
await runSips([
|
|
3080
|
+
"--setProperty",
|
|
3081
|
+
"format",
|
|
3082
|
+
"jpeg",
|
|
3083
|
+
"--setProperty",
|
|
3084
|
+
"formatOptions",
|
|
3085
|
+
String(quality),
|
|
3086
|
+
sourcePath,
|
|
3087
|
+
"--out",
|
|
3088
|
+
targetPath
|
|
3089
|
+
]);
|
|
3090
|
+
if (resizeLimit < maxDimension) {
|
|
3091
|
+
await runSips(["-Z", String(resizeLimit), targetPath]);
|
|
3092
|
+
}
|
|
3093
|
+
const compressedBytes = await fs6.readFile(targetPath);
|
|
3094
|
+
if (compressedBytes.byteLength <= MAX_REFERENCE_IMAGE_BYTES) {
|
|
3095
|
+
return makeOkWith({
|
|
3096
|
+
mimeType: "image/jpeg",
|
|
3097
|
+
bytes: compressedBytes
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
return makeError(1, `\u53C2\u8003\u56FE\u538B\u7F29\u5931\u8D25\uFF0C\u65E0\u6CD5\u538B\u7F29\u5230 2MB \u4EE5\u5185: ${sourcePath}`);
|
|
3103
|
+
} catch (error) {
|
|
3104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3105
|
+
return makeError(1, `\u5904\u7406\u53C2\u8003\u56FE\u5931\u8D25: ${sourcePath}: ${message}`);
|
|
3106
|
+
} finally {
|
|
3107
|
+
await fs6.rm(tempDir, { recursive: true, force: true });
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
async function readImageDimensions(sourcePath) {
|
|
3111
|
+
const { stdout } = await runSips([
|
|
3112
|
+
"-g",
|
|
3113
|
+
"pixelWidth",
|
|
3114
|
+
"-g",
|
|
3115
|
+
"pixelHeight",
|
|
3116
|
+
sourcePath
|
|
3117
|
+
]);
|
|
3118
|
+
const width = readSipsMetric(stdout, "pixelWidth");
|
|
3119
|
+
const height = readSipsMetric(stdout, "pixelHeight");
|
|
3120
|
+
if (!width || !height) {
|
|
3121
|
+
throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247\u5C3A\u5BF8");
|
|
3122
|
+
}
|
|
3123
|
+
return { width, height };
|
|
3124
|
+
}
|
|
3125
|
+
async function runSips(args) {
|
|
3126
|
+
return execFileAsync("/usr/bin/sips", [...args]);
|
|
3127
|
+
}
|
|
3128
|
+
function readSipsMetric(output, key) {
|
|
3129
|
+
const matcher = new RegExp(`${key}:\\s*(\\d+)`);
|
|
3130
|
+
const matched = output.match(matcher);
|
|
3131
|
+
if (!matched) {
|
|
3132
|
+
return void 0;
|
|
3133
|
+
}
|
|
3134
|
+
const value = Number.parseInt(matched[1], 10);
|
|
3135
|
+
return Number.isFinite(value) ? value : void 0;
|
|
3136
|
+
}
|
|
3137
|
+
function uniqueNumbers(values) {
|
|
3138
|
+
return [...new Set(values)];
|
|
3139
|
+
}
|
|
3140
|
+
function toDataUrl(mimeType, fileBytes) {
|
|
3141
|
+
const suffix = mimeType.replace("image/", "");
|
|
3142
|
+
return `data:image/${suffix};base64,${fileBytes.toString("base64")}`;
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
// src/commands/ai/ai-vid-command.ts
|
|
3252
3146
|
var VIDEO_GENERATION_URL = "https://openrouter.ai/api/v1/videos";
|
|
3253
3147
|
var VIDEO_MODEL = "bytedance/seedance-2.0";
|
|
3254
3148
|
var POLLING_LOG_FILE_NAME = "ai-vid-polling.log";
|
|
@@ -3266,34 +3160,34 @@ var AiVidCommand = class extends AiBaseCommand {
|
|
|
3266
3160
|
}
|
|
3267
3161
|
_configureOptions(cmd) {
|
|
3268
3162
|
cmd.addOption(
|
|
3269
|
-
new
|
|
3163
|
+
new Option9(
|
|
3270
3164
|
"-r, --reference <path>",
|
|
3271
3165
|
"\u53C2\u8003\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF0C\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4E5F\u652F\u6301\u7528\u82F1\u6587\u9017\u53F7\u5206\u9694\u591A\u4E2A\u8DEF\u5F84\uFF1B\u6700\u7EC8\u8F6C\u4E3A base64 data URL"
|
|
3272
|
-
).argParser(
|
|
3166
|
+
).argParser(parseReferencePaths2).default([])
|
|
3273
3167
|
);
|
|
3274
3168
|
cmd.addOption(
|
|
3275
|
-
new
|
|
3169
|
+
new Option9("--resolution <resolution>", "\u89C6\u9891\u5206\u8FA8\u7387").choices(["480p", "720p"]).default("480p")
|
|
3276
3170
|
);
|
|
3277
3171
|
cmd.addOption(
|
|
3278
|
-
new
|
|
3172
|
+
new Option9(
|
|
3279
3173
|
"--aspect-ratio <ratio>",
|
|
3280
3174
|
"\u89C6\u9891\u957F\u5BBD\u6BD4\uFF0C\u4F8B\u5982 16:9\u30019:16\u30011:1\uFF0C\u7F3A\u7701\u4E0D\u6307\u5B9A"
|
|
3281
3175
|
)
|
|
3282
3176
|
);
|
|
3283
3177
|
cmd.addOption(
|
|
3284
|
-
new
|
|
3178
|
+
new Option9(
|
|
3285
3179
|
"--duration <seconds>",
|
|
3286
3180
|
"\u89C6\u9891\u65F6\u957F\uFF08\u79D2\uFF09\uFF0C\u7F3A\u7701\u4E0D\u6307\u5B9A"
|
|
3287
3181
|
).argParser(parseDuration)
|
|
3288
3182
|
);
|
|
3289
3183
|
cmd.addOption(
|
|
3290
|
-
new
|
|
3184
|
+
new Option9(
|
|
3291
3185
|
"--polling-only",
|
|
3292
3186
|
"\u4EC5\u63D0\u4EA4\u5E76\u8FD4\u56DE polling_url\uFF0C\u4E0D\u5728\u672C\u5730\u8F6E\u8BE2\u7ED3\u679C"
|
|
3293
3187
|
).default(false)
|
|
3294
3188
|
);
|
|
3295
3189
|
cmd.addOption(
|
|
3296
|
-
new
|
|
3190
|
+
new Option9(
|
|
3297
3191
|
"-o, --output <path>",
|
|
3298
3192
|
"\u89C6\u9891\u4E0B\u8F7D\u8F93\u51FA\u8DEF\u5F84\uFF0C\u53EF\u4F20\u6587\u4EF6\u8DEF\u5F84\u6216\u76EE\u5F55\u8DEF\u5F84\uFF1B\u672A\u4F20\u65F6\u9ED8\u8BA4\u5199\u5165\u5F53\u524D\u76EE\u5F55\u5E76\u81EA\u52A8\u547D\u540D"
|
|
3299
3193
|
)
|
|
@@ -3369,7 +3263,7 @@ var AiVidCommand = class extends AiBaseCommand {
|
|
|
3369
3263
|
this._outputJsonError(1, "\u89C6\u9891\u751F\u6210\u5B8C\u6210\uFF0C\u4F46\u672A\u62FF\u5230 unsigned_urls[0]");
|
|
3370
3264
|
return this._makeOk();
|
|
3371
3265
|
}
|
|
3372
|
-
const outputTargetResult = await
|
|
3266
|
+
const outputTargetResult = await resolveOutputTarget(ctx.options.output);
|
|
3373
3267
|
if (!outputTargetResult.ok) {
|
|
3374
3268
|
this._outputJsonError(outputTargetResult.code, outputTargetResult.msg);
|
|
3375
3269
|
return this._makeOk();
|
|
@@ -3413,7 +3307,7 @@ var AiVidCommand = class extends AiBaseCommand {
|
|
|
3413
3307
|
clearTimeout(timer);
|
|
3414
3308
|
}
|
|
3415
3309
|
const bodyText = await response.text().catch(() => "");
|
|
3416
|
-
const payload =
|
|
3310
|
+
const payload = parseJsonSafely(bodyText);
|
|
3417
3311
|
if (!response.ok) {
|
|
3418
3312
|
const detail = extractErrorText(payload) ?? bodyText.trim();
|
|
3419
3313
|
const suffix = detail ? `: ${detail}` : "";
|
|
@@ -3459,7 +3353,7 @@ var AiVidCommand = class extends AiBaseCommand {
|
|
|
3459
3353
|
continue;
|
|
3460
3354
|
}
|
|
3461
3355
|
const bodyText = await response.text().catch(() => "");
|
|
3462
|
-
const payload =
|
|
3356
|
+
const payload = parseJsonSafely(bodyText);
|
|
3463
3357
|
if (!response.ok) {
|
|
3464
3358
|
const detail = extractErrorText(payload) ?? bodyText.trim();
|
|
3465
3359
|
return makeError(
|
|
@@ -3494,7 +3388,7 @@ var AiVidCommand = class extends AiBaseCommand {
|
|
|
3494
3388
|
function resolveOpenRouterToken() {
|
|
3495
3389
|
return process.env.OPENROUTER_AK?.trim() || OPENROUTER_AK;
|
|
3496
3390
|
}
|
|
3497
|
-
function
|
|
3391
|
+
function parseReferencePaths2(value, previous) {
|
|
3498
3392
|
return previous.concat(
|
|
3499
3393
|
value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
|
|
3500
3394
|
);
|
|
@@ -3506,7 +3400,7 @@ function parseDuration(value) {
|
|
|
3506
3400
|
}
|
|
3507
3401
|
return parsed;
|
|
3508
3402
|
}
|
|
3509
|
-
function
|
|
3403
|
+
function parseJsonSafely(text) {
|
|
3510
3404
|
if (!text.trim()) {
|
|
3511
3405
|
return void 0;
|
|
3512
3406
|
}
|
|
@@ -3558,12 +3452,12 @@ async function appendPollingUrl(pollingUrl, generationId) {
|
|
|
3558
3452
|
return makeError(1, `\u5199\u5165 polling \u65E5\u5FD7\u5931\u8D25 ${logFilePath}: ${message}`);
|
|
3559
3453
|
}
|
|
3560
3454
|
}
|
|
3561
|
-
async function
|
|
3455
|
+
async function resolveOutputTarget(outputPath) {
|
|
3562
3456
|
if (!outputPath) {
|
|
3563
3457
|
const directoryPath = process.cwd();
|
|
3564
3458
|
return makeOkWith({
|
|
3565
3459
|
directoryPath,
|
|
3566
|
-
filePath: path9.join(directoryPath,
|
|
3460
|
+
filePath: path9.join(directoryPath, buildAutoFileName()),
|
|
3567
3461
|
autoNamed: true
|
|
3568
3462
|
});
|
|
3569
3463
|
}
|
|
@@ -3573,7 +3467,7 @@ async function resolveOutputTarget2(outputPath) {
|
|
|
3573
3467
|
if (stats.isDirectory()) {
|
|
3574
3468
|
return makeOkWith({
|
|
3575
3469
|
directoryPath: resolvedPath,
|
|
3576
|
-
filePath: path9.join(resolvedPath,
|
|
3470
|
+
filePath: path9.join(resolvedPath, buildAutoFileName()),
|
|
3577
3471
|
autoNamed: true
|
|
3578
3472
|
});
|
|
3579
3473
|
}
|
|
@@ -3597,7 +3491,7 @@ async function resolveOutputTarget2(outputPath) {
|
|
|
3597
3491
|
}
|
|
3598
3492
|
return makeOkWith({
|
|
3599
3493
|
directoryPath: resolvedPath,
|
|
3600
|
-
filePath: path9.join(resolvedPath,
|
|
3494
|
+
filePath: path9.join(resolvedPath, buildAutoFileName()),
|
|
3601
3495
|
autoNamed: true
|
|
3602
3496
|
});
|
|
3603
3497
|
}
|
|
@@ -3635,7 +3529,7 @@ async function downloadVideoToFile(remoteUrl, target, token) {
|
|
|
3635
3529
|
);
|
|
3636
3530
|
}
|
|
3637
3531
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
3638
|
-
const targetPath = target.autoNamed ?
|
|
3532
|
+
const targetPath = target.autoNamed ? replaceFileExtension(
|
|
3639
3533
|
target.filePath,
|
|
3640
3534
|
inferVideoExtension(remoteUrl, response)
|
|
3641
3535
|
) : target.filePath;
|
|
@@ -3647,11 +3541,11 @@ async function downloadVideoToFile(remoteUrl, target, token) {
|
|
|
3647
3541
|
}
|
|
3648
3542
|
return makeOkWith(path9.resolve(targetPath));
|
|
3649
3543
|
}
|
|
3650
|
-
function
|
|
3544
|
+
function buildAutoFileName() {
|
|
3651
3545
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3652
3546
|
return `ai-vid-${timestamp}.mp4`;
|
|
3653
3547
|
}
|
|
3654
|
-
function
|
|
3548
|
+
function replaceFileExtension(filePath, extension) {
|
|
3655
3549
|
const ext = path9.extname(filePath);
|
|
3656
3550
|
if (!ext) {
|
|
3657
3551
|
return `${filePath}.${extension}`;
|
|
@@ -3681,7 +3575,7 @@ function inferVideoExtension(remoteUrl, response) {
|
|
|
3681
3575
|
}
|
|
3682
3576
|
|
|
3683
3577
|
// src/commands/ai/ai-querytask-command.ts
|
|
3684
|
-
import { spawn as
|
|
3578
|
+
import { spawn as spawn4 } from "child_process";
|
|
3685
3579
|
var BIN = "xx";
|
|
3686
3580
|
var PPE_ENV = "ppe_new_login_cli";
|
|
3687
3581
|
var POLL_INTERVAL_MS2 = 5e3;
|
|
@@ -3738,7 +3632,7 @@ var AiQueryTaskCommand = class extends AiBaseCommand {
|
|
|
3738
3632
|
"--json"
|
|
3739
3633
|
];
|
|
3740
3634
|
return new Promise((resolve8) => {
|
|
3741
|
-
const child =
|
|
3635
|
+
const child = spawn4(BIN, args, { cwd: process.cwd() });
|
|
3742
3636
|
let stdout = "";
|
|
3743
3637
|
let stderr = "";
|
|
3744
3638
|
child.stdout.on("data", (chunk) => {
|
|
@@ -3819,7 +3713,7 @@ var AiCommand = class extends BaseSubcommandHost {
|
|
|
3819
3713
|
this._addSubcommand(new AiCursorCommand());
|
|
3820
3714
|
this._addSubcommand(new AiCodexCommand());
|
|
3821
3715
|
this._addSubcommand(new AiRunCommand());
|
|
3822
|
-
this._addSubcommand(new
|
|
3716
|
+
this._addSubcommand(new AiImageCommandV2());
|
|
3823
3717
|
this._addSubcommand(new AiVideoCommand());
|
|
3824
3718
|
this._addSubcommand(new AiVidCommand());
|
|
3825
3719
|
this._addSubcommand(new AiQueryTaskCommand());
|