maoda-commander-tt 0.0.18 → 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
@@ -2119,9 +2119,26 @@ var ShortcutEmoLynxCommand = class extends ShortcutBaseCommand {
2119
2119
  }
2120
2120
  };
2121
2121
 
2122
- // src/commands/shortcut/shortcut-emo-pippit-command.ts
2122
+ // src/commands/shortcut/shortcut-emo-pippit-cn-command.ts
2123
2123
  import { homedir as homedir3 } from "os";
2124
2124
  import { join as join5 } from "path";
2125
+ var ShortcutEmoPippitCnCommand = class extends ShortcutBaseCommand {
2126
+ _meta() {
2127
+ return {
2128
+ name: "pippit-cn",
2129
+ description: "\u542F\u52A8 pippit-cn \u9879\u76EE",
2130
+ aliases: ["ppc"]
2131
+ };
2132
+ }
2133
+ async _execute(_ctx) {
2134
+ const cwd = join5(homedir3(), "work", "pippit-cn");
2135
+ return this._runCommandLine(`w2 start && emo start platform-xyq`, cwd);
2136
+ }
2137
+ };
2138
+
2139
+ // src/commands/shortcut/shortcut-emo-pippit-command.ts
2140
+ import { homedir as homedir4 } from "os";
2141
+ import { join as join6 } from "path";
2125
2142
  var ShortcutEmoPippitCommand = class extends ShortcutBaseCommand {
2126
2143
  _meta() {
2127
2144
  return {
@@ -2131,7 +2148,7 @@ var ShortcutEmoPippitCommand = class extends ShortcutBaseCommand {
2131
2148
  };
2132
2149
  }
2133
2150
  async _execute(_ctx) {
2134
- const cwd = join5(homedir3(), "work", "pippit");
2151
+ const cwd = join6(homedir4(), "work", "pippit");
2135
2152
  return this._runCommandLine(`w2 start && emo start platform-business`, cwd);
2136
2153
  }
2137
2154
  };
@@ -2148,6 +2165,7 @@ var ShortcutCommand = class extends BaseSubcommandHost {
2148
2165
  _registerSubcommands() {
2149
2166
  this._addSubcommand(new ShortcutEmoLynxCommand());
2150
2167
  this._addSubcommand(new ShortcutEmoPippitCommand());
2168
+ this._addSubcommand(new ShortcutEmoPippitCnCommand());
2151
2169
  }
2152
2170
  };
2153
2171
 
@@ -2322,19 +2340,19 @@ var AiCodexCommand = class extends AiBaseCommand {
2322
2340
  };
2323
2341
 
2324
2342
  // src/commands/ai/ai-run-command.ts
2325
- import { homedir as homedir5 } from "os";
2326
- import { join as join7 } from "path";
2343
+ import { homedir as homedir6 } from "os";
2344
+ import { join as join8 } from "path";
2327
2345
 
2328
2346
  // src/commands/ai/ai-context-path.ts
2329
2347
  import { existsSync as existsSync2 } from "fs";
2330
- import { homedir as homedir4 } from "os";
2331
- import { join as join6 } from "path";
2348
+ import { homedir as homedir5 } from "os";
2349
+ import { join as join7 } from "path";
2332
2350
  function getCurrentCloudRootPath() {
2333
2351
  if (process.platform !== "darwin") {
2334
2352
  return makeError(1, "`--my` \u4EC5\u652F\u6301\u5728 macOS \u4E0A\u4F7F\u7528");
2335
2353
  }
2336
- const cloudRootPath = join6(
2337
- homedir4(),
2354
+ const cloudRootPath = join7(
2355
+ homedir5(),
2338
2356
  "Library",
2339
2357
  "Mobile Documents",
2340
2358
  "com~apple~CloudDocs"
@@ -2362,7 +2380,7 @@ var AI_RUN_CONTEXT_ALIASES = [
2362
2380
  return cloudRoot;
2363
2381
  }
2364
2382
  return ensureDirectoryExists(
2365
- join7(cloudRoot.value, "icloud-hub", "local-knowlege-base")
2383
+ join8(cloudRoot.value, "icloud-hub", "local-knowlege-base")
2366
2384
  );
2367
2385
  }
2368
2386
  },
@@ -2371,7 +2389,7 @@ var AI_RUN_CONTEXT_ALIASES = [
2371
2389
  optionDescription: "\u53C2\u8003 ~/work/smart-agent/dymamic-skills \u5B66\u4E60 skill",
2372
2390
  resolveReferenceDir: () => {
2373
2391
  return ensureDirectoryExists(
2374
- join7(homedir5(), "work", "smart-agent", "dynamic-skills")
2392
+ join8(homedir6(), "work", "smart-agent", "dynamic-skills")
2375
2393
  );
2376
2394
  }
2377
2395
  }
@@ -2437,277 +2455,26 @@ user_prompt:
2437
2455
  }
2438
2456
  };
2439
2457
 
