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 CHANGED
@@ -2455,277 +2455,26 @@ user_prompt:
2455
2455
  }
2456
2456
  };
2457
2457
 
2458
- // src/commands/ai/ai-image-command.ts
2459
- import * as fs6 from "fs/promises";
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 * as os2 from "os";
2461
+ import { homedir as homedir7 } from "os";
2466
2462
  import * as path6 from "path";
2467
- import { execFile } from "child_process";
2468
- import { promisify } from "util";
2469
- var execFileAsync = promisify(execFile);
2470
- var MAX_REFERENCE_IMAGE_BYTES = 2 * 1024 * 1024;
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: "\u8C03\u7528\u8C46\u5305 Seedream \u6A21\u578B\u751F\u6210\u56FE\u7247\u5E76\u4E0B\u8F7D\u5230\u672C\u5730"
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("[content...]", "\u751F\u56FE\u63D0\u793A\u8BCD");
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 referenceResult = await prepareReferenceImages(ctx.options.reference);
2737
- if (!referenceResult.ok) {
2738
- this._outputJsonError(referenceResult.code, referenceResult.msg);
2739
- return this._makeOk();
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 saveResult = await downloadImageToFile(
2772
- remoteUrl,
2773
- outputTargetResult.value
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: saveResult.value
2497
+ image_path: imagePathResult.value
2781
2498
  });
2782
2499
  return this._makeOk();
2783
2500
  }
2784
- async _requestImage(requestBody) {
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
- const response = await fetch(IMAGE_GENERATION_URL, {
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 && error.name === "AbortError" ? "\u8BF7\u6C42\u8D85\u65F6" : error instanceof Error ? error.message : String(error);
2822
- return makeError(1, `\u8C03\u7528\u751F\u56FE\u63A5\u53E3\u5931\u8D25: ${message}`);
2823
- } finally {
2824
- clearTimeout(timer);
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
- async function resolveOutputTarget(outputPath, outputFormat) {
2829
- if (!outputPath) {
2830
- const directoryPath = process.cwd();
2831
- const filePath = path7.join(directoryPath, buildAutoFileName(outputFormat));
2832
- return makeOkWith({
2833
- directoryPath,
2834
- filePath,
2835
- autoNamed: true
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
- const resolvedPath = path7.resolve(process.cwd(), outputPath);
2839
- try {
2840
- const stats = await fs6.stat(resolvedPath);
2841
- if (stats.isDirectory()) {
2842
- return makeOkWith({
2843
- directoryPath: resolvedPath,
2844
- filePath: path7.join(resolvedPath, buildAutoFileName(outputFormat)),
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
- return makeOkWith({
2849
- directoryPath: path7.dirname(resolvedPath),
2850
- filePath: resolvedPath,
2851
- autoNamed: false
2852
- });
2853
- } catch (error) {
2854
- const nodeError = error;
2855
- if (nodeError.code !== "ENOENT") {
2856
- const message = error instanceof Error ? error.message : String(error);
2857
- return makeError(1, `\u8BFB\u53D6\u8F93\u51FA\u8DEF\u5F84\u5931\u8D25: ${resolvedPath}: ${message}`);
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 downloadImageToFile(remoteUrl, target) {
2874
- try {
2875
- await fs6.mkdir(target.directoryPath, { recursive: true });
2876
- } catch (error) {
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 parseJsonSafely(text) {
2912
- if (!text.trim()) {
2913
- return void 0;
2561
+ async function findLatestCreatedDirectory(directoryPath, minCreatedAt) {
2562
+ const entriesResult = await readDirectoryEntries(directoryPath);
2563
+ if (!entriesResult.ok) {
2564
+ return entriesResult;
2914
2565
  }
2915
- try {
2916
- return JSON.parse(text);
2917
- } catch {
2918
- return void 0;
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 hasFailedCode(code) {
2922
- if (code === void 0 || code === null) {
2923
- return false;
2585
+ async function findLatestCreatedFile(directoryPath) {
2586
+ const entriesResult = await readDirectoryEntries(directoryPath);
2587
+ if (!entriesResult.ok) {
2588
+ return entriesResult;
2924
2589
  }
2925
- if (typeof code === "number") {
2926
- return code !== 0;
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 normalized = code.trim();
2929
- return normalized !== "0" && normalized !== "";
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
- const message = payload.msg?.trim() || payload.message?.trim();
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 replaceFileExtension(filePath, extension) {
2943
- const ext = path7.extname(filePath);
2944
- if (!ext) {
2945
- return `${filePath}.${extension}`;
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 inferImageExtension(remoteUrl, response) {
2950
- const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
2951
- if (contentType.includes("png")) {
2952
- return "png";
2953
- }
2954
- if (contentType.includes("webp")) {
2955
- return "webp";
2956
- }
2957
- if (contentType.includes("jpeg") || contentType.includes("jpg")) {
2958
- return "jpeg";
2959
- }
2960
- try {
2961
- const url = new URL(remoteUrl);
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 "jpeg";
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 path8 from "path";
2979
- import { Option as Option9 } from "commander";
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 Option9(
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(parseReferencePaths2).default([])
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: path8.resolve(process.cwd(), referencePath),
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 parseReferencePaths2(value, previous) {
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 Option10 } from "commander";
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 Option10(
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(parseReferencePaths3).default([])
3166
+ ).argParser(parseReferencePaths2).default([])
3273
3167
  );
3274
3168
  cmd.addOption(
3275
- new Option10("--resolution <resolution>", "\u89C6\u9891\u5206\u8FA8\u7387").choices(["480p", "720p"]).default("480p")
3169
+ new Option9("--resolution <resolution>", "\u89C6\u9891\u5206\u8FA8\u7387").choices(["480p", "720p"]).default("480p")
3276
3170
  );
3277
3171
  cmd.addOption(
3278
- new Option10(
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 Option10(
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 Option10(
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 Option10(
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 resolveOutputTarget2(ctx.options.output);
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 = parseJsonSafely2(bodyText);
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 = parseJsonSafely2(bodyText);
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 parseReferencePaths3(value, previous) {
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 parseJsonSafely2(text) {
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 resolveOutputTarget2(outputPath) {
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, buildAutoFileName2()),
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, buildAutoFileName2()),
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, buildAutoFileName2()),
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 ? replaceFileExtension2(
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 buildAutoFileName2() {
3544
+ function buildAutoFileName() {
3651
3545
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3652
3546
  return `ai-vid-${timestamp}.mp4`;
3653
3547
  }
3654
- function replaceFileExtension2(filePath, extension) {
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 spawn3 } from "child_process";
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 = spawn3(BIN, args, { cwd: process.cwd() });
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 AiImageCommand());
3716
+ this._addSubcommand(new AiImageCommandV2());
3823
3717
  this._addSubcommand(new AiVideoCommand());
3824
3718
  this._addSubcommand(new AiVidCommand());
3825
3719
  this._addSubcommand(new AiQueryTaskCommand());