zerocut-cli 0.3.2 → 0.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zerocut-cli",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "ZeroCut CLI: AI assistant CLI for creating and editing images/audio/video",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,6 +14,30 @@ async function ask(question: string, defaults?: string): Promise<string> {
14
14
  return trimmed.length > 0 ? trimmed : (defaults ?? "");
15
15
  }
16
16
 
17
+ async function exchangeOttAndSave(ott: string, region: "cn" | "us"): Promise<void> {
18
+ const base = region === "cn" ? "https://api2.zerocut.cn" : "https://api2.zerocut.art";
19
+ const resp = await fetch(`${base}/api/open/ott/exchange`, {
20
+ method: "POST",
21
+ headers: { "content-type": "application/json" },
22
+ body: JSON.stringify({ ott }),
23
+ });
24
+ if (!resp.ok) {
25
+ process.stderr.write(`OTT exchange failed: HTTP ${resp.status}\n`);
26
+ process.exitCode = 1;
27
+ return;
28
+ }
29
+ const json = (await resp.json()) as { data?: { apiKey?: string } };
30
+ const apiKey = json?.data?.apiKey;
31
+ if (typeof apiKey !== "string" || apiKey.length === 0) {
32
+ process.stderr.write("OTT exchange failed: missing data.apiKey in response\n");
33
+ process.exitCode = 1;
34
+ return;
35
+ }
36
+ setConfigValueSync("apiKey", apiKey);
37
+ setConfigValueSync("region", region);
38
+ process.stdout.write("apiKey set via OTT\n");
39
+ }
40
+
17
41
  export function register(program: Command): void {
18
42
  const parent = program
19
43
  .command("config")
@@ -21,36 +45,36 @@ export function register(program: Command): void {
21
45
  .option("--ott <token>", "One-Time Token (OTT) for fetching API key")
22
46
  .option("--region <region>", "Region for OTT exchange: cn|us")
23
47
  .action(async function (this: Command, opts: { ott?: string; region?: string }) {
24
- const ott = typeof opts.ott === "string" ? opts.ott.trim() : "";
25
- const region = typeof opts.region === "string" ? opts.region.trim().toLowerCase() : "";
26
- if (!ott) return; // no quick params; fall through to subcommands normally
27
- if (region !== "cn" && region !== "us") {
28
- process.stderr.write("Invalid or missing --region. Allowed: cn|us\n");
48
+ const cfg = readConfigSync() as { region?: unknown };
49
+ const regionInput = typeof opts.region === "string" ? opts.region.trim().toLowerCase() : "";
50
+ let region: "cn" | "us";
51
+ if (regionInput === "cn" || regionInput === "us") {
52
+ region = regionInput;
53
+ } else if (regionInput.length > 0) {
54
+ process.stderr.write("Invalid --region. Allowed: cn|us\n");
29
55
  process.exitCode = 1;
30
56
  return;
31
- }
32
- try {
33
- const base = region === "cn" ? "https://api2.zerocut.cn" : "https://api2.zerocut.art";
34
- const resp = await fetch(`${base}/api/open/ott/exchange`, {
35
- method: "POST",
36
- headers: { "content-type": "application/json" },
37
- body: JSON.stringify({ ott }),
38
- });
39
- if (!resp.ok) {
40
- process.stderr.write(`OTT exchange failed: HTTP ${resp.status}\n`);
57
+ } else {
58
+ const defaultRegion = cfg.region === "cn" || cfg.region === "us" ? cfg.region : "us";
59
+ const regionAnswer = (await ask("Choose region (cn/us)", defaultRegion))
60
+ .trim()
61
+ .toLowerCase();
62
+ if (regionAnswer !== "cn" && regionAnswer !== "us") {
63
+ process.stderr.write("Invalid region. Allowed: cn|us\n");
41
64
  process.exitCode = 1;
42
65
  return;
43
66
  }
44
- const json = (await resp.json()) as { data?: { apiKey?: string } };
45
- const apiKey = json?.data?.apiKey;
46
- if (typeof apiKey !== "string" || apiKey.length === 0) {
47
- process.stderr.write("OTT exchange failed: missing data.apiKey in response\n");
48
- process.exitCode = 1;
49
- return;
50
- }
51
- setConfigValueSync("apiKey", apiKey);
52
- setConfigValueSync("region", region);
53
- process.stdout.write("apiKey set via OTT\n");
67
+ region = regionAnswer;
68
+ }
69
+ const ott = typeof opts.ott === "string" ? opts.ott.trim() : "";
70
+ const ottValue = ott.length > 0 ? ott : (await ask("Enter One-Time Token (OTT)")).trim();
71
+ if (!ottValue) {
72
+ process.stderr.write("OTT is required\n");
73
+ process.exitCode = 1;
74
+ return;
75
+ }
76
+ try {
77
+ await exchangeOttAndSave(ottValue, region);
54
78
  } catch (err) {
55
79
  process.stderr.write(`OTT exchange failed: ${(err as Error).message}\n`);
56
80
  process.exitCode = 1;
@@ -82,32 +106,7 @@ export function register(program: Command): void {
82
106
  return;
83
107
  }
84
108
  try {
85
- const base = region === "cn" ? "https://api2.zerocut.cn" : "https://api2.zerocut.art";
86
- const resp = await fetch(`${base}/api/open/ott/exchange`, {
87
- method: "POST",
88
- headers: {
89
- "content-type": "application/json",
90
- },
91
- body: JSON.stringify({ ott }),
92
- });
93
- if (!resp.ok) {
94
- process.stderr.write(`OTT exchange failed: HTTP ${resp.status}\n`);
95
- process.exitCode = 1;
96
- return;
97
- }
98
- const json = (await resp.json()) as {
99
- data?: { apiKey?: string };
100
- [k: string]: unknown;
101
- };
102
- const apiKey = json?.data?.apiKey;
103
- if (typeof apiKey !== "string" || apiKey.length === 0) {
104
- process.stderr.write("OTT exchange failed: missing data.apiKey in response\n");
105
- process.exitCode = 1;
106
- return;
107
- }
108
- setConfigValueSync("apiKey", apiKey);
109
- setConfigValueSync("region", region);
110
- process.stdout.write("apiKey set via OTT\n");
109
+ await exchangeOttAndSave(ott, region);
111
110
  } catch (err) {
112
111
  process.stderr.write(`OTT exchange failed: ${(err as Error).message}\n`);
113
112
  process.exitCode = 1;
@@ -1,5 +1,6 @@
1
1
  import type { Command } from "commander";
2
2
  import fs from "node:fs";
3
+ import os from "node:os";
3
4
  import path from "node:path";
4
5
 
5
6
  export const name = "skill";
@@ -15,7 +16,10 @@ function printSkill(relativePath: string): void {
15
16
  }
16
17
 
17
18
  export function register(program: Command): void {
18
- const parent = program.command("skill").description("Print built-in skill markdown");
19
+ const parent = program
20
+ .command("skill")
21
+ .description("Print built-in skill markdown")
22
+ .option("--claude", "Append Claude skill save hint");
19
23
 
20
24
  parent
21
25
  .command("one-click-video")
@@ -31,10 +35,13 @@ export function register(program: Command): void {
31
35
  printSkill("../skill/edit-video/SKILL.md");
32
36
  });
33
37
 
34
- parent.action(() => {
38
+ parent.action((opts: { claude?: boolean }) => {
35
39
  printSkill("../skill/SKILL.md");
36
- process.stdout.write(
37
- "\nPlease save the markdown above to zerocut/SKILL.md to create the skill.\n"
38
- );
40
+ if (opts.claude) {
41
+ const savePath = path.join(os.homedir(), ".claude", "skills", "zerocut", "SKILL.md");
42
+ process.stdout.write(
43
+ `\n⚠️ Please save the markdown above to ${savePath} to create the skill.\n`
44
+ );
45
+ }
39
46
  });
40
47
  }
@@ -10,6 +10,7 @@ import { register as registerPandoc } from "../commands/pandoc";
10
10
  import { register as registerSkill } from "../commands/skill";
11
11
  import fs from "node:fs";
12
12
  import path from "node:path";
13
+ import { pathToFileURL } from "node:url";
13
14
 
14
15
  export function loadBuiltInCommands(program: Command): void {
15
16
  registerHelp(program);
@@ -31,7 +32,8 @@ export async function loadExternalCommandsAsync(program: Command, dir?: string):
31
32
  for (const f of files) {
32
33
  const full = path.join(d, f);
33
34
  try {
34
- const mod = (await import(full)) as {
35
+ const moduleSpecifier = pathToFileURL(full).href;
36
+ const mod = (await import(moduleSpecifier)) as {
35
37
  register?: (p: Command) => void;
36
38
  default?: (p: Command) => void;
37
39
  };