2440
- // src/commands/ai/ai-image-command.ts
2441
- import * as fs6 from "fs/promises";
2442
- import * as path7 from "path";
2443
- import { Option as Option8 } from "commander";
2444
-
2445
- // src/commands/ai/ai-image-reference.ts
2458
+ // src/commands/ai/ai-image-command-v2.ts
2459
+ import { spawn as spawn3 } from "child_process";
2446
2460
  import * as fs5 from "fs/promises";
2447
- import * as os2 from "os";
2461
+ import { homedir as homedir7 } from "os";
2448
2462
  import * as path6 from "path";
2449
- import { execFile } from "child_process";
2450
- import { promisify } from "util";
2451
- var execFileAsync = promisify(execFile);
2452
- var MAX_REFERENCE_IMAGE_BYTES = 2 * 1024 * 1024;
2453
- var MAX_REFERENCE_IMAGE_COUNT = 14;
2454
- var API_SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
2455
- "image/jpeg",
2456
- "image/png",
2457
- "image/webp",
2458
- "image/bmp",
2459
- "image/tiff",
2460
- "image/gif"
2461
- ]);
2462
- async function prepareReferenceImages(filePaths) {
2463
- if (filePaths.length > MAX_REFERENCE_IMAGE_COUNT) {
2464
- return makeError(
2465
- 1,
2466
- `\u53C2\u8003\u56FE\u6700\u591A\u652F\u6301 ${MAX_REFERENCE_IMAGE_COUNT} \u5F20\uFF0C\u5F53\u524D\u4F20\u5165 ${filePaths.length} \u5F20\u3002`
2467
- );
2468
- }
2469
- const preparedImages = [];
2470
- for (const filePath of filePaths) {
2471
- const prepared = await prepareReferenceImage(filePath);
2472
- if (!prepared.ok) {
2473
- return prepared;
2474
- }
2475
- preparedImages.push(prepared.value);
2476
- }
2477
- return makeOkWith(preparedImages);
2478
- }
2479
- async function prepareReferenceImage(filePath) {
2480
- const resolvedPath = path6.resolve(process.cwd(), filePath);
2481
- let stats;
2482
- try {
2483
- stats = await fs5.stat(resolvedPath);
2484
- } catch (error) {
2485
- const message = error instanceof Error ? error.message : String(error);
2486
- return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5931\u8D25: ${resolvedPath}: ${message}`);
2487
- }
2488
- if (!stats.isFile()) {
2489
- return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u6587\u4EF6: ${resolvedPath}`);
2490
- }
2491
- let fileBytes;
2492
- try {
2493
- fileBytes = await fs5.readFile(resolvedPath);
2494
- } catch (error) {
2495
- const message = error instanceof Error ? error.message : String(error);
2496
- return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5185\u5BB9\u5931\u8D25: ${resolvedPath}: ${message}`);
2497
- }
2498
- const detectedType = detectMimeType(fileBytes, resolvedPath);
2499
- if (!detectedType) {
2500
- return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${resolvedPath}`);
2501
- }
2502
- const needsTranscode = !API_SUPPORTED_MIME_TYPES.has(detectedType.mimeType) || fileBytes.byteLength > MAX_REFERENCE_IMAGE_BYTES;
2503
- if (!needsTranscode) {
2504
- return makeOkWith({
2505
- inputPath: filePath,
2506
- resolvedPath,
2507
- sourceMimeType: detectedType.mimeType,
2508
- requestMimeType: detectedType.mimeType,
2509
- originalBytes: fileBytes.byteLength,
2510
- finalBytes: fileBytes.byteLength,
2511
- compressed: false,
2512
- dataUrl: toDataUrl(detectedType.mimeType, fileBytes)
2513
- });
2514
- }
2515
- const compressed = await compressImageForReference(resolvedPath);
2516
- if (!compressed.ok) {
2517
- return compressed;
2518
- }
2519
- return makeOkWith({
2520
- inputPath: filePath,
2521
- resolvedPath,
2522
- sourceMimeType: detectedType.mimeType,
2523
- requestMimeType: compressed.value.mimeType,
2524
- originalBytes: fileBytes.byteLength,
2525
- finalBytes: compressed.value.bytes.byteLength,
2526
- compressed: true,
2527
- dataUrl: toDataUrl(compressed.value.mimeType, compressed.value.bytes)
2528
- });
2529
- }
2530
- function detectMimeType(fileBytes, filePath) {
2531
- if (fileBytes.length >= 3 && fileBytes[0] === 255 && fileBytes[1] === 216 && fileBytes[2] === 255) {
2532
- return { mimeType: "image/jpeg", extension: "jpeg" };
2533
- }
2534
- 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) {
2535
- return { mimeType: "image/png", extension: "png" };
2536
- }
2537
- if (fileBytes.length >= 6) {
2538
- const gifHeader = fileBytes.subarray(0, 6).toString("ascii");
2539
- if (gifHeader === "GIF87a" || gifHeader === "GIF89a") {
2540
- return { mimeType: "image/gif", extension: "gif" };
2541
- }
2542
- }
2543
- if (fileBytes.length >= 12 && fileBytes.subarray(0, 4).toString("ascii") === "RIFF" && fileBytes.subarray(8, 12).toString("ascii") === "WEBP") {
2544
- return { mimeType: "image/webp", extension: "webp" };
2545
- }
2546
- if (fileBytes.length >= 2 && fileBytes.subarray(0, 2).toString("ascii") === "BM") {
2547
- return { mimeType: "image/bmp", extension: "bmp" };
2548
- }
2549
- 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)) {
2550
- return { mimeType: "image/tiff", extension: "tiff" };
2551
- }
2552
- if (fileBytes.length >= 12 && fileBytes.subarray(4, 8).toString("ascii") === "ftyp") {
2553
- const brand = fileBytes.subarray(8, 12).toString("ascii").trim();
2554
- if (brand.startsWith("avif") || brand === "avis") {
2555
- return { mimeType: "image/avif", extension: "avif" };
2556
- }
2557
- if (brand.startsWith("hei") || brand.startsWith("hev") || brand === "mif1" || brand === "msf1") {
2558
- return { mimeType: "image/heic", extension: "heic" };
2559
- }
2560
- }
2561
- const ext = path6.extname(filePath).toLowerCase();
2562
- switch (ext) {
2563
- case ".jpg":
2564
- case ".jpeg":
2565
- return { mimeType: "image/jpeg", extension: "jpeg" };
2566
- case ".png":
2567
- return { mimeType: "image/png", extension: "png" };
2568
- case ".gif":
2569
- return { mimeType: "image/gif", extension: "gif" };
2570
- case ".webp":
2571
- return { mimeType: "image/webp", extension: "webp" };
2572
- case ".bmp":
2573
- return { mimeType: "image/bmp", extension: "bmp" };
2574
- case ".tif":
2575
- case ".tiff":
2576
- return { mimeType: "image/tiff", extension: "tiff" };
2577
- case ".heic":
2578
- case ".heif":
2579
- return { mimeType: "image/heic", extension: "heic" };
2580
- case ".avif":
2581
- return { mimeType: "image/avif", extension: "avif" };
2582
- default:
2583
- return void 0;
2584
- }
2585
- }
2586
- async function compressImageForReference(sourcePath) {
2587
- if (process.platform !== "darwin") {
2588
- return makeError(
2589
- 1,
2590
- `\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`
2591
- );
2592
- }
2593
- const tempDir = await fs5.mkdtemp(path6.join(os2.tmpdir(), "tt-ai-image-"));
2594
- try {
2595
- const dimensions = await readImageDimensions(sourcePath);
2596
- const maxDimension = Math.max(dimensions.width, dimensions.height);
2597
- const resizeLimits = uniqueNumbers([
2598
- maxDimension,
2599
- Math.floor(maxDimension * 0.85),
2600
- Math.floor(maxDimension * 0.7),
2601
- Math.floor(maxDimension * 0.55),
2602
- Math.floor(maxDimension * 0.4)
2603
- ]).filter((value) => value >= 64);
2604
- const qualityLevels = [85, 75, 65, 55, 45, 35];
2605
- let attemptIndex = 0;
2606
- for (const resizeLimit of resizeLimits) {
2607
- for (const quality of qualityLevels) {
2608
- const targetPath = path6.join(tempDir, `compressed-${attemptIndex}.jpeg`);
2609
- attemptIndex += 1;
2610
- await runSips([
2611
- "--setProperty",
2612
- "format",
2613
- "jpeg",
2614
- "--setProperty",
2615
- "formatOptions",
2616
- String(quality),
2617
- sourcePath,
2618
- "--out",
2619
- targetPath
2620
- ]);
2621
- if (resizeLimit < maxDimension) {
2622
- await runSips(["-Z", String(resizeLimit), targetPath]);
2623
- }
2624
- const compressedBytes = await fs5.readFile(targetPath);
2625
- if (compressedBytes.byteLength <= MAX_REFERENCE_IMAGE_BYTES) {
2626
- return makeOkWith({
2627
- mimeType: "image/jpeg",
2628
- bytes: compressedBytes
2629
- });
2630
- }
2631
- }
2632
- }
2633
- return makeError(1, `\u53C2\u8003\u56FE\u538B\u7F29\u5931\u8D25\uFF0C\u65E0\u6CD5\u538B\u7F29\u5230 2MB \u4EE5\u5185: ${sourcePath}`);
2634
- } catch (error) {
2635
- const message = error instanceof Error ? error.message : String(error);
2636
- return makeError(1, `\u5904\u7406\u53C2\u8003\u56FE\u5931\u8D25: ${sourcePath}: ${message}`);
2637
- } finally {
2638
- await fs5.rm(tempDir, { recursive: true, force: true });
2639
- }
2640
- }
2641
- async function readImageDimensions(sourcePath) {
2642
- const { stdout } = await runSips([
2643
- "-g",
2644
- "pixelWidth",
2645
- "-g",
2646
- "pixelHeight",
2647
- sourcePath
2648
- ]);
2649
- const width = readSipsMetric(stdout, "pixelWidth");
2650
- const height = readSipsMetric(stdout, "pixelHeight");
2651
- if (!width || !height) {
2652
- throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247\u5C3A\u5BF8");
2653
- }
2654
- return { width, height };
2655
- }
2656
- async function runSips(args) {
2657
- return execFileAsync("/usr/bin/sips", [...args]);
2658
- }
2659
- function readSipsMetric(output, key) {
2660
- const matcher = new RegExp(`${key}:\\s*(\\d+)`);
2661
- const matched = output.match(matcher);
2662
- if (!matched) {
2663
- return void 0;
2664
- }
2665
- const value = Number.parseInt(matched[1], 10);
2666
- return Number.isFinite(value) ? value : void 0;
2667
- }
2668
- function uniqueNumbers(values) {
2669
- return [...new Set(values)];
2670
- }
2671
- function toDataUrl(mimeType, fileBytes) {
2672
- const suffix = mimeType.replace("image/", "");
2673
- return `data:image/${suffix};base64,${fileBytes.toString("base64")}`;
2674
- }
2675
-
2676
- // src/commands/ai/ai-image-command.ts
2677
- var IMAGE_GENERATION_URL = "https://ark.cn-beijing.volces.com/api/v3/images/generations";
2678
- var IMAGE_MODEL = "doubao-seedream-5-0-260128";
2679
- 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 {
2680
2467
  _meta() {
2681
2468
  return {
2682
2469
  name: "image",
2683
- 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"
2684
2471
  };
2685
2472
  }
2686
- _configureOptions(cmd) {
2687
- cmd.addOption(
2688
- new Option8(
2689
- "-r, --reference <path>",
2690
- "\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"
2691
- ).argParser(parseReferencePaths).default([])
2692
- );
2693
- cmd.addOption(
2694
- new Option8(
2695
- "-s, --size <size>",
2696
- "\u8F93\u51FA\u5C3A\u5BF8\uFF0C\u4F8B\u5982 1024x1024\u30012048x2048\u30012K"
2697
- ).default("2K")
2698
- );
2699
- cmd.addOption(
2700
- new Option8("--output-format <format>", "\u751F\u6210\u56FE\u7247\u7F16\u7801\u683C\u5F0F").choices(["jpeg", "png", "webp"]).default("jpeg")
2701
- );
2702
- cmd.addOption(
2703
- new Option8(
2704
- "-o, --output <path>",
2705
- "\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"
2706
- )
2707
- );
2708
- }
2709
2473
  _configureArguments(cmd) {
2710
- 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
+ );
2711
2478
  }
