@yandy0725/mediagen-cli 0.1.1 → 0.3.0
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/README.md +28 -6
- package/README.zh-CN.md +25 -5
- package/dist/cli.js +163 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ Node >= 18, two runtime dependencies, a DashScope and/or Ark API key.
|
|
|
39
39
|
| `mediagen login` / `logout` / `auth status` | store, remove, inspect API keys |
|
|
40
40
|
| `mediagen config show` / `config path` | resolved config, and where it lives |
|
|
41
41
|
| `mediagen config add <name>` | create a provider in config.toml; everything else is a manual edit |
|
|
42
|
+
| `mediagen skill install --dir <path>` | copy the packaged agent skill into `<path>/mediagen/`; re-run to replace it |
|
|
42
43
|
|
|
43
44
|
`mediagen image generate --help` and `mediagen video generate --help` are the
|
|
44
45
|
reference: every flag, the model table, the size reference, examples. Everything below
|
|
@@ -122,15 +123,30 @@ code `TaskNotFinished`, and `video status <id>` resumes it without paying again.
|
|
|
122
123
|
## Configuration
|
|
123
124
|
|
|
124
125
|
`config.toml` — the file you edit — lives in the first of
|
|
125
|
-
`$MEDIAGEN_CLI_CONFIG_DIR`, `$XDG_CONFIG_HOME/mediagen
|
|
126
|
+
`$MEDIAGEN_CLI_CONFIG_DIR`, `$XDG_CONFIG_HOME/mediagen`, `~/.config/mediagen`.
|
|
126
127
|
|
|
127
128
|
`auth.json` (mode `600`) is **state, not config**, and lives apart from it: the first
|
|
128
|
-
of `$MEDIAGEN_CLI_STATE_DIR`, `$XDG_STATE_HOME/mediagen
|
|
129
|
-
`~/.local/state/mediagen
|
|
129
|
+
of `$MEDIAGEN_CLI_STATE_DIR`, `$XDG_STATE_HOME/mediagen`,
|
|
130
|
+
`~/.local/state/mediagen`. `~/.config` is the directory people sync, share and
|
|
130
131
|
commit; a key store there is one `.gitignore` mistake away from a leaked key. The
|
|
131
132
|
state directory is machine-local by definition, which is what a device-bound
|
|
132
133
|
credential is.
|
|
133
134
|
|
|
135
|
+
Upgrading from 0.1.1 or earlier: both directories dropped the `-cli` suffix, and nothing
|
|
136
|
+
migrates them for you. Move the files, not the directories — a first run of this version
|
|
137
|
+
already creates the new directories, and `mv dir dir` would bury your config inside them:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
mkdir -p ~/.config/mediagen ~/.local/state/mediagen
|
|
141
|
+
mv ~/.config/mediagen-cli/config.toml ~/.config/mediagen/config.toml
|
|
142
|
+
mv ~/.local/state/mediagen-cli/auth.json ~/.local/state/mediagen/auth.json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
(With `XDG_CONFIG_HOME` or `XDG_STATE_HOME` set, the paths are `<that>/mediagen-cli` →
|
|
146
|
+
`<that>/mediagen` instead. If you set `MEDIAGEN_CLI_CONFIG_DIR` or
|
|
147
|
+
`MEDIAGEN_CLI_STATE_DIR`, the names are unchanged and there is nothing to move. Skip a
|
|
148
|
+
line whose `mediagen-cli` path does not exist — most users never had an `auth.json`.)
|
|
149
|
+
|
|
134
150
|
```toml
|
|
135
151
|
default_provider = "volcengine"
|
|
136
152
|
# output_dir = "./media-output"
|
|
@@ -238,9 +254,11 @@ download and 600 s per video download. For video that budget is the *whole* poll
|
|
|
238
254
|
window, not one request: each create or poll gets 60 s, so a hung request cannot eat
|
|
239
255
|
the budget, and the download has its own 600 s and is not counted against it.
|
|
240
256
|
|
|
241
|
-
Working from an agent? `
|
|
242
|
-
|
|
243
|
-
|
|
257
|
+
Working from an agent? `mediagen skill install --dir <skills dir>` puts
|
|
258
|
+
`skills/mediagen/SKILL.md` where your agent already looks — e.g. `--dir ~/.claude/skills` — and
|
|
259
|
+
re-running it after an upgrade replaces the old copy. The skill describes what this CLI does,
|
|
260
|
+
which tool to prefer when the project already generates media another way, and the rules that
|
|
261
|
+
keep a paid request from being wasted.
|
|
244
262
|
|
|
245
263
|
---
|
|
246
264
|
|
|
@@ -253,6 +271,10 @@ npm test # vitest, fully offline: fetch is stubbed everywhere
|
|
|
253
271
|
npm run dev -- image generate --help
|
|
254
272
|
```
|
|
255
273
|
|
|
274
|
+
Tests live in `tests/` — one file per module, imported from `../src/` — and never
|
|
275
|
+
beside the sources; `vitest.config.ts` picks up `tests/**/*.test.ts` and nothing else.
|
|
276
|
+
`tsc --noEmit` typechecks them too.
|
|
277
|
+
|
|
256
278
|
`src/cli.ts` wires commander and maps errors to exit codes; `src/generate.ts` runs
|
|
257
279
|
the pipeline (validate → resolve → call → download → report); `src/api/{qwen,ark,dashscope}.ts`
|
|
258
280
|
are the dialect adapters behind the `DialectAdapter` seam in `src/types.ts`, with
|
package/README.zh-CN.md
CHANGED
|
@@ -38,6 +38,7 @@ Node >= 18,两个运行时依赖,需要 DashScope 和/或方舟的 API key
|
|
|
38
38
|
| `mediagen login` / `logout` / `auth status` | 保存、删除、查看 API key |
|
|
39
39
|
| `mediagen config show` / `config path` | 解析后的配置,以及它存在哪里 |
|
|
40
40
|
| `mediagen config add <name>` | 在 config.toml 里新建一条 provider(其余修改手写文件) |
|
|
41
|
+
| `mediagen skill install --dir <path>` | 把自带的 agent 技能装到 `<path>/mediagen/`;重跑即覆盖 |
|
|
41
42
|
|
|
42
43
|
`mediagen image generate --help` 与 `mediagen video generate --help` 才是参考手册:
|
|
43
44
|
全部参数、模型表、尺寸表、示例。下面只是简化版。
|
|
@@ -110,15 +111,29 @@ stdout 上每行打印一个绝对路径;`--json` 打印一个对象:
|
|
|
110
111
|
## 配置
|
|
111
112
|
|
|
112
113
|
`config.toml` —— 你手动编辑的那个文件 —— 位于
|
|
113
|
-
`$MEDIAGEN_CLI_CONFIG_DIR`、`$XDG_CONFIG_HOME/mediagen
|
|
114
|
-
`~/.config/mediagen
|
|
114
|
+
`$MEDIAGEN_CLI_CONFIG_DIR`、`$XDG_CONFIG_HOME/mediagen`、
|
|
115
|
+
`~/.config/mediagen` 三者中第一个存在的。
|
|
115
116
|
|
|
116
117
|
`auth.json`(权限 `600`)属于**状态而非配置**,单独存放:取
|
|
117
|
-
`$MEDIAGEN_CLI_STATE_DIR`、`$XDG_STATE_HOME/mediagen
|
|
118
|
-
`~/.local/state/mediagen
|
|
118
|
+
`$MEDIAGEN_CLI_STATE_DIR`、`$XDG_STATE_HOME/mediagen`、
|
|
119
|
+
`~/.local/state/mediagen` 三者中第一个。`~/.config` 是人们会同步、分享、提交到
|
|
119
120
|
仓库的目录,把密钥放在那里,一次 `.gitignore` 疏忽就等于泄露。state 目录按定义是
|
|
120
121
|
「本机状态、不该被同步」,正合设备绑定的凭证。
|
|
121
122
|
|
|
123
|
+
从 0.1.1 及更早版本升级:两个目录都去掉了 `-cli` 后缀,程序不会替你迁移。挪文件,别挪目录
|
|
124
|
+
—— 新版本第一次运行就已经建好了新目录,`mv 目录 目录` 只会把你的配置埋进去:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
mkdir -p ~/.config/mediagen ~/.local/state/mediagen
|
|
128
|
+
mv ~/.config/mediagen-cli/config.toml ~/.config/mediagen/config.toml
|
|
129
|
+
mv ~/.local/state/mediagen-cli/auth.json ~/.local/state/mediagen/auth.json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
(设置了 `XDG_CONFIG_HOME` 或 `XDG_STATE_HOME` 的,路径相应改为 `<该目录>/mediagen-cli` →
|
|
133
|
+
`<该目录>/mediagen`。用了 `MEDIAGEN_CLI_CONFIG_DIR` 或 `MEDIAGEN_CLI_STATE_DIR` 的,变量名
|
|
134
|
+
没变,不需要动。`mediagen-cli` 路径不存在的那一行跳过就好 —— 大多数用户本来就没有
|
|
135
|
+
`auth.json`。)
|
|
136
|
+
|
|
122
137
|
```toml
|
|
123
138
|
default_provider = "volcengine"
|
|
124
139
|
# output_dir = "./media-output"
|
|
@@ -214,7 +229,9 @@ OpenAI 兼容端点(`compatible-mode`)只在业务空间专属域名上提
|
|
|
214
229
|
`timeout_seconds` 是**整个轮询窗口**,不是单次请求:建任务与每次轮询各自 60 秒,这样一个挂住
|
|
215
230
|
的请求吃不掉预算;下载另有 600 秒,不计入预算。
|
|
216
231
|
|
|
217
|
-
在 Agent 里用?`
|
|
232
|
+
在 Agent 里用?`mediagen skill install --dir <技能目录>` 会把这个包自带的
|
|
233
|
+
`skills/mediagen/SKILL.md` 装到你 agent 已经在读的位置(例如 `--dir ~/.claude/skills`),升级后
|
|
234
|
+
重跑一次即可覆盖旧版本。这份技能说明这个 CLI 能做什么、项目已有别的生成工具时该用哪个,
|
|
218
235
|
以及哪些规则能避免白花钱。
|
|
219
236
|
|
|
220
237
|
---
|
|
@@ -228,6 +245,9 @@ npm test # vitest,完全离线:所有地方的 fetch 都被打桩
|
|
|
228
245
|
npm run dev -- image generate --help
|
|
229
246
|
```
|
|
230
247
|
|
|
248
|
+
测试统一放在 `tests/`:一个模块一个文件,从 `../src/` 引入,不再与源码同目录;
|
|
249
|
+
`vitest.config.ts` 只收集 `tests/**/*.test.ts`,`tsc --noEmit` 也一并检查它们。
|
|
250
|
+
|
|
231
251
|
`src/cli.ts` 负责 commander 接线和错误到退出码的映射;`src/generate.ts` 是流水线
|
|
232
252
|
(校验 → 解析 → 调用 → 下载 → 汇报);`src/api/{qwen,ark,dashscope}.ts` 是
|
|
233
253
|
`src/types.ts` 中 `DialectAdapter` 接缝背后的方言适配器,周围是 `config.ts`、
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import
|
|
4
|
+
import fs7 from "fs";
|
|
5
|
+
import path7 from "path";
|
|
5
6
|
import readline from "readline";
|
|
6
7
|
import { Writable } from "stream";
|
|
7
|
-
import { fileURLToPath, pathToFileURL } from "url";
|
|
8
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
8
9
|
import { Command, CommanderError } from "commander";
|
|
9
10
|
import { parse as parseToml } from "smol-toml";
|
|
10
11
|
|
|
@@ -753,19 +754,20 @@ function getAdapter(apiType, task) {
|
|
|
753
754
|
}
|
|
754
755
|
|
|
755
756
|
// src/config.ts
|
|
757
|
+
var APP_DIR_NAME = "mediagen";
|
|
756
758
|
function configDir() {
|
|
757
759
|
const override = process.env.MEDIAGEN_CLI_CONFIG_DIR;
|
|
758
760
|
if (override) return override;
|
|
759
761
|
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
760
|
-
if (xdgConfigHome) return path2.join(xdgConfigHome,
|
|
761
|
-
return path2.join(os.homedir(), ".config",
|
|
762
|
+
if (xdgConfigHome) return path2.join(xdgConfigHome, APP_DIR_NAME);
|
|
763
|
+
return path2.join(os.homedir(), ".config", APP_DIR_NAME);
|
|
762
764
|
}
|
|
763
765
|
function stateDir() {
|
|
764
766
|
const override = process.env.MEDIAGEN_CLI_STATE_DIR;
|
|
765
767
|
if (override) return override;
|
|
766
768
|
const xdgStateHome = process.env.XDG_STATE_HOME;
|
|
767
|
-
if (xdgStateHome) return path2.join(xdgStateHome,
|
|
768
|
-
return path2.join(os.homedir(), ".local", "state",
|
|
769
|
+
if (xdgStateHome) return path2.join(xdgStateHome, APP_DIR_NAME);
|
|
770
|
+
return path2.join(os.homedir(), ".local", "state", APP_DIR_NAME);
|
|
769
771
|
}
|
|
770
772
|
function configPath() {
|
|
771
773
|
return path2.join(configDir(), "config.toml");
|
|
@@ -2352,6 +2354,130 @@ function describeError(err) {
|
|
|
2352
2354
|
return "unknown error";
|
|
2353
2355
|
}
|
|
2354
2356
|
|
|
2357
|
+
// src/skill.ts
|
|
2358
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2359
|
+
import fs6 from "fs";
|
|
2360
|
+
import os3 from "os";
|
|
2361
|
+
import path6 from "path";
|
|
2362
|
+
import { fileURLToPath } from "url";
|
|
2363
|
+
function installSkill(sourceDir, targetDir, deps = {}) {
|
|
2364
|
+
const root = path6.dirname(targetDir);
|
|
2365
|
+
fs6.mkdirSync(root, { recursive: true });
|
|
2366
|
+
const staging = stashPath(root, "install");
|
|
2367
|
+
const retired = stashPath(root, "retired");
|
|
2368
|
+
const cp = deps.cp ?? ((from, to) => fs6.cpSync(from, to, { recursive: true }));
|
|
2369
|
+
const rename = deps.rename ?? ((from, to) => fs6.renameSync(from, to));
|
|
2370
|
+
const existing = lstatOrUndefined(targetDir);
|
|
2371
|
+
if (existing !== void 0 && !existing.isDirectory()) {
|
|
2372
|
+
throw new UsageError(
|
|
2373
|
+
`${targetDir} exists and is not a directory: move it aside, then run the command again`
|
|
2374
|
+
);
|
|
2375
|
+
}
|
|
2376
|
+
let retiredPath;
|
|
2377
|
+
let replaced = false;
|
|
2378
|
+
try {
|
|
2379
|
+
cp(sourceDir, staging);
|
|
2380
|
+
if (existing !== void 0) {
|
|
2381
|
+
rename(targetDir, retired);
|
|
2382
|
+
retiredPath = retired;
|
|
2383
|
+
replaced = true;
|
|
2384
|
+
}
|
|
2385
|
+
rename(staging, targetDir);
|
|
2386
|
+
if (retiredPath !== void 0) {
|
|
2387
|
+
fs6.rmSync(retiredPath, { recursive: true, force: true });
|
|
2388
|
+
retiredPath = void 0;
|
|
2389
|
+
}
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
if (retiredPath !== void 0) {
|
|
2392
|
+
try {
|
|
2393
|
+
rename(retiredPath, targetDir);
|
|
2394
|
+
retiredPath = void 0;
|
|
2395
|
+
} catch {
|
|
2396
|
+
throw new Error(
|
|
2397
|
+
`${messageOf(error)}; the previous copy could not be put back and is at ${retiredPath}`
|
|
2398
|
+
);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
throw error;
|
|
2402
|
+
} finally {
|
|
2403
|
+
try {
|
|
2404
|
+
fs6.rmSync(staging, { recursive: true, force: true });
|
|
2405
|
+
} catch {
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
return replaced;
|
|
2409
|
+
}
|
|
2410
|
+
function lstatOrUndefined(target) {
|
|
2411
|
+
try {
|
|
2412
|
+
return fs6.lstatSync(target);
|
|
2413
|
+
} catch {
|
|
2414
|
+
return void 0;
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
function stashPath(root, kind) {
|
|
2418
|
+
return path6.join(root, `.mediagen-${kind}-${process.pid}-${randomUUID3().slice(0, 8)}`);
|
|
2419
|
+
}
|
|
2420
|
+
function messageOf(error) {
|
|
2421
|
+
return error instanceof Error ? error.message : String(error);
|
|
2422
|
+
}
|
|
2423
|
+
function bundledSkillsDir() {
|
|
2424
|
+
return fileURLToPath(new URL("../skills/", import.meta.url));
|
|
2425
|
+
}
|
|
2426
|
+
function bundledSkillNames(root = bundledSkillsDir()) {
|
|
2427
|
+
try {
|
|
2428
|
+
return fs6.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
2429
|
+
} catch (error) {
|
|
2430
|
+
throw new Error(
|
|
2431
|
+
`the skills this package ships were not found at ${root} (${messageOf(error)}); run mediagen from a normal npm install, where the skills/ directory ships beside dist/cli.js`
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
function installSkills(targetDir, deps = {}) {
|
|
2436
|
+
const sourceRoot = deps.sourceRoot ?? bundledSkillsDir();
|
|
2437
|
+
const names = bundledSkillNames(sourceRoot);
|
|
2438
|
+
if (names.length === 0) {
|
|
2439
|
+
throw new Error(
|
|
2440
|
+
`no skills were found in ${sourceRoot}: the package is incomplete, reinstall mediagen`
|
|
2441
|
+
);
|
|
2442
|
+
}
|
|
2443
|
+
const root = prepareTargetDir(targetDir);
|
|
2444
|
+
return names.map((name) => ({
|
|
2445
|
+
name,
|
|
2446
|
+
path: path6.join(root, name),
|
|
2447
|
+
replaced: installSkill(path6.join(sourceRoot, name), path6.join(root, name), deps)
|
|
2448
|
+
}));
|
|
2449
|
+
}
|
|
2450
|
+
function prepareTargetDir(value) {
|
|
2451
|
+
const trimmed = value.trim();
|
|
2452
|
+
if (trimmed === "") {
|
|
2453
|
+
throw new UsageError(
|
|
2454
|
+
'a destination is required: pass --dir <path>, e.g. --dir "~/.claude/skills"'
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2457
|
+
const expanded = trimmed === "~" || trimmed.startsWith("~/") ? path6.join(os3.homedir(), trimmed.slice(1)) : trimmed;
|
|
2458
|
+
const dir = path6.resolve(expanded);
|
|
2459
|
+
if (fs6.existsSync(dir) && !fs6.statSync(dir).isDirectory()) {
|
|
2460
|
+
throw new UsageError(
|
|
2461
|
+
`--dir ${dir} is a file, not a directory: name the directory that holds skills`
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
try {
|
|
2465
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
2466
|
+
} catch (error) {
|
|
2467
|
+
throw new UsageError(
|
|
2468
|
+
`cannot create the skills directory ${dir}: ${messageOf(error)}; check --dir`
|
|
2469
|
+
);
|
|
2470
|
+
}
|
|
2471
|
+
try {
|
|
2472
|
+
fs6.accessSync(dir, fs6.constants.W_OK);
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
throw new UsageError(
|
|
2475
|
+
`cannot write to the skills directory ${dir}: ${messageOf(error)}; check --dir`
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
return dir;
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2355
2481
|
// src/cli.ts
|
|
2356
2482
|
var ROOT_HELP = `
|
|
2357
2483
|
Built for AI agents: explicit flags, no interactive prompts, machine-readable output.
|
|
@@ -2374,6 +2500,7 @@ Subcommands:
|
|
|
2374
2500
|
config show the resolved configuration, with a source note for each value
|
|
2375
2501
|
config path where config.toml lives
|
|
2376
2502
|
config add create a provider in config.toml (edit the file for anything else)
|
|
2503
|
+
skill install copy the agent skill this package ships into a directory you name
|
|
2377
2504
|
video generate create and pay for a video task, then save the result
|
|
2378
2505
|
video status look at one task once, and save its video if it finished
|
|
2379
2506
|
|
|
@@ -2579,7 +2706,7 @@ function addVideoOptions(command) {
|
|
|
2579
2706
|
}
|
|
2580
2707
|
function packageVersion() {
|
|
2581
2708
|
try {
|
|
2582
|
-
const raw =
|
|
2709
|
+
const raw = fs7.readFileSync(new URL("../package.json", import.meta.url), "utf8");
|
|
2583
2710
|
const parsed = JSON.parse(raw);
|
|
2584
2711
|
return typeof parsed.version === "string" ? parsed.version : void 0;
|
|
2585
2712
|
} catch {
|
|
@@ -2619,6 +2746,10 @@ function createProgram(options = {}) {
|
|
|
2619
2746
|
config.command("add <name>").description("Create a [providers.<name>] table in config.toml").option("--api-type <dialect>", "protocol dialect: openai, dashscope, ark or minimax").option("--base-url <url>", "the provider's origin, e.g. https://ark.cn-beijing.volces.com").option("--task <image|video>", "which task table to create (default: image)").option("--model <id>", "the model id this task defaults to").option("--api-key-env <VAR>", "environment variable holding this provider's key").option("--json", "print a JSON result").action(async (name, flags) => {
|
|
2620
2747
|
runConfigAdd(name, flags);
|
|
2621
2748
|
});
|
|
2749
|
+
const skill = program.command("skill").description("Install the agent skill this package ships");
|
|
2750
|
+
skill.command("install").description("Copy the skill into a directory an agent reads skills from, replacing an older copy").option("--dir <path>", "the skills directory, e.g. ~/.claude/skills (the skill lands in <path>/mediagen)").option("--json", "print a JSON result").action(async (flags) => {
|
|
2751
|
+
runSkillInstall(flags);
|
|
2752
|
+
});
|
|
2622
2753
|
const image = program.command("image").description("Generate and edit images");
|
|
2623
2754
|
const generate = image.command("generate").description("Generate an image from a prompt; add --image to edit").argument("<prompt>", "what to draw; quote it and describe the subject, style and text");
|
|
2624
2755
|
addImageOptions(generate).addHelpText("after", imageHelpText("generate"));
|
|
@@ -2941,6 +3072,23 @@ function runConfigPath(flags) {
|
|
|
2941
3072
|
}
|
|
2942
3073
|
writeLine(configPath());
|
|
2943
3074
|
}
|
|
3075
|
+
function runSkillInstall(flags) {
|
|
3076
|
+
const installed = installSkills(flags.dir ?? "");
|
|
3077
|
+
const container = path7.dirname(installed[0].path);
|
|
3078
|
+
if (fs7.existsSync(path7.join(container, "SKILL.md"))) {
|
|
3079
|
+
writeErrLine(
|
|
3080
|
+
`note: ${container} already holds a SKILL.md; --dir names the directory that contains skills, so the skill landed in ${installed[0].path}`
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
if (flags.json === true) {
|
|
3084
|
+
writeLine(JSON.stringify({ skills: installed }));
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
3087
|
+
for (const entry of installed) {
|
|
3088
|
+
if (entry.replaced) writeErrLine(`replaced the existing copy at ${entry.path}`);
|
|
3089
|
+
}
|
|
3090
|
+
for (const entry of installed) writeLine(entry.path);
|
|
3091
|
+
}
|
|
2944
3092
|
function runConfigAdd(name, flags) {
|
|
2945
3093
|
const provider = name.trim();
|
|
2946
3094
|
if (provider === "") throw new UsageError("a provider name is required");
|
|
@@ -2974,7 +3122,7 @@ function runConfigAdd(name, flags) {
|
|
|
2974
3122
|
base_url: originBaseUrl(flags.baseUrl)
|
|
2975
3123
|
};
|
|
2976
3124
|
if (apiKeyEnv !== void 0) fields.api_key_env = apiKeyEnv;
|
|
2977
|
-
const before =
|
|
3125
|
+
const before = fs7.existsSync(file) ? fs7.readFileSync(file, "utf8") : "";
|
|
2978
3126
|
const beforeTable = before.trim() === "" ? void 0 : parseToml(before);
|
|
2979
3127
|
if (apiType === "qwen") {
|
|
2980
3128
|
writeErrLine('note: api_type "qwen" is a deprecated alias; writing api_type = "openai"');
|
|
@@ -3018,8 +3166,8 @@ function runConfigAdd(name, flags) {
|
|
|
3018
3166
|
function runConfigShow(flags) {
|
|
3019
3167
|
const config = loadConfig();
|
|
3020
3168
|
const file = configPath();
|
|
3021
|
-
const exists =
|
|
3022
|
-
const declared = exists ? parseToml(
|
|
3169
|
+
const exists = fs7.existsSync(file);
|
|
3170
|
+
const declared = exists ? parseToml(fs7.readFileSync(file, "utf8")) : {};
|
|
3023
3171
|
const defaultProvider = configuredValue(
|
|
3024
3172
|
config.default_provider,
|
|
3025
3173
|
declared.default_provider
|
|
@@ -3156,17 +3304,17 @@ function buildProviderView(name, entry) {
|
|
|
3156
3304
|
if (block.path !== void 0) {
|
|
3157
3305
|
taskView.path = { value: block.path, source: "config.toml" };
|
|
3158
3306
|
}
|
|
3159
|
-
const
|
|
3307
|
+
const path8 = block.path ?? (effectiveDialect === void 0 ? void 0 : adapterPath(effectiveDialect, task));
|
|
3160
3308
|
if (effectiveDialect === void 0) {
|
|
3161
3309
|
taskView.endpoint_note = "api_type is not set";
|
|
3162
|
-
} else if (
|
|
3310
|
+
} else if (path8 === void 0) {
|
|
3163
3311
|
taskView.endpoint_note = `the ${effectiveDialect} dialect has no ${task} task`;
|
|
3164
3312
|
} else if (base === void 0) {
|
|
3165
3313
|
taskView.endpoint_note = "base_url is not a URL yet";
|
|
3166
3314
|
} else if (block.path === void 0 && vendorPathIssue(base) !== void 0) {
|
|
3167
3315
|
taskView.endpoint_note = "base_url carries a path; declare path = ... or use the origin";
|
|
3168
3316
|
} else {
|
|
3169
|
-
taskView.endpoint = `${base}${
|
|
3317
|
+
taskView.endpoint = `${base}${path8}`;
|
|
3170
3318
|
}
|
|
3171
3319
|
return taskView;
|
|
3172
3320
|
}
|
|
@@ -3282,14 +3430,14 @@ function sameInstant(before, after) {
|
|
|
3282
3430
|
function realHref(modulePath) {
|
|
3283
3431
|
if (modulePath === void 0) return void 0;
|
|
3284
3432
|
try {
|
|
3285
|
-
return pathToFileURL(
|
|
3433
|
+
return pathToFileURL(fs7.realpathSync(modulePath)).href;
|
|
3286
3434
|
} catch {
|
|
3287
3435
|
return void 0;
|
|
3288
3436
|
}
|
|
3289
3437
|
}
|
|
3290
3438
|
function selfHref(moduleUrl) {
|
|
3291
3439
|
try {
|
|
3292
|
-
return realHref(
|
|
3440
|
+
return realHref(fileURLToPath2(moduleUrl));
|
|
3293
3441
|
} catch {
|
|
3294
3442
|
return void 0;
|
|
3295
3443
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yandy0725/mediagen-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Non-interactive image generation and editing, plus short video generation, for agents: qwen-image, doubao/seedream, doubao/seedance, MiniMax H3, files on disk, JSON output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|