jimeng-cli 0.2.1 → 0.3.1

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/cli/index.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  generateImageComposition,
6
6
  generateImages,
7
7
  generateVideo,
8
+ getAssetList,
8
9
  getCredit,
9
10
  getLiveModels,
10
11
  getTaskResponse,
@@ -12,9 +13,11 @@ import {
12
13
  logger_default,
13
14
  parseRegionCode,
14
15
  receiveCredit,
16
+ refreshAllTokenModels,
15
17
  session_pool_default,
18
+ upscaleImage,
16
19
  waitForTaskResponse
17
- } from "../chunk-3SUCLOAC.js";
20
+ } from "../chunk-2IIK4X7C.js";
18
21
 
19
22
  // src/cli/app.ts
20
23
  import process2 from "process";
@@ -495,6 +498,34 @@ function printTaskInfo(task, deps) {
495
498
  }
496
499
  }
497
500
  function createQueryCommandHandlers(deps) {
501
+ const handleModelsRefresh = async (argv) => {
502
+ const args = minimist2(argv, {
503
+ boolean: ["help", "json"]
504
+ });
505
+ if (args.help) {
506
+ console.log(deps.usageModelsRefresh());
507
+ return;
508
+ }
509
+ await deps.ensureTokenPoolReady();
510
+ const results = await refreshAllTokenModels();
511
+ const isJson = Boolean(args.json);
512
+ if (isJson) {
513
+ deps.printCommandJson("models.refresh", results);
514
+ return;
515
+ }
516
+ if (results.length === 0) {
517
+ console.log("No enabled+live tokens found in pool. Nothing to refresh.");
518
+ return;
519
+ }
520
+ console.log(`Refreshed ${results.length} token(s).`);
521
+ console.log("");
522
+ console.log("token region imageModels videoModels capabilityTags error");
523
+ for (const r of results) {
524
+ const tags = r.capabilityTags.length > 0 ? r.capabilityTags.join(",") : "-";
525
+ const err = r.error ? r.error.slice(0, 60) : "-";
526
+ console.log(`${r.token} ${r.region} ${r.imageModels} ${r.videoModels} ${tags} ${err}`);
527
+ }
528
+ };
498
529
  const handleModelsList = async (argv) => {
499
530
  const args = minimist2(argv, {
500
531
  string: ["region", "token"],
@@ -638,10 +669,62 @@ ${deps.usageTaskWait()}`);
638
669
  if (isJson) deps.printCommandJson("task.wait", taskInfo);
639
670
  else printTaskInfo(taskInfo, deps);
640
671
  };
672
+ const handleTaskList = async (argv) => {
673
+ const args = minimist2(argv, {
674
+ string: ["token", "region", "type", "count"],
675
+ boolean: ["help", "json"]
676
+ });
677
+ if (args.help) {
678
+ console.log(deps.usageTaskList());
679
+ return;
680
+ }
681
+ const token = deps.getSingleString(args, "token");
682
+ const region = deps.getRegionWithDefault(args);
683
+ const type = deps.getSingleString(args, "type");
684
+ const countRaw = deps.getSingleString(args, "count");
685
+ const count = countRaw ? Number(countRaw) : 20;
686
+ const isJson = Boolean(args.json);
687
+ if (type && type !== "image" && type !== "video" && type !== "all") {
688
+ deps.fail(`Invalid --type: ${type}. Use image, video, or all.`);
689
+ }
690
+ const pick = await deps.pickDirectTokenForTask(token, region);
691
+ const result = await getAssetList(
692
+ pick.token,
693
+ buildRegionInfo(pick.region),
694
+ {
695
+ count: Number.isFinite(count) && count > 0 ? count : 20,
696
+ type
697
+ }
698
+ );
699
+ if (isJson) {
700
+ deps.printCommandJson("task.list", {
701
+ has_more: result.hasMore,
702
+ next_offset: result.nextOffset,
703
+ total: result.items.length,
704
+ items: result.items
705
+ });
706
+ return;
707
+ }
708
+ console.log(`Total: ${result.items.length} items${result.hasMore ? " (more available)" : ""}
709
+ `);
710
+ for (const item of result.items) {
711
+ const typeLabel = item.type === 1 ? "IMG" : "VID";
712
+ const statusLabel = item.status === 144 || item.status === 10 ? "DONE" : item.status === 30 ? "FAIL" : "PROC";
713
+ const time = item.createdTime > 0 ? new Date(item.createdTime * 1e3).toLocaleString() : "-";
714
+ const modelShort = item.modelName || item.modelReqKey || "-";
715
+ const promptShort = item.prompt.length > 50 ? item.prompt.slice(0, 50) + "..." : item.prompt;
716
+ console.log(`${item.id} ${typeLabel} ${statusLabel.padEnd(4)} ${time} ${modelShort.padEnd(20)} ${promptShort}`);
717
+ if (item.imageUrl) {
718
+ console.log(` ${item.imageUrl}`);
719
+ }
720
+ }
721
+ };
641
722
  return {
642
723
  handleModelsList,
724
+ handleModelsRefresh,
643
725
  handleTaskGet,
644
726
  handleTaskWait,
727
+ handleTaskList,
645
728
  printTaskInfo: (task) => {
646
729
  const normalized = collectTaskInfo(task, deps);
647
730
  if (!normalized) {
@@ -1187,13 +1270,260 @@ function createMediaCommandHandlers(deps) {
1187
1270
  deps.printDownloadSummary("video", [filePath]);
1188
1271
  }
1189
1272
  };
1273
+ const handleImageUpscale = async (argv) => {
1274
+ const args = minimist3(argv, {
1275
+ string: [
1276
+ "token",
1277
+ "region",
1278
+ "image",
1279
+ "model",
1280
+ "resolution",
1281
+ "output-dir",
1282
+ "wait-timeout-seconds",
1283
+ "poll-interval-ms"
1284
+ ],
1285
+ boolean: ["help", "wait", "json"],
1286
+ default: { wait: true }
1287
+ });
1288
+ if (args.help) {
1289
+ console.log(deps.usageImageUpscale());
1290
+ return;
1291
+ }
1292
+ const token = deps.getSingleString(args, "token");
1293
+ const region = deps.getRegionWithDefault(args);
1294
+ const imageSource = deps.getSingleString(args, "image");
1295
+ if (!imageSource) deps.failWithUsage("Missing required --image.", deps.usageImageUpscale());
1296
+ const outputDir = deps.getSingleString(args, "output-dir") || "./pic/cli-image-upscale";
1297
+ const model = deps.getSingleString(args, "model") || "jimeng-5.0";
1298
+ const resolution = deps.getSingleString(args, "resolution") || "4k";
1299
+ const wait = Boolean(args.wait);
1300
+ const isJson = Boolean(args.json);
1301
+ const pick = await deps.pickDirectTokenForGeneration(token, region, model, "image");
1302
+ let image;
1303
+ if (isHttpUrl(imageSource)) {
1304
+ image = imageSource;
1305
+ } else {
1306
+ const imagePath = path3.resolve(imageSource);
1307
+ if (!await pathExists2(imagePath)) deps.fail(`Image file not found: ${imagePath}`);
1308
+ image = await readFile2(imagePath);
1309
+ }
1310
+ const result = await upscaleImage(
1311
+ model,
1312
+ image,
1313
+ {
1314
+ resolution,
1315
+ wait,
1316
+ waitTimeoutSeconds: parsePositiveNumberOption2(args, "wait-timeout-seconds", deps),
1317
+ pollIntervalMs: parsePositiveNumberOption2(args, "poll-interval-ms", deps)
1318
+ },
1319
+ pick.token,
1320
+ buildRegionInfo(pick.region)
1321
+ );
1322
+ if (!Array.isArray(result)) {
1323
+ if (isJson) deps.printCommandJson("image.upscale", result, { wait });
1324
+ else deps.printTaskInfo(result);
1325
+ return;
1326
+ }
1327
+ const urls = result;
1328
+ if (urls.length === 0) deps.fail("No image URL found in response.");
1329
+ const savedFiles = await downloadImages(urls, outputDir, "jimeng-image-upscale", deps);
1330
+ if (isJson) {
1331
+ deps.printCommandJson(
1332
+ "image.upscale",
1333
+ { data: urls.map((url) => ({ url })), files: savedFiles },
1334
+ { wait, resolution }
1335
+ );
1336
+ } else {
1337
+ deps.printDownloadSummary("image", savedFiles);
1338
+ }
1339
+ };
1190
1340
  return {
1191
1341
  handleImageGenerate,
1192
1342
  handleImageEdit,
1343
+ handleImageUpscale,
1193
1344
  handleVideoGenerate
1194
1345
  };
1195
1346
  }
1196
1347
 
1348
+ // src/cli/login.ts
1349
+ import { execSync, spawn } from "child_process";
1350
+ import { existsSync } from "fs";
1351
+ import path4 from "path";
1352
+ import os from "os";
1353
+ import { fileURLToPath } from "url";
1354
+ import minimist4 from "minimist";
1355
+ var __filename = fileURLToPath(import.meta.url);
1356
+ var __dirname = path4.dirname(__filename);
1357
+ var LOGIN_SCRIPT = path4.join(__dirname, "..", "..", "scripts", "jimeng_login_helper.py");
1358
+ function createLoginCommandHandler(deps) {
1359
+ return async (argv) => {
1360
+ const args = minimist4(argv, {
1361
+ string: ["region", "sessionid", "debug-port"],
1362
+ boolean: ["help", "headless", "json"]
1363
+ });
1364
+ if (args.help) {
1365
+ console.log(usageLogin());
1366
+ return;
1367
+ }
1368
+ const region = deps.getRegionWithDefault(args);
1369
+ const parsedRegion = deps.parseRegionOrFail(region);
1370
+ const isJson = Boolean(args.json);
1371
+ const sessionid = deps.getSingleString(args, "sessionid");
1372
+ const debugPort = deps.getSingleString(args, "debug-port") || "9333";
1373
+ const headless = Boolean(args.headless);
1374
+ if (sessionid) {
1375
+ await addSessionToPool(sessionid, parsedRegion || "cn", deps, isJson);
1376
+ return;
1377
+ }
1378
+ const pythonLoginResult = await tryPythonLogin(debugPort, headless);
1379
+ if (pythonLoginResult) {
1380
+ await addSessionToPool(pythonLoginResult, parsedRegion || "cn", deps, isJson);
1381
+ return;
1382
+ }
1383
+ console.log("");
1384
+ console.log("Auto login not available. Please provide your sessionid manually.");
1385
+ console.log("You can get it from:");
1386
+ console.log(" 1. Login at https://jimeng.jianying.com/ai-tool/home/");
1387
+ console.log(" 2. Open DevTools > Application > Cookies > sessionid");
1388
+ console.log("");
1389
+ console.log("Then run: jimeng login --sessionid <your_sessionid> --region cn");
1390
+ console.log("");
1391
+ console.log("Or install Python 3 to use the automatic browser login.");
1392
+ };
1393
+ }
1394
+ async function tryPythonLogin(debugPort, headless) {
1395
+ const loginScript = findLoginScript();
1396
+ if (!loginScript) {
1397
+ return null;
1398
+ }
1399
+ console.log(`Launching browser login (debug port: ${debugPort})...`);
1400
+ console.log("Please complete login in the Chrome window.\n");
1401
+ try {
1402
+ const cmdArgs = [loginScript, "--debug-port", debugPort];
1403
+ if (headless) cmdArgs.push("--headless");
1404
+ const result = await runPythonScript(cmdArgs);
1405
+ const output = result.stdout.trim();
1406
+ const match = output.match(/sessionid:\s*(\S+)/);
1407
+ if (match) {
1408
+ return match[1];
1409
+ }
1410
+ console.log("Login script completed but sessionid not found in output.");
1411
+ return null;
1412
+ } catch (error) {
1413
+ console.log(`Auto login failed: ${error.message}`);
1414
+ return null;
1415
+ }
1416
+ }
1417
+ function findLoginScript() {
1418
+ if (existsSync(LOGIN_SCRIPT)) {
1419
+ return LOGIN_SCRIPT;
1420
+ }
1421
+ const candidates = [
1422
+ path4.join(process.cwd(), "jimeng_login.py"),
1423
+ path4.join(os.homedir(), ".jimeng_login.py")
1424
+ ];
1425
+ try {
1426
+ const whichResult = execSync("which jimeng_login.py 2>/dev/null || echo ''", { encoding: "utf-8" }).trim();
1427
+ if (whichResult) return whichResult;
1428
+ } catch {
1429
+ }
1430
+ for (const candidate of candidates) {
1431
+ if (existsSync(candidate)) return candidate;
1432
+ }
1433
+ return null;
1434
+ }
1435
+ function runPythonScript(args) {
1436
+ return new Promise((resolve, reject) => {
1437
+ var _a, _b;
1438
+ const pythonCmd = process.platform === "win32" ? "python" : "python3";
1439
+ const proc = spawn(pythonCmd, args, {
1440
+ stdio: ["ignore", "pipe", "pipe"],
1441
+ env: { ...process.env }
1442
+ });
1443
+ let stdout = "";
1444
+ let stderr = "";
1445
+ (_a = proc.stdout) == null ? void 0 : _a.on("data", (data) => {
1446
+ stdout += data.toString();
1447
+ const lines = data.toString().split("\n").filter(Boolean);
1448
+ for (const line of lines) {
1449
+ console.log(line);
1450
+ }
1451
+ });
1452
+ (_b = proc.stderr) == null ? void 0 : _b.on("data", (data) => {
1453
+ stderr += data.toString();
1454
+ });
1455
+ proc.on("close", (code) => {
1456
+ if (code === 0) {
1457
+ resolve({ stdout, stderr });
1458
+ } else {
1459
+ reject(new Error(`Login script exited with code ${code}: ${stderr.slice(-200)}`));
1460
+ }
1461
+ });
1462
+ proc.on("error", (error) => {
1463
+ reject(new Error(`Failed to run login script: ${error.message}`));
1464
+ });
1465
+ });
1466
+ }
1467
+ async function addSessionToPool(sessionid, region, deps, isJson) {
1468
+ await deps.ensureTokenPoolReady();
1469
+ const { added, total } = await session_pool_default.addTokens([sessionid], { defaultRegion: region });
1470
+ if (added > 0) {
1471
+ const masked = sessionid.length > 10 ? `${sessionid.slice(0, 4)}...${sessionid.slice(-4)}` : "***";
1472
+ let creditInfo = "";
1473
+ try {
1474
+ const { totalCredit } = await getCredit(sessionid, buildRegionInfo(region));
1475
+ creditInfo = ` (credits: ${totalCredit})`;
1476
+ } catch {
1477
+ creditInfo = " (credit check failed)";
1478
+ }
1479
+ if (isJson) {
1480
+ deps.printCommandJson("login", {
1481
+ token: masked,
1482
+ region,
1483
+ added: true,
1484
+ total,
1485
+ creditCheck: creditInfo.trim()
1486
+ });
1487
+ } else {
1488
+ console.log(`
1489
+ Login successful!`);
1490
+ console.log(` Token: ${masked}`);
1491
+ console.log(` Region: ${region}${creditInfo}`);
1492
+ console.log(` Pool size: ${total} token(s)`);
1493
+ }
1494
+ } else {
1495
+ if (isJson) {
1496
+ deps.printCommandJson("login", {
1497
+ added: false,
1498
+ total,
1499
+ reason: "token already exists in pool"
1500
+ });
1501
+ } else {
1502
+ console.log(`Token already exists in pool. Pool size: ${total} token(s)`);
1503
+ }
1504
+ }
1505
+ }
1506
+ function usageLogin() {
1507
+ return [
1508
+ "Usage:",
1509
+ " jimeng login [options]",
1510
+ "",
1511
+ "Options:",
1512
+ " --region <region> Region for the token (default cn)",
1513
+ " --sessionid <token> Add sessionid directly (skip browser login)",
1514
+ " --debug-port <port> Chrome debug port (default 9333)",
1515
+ " --headless Run Chrome in headless mode",
1516
+ " --json Output structured JSON",
1517
+ " --help Show help",
1518
+ "",
1519
+ "Notes:",
1520
+ " - Without --sessionid, launches Chrome for browser login (requires Python 3).",
1521
+ " - With --sessionid, directly adds the token to the pool.",
1522
+ " - After login, the token is automatically added to the token pool.",
1523
+ " - Token is validated by checking credit balance."
1524
+ ].join("\n");
1525
+ }
1526
+
1197
1527
  // src/cli/app.ts
1198
1528
  var JSON_OPTION = " --json Output structured JSON";
1199
1529
  var HELP_OPTION = " --help Show help";
@@ -1234,6 +1564,21 @@ function usageModelsList() {
1234
1564
  HELP_OPTION
1235
1565
  ]);
1236
1566
  }
1567
+ function usageModelsRefresh() {
1568
+ return buildUsageText(" jimeng models refresh [options]", [
1569
+ " --json Output structured JSON",
1570
+ HELP_OPTION
1571
+ ], [
1572
+ {
1573
+ title: "Notes:",
1574
+ lines: [
1575
+ " - Refreshes dynamicCapabilities (imageModels, videoModels, capabilityTags) for",
1576
+ " all enabled+live tokens in the token pool.",
1577
+ " - Results are persisted to token-pool.json automatically."
1578
+ ]
1579
+ }
1580
+ ]);
1581
+ }
1237
1582
  function usageTokenSubcommand(name) {
1238
1583
  const subcommand = TOKEN_SUBCOMMANDS_BY_NAME[name];
1239
1584
  return buildUsageText(subcommand.usageLine, subcommand.options, subcommand.sections);
@@ -1300,6 +1645,34 @@ function usageImageEdit() {
1300
1645
  ]
1301
1646
  );
1302
1647
  }
1648
+ function usageImageUpscale() {
1649
+ return buildUsageText(
1650
+ " jimeng image upscale --image <path_or_url> [options]",
1651
+ [
1652
+ " --token <token> Optional, override token-pool selection",
1653
+ " --region <region> X-Region header, default cn (cn/us/hk/jp/sg)",
1654
+ " --image <path_or_url> Required, local file or URL",
1655
+ " --model <model> Default jimeng-5.0",
1656
+ " --resolution <res> Default 4k (target resolution)",
1657
+ " --wait / --no-wait Default wait; --no-wait returns task only",
1658
+ " --wait-timeout-seconds Optional wait timeout override",
1659
+ " --poll-interval-ms Optional poll interval override",
1660
+ JSON_OPTION,
1661
+ " --output-dir <dir> Default ./pic/cli-image-upscale",
1662
+ HELP_OPTION
1663
+ ],
1664
+ [
1665
+ {
1666
+ title: "Notes:",
1667
+ lines: [
1668
+ " - Upscales an existing image to higher resolution using super_resolution.",
1669
+ " - Supports 2k and 4k target resolutions.",
1670
+ " - Image source can be a local file path or HTTP URL."
1671
+ ]
1672
+ }
1673
+ ]
1674
+ );
1675
+ }
1303
1676
  function usageVideoGenerate() {
1304
1677
  return buildUsageText(" jimeng video generate --prompt <text> [options]", [
1305
1678
  " --token <token> Optional, override token-pool selection",
@@ -1369,6 +1742,16 @@ function usageTaskWait() {
1369
1742
  HELP_OPTION
1370
1743
  ]);
1371
1744
  }
1745
+ function usageTaskList() {
1746
+ return buildUsageText(" jimeng task list [options]", [
1747
+ " --token <token> Optional, override token-pool selection",
1748
+ " --region <region> X-Region header, default cn (cn/us/hk/jp/sg)",
1749
+ " --type <type> Filter by type: image, video, or all (default all)",
1750
+ " --count <num> Number of items per page (default 20)",
1751
+ JSON_OPTION,
1752
+ HELP_OPTION
1753
+ ]);
1754
+ }
1372
1755
  function configureCliLogging(command) {
1373
1756
  if (process2.env.JIMENG_CLI_VERBOSE_LOGS === "true") {
1374
1757
  process2.env.JIMENG_CLI_SILENT_LOGS = "false";
@@ -1502,8 +1885,10 @@ var TOKEN_SUBCOMMANDS = createTokenSubcommands({
1502
1885
  });
1503
1886
  var queryHandlers = createQueryCommandHandlers({
1504
1887
  usageModelsList,
1888
+ usageModelsRefresh,
1505
1889
  usageTaskGet,
1506
1890
  usageTaskWait,
1891
+ usageTaskList,
1507
1892
  getSingleString,
1508
1893
  getRegionWithDefault,
1509
1894
  parseRegionOrFail,
@@ -1517,6 +1902,7 @@ var queryHandlers = createQueryCommandHandlers({
1517
1902
  var mediaHandlers = createMediaCommandHandlers({
1518
1903
  usageImageGenerate,
1519
1904
  usageImageEdit,
1905
+ usageImageUpscale,
1520
1906
  usageVideoGenerate,
1521
1907
  getSingleString,
1522
1908
  getRegionWithDefault,
@@ -1535,10 +1921,26 @@ function buildHandlersMap(subcommands) {
1535
1921
  return Object.fromEntries(subcommands.map((item) => [item.name, item.handler]));
1536
1922
  }
1537
1923
  var COMMAND_SPECS = [
1924
+ {
1925
+ name: "login",
1926
+ description: "Login and add session to token pool",
1927
+ handler: createLoginCommandHandler({
1928
+ getSingleString,
1929
+ getRegionWithDefault,
1930
+ parseRegionOrFail,
1931
+ ensureTokenPoolReady,
1932
+ fail,
1933
+ printJson,
1934
+ printCommandJson
1935
+ })
1936
+ },
1538
1937
  {
1539
1938
  name: "models",
1540
1939
  description: "Model commands",
1541
- subcommands: [{ name: "list", description: "List available models", handler: queryHandlers.handleModelsList }],
1940
+ subcommands: [
1941
+ { name: "list", description: "List available models", handler: queryHandlers.handleModelsList },
1942
+ { name: "refresh", description: "Refresh token dynamic capabilities (model list)", handler: queryHandlers.handleModelsRefresh }
1943
+ ],
1542
1944
  usage: usageRoot
1543
1945
  },
1544
1946
  {
@@ -1546,7 +1948,8 @@ var COMMAND_SPECS = [
1546
1948
  description: "Image commands",
1547
1949
  subcommands: [
1548
1950
  { name: "generate", description: "Generate image from text", handler: mediaHandlers.handleImageGenerate },
1549
- { name: "edit", description: "Edit image(s) with prompt", handler: mediaHandlers.handleImageEdit }
1951
+ { name: "edit", description: "Edit image(s) with prompt", handler: mediaHandlers.handleImageEdit },
1952
+ { name: "upscale", description: "Upscale image to higher resolution", handler: mediaHandlers.handleImageUpscale }
1550
1953
  ],
1551
1954
  usage: usageRoot
1552
1955
  },
@@ -1567,7 +1970,8 @@ var COMMAND_SPECS = [
1567
1970
  description: "Task commands",
1568
1971
  subcommands: [
1569
1972
  { name: "get", description: "Get task status", handler: queryHandlers.handleTaskGet },
1570
- { name: "wait", description: "Wait until task completion", handler: queryHandlers.handleTaskWait }
1973
+ { name: "wait", description: "Wait until task completion", handler: queryHandlers.handleTaskWait },
1974
+ { name: "list", description: "List task history", handler: queryHandlers.handleTaskList }
1571
1975
  ],
1572
1976
  usage: usageRoot
1573
1977
  },
@@ -1629,7 +2033,7 @@ async function run() {
1629
2033
  failWithUsage(`Unknown command: ${[command, subcommand].filter(Boolean).join(" ")}`, usageRoot());
1630
2034
  }
1631
2035
  if (spec.handler) {
1632
- await spec.handler(rest);
2036
+ await spec.handler(subcommand ? [subcommand, ...rest] : rest);
1633
2037
  return;
1634
2038
  }
1635
2039
  if (spec.subcommands) {