2712
2479
  async _execute(ctx) {
2713
2480
  const prompt = this._readPrompt(ctx);
@@ -2715,250 +2482,162 @@ var AiImageCommand = class extends AiBaseCommand {
2715
2482
  this._outputJsonError(1, "\u8BF7\u8F93\u5165\u751F\u56FE\u63D0\u793A\u8BCD");
2716
2483
  return this._makeOk();
2717
2484
  }
2718
- const referenceResult = await prepareReferenceImages(ctx.options.reference);
2719
- if (!referenceResult.ok) {
2720
- this._outputJsonError(referenceResult.code, referenceResult.msg);
2721
- return this._makeOk();
2722
- }
2723
- const requestBody = {
2724
- model: IMAGE_MODEL,
2725
- prompt,
2726
- size: ctx.options.size ?? "2K",
2727
- response_format: "url",
2728
- output_format: ctx.options.outputFormat,
2729
- watermark: false,
2730
- ...referenceResult.value.length > 0 ? {
2731
- image: referenceResult.value.map((item) => item.dataUrl)
2732
- } : {}
2733
- };
2734
- const responseResult = await this._requestImage(requestBody);
2735
- if (!responseResult.ok) {
2736
- this._outputJsonError(responseResult.code, responseResult.msg);
2737
- return this._makeOk();
2738
- }
2739
- const payload = responseResult.value;
2740
- const remoteUrl = payload.data?.[0]?.url?.trim();
2741
- if (!remoteUrl) {
2742
- this._outputJsonError(1, "\u63A5\u53E3\u8C03\u7528\u6210\u529F\uFF0C\u4F46 data.[0].url \u4E3A\u7A7A");
2743
- return this._makeOk();
2744
- }
2745
- const outputTargetResult = await resolveOutputTarget(
2746
- ctx.options.output,
2747
- ctx.options.outputFormat
2748
- );
2749
- if (!outputTargetResult.ok) {
2750
- 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);
2751
2489
  return this._makeOk();
2752
2490
  }
