maoda-commander-tt 0.0.12 → 0.0.14
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 +533 -10
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2078,9 +2078,9 @@ var ShortcutBaseCommand = class extends BaseCommand {
|
|
|
2078
2078
|
stdio: "inherit",
|
|
2079
2079
|
shell: true
|
|
2080
2080
|
});
|
|
2081
|
-
return new Promise((
|
|
2081
|
+
return new Promise((resolve6) => {
|
|
2082
2082
|
child.on("error", (error) => {
|
|
2083
|
-
|
|
2083
|
+
resolve6(
|
|
2084
2084
|
this._makeError(
|
|
2085
2085
|
1,
|
|
2086
2086
|
`\u6267\u884C ${cmdLine} \u5931\u8D25: ${error.message}`
|
|
@@ -2089,11 +2089,11 @@ var ShortcutBaseCommand = class extends BaseCommand {
|
|
|
2089
2089
|
});
|
|
2090
2090
|
child.on("close", (code, signal) => {
|
|
2091
2091
|
if (code === 0) {
|
|
2092
|
-
|
|
2092
|
+
resolve6(this._makeOk());
|
|
2093
2093
|
return;
|
|
2094
2094
|
}
|
|
2095
2095
|
const signalText = signal ? `, signal: ${signal}` : "";
|
|
2096
|
-
|
|
2096
|
+
resolve6(
|
|
2097
2097
|
this._makeError(
|
|
2098
2098
|
code ?? 1,
|
|
2099
2099
|
`\u6267\u884C ${cmdLine} \u5931\u8D25${signalText}`
|
|
@@ -2170,9 +2170,9 @@ var AiBaseCommand = class extends BaseCommand {
|
|
|
2170
2170
|
cwd: process.cwd(),
|
|
2171
2171
|
stdio: "inherit"
|
|
2172
2172
|
});
|
|
2173
|
-
return new Promise((
|
|
2173
|
+
return new Promise((resolve6) => {
|
|
2174
2174
|
child.on("error", (error) => {
|
|
2175
|
-
|
|
2175
|
+
resolve6(
|
|
2176
2176
|
this._makeError(
|
|
2177
2177
|
1,
|
|
2178
2178
|
`\u6267\u884C ${command} ${args.join(" ")} \u5931\u8D25: ${error.message}`
|
|
@@ -2181,11 +2181,11 @@ var AiBaseCommand = class extends BaseCommand {
|
|
|
2181
2181
|
});
|
|
2182
2182
|
child.on("close", (code, signal) => {
|
|
2183
2183
|
if (code === 0) {
|
|
2184
|
-
|
|
2184
|
+
resolve6(this._makeOk());
|
|
2185
2185
|
return;
|
|
2186
2186
|
}
|
|
2187
2187
|
const signalText = signal ? `, signal: ${signal}` : "";
|
|
2188
|
-
|
|
2188
|
+
resolve6(
|
|
2189
2189
|
this._makeError(
|
|
2190
2190
|
code ?? 1,
|
|
2191
2191
|
`\u6267\u884C ${command} ${args.join(" ")} \u5931\u8D25${signalText}`
|
|
@@ -2196,6 +2196,9 @@ var AiBaseCommand = class extends BaseCommand {
|
|
|
2196
2196
|
}
|
|
2197
2197
|
};
|
|
2198
2198
|
|
|
2199
|
+
// src/constants/ak.ts
|
|
2200
|
+
var ARK_AK = "6990de07-83be-487d-930d-f5dd3b6d5993";
|
|
2201
|
+
|
|
2199
2202
|
// src/commands/ai/ai-websearch-command.ts
|
|
2200
2203
|
var AiWebSearchCommand = class extends AiBaseCommand {
|
|
2201
2204
|
_meta() {
|
|
@@ -2213,7 +2216,7 @@ var AiWebSearchCommand = class extends AiBaseCommand {
|
|
|
2213
2216
|
return this._makeError(1, "\u8BF7\u8F93\u5165\u641C\u7D22\u5185\u5BB9");
|
|
2214
2217
|
}
|
|
2215
2218
|
const url = "https://ark.cn-beijing.volces.com/api/v3/responses";
|
|
2216
|
-
const token =
|
|
2219
|
+
const token = ARK_AK;
|
|
2217
2220
|
const response = await fetch(url, {
|
|
2218
2221
|
method: "POST",
|
|
2219
2222
|
headers: {
|
|
@@ -2423,7 +2426,7 @@ var AiRunCommand = class extends AiBaseCommand {
|
|
|
2423
2426
|
}
|
|
2424
2427
|
_createSystemReminder(referenceDir) {
|
|
2425
2428
|
return `<system-reminder>
|
|
2426
|
-
\u53C2\u8003\u6587\u4EF6\u8DEF\u5F84 ${referenceDir} \u6587\u4EF6\u5939\u7684\u5185\u5BB9\uFF0C\u6267\u884C\u7528\u6237\u7684\u8981\u6C42\uFF0C\u82E5\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\uFF0C\u4E0D\u8981\u5BF9\u8BE5\u6587\u4EF6\u5939\u7684\u5185\u5BB9\u8FDB\u884C\u4FEE\u6539\uFF0C\u800C\u662F\u67E5\u9605\u91CC\u9762\u7684\u5185\u5BB9\u4F5C\u4E3A\u4E0A\u4E0B\u6587\u8865\u5145\u6BD4\u5982\u4F5C\u4E3A\u4FE1\u606F\u53C2\u8003\uFF0C\u6216\u8005\u6267\u884C\u6307\u5F15\uFF1B
|
|
2429
|
+
\u53C2\u8003\u6587\u4EF6\u8DEF\u5F84 ${referenceDir} \u4E0B\uFF0C\u6587\u4EF6\u5939\u91CC\u7684\u5185\u5BB9\uFF0C\u6267\u884C\u7528\u6237\u7684\u8981\u6C42\uFF0C\u82E5\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\uFF0C\u4E0D\u8981\u5BF9\u8BE5\u6587\u4EF6\u5939\u7684\u5185\u5BB9\u8FDB\u884C\u4FEE\u6539\uFF0C\u800C\u662F\u67E5\u9605\u91CC\u9762\u7684\u5185\u5BB9\u4F5C\u4E3A\u4E0A\u4E0B\u6587\u8865\u5145\u6BD4\u5982\u4F5C\u4E3A\u4FE1\u606F\u53C2\u8003\uFF0C\u6216\u8005\u6267\u884C\u6307\u5F15\uFF1B
|
|
2427
2430
|
\u5982\u679C user_prompt \u91CC\uFF0C\u7ED9\u51FA\u4E00\u4E9B\u6587\u4EF6\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u662F\u5F53\u524D\u6267\u884C\u547D\u4EE4\u8DEF\u5F84 (cwd) \u4E0B\u7684\u6587\u4EF6\uFF0C\u63D0\u4F9B\u7684\u662F\u672C\u6B21\u8981\u6C42\u7684\u8BE6\u7EC6\u6307\u4EE4\u6216\u8005\u91CD\u8981\u7684\u53C2\u8003\u4FE1\u606F
|
|
2428
2431
|
\u82E5\u7528\u6237\u672A\u63D0\u53CA\u4EFB\u4F55\u6587\u4EF6\u548C\u76EE\u5F55\uFF0C\u4F60\u65E0\u9700\u8BFB\u53D6\u547D\u4EE4\u6267\u884C\u76EE\u5F55\u4E0B\u7684\u4EFB\u4F55\u5185\u5BB9\uFF0C\u53EA\u9700\u5728\u53C2\u8003\u6587\u4EF6\u8DEF\u5F84\u4E0B\u641C\u7D22\u76F8\u5173\u7684\u4FE1\u606F\u5373\u53EF
|
|
2429
2432
|
\u6700\u540E\u7ED9\u51FA\u7684\u7528\u6237\u56DE\u590D\uFF0C\u65E0\u9700\u5F15\u7528\u53C2\u8003\u6587\u4EF6\u76EE\u5F55\u4E0B\u7684\u5177\u4F53\u6587\u4EF6\uFF0C\u56E0\u4E3A\u7528\u6237\u4E0D\u4F1A\u53BB\u770B\u5BF9\u5E94\u7684\u6587\u4EF6\uFF0C\u800C\u4E14\u8FD9\u4E5F\u662F\u5185\u90E8\u79C1\u6709\u4FE1\u606F
|
|
@@ -2433,6 +2436,525 @@ user_prompt:
|
|
|
2433
2436
|
}
|
|
2434
2437
|
};
|
|
2435
2438
|
|
|
2439
|
+
// src/commands/ai/ai-image-command.ts
|
|
2440
|
+
import * as fs6 from "fs/promises";
|
|
2441
|
+
import * as path7 from "path";
|
|
2442
|
+
import { Option as Option8 } from "commander";
|
|
2443
|
+
|
|
2444
|
+
// src/commands/ai/ai-image-reference.ts
|
|
2445
|
+
import * as fs5 from "fs/promises";
|
|
2446
|
+
import * as os2 from "os";
|
|
2447
|
+
import * as path6 from "path";
|
|
2448
|
+
import { execFile } from "child_process";
|
|
2449
|
+
import { promisify } from "util";
|
|
2450
|
+
var execFileAsync = promisify(execFile);
|
|
2451
|
+
var MAX_REFERENCE_IMAGE_BYTES = 2 * 1024 * 1024;
|
|
2452
|
+
var MAX_REFERENCE_IMAGE_COUNT = 14;
|
|
2453
|
+
var API_SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
2454
|
+
"image/jpeg",
|
|
2455
|
+
"image/png",
|
|
2456
|
+
"image/webp",
|
|
2457
|
+
"image/bmp",
|
|
2458
|
+
"image/tiff",
|
|
2459
|
+
"image/gif"
|
|
2460
|
+
]);
|
|
2461
|
+
async function prepareReferenceImages(filePaths) {
|
|
2462
|
+
if (filePaths.length > MAX_REFERENCE_IMAGE_COUNT) {
|
|
2463
|
+
return makeError(
|
|
2464
|
+
1,
|
|
2465
|
+
`\u53C2\u8003\u56FE\u6700\u591A\u652F\u6301 ${MAX_REFERENCE_IMAGE_COUNT} \u5F20\uFF0C\u5F53\u524D\u4F20\u5165 ${filePaths.length} \u5F20\u3002`
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
const preparedImages = [];
|
|
2469
|
+
for (const filePath of filePaths) {
|
|
2470
|
+
const prepared = await prepareReferenceImage(filePath);
|
|
2471
|
+
if (!prepared.ok) {
|
|
2472
|
+
return prepared;
|
|
2473
|
+
}
|
|
2474
|
+
preparedImages.push(prepared.value);
|
|
2475
|
+
}
|
|
2476
|
+
return makeOkWith(preparedImages);
|
|
2477
|
+
}
|
|
2478
|
+
async function prepareReferenceImage(filePath) {
|
|
2479
|
+
const resolvedPath = path6.resolve(process.cwd(), filePath);
|
|
2480
|
+
let stats;
|
|
2481
|
+
try {
|
|
2482
|
+
stats = await fs5.stat(resolvedPath);
|
|
2483
|
+
} catch (error) {
|
|
2484
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2485
|
+
return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
2486
|
+
}
|
|
2487
|
+
if (!stats.isFile()) {
|
|
2488
|
+
return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u6587\u4EF6: ${resolvedPath}`);
|
|
2489
|
+
}
|
|
2490
|
+
let fileBytes;
|
|
2491
|
+
try {
|
|
2492
|
+
fileBytes = await fs5.readFile(resolvedPath);
|
|
2493
|
+
} catch (error) {
|
|
2494
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2495
|
+
return makeError(1, `\u8BFB\u53D6\u53C2\u8003\u56FE\u5185\u5BB9\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
2496
|
+
}
|
|
2497
|
+
const detectedType = detectMimeType(fileBytes, resolvedPath);
|
|
2498
|
+
if (!detectedType) {
|
|
2499
|
+
return makeError(1, `\u53C2\u8003\u56FE\u4E0D\u662F\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${resolvedPath}`);
|
|
2500
|
+
}
|
|
2501
|
+
const needsTranscode = !API_SUPPORTED_MIME_TYPES.has(detectedType.mimeType) || fileBytes.byteLength > MAX_REFERENCE_IMAGE_BYTES;
|
|
2502
|
+
if (!needsTranscode) {
|
|
2503
|
+
return makeOkWith({
|
|
2504
|
+
inputPath: filePath,
|
|
2505
|
+
resolvedPath,
|
|
2506
|
+
sourceMimeType: detectedType.mimeType,
|
|
2507
|
+
requestMimeType: detectedType.mimeType,
|
|
2508
|
+
originalBytes: fileBytes.byteLength,
|
|
2509
|
+
finalBytes: fileBytes.byteLength,
|
|
2510
|
+
compressed: false,
|
|
2511
|
+
dataUrl: toDataUrl(detectedType.mimeType, fileBytes)
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2514
|
+
const compressed = await compressImageForReference(resolvedPath);
|
|
2515
|
+
if (!compressed.ok) {
|
|
2516
|
+
return compressed;
|
|
2517
|
+
}
|
|
2518
|
+
return makeOkWith({
|
|
2519
|
+
inputPath: filePath,
|
|
2520
|
+
resolvedPath,
|
|
2521
|
+
sourceMimeType: detectedType.mimeType,
|
|
2522
|
+
requestMimeType: compressed.value.mimeType,
|
|
2523
|
+
originalBytes: fileBytes.byteLength,
|
|
2524
|
+
finalBytes: compressed.value.bytes.byteLength,
|
|
2525
|
+
compressed: true,
|
|
2526
|
+
dataUrl: toDataUrl(compressed.value.mimeType, compressed.value.bytes)
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
function detectMimeType(fileBytes, filePath) {
|
|
2530
|
+
if (fileBytes.length >= 3 && fileBytes[0] === 255 && fileBytes[1] === 216 && fileBytes[2] === 255) {
|
|
2531
|
+
return { mimeType: "image/jpeg", extension: "jpeg" };
|
|
2532
|
+
}
|
|
2533
|
+
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) {
|
|
2534
|
+
return { mimeType: "image/png", extension: "png" };
|
|
2535
|
+
}
|
|
2536
|
+
if (fileBytes.length >= 6) {
|
|
2537
|
+
const gifHeader = fileBytes.subarray(0, 6).toString("ascii");
|
|
2538
|
+
if (gifHeader === "GIF87a" || gifHeader === "GIF89a") {
|
|
2539
|
+
return { mimeType: "image/gif", extension: "gif" };
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
if (fileBytes.length >= 12 && fileBytes.subarray(0, 4).toString("ascii") === "RIFF" && fileBytes.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
2543
|
+
return { mimeType: "image/webp", extension: "webp" };
|
|
2544
|
+
}
|
|
2545
|
+
if (fileBytes.length >= 2 && fileBytes.subarray(0, 2).toString("ascii") === "BM") {
|
|
2546
|
+
return { mimeType: "image/bmp", extension: "bmp" };
|
|
2547
|
+
}
|
|
2548
|
+
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)) {
|
|
2549
|
+
return { mimeType: "image/tiff", extension: "tiff" };
|
|
2550
|
+
}
|
|
2551
|
+
if (fileBytes.length >= 12 && fileBytes.subarray(4, 8).toString("ascii") === "ftyp") {
|
|
2552
|
+
const brand = fileBytes.subarray(8, 12).toString("ascii").trim();
|
|
2553
|
+
if (brand.startsWith("avif") || brand === "avis") {
|
|
2554
|
+
return { mimeType: "image/avif", extension: "avif" };
|
|
2555
|
+
}
|
|
2556
|
+
if (brand.startsWith("hei") || brand.startsWith("hev") || brand === "mif1" || brand === "msf1") {
|
|
2557
|
+
return { mimeType: "image/heic", extension: "heic" };
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
const ext = path6.extname(filePath).toLowerCase();
|
|
2561
|
+
switch (ext) {
|
|
2562
|
+
case ".jpg":
|
|
2563
|
+
case ".jpeg":
|
|
2564
|
+
return { mimeType: "image/jpeg", extension: "jpeg" };
|
|
2565
|
+
case ".png":
|
|
2566
|
+
return { mimeType: "image/png", extension: "png" };
|
|
2567
|
+
case ".gif":
|
|
2568
|
+
return { mimeType: "image/gif", extension: "gif" };
|
|
2569
|
+
case ".webp":
|
|
2570
|
+
return { mimeType: "image/webp", extension: "webp" };
|
|
2571
|
+
case ".bmp":
|
|
2572
|
+
return { mimeType: "image/bmp", extension: "bmp" };
|
|
2573
|
+
case ".tif":
|
|
2574
|
+
case ".tiff":
|
|
2575
|
+
return { mimeType: "image/tiff", extension: "tiff" };
|
|
2576
|
+
case ".heic":
|
|
2577
|
+
case ".heif":
|
|
2578
|
+
return { mimeType: "image/heic", extension: "heic" };
|
|
2579
|
+
case ".avif":
|
|
2580
|
+
return { mimeType: "image/avif", extension: "avif" };
|
|
2581
|
+
default:
|
|
2582
|
+
return void 0;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
async function compressImageForReference(sourcePath) {
|
|
2586
|
+
if (process.platform !== "darwin") {
|
|
2587
|
+
return makeError(
|
|
2588
|
+
1,
|
|
2589
|
+
`\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`
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
const tempDir = await fs5.mkdtemp(path6.join(os2.tmpdir(), "tt-ai-image-"));
|
|
2593
|
+
try {
|
|
2594
|
+
const dimensions = await readImageDimensions(sourcePath);
|
|
2595
|
+
const maxDimension = Math.max(dimensions.width, dimensions.height);
|
|
2596
|
+
const resizeLimits = uniqueNumbers([
|
|
2597
|
+
maxDimension,
|
|
2598
|
+
Math.floor(maxDimension * 0.85),
|
|
2599
|
+
Math.floor(maxDimension * 0.7),
|
|
2600
|
+
Math.floor(maxDimension * 0.55),
|
|
2601
|
+
Math.floor(maxDimension * 0.4)
|
|
2602
|
+
]).filter((value) => value >= 64);
|
|
2603
|
+
const qualityLevels = [85, 75, 65, 55, 45, 35];
|
|
2604
|
+
let attemptIndex = 0;
|
|
2605
|
+
for (const resizeLimit of resizeLimits) {
|
|
2606
|
+
for (const quality of qualityLevels) {
|
|
2607
|
+
const targetPath = path6.join(tempDir, `compressed-${attemptIndex}.jpeg`);
|
|
2608
|
+
attemptIndex += 1;
|
|
2609
|
+
await runSips([
|
|
2610
|
+
"--setProperty",
|
|
2611
|
+
"format",
|
|
2612
|
+
"jpeg",
|
|
2613
|
+
"--setProperty",
|
|
2614
|
+
"formatOptions",
|
|
2615
|
+
String(quality),
|
|
2616
|
+
sourcePath,
|
|
2617
|
+
"--out",
|
|
2618
|
+
targetPath
|
|
2619
|
+
]);
|
|
2620
|
+
if (resizeLimit < maxDimension) {
|
|
2621
|
+
await runSips(["-Z", String(resizeLimit), targetPath]);
|
|
2622
|
+
}
|
|
2623
|
+
const compressedBytes = await fs5.readFile(targetPath);
|
|
2624
|
+
if (compressedBytes.byteLength <= MAX_REFERENCE_IMAGE_BYTES) {
|
|
2625
|
+
return makeOkWith({
|
|
2626
|
+
mimeType: "image/jpeg",
|
|
2627
|
+
bytes: compressedBytes
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
return makeError(1, `\u53C2\u8003\u56FE\u538B\u7F29\u5931\u8D25\uFF0C\u65E0\u6CD5\u538B\u7F29\u5230 2MB \u4EE5\u5185: ${sourcePath}`);
|
|
2633
|
+
} catch (error) {
|
|
2634
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2635
|
+
return makeError(1, `\u5904\u7406\u53C2\u8003\u56FE\u5931\u8D25: ${sourcePath}: ${message}`);
|
|
2636
|
+
} finally {
|
|
2637
|
+
await fs5.rm(tempDir, { recursive: true, force: true });
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
async function readImageDimensions(sourcePath) {
|
|
2641
|
+
const { stdout } = await runSips([
|
|
2642
|
+
"-g",
|
|
2643
|
+
"pixelWidth",
|
|
2644
|
+
"-g",
|
|
2645
|
+
"pixelHeight",
|
|
2646
|
+
sourcePath
|
|
2647
|
+
]);
|
|
2648
|
+
const width = readSipsMetric(stdout, "pixelWidth");
|
|
2649
|
+
const height = readSipsMetric(stdout, "pixelHeight");
|
|
2650
|
+
if (!width || !height) {
|
|
2651
|
+
throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247\u5C3A\u5BF8");
|
|
2652
|
+
}
|
|
2653
|
+
return { width, height };
|
|
2654
|
+
}
|
|
2655
|
+
async function runSips(args) {
|
|
2656
|
+
return execFileAsync("/usr/bin/sips", [...args]);
|
|
2657
|
+
}
|
|
2658
|
+
function readSipsMetric(output, key) {
|
|
2659
|
+
const matcher = new RegExp(`${key}:\\s*(\\d+)`);
|
|
2660
|
+
const matched = output.match(matcher);
|
|
2661
|
+
if (!matched) {
|
|
2662
|
+
return void 0;
|
|
2663
|
+
}
|
|
2664
|
+
const value = Number.parseInt(matched[1], 10);
|
|
2665
|
+
return Number.isFinite(value) ? value : void 0;
|
|
2666
|
+
}
|
|
2667
|
+
function uniqueNumbers(values) {
|
|
2668
|
+
return [...new Set(values)];
|
|
2669
|
+
}
|
|
2670
|
+
function toDataUrl(mimeType, fileBytes) {
|
|
2671
|
+
const suffix = mimeType.replace("image/", "");
|
|
2672
|
+
return `data:image/${suffix};base64,${fileBytes.toString("base64")}`;
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
// src/commands/ai/ai-image-command.ts
|
|
2676
|
+
var IMAGE_GENERATION_URL = "https://ark.cn-beijing.volces.com/api/v3/images/generations";
|
|
2677
|
+
var IMAGE_MODEL = "doubao-seedream-5-0-260128";
|
|
2678
|
+
var AiImageCommand = class extends AiBaseCommand {
|
|
2679
|
+
_meta() {
|
|
2680
|
+
return {
|
|
2681
|
+
name: "image",
|
|
2682
|
+
description: "\u8C03\u7528\u8C46\u5305 Seedream \u6A21\u578B\u751F\u6210\u56FE\u7247\u5E76\u4E0B\u8F7D\u5230\u672C\u5730"
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
_configureOptions(cmd) {
|
|
2686
|
+
cmd.addOption(
|
|
2687
|
+
new Option8(
|
|
2688
|
+
"-r, --reference <path>",
|
|
2689
|
+
"\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"
|
|
2690
|
+
).argParser(parseReferencePaths).default([])
|
|
2691
|
+
);
|
|
2692
|
+
cmd.addOption(
|
|
2693
|
+
new Option8(
|
|
2694
|
+
"-s, --size <size>",
|
|
2695
|
+
"\u8F93\u51FA\u5C3A\u5BF8\uFF0C\u4F8B\u5982 1024x1024\u30012048x2048\u30012K"
|
|
2696
|
+
).default("2K")
|
|
2697
|
+
);
|
|
2698
|
+
cmd.addOption(
|
|
2699
|
+
new Option8("--output-format <format>", "\u751F\u6210\u56FE\u7247\u7F16\u7801\u683C\u5F0F").choices(["jpeg", "png", "webp"]).default("jpeg")
|
|
2700
|
+
);
|
|
2701
|
+
cmd.addOption(
|
|
2702
|
+
new Option8(
|
|
2703
|
+
"-o, --output <path>",
|
|
2704
|
+
"\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"
|
|
2705
|
+
)
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
_configureArguments(cmd) {
|
|
2709
|
+
cmd.argument("[content...]", "\u751F\u56FE\u63D0\u793A\u8BCD");
|
|
2710
|
+
}
|
|
2711
|
+
async _execute(ctx) {
|
|
2712
|
+
const prompt = this._readPrompt(ctx);
|
|
2713
|
+
if (!prompt) {
|
|
2714
|
+
this._outputJsonError(1, "\u8BF7\u8F93\u5165\u751F\u56FE\u63D0\u793A\u8BCD");
|
|
2715
|
+
return this._makeOk();
|
|
2716
|
+
}
|
|
2717
|
+
const referenceResult = await prepareReferenceImages(ctx.options.reference);
|
|
2718
|
+
if (!referenceResult.ok) {
|
|
2719
|
+
this._outputJsonError(referenceResult.code, referenceResult.msg);
|
|
2720
|
+
return this._makeOk();
|
|
2721
|
+
}
|
|
2722
|
+
const requestBody = {
|
|
2723
|
+
model: IMAGE_MODEL,
|
|
2724
|
+
prompt,
|
|
2725
|
+
size: ctx.options.size ?? "2K",
|
|
2726
|
+
response_format: "url",
|
|
2727
|
+
output_format: ctx.options.outputFormat,
|
|
2728
|
+
watermark: false,
|
|
2729
|
+
...referenceResult.value.length > 0 ? {
|
|
2730
|
+
image: referenceResult.value.map((item) => item.dataUrl)
|
|
2731
|
+
} : {}
|
|
2732
|
+
};
|
|
2733
|
+
const responseResult = await this._requestImage(requestBody);
|
|
2734
|
+
if (!responseResult.ok) {
|
|
2735
|
+
this._outputJsonError(responseResult.code, responseResult.msg);
|
|
2736
|
+
return this._makeOk();
|
|
2737
|
+
}
|
|
2738
|
+
const payload = responseResult.value;
|
|
2739
|
+
const remoteUrl = payload.data?.[0]?.url?.trim();
|
|
2740
|
+
if (!remoteUrl) {
|
|
2741
|
+
this._outputJsonError(1, "\u63A5\u53E3\u8C03\u7528\u6210\u529F\uFF0C\u4F46 data.[0].url \u4E3A\u7A7A");
|
|
2742
|
+
return this._makeOk();
|
|
2743
|
+
}
|
|
2744
|
+
const outputTargetResult = await resolveOutputTarget(
|
|
2745
|
+
ctx.options.output,
|
|
2746
|
+
ctx.options.outputFormat
|
|
2747
|
+
);
|
|
2748
|
+
if (!outputTargetResult.ok) {
|
|
2749
|
+
this._outputJsonError(outputTargetResult.code, outputTargetResult.msg);
|
|
2750
|
+
return this._makeOk();
|
|
2751
|
+
}
|
|
2752
|
+
const saveResult = await downloadImageToFile(
|
|
2753
|
+
remoteUrl,
|
|
2754
|
+
outputTargetResult.value
|
|
2755
|
+
);
|
|
2756
|
+
if (!saveResult.ok) {
|
|
2757
|
+
this._outputJsonError(saveResult.code, saveResult.msg);
|
|
2758
|
+
return this._makeOk();
|
|
2759
|
+
}
|
|
2760
|
+
this._outputJsonOk({
|
|
2761
|
+
image_path: saveResult.value
|
|
2762
|
+
});
|
|
2763
|
+
return this._makeOk();
|
|
2764
|
+
}
|
|
2765
|
+
async _requestImage(requestBody) {
|
|
2766
|
+
const token = process.env.ARK_AK?.trim() || ARK_AK;
|
|
2767
|
+
if (!token) {
|
|
2768
|
+
return makeError(1, "\u7F3A\u5C11 ARK_AK\uFF0C\u8BF7\u5148\u914D\u7F6E\u53EF\u7528\u7684 API Key");
|
|
2769
|
+
}
|
|
2770
|
+
const controller = new AbortController();
|
|
2771
|
+
const timer = setTimeout(() => controller.abort(), 12e4);
|
|
2772
|
+
try {
|
|
2773
|
+
const response = await fetch(IMAGE_GENERATION_URL, {
|
|
2774
|
+
method: "POST",
|
|
2775
|
+
headers: {
|
|
2776
|
+
Authorization: `Bearer ${token}`,
|
|
2777
|
+
"Content-Type": "application/json"
|
|
2778
|
+
},
|
|
2779
|
+
body: JSON.stringify(requestBody),
|
|
2780
|
+
signal: controller.signal
|
|
2781
|
+
});
|
|
2782
|
+
const bodyText = await response.text();
|
|
2783
|
+
const payload = parseJsonSafely(bodyText);
|
|
2784
|
+
if (!response.ok) {
|
|
2785
|
+
const suffix = extractErrorMessage(payload) ?? bodyText.trim();
|
|
2786
|
+
return makeError(
|
|
2787
|
+
response.status,
|
|
2788
|
+
suffix ? `\u751F\u56FE\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}: ${suffix}` : `\u751F\u56FE\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}`
|
|
2789
|
+
);
|
|
2790
|
+
}
|
|
2791
|
+
if (!payload) {
|
|
2792
|
+
return makeError(1, "\u751F\u56FE\u63A5\u53E3\u8FD4\u56DE\u975E JSON \u54CD\u5E94");
|
|
2793
|
+
}
|
|
2794
|
+
if (hasFailedCode(payload.code)) {
|
|
2795
|
+
return makeError(
|
|
2796
|
+
1,
|
|
2797
|
+
extractErrorMessage(payload) ?? "\u751F\u56FE\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF"
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2800
|
+
return makeOkWith(payload);
|
|
2801
|
+
} catch (error) {
|
|
2802
|
+
const message = error instanceof Error && error.name === "AbortError" ? "\u8BF7\u6C42\u8D85\u65F6" : error instanceof Error ? error.message : String(error);
|
|
2803
|
+
return makeError(1, `\u8C03\u7528\u751F\u56FE\u63A5\u53E3\u5931\u8D25: ${message}`);
|
|
2804
|
+
} finally {
|
|
2805
|
+
clearTimeout(timer);
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
};
|
|
2809
|
+
async function resolveOutputTarget(outputPath, outputFormat) {
|
|
2810
|
+
if (!outputPath) {
|
|
2811
|
+
const directoryPath = process.cwd();
|
|
2812
|
+
const filePath = path7.join(directoryPath, buildAutoFileName(outputFormat));
|
|
2813
|
+
return makeOkWith({
|
|
2814
|
+
directoryPath,
|
|
2815
|
+
filePath,
|
|
2816
|
+
autoNamed: true
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
const resolvedPath = path7.resolve(process.cwd(), outputPath);
|
|
2820
|
+
try {
|
|
2821
|
+
const stats = await fs6.stat(resolvedPath);
|
|
2822
|
+
if (stats.isDirectory()) {
|
|
2823
|
+
return makeOkWith({
|
|
2824
|
+
directoryPath: resolvedPath,
|
|
2825
|
+
filePath: path7.join(resolvedPath, buildAutoFileName(outputFormat)),
|
|
2826
|
+
autoNamed: true
|
|
2827
|
+
});
|
|
2828
|
+
}
|
|
2829
|
+
return makeOkWith({
|
|
2830
|
+
directoryPath: path7.dirname(resolvedPath),
|
|
2831
|
+
filePath: resolvedPath,
|
|
2832
|
+
autoNamed: false
|
|
2833
|
+
});
|
|
2834
|
+
} catch (error) {
|
|
2835
|
+
const nodeError = error;
|
|
2836
|
+
if (nodeError.code !== "ENOENT") {
|
|
2837
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2838
|
+
return makeError(1, `\u8BFB\u53D6\u8F93\u51FA\u8DEF\u5F84\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
2839
|
+
}
|
|
2840
|
+
if (path7.extname(resolvedPath)) {
|
|
2841
|
+
return makeOkWith({
|
|
2842
|
+
directoryPath: path7.dirname(resolvedPath),
|
|
2843
|
+
filePath: resolvedPath,
|
|
2844
|
+
autoNamed: false
|
|
2845
|
+
});
|
|
2846
|
+
}
|
|
2847
|
+
return makeOkWith({
|
|
2848
|
+
directoryPath: resolvedPath,
|
|
2849
|
+
filePath: path7.join(resolvedPath, buildAutoFileName(outputFormat)),
|
|
2850
|
+
autoNamed: true
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
async function downloadImageToFile(remoteUrl, target) {
|
|
2855
|
+
try {
|
|
2856
|
+
await fs6.mkdir(target.directoryPath, { recursive: true });
|
|
2857
|
+
} catch (error) {
|
|
2858
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2859
|
+
return makeError(
|
|
2860
|
+
1,
|
|
2861
|
+
`\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55\u5931\u8D25: ${target.directoryPath}: ${message}`
|
|
2862
|
+
);
|
|
2863
|
+
}
|
|
2864
|
+
let response;
|
|
2865
|
+
try {
|
|
2866
|
+
response = await fetch(remoteUrl);
|
|
2867
|
+
} catch (error) {
|
|
2868
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2869
|
+
return makeError(1, `\u4E0B\u8F7D\u56FE\u7247\u5931\u8D25: ${message}`);
|
|
2870
|
+
}
|
|
2871
|
+
if (!response.ok) {
|
|
2872
|
+
return makeError(response.status, `\u4E0B\u8F7D\u56FE\u7247\u5931\u8D25: HTTP ${response.status}`);
|
|
2873
|
+
}
|
|
2874
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
2875
|
+
const targetPath = target.autoNamed ? replaceFileExtension(
|
|
2876
|
+
target.filePath,
|
|
2877
|
+
inferImageExtension(remoteUrl, response)
|
|
2878
|
+
) : target.filePath;
|
|
2879
|
+
try {
|
|
2880
|
+
await fs6.writeFile(targetPath, bytes);
|
|
2881
|
+
} catch (error) {
|
|
2882
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2883
|
+
return makeError(1, `\u5199\u5165\u56FE\u7247\u5931\u8D25: ${targetPath}: ${message}`);
|
|
2884
|
+
}
|
|
2885
|
+
return makeOkWith(path7.resolve(targetPath));
|
|
2886
|
+
}
|
|
2887
|
+
function parseReferencePaths(value, previous) {
|
|
2888
|
+
return previous.concat(
|
|
2889
|
+
value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
|
|
2890
|
+
);
|
|
2891
|
+
}
|
|
2892
|
+
function parseJsonSafely(text) {
|
|
2893
|
+
if (!text.trim()) {
|
|
2894
|
+
return void 0;
|
|
2895
|
+
}
|
|
2896
|
+
try {
|
|
2897
|
+
return JSON.parse(text);
|
|
2898
|
+
} catch {
|
|
2899
|
+
return void 0;
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
function hasFailedCode(code) {
|
|
2903
|
+
if (code === void 0 || code === null) {
|
|
2904
|
+
return false;
|
|
2905
|
+
}
|
|
2906
|
+
if (typeof code === "number") {
|
|
2907
|
+
return code !== 0;
|
|
2908
|
+
}
|
|
2909
|
+
const normalized = code.trim();
|
|
2910
|
+
return normalized !== "0" && normalized !== "";
|
|
2911
|
+
}
|
|
2912
|
+
function extractErrorMessage(payload) {
|
|
2913
|
+
if (!payload) {
|
|
2914
|
+
return void 0;
|
|
2915
|
+
}
|
|
2916
|
+
const message = payload.msg?.trim() || payload.message?.trim();
|
|
2917
|
+
return message || void 0;
|
|
2918
|
+
}
|
|
2919
|
+
function buildAutoFileName(outputFormat) {
|
|
2920
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2921
|
+
return `ai-image-${timestamp}.${outputFormat}`;
|
|
2922
|
+
}
|
|
2923
|
+
function replaceFileExtension(filePath, extension) {
|
|
2924
|
+
const ext = path7.extname(filePath);
|
|
2925
|
+
if (!ext) {
|
|
2926
|
+
return `${filePath}.${extension}`;
|
|
2927
|
+
}
|
|
2928
|
+
return `${filePath.slice(0, -ext.length)}.${extension}`;
|
|
2929
|
+
}
|
|
2930
|
+
function inferImageExtension(remoteUrl, response) {
|
|
2931
|
+
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
2932
|
+
if (contentType.includes("png")) {
|
|
2933
|
+
return "png";
|
|
2934
|
+
}
|
|
2935
|
+
if (contentType.includes("webp")) {
|
|
2936
|
+
return "webp";
|
|
2937
|
+
}
|
|
2938
|
+
if (contentType.includes("jpeg") || contentType.includes("jpg")) {
|
|
2939
|
+
return "jpeg";
|
|
2940
|
+
}
|
|
2941
|
+
try {
|
|
2942
|
+
const url = new URL(remoteUrl);
|
|
2943
|
+
const ext = path7.extname(url.pathname).toLowerCase();
|
|
2944
|
+
if (ext === ".png") {
|
|
2945
|
+
return "png";
|
|
2946
|
+
}
|
|
2947
|
+
if (ext === ".webp") {
|
|
2948
|
+
return "webp";
|
|
2949
|
+
}
|
|
2950
|
+
if (ext === ".jpg" || ext === ".jpeg") {
|
|
2951
|
+
return "jpeg";
|
|
2952
|
+
}
|
|
2953
|
+
} catch {
|
|
2954
|
+
}
|
|
2955
|
+
return "jpeg";
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2436
2958
|
// src/commands/ai/ai-command.ts
|
|
2437
2959
|
var AiCommand = class extends BaseSubcommandHost {
|
|
2438
2960
|
_meta() {
|
|
@@ -2446,6 +2968,7 @@ var AiCommand = class extends BaseSubcommandHost {
|
|
|
2446
2968
|
this._addSubcommand(new AiCursorCommand());
|
|
2447
2969
|
this._addSubcommand(new AiCodexCommand());
|
|
2448
2970
|
this._addSubcommand(new AiRunCommand());
|
|
2971
|
+
this._addSubcommand(new AiImageCommand());
|
|
2449
2972
|
}
|
|
2450
2973
|
};
|
|
2451
2974
|
|