2753
- const saveResult = await downloadImageToFile(
2754
- remoteUrl,
2755
- outputTargetResult.value
2756
- );
2757
- if (!saveResult.ok) {
2758
- this._outputJsonError(saveResult.code, saveResult.msg);
2491
+ const imagePathResult = await findLatestGeneratedImageFile(startedAt);
2492
+ if (!imagePathResult.ok) {
2493
+ this._outputJsonError(imagePathResult.code, imagePathResult.msg);
2759
2494
  return this._makeOk();
2760
2495
  }
2761
2496
  this._outputJsonOk({
2762
- image_path: saveResult.value
2497
+ image_path: imagePathResult.value
2763
2498
  });
2764
2499
  return this._makeOk();
2765
2500
  }
2766
- async _requestImage(requestBody) {
2767
- const token = process.env.ARK_AK?.trim() || ARK_AK;
2768
- if (!token) {
2769
- return makeError(1, "\u7F3A\u5C11 ARK_AK\uFF0C\u8BF7\u5148\u914D\u7F6E\u53EF\u7528\u7684 API Key");
2770
- }
2771
- const controller = new AbortController();
2772
- const timer = setTimeout(() => controller.abort(), 12e4);
2501
+ async _runCodexImagePrompt(prompt) {
2773
2502
  try {
2774
- const response = await fetch(IMAGE_GENERATION_URL, {
2775
- method: "POST",
2776
- headers: {
2777
- Authorization: `Bearer ${token}`,
2778
- "Content-Type": "application/json"
2779
- },
2780
- body: JSON.stringify(requestBody),
2781
- signal: controller.signal
2782
- });
2783
- const bodyText = await response.text();
2784
- const payload = parseJsonSafely(bodyText);
2785
- if (!response.ok) {
2786
- const suffix = extractErrorMessage(payload) ?? bodyText.trim();
2787
- return makeError(
2788
- response.status,
2789
- suffix ? `\u751F\u56FE\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}: ${suffix}` : `\u751F\u56FE\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}`
2790
- );
2791
- }
2792
- if (!payload) {
2793
- return makeError(1, "\u751F\u56FE\u63A5\u53E3\u8FD4\u56DE\u975E JSON \u54CD\u5E94");
2794
- }
2795
- if (hasFailedCode(payload.code)) {
2796
- return makeError(
2797
- 1,
2798
- extractErrorMessage(payload) ?? "\u751F\u56FE\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF"
2799
- );
2800
- }
2801
- return makeOkWith(payload);
2503
+ await fs5.mkdir(CODEX_EMPTY_WORKDIR, { recursive: true });
2802
2504
  } catch (error) {
2803
- const message = error instanceof Error && error.name === "AbortError" ? "\u8BF7\u6C42\u8D85\u65F6" : error instanceof Error ? error.message : String(error);
2804
- return makeError(1, `\u8C03\u7528\u751F\u56FE\u63A5\u53E3\u5931\u8D25: ${message}`);
2805
- } finally {
2806
- 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
+ );
2807
2510
  }
2808
- }
2809
- };
2810
- async function resolveOutputTarget(outputPath, outputFormat) {
2811
- if (!outputPath) {
2812
- const directoryPath = process.cwd();
2813
- const filePath = path7.join(directoryPath, buildAutoFileName(outputFormat));
2814
- return makeOkWith({
2815
- directoryPath,
2816
- filePath,
2817
- 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"
2818
2525
  });
2819
- }
2820
- const resolvedPath = path7.resolve(process.cwd(), outputPath);
2821
- try {
2822
- const stats = await fs6.stat(resolvedPath);
2823
- if (stats.isDirectory()) {
2824
- return makeOkWith({
2825
- directoryPath: resolvedPath,
2826
- filePath: path7.join(resolvedPath, buildAutoFileName(outputFormat)),
2827
- 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
+ );
2828
2534
  });
2829
- }
2830
- return makeOkWith({
2831
- directoryPath: path7.dirname(resolvedPath),
2832
- filePath: resolvedPath,
2833
- autoNamed: false
2834
- });
2835
- } catch (error) {
2836
- const nodeError = error;
2837
- if (nodeError.code !== "ENOENT") {
2838
- const message = error instanceof Error ? error.message : String(error);
2839
- return makeError(1, `\u8BFB\u53D6\u8F93\u51FA\u8DEF\u5F84\u5931\u8D25: ${resolvedPath}: ${message}`);
2840
- }
2841
- if (path7.extname(resolvedPath)) {
2842
- return makeOkWith({
2843
- directoryPath: path7.dirname(resolvedPath),
2844
- filePath: resolvedPath,
2845
- 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
+ );
2846
2547
  });
2847
- }
2848
- return makeOkWith({
2849
- directoryPath: resolvedPath,
2850
- filePath: path7.join(resolvedPath, buildAutoFileName(outputFormat)),
2851
- autoNamed: true
2852
2548
  });
2853
2549
  }
2854
- }
2855
- async function downloadImageToFile(remoteUrl, target) {
2856
- try {
2857
- await fs6.mkdir(target.directoryPath, { recursive: true });
2858
- } catch (error) {
2859
- const message = error instanceof Error ? error.message : String(error);
2860
- return makeError(
2861
- 1,
2862
- `\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55\u5931\u8D25: ${target.directoryPath}: ${message}`
2863
- );
2864
- }
2865
- let response;
2866
- try {
2867
- response = await fetch(remoteUrl);
2868
- } catch (error) {
2869
- const message = error instanceof Error ? error.message : String(error);
2870
- return makeError(1, `\u4E0B\u8F7D\u56FE\u7247\u5931\u8D25: ${message}`);
2871
- }
2872
- if (!response.ok) {
2873
- return makeError(response.status, `\u4E0B\u8F7D\u56FE\u7247\u5931\u8D25: HTTP ${response.status}`);
2874
- }
2875
- const bytes = Buffer.from(await response.arrayBuffer());
2876
- const targetPath = target.autoNamed ? replaceFileExtension(
2877
- target.filePath,
2878
- inferImageExtension(remoteUrl, response)
2879
- ) : target.filePath;
2880
- try {
2881
- await fs6.writeFile(targetPath, bytes);
2882
- } catch (error) {
2883
- const message = error instanceof Error ? error.message : String(error);
2884
- return makeError(1, `\u5199\u5165\u56FE\u7247\u5931\u8D25: ${targetPath}: ${message}`);
2885
- }
2886
- return makeOkWith(path7.resolve(targetPath));
2887
- }
2888
- function parseReferencePaths(value, previous) {
2889
- return previous.concat(
2890
- 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
2891
2555
  );
2556
+ if (!latestDirectoryResult.ok) {
2557
+ return latestDirectoryResult;
2558
+ }
2559
+ return findLatestCreatedFile(latestDirectoryResult.value);
2892
2560
  }
2893
- function parseJsonSafely(text) {
2894
- if (!text.trim()) {
2895
- return void 0;
2561
+ async function findLatestCreatedDirectory(directoryPath, minCreatedAt) {
2562
+ const entriesResult = await readDirectoryEntries(directoryPath);
2563
+ if (!entriesResult.ok) {
2564
+ return entriesResult;
2896
2565
  }
2897
- try {
2898
- return JSON.parse(text);
2899
- } catch {
2900
- 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
+ );
2901
2582
  }
2583
+ return makeOkWith(latest.filePath);
2902
2584
  }
2903
- function hasFailedCode(code) {
2904
- if (code === void 0 || code === null) {
2905
- return false;
2585
+ async function findLatestCreatedFile(directoryPath) {
2586
+ const entriesResult = await readDirectoryEntries(directoryPath);
2587
+ if (!entriesResult.ok) {
2588
+ return entriesResult;
2906
2589
  }
2907
- if (typeof code === "number") {
2908
- 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;
2909
2596
  }
2910
- const normalized = code.trim();
2911
- return normalized !== "0" && normalized !== "";
2912
- }
2913
- function extractErrorMessage(payload) {
2914
- if (!payload) {
2915
- 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);
2916
2600
  }
2917
- const message = payload.msg?.trim() || payload.message?.trim();
2918
- return message || void 0;
2919
- }
2920
- function buildAutoFileName(outputFormat) {
2921
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2922
- return `ai-image-${timestamp}.${outputFormat}`;
2601
+ return makeOkWith(path6.resolve(latest.filePath));
2923
2602
  }
2924
- function replaceFileExtension(filePath, extension) {
2925
- const ext = path7.extname(filePath);
2926
- if (!ext) {
2927
- 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);
2928
2609
  }
2929
- return `${filePath.slice(0, -ext.length)}.${extension}`;
2930
2610
  }
2931
- function inferImageExtension(remoteUrl, response) {
2932
- const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
2933
- if (contentType.includes("png")) {
2934
- return "png";
2935
- }
2936
- if (contentType.includes("webp")) {
2937
- return "webp";
2938
- }
2939
- if (contentType.includes("jpeg") || contentType.includes("jpg")) {
2940
- return "jpeg";
2941
- }
2942
- try {
2943
- const url = new URL(remoteUrl);
2944
- const ext = path7.extname(url.pathname).toLowerCase();
2945
- if (ext === ".png") {
2946
- return "png";
2947
- }
2948
- if (ext === ".webp") {
2949
- return "webp";
2950
- }
2951
- if (ext === ".jpg" || ext === ".jpeg") {
2952
- 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);
2953
2624
  }
2954
- } catch {
2955
2625
  }
2956
- 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}`);
2957
2636
  }
2958
2637
 
2959
2638
  // src/commands/ai/ai-video-command.ts
2960
- import * as path8 from "path";
2961
- import { Option as Option9 } from "commander";
2639
+ import * as path7 from "path";
2640
+ import { Option as Option8 } from "commander";
2962
2641
 
2963
2642
  // src/bedrock/uuid/uuid.ts
2964
2643
  var generateUuid = (() => {
@@ -3150,10 +2829,10 @@ var AiVideoCommand = class extends AiBaseCommand {
3150
2829
  }
3151
2830
  _configureOptions(cmd) {
3152
2831
  cmd.addOption(
3153
- new Option9(
2832
+ new Option8(
3154
2833
  "-r, --reference <path>",
3155
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"
3156
- ).argParser(parseReferencePaths2).default([])
2835
+ ).argParser(parseReferencePaths).default([])
3157
2836
  );
3158
2837
  }
3159
2838
  _configureArguments(cmd) {
@@ -3201,7 +2880,7 @@ var AiVideoCommand = class extends AiBaseCommand {
3201
2880
  return makeOkWith({
3202
2881
  asset_id: result.value.assetId,
3203
2882
  asset_type: result.value.fileInfo.assetType,
3204
- file_path: path8.resolve(process.cwd(), referencePath),
2883
+ file_path: path7.resolve(process.cwd(), referencePath),
3205
2884
  region: result.value.region
3206
2885
  });
3207
2886
  })
@@ -3221,7 +2900,7 @@ var AiVideoCommand = class extends AiBaseCommand {
3221
2900
  });
3222
2901
  }
3223
2902
  };
3224
- function parseReferencePaths2(value, previous) {
2903
+ function parseReferencePaths(value, previous) {
3225
2904
  return previous.concat(
3226
2905
  value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
3227
2906
  );
@@ -3230,7 +2909,240 @@ function parseReferencePaths2(value, previous) {
3230
2909
  // src/commands/ai/ai-vid-command.ts
3231
2910
  import * as fs7 from "fs/promises";
3232
2911
  import * as path9 from "path";
3233
- 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
3234
3146
  var VIDEO_GENERATION_URL = "https://openrouter.ai/api/v1/videos";
3235
3147
  var VIDEO_MODEL = "bytedance/seedance-2.0";
3236
3148
  var POLLING_LOG_FILE_NAME = "ai-vid-polling.log";
@@ -3248,34 +3160,34 @@ var AiVidCommand = class extends AiBaseCommand {
3248
3160
  }
3249
3161
  _configureOptions(cmd) {
3250
3162
  cmd.addOption(
3251
- new Option10(
3163
+ new Option9(
3252
3164
  "-r, --reference <path>",
3253
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"
3254
- ).argParser(parseReferencePaths3).default([])
3166
+ ).argParser(parseReferencePaths2).default([])
3255
3167
  );
3256
3168
  cmd.addOption(
3257
- 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")
3258
3170
  );
3259
3171
  cmd.addOption(
3260
- new Option10(
3172
+ new Option9(
3261
3173
  "--aspect-ratio <ratio>",
3262
3174
  "\u89C6\u9891\u957F\u5BBD\u6BD4\uFF0C\u4F8B\u5982 16:9\u30019:16\u30011:1\uFF0C\u7F3A\u7701\u4E0D\u6307\u5B9A"
3263
3175
  )
3264
3176
  );
3265
3177
  cmd.addOption(
3266
- new Option10(
3178
+ new Option9(
3267
3179
  "--duration <seconds>",
3268
3180
  "\u89C6\u9891\u65F6\u957F\uFF08\u79D2\uFF09\uFF0C\u7F3A\u7701\u4E0D\u6307\u5B9A"
3269
3181
  ).argParser(parseDuration)
3270
3182
  );
3271
3183
  cmd.addOption(
3272
- new Option10(
3184
+ new Option9(
3273
3185
  "--polling-only",
3274
3186
  "\u4EC5\u63D0\u4EA4\u5E76\u8FD4\u56DE polling_url\uFF0C\u4E0D\u5728\u672C\u5730\u8F6E\u8BE2\u7ED3\u679C"
3275
3187
  ).default(false)
3276
3188
  );
3277
3189
  cmd.addOption(
3278
- new Option10(
3190
+ new Option9(
3279
3191
  "-o, --output <path>",
3280
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"
3281
3193
  )
@@ -3351,7 +3263,7 @@ var AiVidCommand = class extends AiBaseCommand {
3351
3263
  this._outputJsonError(1, "\u89C6\u9891\u751F\u6210\u5B8C\u6210\uFF0C\u4F46\u672A\u62FF\u5230 unsigned_urls[0]");
3352
3264
  return this._makeOk();
3353
3265
  }
3354
- const outputTargetResult = await resolveOutputTarget2(ctx.options.output);
3266
+ const outputTargetResult = await resolveOutputTarget(ctx.options.output);
3355
3267
  if (!outputTargetResult.ok) {
3356
3268
  this._outputJsonError(outputTargetResult.code, outputTargetResult.msg);
3357
3269
  return this._makeOk();
@@ -3395,7 +3307,7 @@ var AiVidCommand = class extends AiBaseCommand {
3395
3307
  clearTimeout(timer);
3396
3308
  }
3397
3309
  const bodyText = await response.text().catch(() => "");
3398
- const payload = parseJsonSafely2(bodyText);
3310
+ const payload = parseJsonSafely(bodyText);
3399
3311
  if (!response.ok) {
3400
3312
  const detail = extractErrorText(payload) ?? bodyText.trim();
3401
3313
  const suffix = detail ? `: ${detail}` : "";
@@ -3441,7 +3353,7 @@ var AiVidCommand = class extends AiBaseCommand {
3441
3353
  continue;
3442
3354
  }
3443
3355
  const bodyText = await response.text().catch(() => "");
3444
- const payload = parseJsonSafely2(bodyText);
3356
+ const payload = parseJsonSafely(bodyText);
3445
3357
  if (!response.ok) {
3446
3358
  const detail = extractErrorText(payload) ?? bodyText.trim();
3447
3359
  return makeError(
@@ -3476,7 +3388,7 @@ var AiVidCommand = class extends AiBaseCommand {
3476
3388
  function resolveOpenRouterToken() {
3477
3389
  return process.env.OPENROUTER_AK?.trim() || OPENROUTER_AK;
3478
3390
  }
3479
- function parseReferencePaths3(value, previous) {
3391
+ function parseReferencePaths2(value, previous) {
3480
3392
  return previous.concat(
3481
3393
  value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
3482
3394
  );
@@ -3488,7 +3400,7 @@ function parseDuration(value) {
3488
3400
  }
3489
3401
  return parsed;
3490
3402
  }
3491
- function parseJsonSafely2(text) {
3403
+ function parseJsonSafely(text) {
3492
3404
  if (!text.trim()) {
3493
3405
  return void 0;
3494
3406
  }
@@ -3540,12 +3452,12 @@ async function appendPollingUrl(pollingUrl, generationId) {
3540
3452
  return makeError(1, `\u5199\u5165 polling \u65E5\u5FD7\u5931\u8D25 ${logFilePath}: ${message}`);
3541
3453
  }
3542
3454
  }
3543
- async function resolveOutputTarget2(outputPath) {
3455
+ async function resolveOutputTarget(outputPath) {
3544
3456
  if (!outputPath) {
3545
3457
  const directoryPath = process.cwd();
3546
3458
  return makeOkWith({
3547
3459
  directoryPath,
3548
- filePath: path9.join(directoryPath, buildAutoFileName2()),
3460
+ filePath: path9.join(directoryPath, buildAutoFileName()),
3549
3461
  autoNamed: true
3550
3462
  });
3551
3463
  }
@@ -3555,7 +3467,7 @@ async function resolveOutputTarget2(outputPath) {
3555
3467
  if (stats.isDirectory()) {
3556
3468
  return makeOkWith({
3557
3469
  directoryPath: resolvedPath,
3558
- filePath: path9.join(resolvedPath, buildAutoFileName2()),
3470
+ filePath: path9.join(resolvedPath, buildAutoFileName()),
3559
3471
  autoNamed: true
3560
3472
  });
3561
3473
  }
@@ -3579,7 +3491,7 @@ async function resolveOutputTarget2(outputPath) {
3579
3491
  }
3580
3492
  return makeOkWith({
3581
3493
  directoryPath: resolvedPath,
3582
- filePath: path9.join(resolvedPath, buildAutoFileName2()),
3494
+ filePath: path9.join(resolvedPath, buildAutoFileName()),
3583
3495
  autoNamed: true
3584
3496
  });
3585
3497
  }
@@ -3617,7 +3529,7 @@ async function downloadVideoToFile(remoteUrl, target, token) {
3617
3529
  );
3618
3530
  }
3619
3531
  const bytes = Buffer.from(await response.arrayBuffer());
3620
- const targetPath = target.autoNamed ? replaceFileExtension2(
3532
+ const targetPath = target.autoNamed ? replaceFileExtension(
3621
3533
  target.filePath,
3622
3534
  inferVideoExtension(remoteUrl, response)
3623
3535
  ) : target.filePath;
@@ -3629,11 +3541,11 @@ async function downloadVideoToFile(remoteUrl, target, token) {
3629
3541
  }
3630
3542
  return makeOkWith(path9.resolve(targetPath));
3631
3543
  }
3632
- function buildAutoFileName2() {
3544
+ function buildAutoFileName() {
3633
3545
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3634
3546
  return `ai-vid-${timestamp}.mp4`;
3635
3547
  }
3636
- function replaceFileExtension2(filePath, extension) {
3548
+ function replaceFileExtension(filePath, extension) {
3637
3549
  const ext = path9.extname(filePath);
3638
3550
  if (!ext) {
3639
3551
  return `${filePath}.${extension}`;
@@ -3663,7 +3575,7 @@ function inferVideoExtension(remoteUrl, response) {
3663
3575
  }
3664
3576
 
3665
3577
  // src/commands/ai/ai-querytask-command.ts
3666
- import { spawn as spawn3 } from "child_process";
3578
+ import { spawn as spawn4 } from "child_process";
3667
3579
  var BIN = "xx";
3668
3580
  var PPE_ENV = "ppe_new_login_cli";
3669
3581
  var POLL_INTERVAL_MS2 = 5e3;
@@ -3720,7 +3632,7 @@ var AiQueryTaskCommand = class extends AiBaseCommand {
3720
3632
  "--json"
3721
3633
  ];
3722
3634
  return new Promise((resolve8) => {
3723
- const child = spawn3(BIN, args, { cwd: process.cwd() });
3635
+ const child = spawn4(BIN, args, { cwd: process.cwd() });
3724
3636
  let stdout = "";
3725
3637
  let stderr = "";
3726
3638
  child.stdout.on("data", (chunk) => {
@@ -3801,7 +3713,7 @@ var AiCommand = class extends BaseSubcommandHost {
3801
3713
  this._addSubcommand(new AiCursorCommand());
3802
3714
  this._addSubcommand(new AiCodexCommand());
3803
3715
  this._addSubcommand(new AiRunCommand());
3804
- this._addSubcommand(new AiImageCommand());
3716
+ this._addSubcommand(new AiImageCommandV2());
3805
3717
  this._addSubcommand(new AiVideoCommand());
3806
3718
  this._addSubcommand(new AiVidCommand());
3807
3719
  this._addSubcommand(new AiQueryTaskCommand());