feat: cli 更新检查与skill数据同步

This commit is contained in:
tiehongji
2026-06-30 21:44:14 +08:00
parent 55bd51b669
commit c263822e7c
27 changed files with 1271 additions and 177 deletions
+28 -7
View File
@@ -14,11 +14,16 @@
## ✨ What it can do
| Capability | Runs on | Status |
| ----------------------------------------------------------------------------------------------------------------------------------- | ------------------ | -------------- |
| **AI** — video enhancement · subtitle removal | cloud | ✅ Available |
| **Editing** (11) — trim · concat · watermark · subtitle · speed · flip · extract / mux audio · image-to-video | cloud **or** local | ✅ Available |
| **More AI workflows** — video understanding · translation · highlight clipping · script extraction · commentary · anime restyling … | cloud | 🚧 Rolling out |
**40+ capabilities across 5 domains** — run `mediakit-cli --help-full` to list them all.
| Domain | Capabilities | Runs on | Status |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ------------ |
| 🎬 **Editing** (17) | trim · concat · watermark · subtitle · speed · volume · filter · flip · fade · mix · mux · extract audio · image-to-video | cloud **or** local | ✅ Available |
| 🎚️ **Audio** (2) | voice / background separation · audio metadata probe | cloud | ✅ Available |
| 🖼️ **Image AI** (5) | enhance · object / text erase · quality scoring · OCR · background removal | cloud | ✅ Available |
| 🎥 **Video AI** (14) | enhancement (+ generative restore) · subtitle removal · ASR subtitles · OCR · highlight clipping (short-drama / mini-game) · storyline analysis · scene split · portrait & green-screen matting · metadata probe | cloud | ✅ Available |
| 🔧 **Shared** (2) | async task query · remote-file fetch | local / cloud | ✅ Available |
| 🚧 **Coming** | video translation · commentary generation · anime restyling | cloud | Rolling out |
> AI capabilities run in the cloud (elastic compute, async). Editing runs **either** in the cloud **or** locally (sync, zero cost) — pick per command with `--cloud` / `--local`.
@@ -44,7 +49,10 @@ mediakit-cli --local editing trim-video --video-url ./in.mp4 --start-time 3 --en
## 📦 Install
```bash
# npm (recommended, cross-platform — pulls the right build for your OS / arch)
# One-click install (CLI + AI agent Skills)
npx @volcengine/mediakit-cli install -y
# npm only (CLI, recommended, cross-platform — pulls the right build for your OS / arch)
npm install -g @volcengine/mediakit-cli
# npx (no install)
@@ -58,6 +66,18 @@ Pin a version or path: `VERSION=<version> INSTALL_DIR="$HOME/.local/bin" curl -f
Verify: `mediakit-cli doctor` (checks cloud readiness + local tool deps + install hints).
### Update
The CLI checks the npm registry for new releases once a day (TTL 24h). When an update is available you'll see a hint in `stderr` and a `_notice.update` field in the stdout JSON.
```bash
mediakit-cli version --check # report current vs latest as JSON
mediakit-cli update --check # check only, no install
mediakit-cli update # install the latest via `npm install -g`
```
Suppress the check with `MEDIAKIT_DISABLE_UPDATE_CHECK=1` or in CI (`CI` env set).
---
## 🤖 Use with AI Agents
@@ -78,7 +98,7 @@ Every capability is also **MCP-compatible** — `mediakit-cli <domain> <tool> --
## 🧩 How it works
- **Two modes, one command surface.** `--cloud` runs heavy AI in Volcengine's cloud (elastic compute, async `task_id`); `--local` runs deterministic editing locally (sync, zero cloud cost). Default mode is `cloud-first`; per-command flags override it.
- **Command structure:** `mediakit-cli [--cloud|--local] <domain> <tool> [flags]` — domains are `editing` · `video` · `shared`.
- **Command structure:** `mediakit-cli [--cloud|--local] <domain> <tool> [flags]` — domains are `editing` · `audio` · `image` · `video` · `shared`.
- **Outputs:** cloud results are returned as URLs; local results write to `~/.mediakit/temp` (override with `--output-path` or `MEDIAKIT_OUTPUT_PATH`).
---
@@ -87,6 +107,7 @@ Every capability is also **MCP-compatible** — `mediakit-cli <domain> <tool> --
- Volcengine AI MediaKit product docs & pricing: https://www.volcengine.com/docs/6448
- Full command reference & FAQ: see the docs site.
- [Error codes & exit code contract](./docs/error-codes.md) — stdout JSON protocol and exit-code rules.
---
+29 -8
View File
@@ -14,11 +14,16 @@
## ✨ 能做什么
| 能力 | 运行 | 状态 |
| ------------------------------------------------------------------------------------------------- | ---------------- | ----------- |
| **AI 能力** — 画质增强 · 字幕擦除 | 云端 | ✅ 已上线 |
| **剪辑能力**11 个)— 裁剪 · 拼接 · 加水印 · 加字幕 · 调速 · 翻转 · 提取 / 合成音频 · 图片转视频 | 云端 **或** 本地 | ✅ 已上线 |
| **更多 AI 工作流** — 视频理解 · 视频翻译 · 高光智剪 · 剧本还原 · 解说 · 漫剧转绘 … | 云端 | 🚧 陆续上线 |
**5 大领域、40+ 能力** —— 运行 `mediakit-cli --help-full` 可列全。
| 领域 | 能力 | 运行 | 状态 |
| --- | --- | --- | --- |
| 🎬 **剪辑** (17) | 裁剪 · 拼接 · 加水印 · 加字幕 · 调速 · 调音量 · 滤镜 · 翻转 · 淡入淡出 · 混音 · 合成 · 提取音频 · 图片转视频 | 云端 **或** 本地 | ✅ 已上线 |
| 🎚️ **音频** (2) | 人声 / 背景音分离 · 音频元信息探测 | 云端 | ✅ 已上线 |
| 🖼️ **图像 AI** (5) | 画质增强 · 擦除修复 · 画质评分 · OCR · 背景移除 | 云端 | ✅ 已上线 |
| 🎥 **视频 AI** (14) | 画质增强(含生成式修复)· 字幕擦除 · ASR 字幕 · OCR · 高光智剪(短剧 / 小游戏)· 剧情线分析 · 场景切分 · 人像 & 绿幕抠图 · 元信息探测 | 云端 | ✅ 已上线 |
| 🔧 **通用** (2) | 异步任务查询 · 远程文件拉取 | 本地 / 云端 | ✅ 已上线 |
| 🚧 **即将上线** | 视频翻译 · 解说生成 · 漫剧转绘 | 云端 | 陆续上线 |
> AI 能力跑在云端(弹性算力、异步);剪辑能力**云端或本地**皆可(本地跑,同步、零成本)—— 每条命令用 `--cloud` / `--local` 选。
@@ -44,7 +49,10 @@ mediakit-cli --local editing trim-video --video-url ./in.mp4 --start-time 3 --en
## 📦 安装
```bash
# npm推荐跨平台——自动拉取对应平台 / 架构的构建产物
# 一键安装CLI + AI Agent Skill
npx @volcengine/mediakit-cli install -y
# 仅装 CLI推荐跨平台——自动拉取对应平台 / 架构的构建产物)
npm install -g @volcengine/mediakit-cli
# npx免安装
@@ -58,11 +66,23 @@ curl -fsSL https://raw.githubusercontent.com/volcengine/mediakit-cli/main/script
验证环境:`mediakit-cli doctor`(检查云端就绪 + 本地工具依赖 + 安装指引)。
### 更新
CLI 每天会向 npm registry 检查一次新版本TTL 24h。有更新时`stderr` 会出现提示stdout JSON 会带上 `_notice.update` 字段。
```bash
mediakit-cli version --check # 以 JSON 输出当前版本 vs 最新版本
mediakit-cli update --check # 只检查,不安装
mediakit-cli update # 通过 `npm install -g` 安装最新版
```
如需关闭自动检查,设置 `MEDIAKIT_DISABLE_UPDATE_CHECK=1`,或在 CI 中运行(`CI` 环境变量被设置时也会自动抑制)。
---
## 🤖 配合 AI Agent 使用
`mediakit-cli` 自带 **AI Agent Skill**——教 Agent 怎么调它。于是用户只需说一句*把这个视频增强到 1080p再剪出最精彩的 5 秒*Agent 就能自动编排命令。
`mediakit-cli` 自带 **AI Agent Skill**——教 Agent 怎么调它。于是用户只需说一句*"把这个视频增强到 1080p再剪出最精彩的 5 秒"*Agent 就能自动编排命令。
```bash
# 一个命令把 Skill 装进本机所有支持的 Agent
@@ -78,7 +98,7 @@ npx skills add volcengine/mediakit-cli -g -y
## 🧩 工作原理
- **两种模式,同一套命令。** `--cloud` 把重算力 AI 跑在火山引擎云端(弹性算力、异步 `task_id``--local` 在本机跑确定性剪辑(同步、零云端成本)。默认 `cloud-first`,单命令 flag 可覆盖。
- **命令结构:** `mediakit-cli [--cloud|--local] <domain> <tool> [flags]`——domain 为 `editing` · `video` · `shared`
- **命令结构:** `mediakit-cli [--cloud|--local] <domain> <tool> [flags]`——domain 为 `editing` · `audio` · `image` · `video` · `shared`
- **输出:** 云端结果以 URL 返回;本地结果写到 `~/.mediakit/temp`(可用 `--output-path``MEDIAKIT_OUTPUT_PATH` 覆盖)。
---
@@ -87,6 +107,7 @@ npx skills add volcengine/mediakit-cli -g -y
- Volcengine AI MediaKit 产品文档 & 定价https://www.volcengine.com/docs/6448
- 完整命令参考 & FAQ见文档站。
- [错误码与退出码契约](./docs/error-codes.md) —— stdout JSON 协议与退出码规则。
---
+14 -7
View File
@@ -1,15 +1,22 @@
package main
import (
"fmt"
"os"
"errors"
"fmt"
"os"
"mediakit-cli/internal/commands"
"mediakit-cli/internal/cliexit"
"mediakit-cli/internal/commands"
)
func main() {
if err := commands.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
err := commands.Execute()
if err == nil {
return
}
// 业务失败:结构化错误已写入 stdout JSONstderr 不重复打印
if !errors.Is(err, cliexit.ErrBusinessFailure) {
fmt.Fprintln(os.Stderr, err)
}
os.Exit(1)
}
+118
View File
@@ -0,0 +1,118 @@
# mediakit-cli 错误码与退出码契约
本文档描述 `mediakit-cli` 在 cloud / local 两种执行路径下的错误协议、字段含义与进程退出码exit code规则作为 SDK / Skill / Plugin 等上层调用方解析的稳定契约。
## 一、退出码总览
| 退出码 | 含义 | 触发场景 |
| ------ | ------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `0` | 命令成功 | 所有路径成功完成;同步成功 / 异步提交成功(拿到 task_id/ query-task 终态为 `completed` |
| `1` | 业务失败或框架/参数错误 | 任意 stdout JSON 出现 `success: false`cloud`error` 字段local`status: failed/canceled/cancelled`query-task 终态),或参数解析失败 |
> `mediakit-cli` 不再使用退出码区分"框架错误"和"业务错误";上层调用方应**优先解析 stdout JSON**,把退出码当作辅助信号。
## 二、cloud 路径(远程 API
### 2.1 输出落点
- 业务结果与业务错误stdout一个 JSON 对象pretty 缩进)
- stderr保留给 update notice、调试日志**不再写业务错误**
### 2.2 成功响应(同步 / 异步提交)
异步主任务提交成功时HTTP 2xx + `success=true`CLI 直接透传:
```json
{
"success": true,
"task_id": "task_xxx",
"request_id": "req_xxx",
"result": { "...": "..." }
}
```
退出码:`0`
### 2.3 业务失败响应
任意以下情形都视为业务失败CLI 写完 JSON 后退出码 `1`
| 情形 | 判定字段 |
| ------------------------------------------------- | -------------------------------------------------------------- |
| 主调用 HTTP 2xx 但 `success=false` | `success == false` |
| 主调用 HTTP ≥ 400 网络/服务错误 | `errorResponse(...)` 统一写 `success: false` + `error: {...}` |
| `query-task` 终态为 `failed` / `canceled` / `cancelled` | `status` 字段(注意 queryTaskResponse 没有 `success` 字段) |
业务失败 JSON 结构:
```json
{
"success": false,
"error": {
"code": "InvalidParameter",
"message": "video_url is required"
},
"task_id": "task_xxx",
"request_id": "req_xxx"
}
```
### 2.4 query-task 终态字段
`mediakit-cli shared query-task` 返回的 `status` 取值:
| status | 含义 | 退出码 |
| ----------- | -------------------------- | ------ |
| `running` | 进行中(非终态) | `0` |
| `queued` | 排队中(非终态) | `0` |
| `completed` | 已完成(终态,成功) | `0` |
| `failed` | 已失败(终态,业务失败) | `1` |
| `canceled` | 已取消(终态,业务失败) | `1` |
| `cancelled` | 同 `canceled`(兼容拼写) | `1` |
只有 `failed/canceled/cancelled` 三个状态被识别为"业务失败终态",退出码为 `1`;其他状态退出码 `0`
## 三、local 路径(本地执行)
### 3.1 输出落点
- 成功结果stdout平铺业务字段与 swagger response schema 对齐,详见 Rule 23
- 业务失败stdout 写 `{"error": {...}}` 结构化错误
- stderr保留给 update notice、调试日志
### 3.2 业务失败响应
```json
{
"error": {
"type": "invalid_parameter | security_violation | environment_error | execution_error",
"code": "missing_required_param | invalid_param_type | param_out_of_range | param_insufficient | unsupported_value | forbidden_operation | not_whitelisted | unsafe_characters | handler_not_implemented | local_unsupported | dependency_missing | execution_failed | download_failed | unknown",
"message": "<原始错误信息(不截断、不摘要)>"
}
}
```
退出码:`1`
`type``code` 取值范围见 `AGENTS.md` Rule 25。
## 四、上层调用方解析建议
1. 优先解析 stdout JSON
- cloud`success === false` 即业务失败;终态 `status``failed/canceled/cancelled` 也视为失败
- local存在非空 `error` 字段即业务失败
2. 退出码作为辅助信号:`exit code === 0` 表示无业务失败信号,`exit code === 1` 表示存在业务失败或框架/参数错误
3. stderr 不再承载业务错误内容;只用来读取 update notice 与调试日志
## 五、Boolean flag 传参
所有 boolean 类型 flag 必须使用以下三种形式之一:
- 裸 flag`--enable-foo`(等价于 `--enable-foo=true`
- 显式赋值:`--enable-foo=true` / `--enable-foo=false`
禁止使用空格分隔写法(`--enable-foo true`cobra 会把空格后的 token 视为 positional 参数。`mediakit-cli` 会在该 capability 含 boolean 参数时给出明确提示。
## 六、--schema 输出中的 Async 字段
每个 capability 命令的 `--schema` 输出在 description 中固定包含一行 `Async: 是/否`;异步 capability 额外打印 `轮询命令: mediakit-cli shared query-task --task-id <id> --poll-complete`,供上层 agent 识别"需要轮询拿终态结果"的语义。
+5 -5
View File
@@ -2,9 +2,9 @@ module mediakit-cli
go 1.22
require (
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.9
)
require github.com/spf13/cobra v1.10.2
require github.com/inconshreveable/mousetrap v1.1.0 // indirect
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.9 // indirect
)
+27
View File
@@ -0,0 +1,27 @@
// Package cliexit 定义 CLI 业务失败的哨兵错误。
//
// "业务失败" 在 CLI 视角下采用宽口径定义:本次命令未达成功状态。
// 涵盖三档:
// 1. 业务级失败 — cloud success=false / query-task 失败终态 / local error 字段
// 2. 框架/参数错误 — capability 参数校验失败、handler 未实现、依赖缺失
// 3. 透传错误 — 底层 modes/cloud/local 抛出的非 sentinel 错误经 writeCapabilityError 包装
//
// 三档统一映射到 exit=1但结构化错误已经写入 stdout JSON
// main.go 通过 errors.Is 识别此哨兵,跳过 stderr 重复打印。
//
// 详见 AGENTS.md Rule 32: CLI Exit Code Contract。
package cliexit
import "errors"
// ErrBusinessFailure 表示 CLI 本次命令未达成功状态。
//
// 由以下三个 writer 统一抛出:
// - internal/cloud/executor.go::writeJSONcloud 主路径与 query-task 终态)
// - internal/local/executor.go::writeJSONlocal 主路径)
// - internal/commands/registry.go::writeCapabilityError参数/依赖/兜底错误)
//
// cmd/mediakit/main.go 用 errors.Is 识别后:
// - 跳过 stderr 打印(错误结构已在 stdout JSON 中)
// - 进程退出码 os.Exit(1)
var ErrBusinessFailure = errors.New("mediakit-cli: business failure")
+65 -2
View File
@@ -9,6 +9,9 @@ import (
"time"
"github.com/spf13/cobra"
"mediakit-cli/internal/cliexit"
"mediakit-cli/internal/updatecheck"
)
// Executor is the unified cloud execution entry for generated capability handlers.
@@ -193,6 +196,11 @@ func syncToolResponse(result map[string]any) map[string]any {
}
output := map[string]any{}
// 透传后端 success 字段:提交成功返回 true。
// 失败路径在 formatCommandResponse 处已被 businessFailureResponse 截获,此处不会出现 success=false。
if success, ok := result["success"].(bool); ok {
output["success"] = success
}
if taskID := strings.TrimSpace(fmt.Sprint(result["task_id"])); taskID != "" && taskID != "<nil>" {
output["task_id"] = taskID
}
@@ -216,6 +224,10 @@ func asyncTaskResponse(result map[string]any) map[string]any {
output := map[string]any{
"task_id": "",
}
// 透传后端 success 字段:发起任务成功返回 true失败返回 false详细 error 参考 error 字段)。
if success, ok := result["success"].(bool); ok {
output["success"] = success
}
if taskID, ok := result["task_id"]; ok {
output["task_id"] = strings.TrimSpace(fmt.Sprint(taskID))
}
@@ -239,6 +251,14 @@ func queryTaskResponse(result map[string]any) map[string]any {
if status := strings.TrimSpace(fmt.Sprint(result["status"])); status != "" && status != "<nil>" {
output["status"] = status
}
// query-task 失败终态:注入 success: false统一交给 writeJSON 触发 sentinel。
// 仅在此处做语义判定,避免污染非 query-task 路径。
if isTerminalFailure(result) {
output["success"] = false
if errField, ok := result["error"]; ok && errField != nil {
output["error"] = errField
}
}
taskResult, ok := result["result"].(map[string]any)
if !ok {
return output
@@ -249,21 +269,64 @@ func queryTaskResponse(result map[string]any) map[string]any {
return output
}
// isTerminalTaskStatus 判断 query-task 是否处于终态。
//
// 协议 status 枚举running / queued / completed / failed / canceled。
// 终态 = completed / failed / canceledcancelled 拼写兼容)。
// 非终态 = running / queued。
func isTerminalTaskStatus(response map[string]any) bool {
status := strings.ToLower(strings.TrimSpace(fmt.Sprint(response["status"])))
switch status {
case "completed", "succeeded", "success", "failed", "cancelled", "canceled":
case "completed", "failed", "canceled", "cancelled":
return true
default:
return false
}
}
// isTerminalFailure 判断异步任务是否处于失败终态。
// 区别于 isTerminalTaskStatus后者还包含 completed 这一成功终态;
// 本函数仅匹配 failed / canceledcancelled 拼写兼容),用于驱动 ErrBusinessFailure sentinel。
func isTerminalFailure(payload map[string]any) bool {
status := strings.ToLower(strings.TrimSpace(fmt.Sprint(payload["status"])))
switch status {
case "failed", "canceled", "cancelled":
return true
default:
return false
}
}
// isSuccessFalse 判断 payload 中 success 字段是否显式为 false。
func isSuccessFalse(payload map[string]any) bool {
value, ok := payload["success"]
if !ok {
return false
}
if success, ok := value.(bool); ok {
return !success
}
return false
}
func writeJSON(writer io.Writer, value any) error {
if m, ok := value.(map[string]any); ok {
updatecheck.InjectNotice(m)
}
encoder := json.NewEncoder(writer)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
return encoder.Encode(value)
if err := encoder.Encode(value); err != nil {
return err
}
// 单信号判定cloud 所有失败路径HTTP 错误、success=false、query-task 失败终态)
// 都会在最终响应中显式写入 success=falsewriteJSON 只需识别该字段。
if m, ok := value.(map[string]any); ok {
if isSuccessFalse(m) {
return cliexit.ErrBusinessFailure
}
}
return nil
}
func cloneParams(params map[string]any) map[string]any {
+2 -2
View File
@@ -87,9 +87,9 @@ func (c *Client) newRequest(method string, path string, query map[string]any, bo
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-surface", resolveSurface(c.Surface))
req.Header.Set("x-runtime", resolveRuntime(c.Runtime))
req.Header.Set("X-Amk-Task-Source", "cli")
req.Header.Set("X-Amk-Task-Source", "cli")
if c.APIKey != "" {
req.Header.Set("Authorization", "Bearer "+c.APIKey)
}
return req, nil
}
}
+6 -2
View File
@@ -68,7 +68,9 @@ func newAPIError(statusCode int, payloadBytes []byte) error {
}
func errorResponse(err error, taskID string, requestID string) map[string]any {
output := map[string]any{}
output := map[string]any{
"success": false,
}
if apiErr, ok := err.(*APIError); ok {
if apiErr.Payload != nil {
@@ -115,7 +117,9 @@ func isBusinessFailure(payload map[string]any) bool {
// businessFailureResponse 把业务级失败的响应体直接透传输出。
func businessFailureResponse(payload map[string]any) map[string]any {
output := map[string]any{}
output := map[string]any{
"success": false,
}
if errField, ok := payload["error"]; ok && errField != nil {
output["error"] = errField
} else {
+81 -11
View File
@@ -12,9 +12,11 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"mediakit-cli/internal/cliexit"
cliconfig "mediakit-cli/internal/config"
"mediakit-cli/internal/local"
"mediakit-cli/internal/modes"
"mediakit-cli/internal/updatecheck"
)
type DomainMeta struct {
@@ -2647,7 +2649,7 @@ func newCapabilityCommand(meta CapabilityMeta) *cobra.Command {
Use: capabilityMeta.Name,
Short: capabilityMeta.Description,
Long: renderCapabilityHelp(capabilityMeta),
Args: cobra.NoArgs,
Args: capabilityArgsValidator(capabilityMeta),
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
// --schema: 输出工具 schema 后退出
@@ -2660,6 +2662,11 @@ func newCapabilityCommand(meta CapabilityMeta) *cobra.Command {
return err
}
if err := modes.Dispatch(cmd, capabilityMeta.runtimeMeta(), params); err != nil {
// 业务失败 sentinelJSON 已由底层 writer 写入 stdout
// 这里直接向上传递,避免再次写一份 capability error JSON。
if errors.Is(err, cliexit.ErrBusinessFailure) {
return err
}
return writeCapabilityError(cmd.OutOrStdout(), err)
}
return nil
@@ -2673,6 +2680,53 @@ func newCapabilityCommand(meta CapabilityMeta) *cobra.Command {
return cmd
}
// capabilityArgsValidator 为 capability 命令构建自定义 Args 校验器。
// 在拒绝位置参数(等价 cobra.NoArgs的同时识别 bool flag 误用模式
// (`--flag value` 而非 `--flag=value` 或裸 `--flag`),给出可操作提示。
func capabilityArgsValidator(meta CapabilityMeta) cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return nil
}
boolFlags := collectBoolFlagNames(meta)
if len(boolFlags) > 0 {
for _, arg := range args {
if isBoolLiteral(arg) {
return fmt.Errorf(
"boolean flag 必须写成 --flag=true / --flag=false 或裸 --flag不能用空格分隔当前命令的 boolean 参数: %s",
strings.Join(boolFlags, ", "),
)
}
}
}
return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath())
}
}
// isBoolLiteral 判断字符串是否是 bool 字面量。
func isBoolLiteral(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "true", "false", "1", "0":
return true
}
return false
}
// collectBoolFlagNames 返回 capability 所有 boolean 类型参数的 flag 名称。
func collectBoolFlagNames(meta CapabilityMeta) []string {
var names []string
for _, p := range meta.Params {
if p.Type == "boolean" {
flag := p.FlagName
if flag == "" {
flag = strings.ReplaceAll(p.Name, "_", "-")
}
names = append(names, "--"+flag)
}
}
return names
}
func renderDomainHelp(domain DomainMeta, capabilities []CapabilityMeta) string {
lines := []string{fmt.Sprintf("%s — %s", domain.Name, domain.Description), "", "Available commands:"}
if len(capabilities) == 0 {
@@ -3083,22 +3137,32 @@ func writeCapabilityError(writer io.Writer, err error) error {
// 如果是 DependencyError使用其自带的结构化输出含 install_guide
var depErr *local.DependencyError
if errors.As(err, &depErr) {
payload := depErr.StructuredError()
updatecheck.InjectNotice(payload)
encoder := json.NewEncoder(writer)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
return encoder.Encode(depErr.StructuredError())
if encErr := encoder.Encode(payload); encErr != nil {
return encErr
}
return cliexit.ErrBusinessFailure
}
encoder := json.NewEncoder(writer)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
return encoder.Encode(map[string]any{
payload := map[string]any{
"error": map[string]any{
"type": classifyErrorType(err),
"code": classifyErrorCode(err),
"message": err.Error(),
},
})
}
updatecheck.InjectNotice(payload)
encoder := json.NewEncoder(writer)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
if encErr := encoder.Encode(payload); encErr != nil {
return encErr
}
return cliexit.ErrBusinessFailure
}
func classifyErrorType(err error) string {
@@ -3164,6 +3228,9 @@ func classifyErrorCode(err error) string {
}
func writeJSON(out io.Writer, value any) error {
if m, ok := value.(map[string]any); ok {
updatecheck.InjectNotice(m)
}
encoder := json.NewEncoder(out)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
@@ -3221,11 +3288,14 @@ func buildCapabilitySchema(meta CapabilityMeta, resolvedMode string) map[string]
// 构建描述
description := meta.Description
if meta.Async {
description += "\n\n- Async: 是,用 mediakit_shared_query_task 查询结果"
}
modeLabel := modes.ModeLabel(meta.runtimeMeta())
description += "\n- Mode: " + modeLabel
description += "\n\n- Mode: " + modeLabel
if meta.Async {
description += "\n- Async: 是"
description += "\n- 轮询命令: mediakit-cli shared query-task --task-id <id> --poll-complete"
} else {
description += "\n- Async: 否"
}
// 构建工具名domain_tool 格式)
toolName := strings.ReplaceAll(meta.Name, "-", "_")
+22
View File
@@ -1,7 +1,10 @@
package commands
import (
"time"
buildinfo "mediakit-cli/internal/build"
"mediakit-cli/internal/updatecheck"
"github.com/spf13/cobra"
)
@@ -23,6 +26,14 @@ func newRootCmd() *cobra.Command {
Short: "MediaKit command line interface",
Long: `MediaKit CLI provides system commands, domain navigation, and generated capability commands.
One-click install (CLI + AI agent Skills):
npx @volcengine/mediakit-cli install -y
Update:
mediakit-cli update # install latest via npm install -g
mediakit-cli update --check # only report status
mediakit-cli version --check # show current vs latest
AI Agent Skills:
mediakit-cli pairs with AI agent skills (Claude Code, etc.) that
teach the agent MediaKit CLI patterns, best practices, and workflows.
@@ -32,11 +43,21 @@ AI Agent Skills:
Or pick specific domains:
npx skills add volcengine/mediakit-cli -s byted-mediakit-editing -y
npx skills add volcengine/mediakit-cli -s byted-mediakit-audio -y
npx skills add volcengine/mediakit-cli -s byted-mediakit-image -y
npx skills add volcengine/mediakit-cli -s byted-mediakit-video -y
npx skills add volcengine/mediakit-cli -s byted-mediakit-shared -y`,
SilenceUsage: true,
SilenceErrors: true,
DisableAutoGenTag: true,
Version: buildinfo.Version,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
updatecheck.StartAsync()
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
updatecheck.WaitForResult(100 * time.Millisecond)
updatecheck.PrintStderrNag(cmd.ErrOrStderr())
},
RunE: func(cmd *cobra.Command, args []string) error {
switch {
case showDomains:
@@ -58,6 +79,7 @@ AI Agent Skills:
cmd.AddCommand(newDoctorCmd())
cmd.AddCommand(newConfigCmd())
cmd.AddCommand(newVersionCmd())
cmd.AddCommand(newUpdateCmd())
for _, domainCmd := range newGeneratedDomainCommands() {
cmd.AddCommand(domainCmd)
+81
View File
@@ -0,0 +1,81 @@
package commands
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"time"
buildinfo "mediakit-cli/internal/build"
"mediakit-cli/internal/updatecheck"
"github.com/spf13/cobra"
)
func newUpdateCmd() *cobra.Command {
var checkOnly bool
cmd := &cobra.Command{
Use: "update",
Short: "Check and apply mediakit-cli updates from the npm registry",
Long: "Check the npm registry for the latest @volcengine/mediakit-cli release and optionally install it via npm install -g.",
Args: cobra.NoArgs,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
updatecheck.StartAsync()
r := updatecheck.WaitForResult(3 * time.Second)
if r == nil {
return writeUpdatePayload(cmd, map[string]any{
"current": buildinfo.Version,
"action": "skipped",
"reason": "update check disabled or unavailable in this environment",
})
}
payload := map[string]any{
"current": r.Current,
"latest": r.Latest,
"has_update": r.HasUpdate,
}
if r.Err != nil {
payload["error"] = r.Err.Error()
payload["action"] = "skipped"
return writeUpdatePayload(cmd, payload)
}
if !r.HasUpdate {
payload["action"] = "noop"
return writeUpdatePayload(cmd, payload)
}
payload["upgrade_command"] = fmt.Sprintf("npm install -g %s@latest", updatecheck.PackageName)
if checkOnly {
payload["action"] = "check"
return writeUpdatePayload(cmd, payload)
}
payload["action"] = "install"
if err := runNpmInstallLatest(cmd); err != nil {
payload["install_status"] = "failed"
payload["error"] = err.Error()
_ = writeUpdatePayload(cmd, payload)
return err
}
payload["install_status"] = "ok"
return writeUpdatePayload(cmd, payload)
},
}
cmd.Flags().BoolVar(&checkOnly, "check", false, "Only check for updates; do not install")
return cmd
}
func writeUpdatePayload(cmd *cobra.Command, payload map[string]any) error {
encoder := json.NewEncoder(cmd.OutOrStdout())
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
return encoder.Encode(payload)
}
func runNpmInstallLatest(cmd *cobra.Command) error {
target := fmt.Sprintf("%s@latest", updatecheck.PackageName)
c := exec.Command("npm", "install", "-g", target)
c.Stdout = os.Stderr
c.Stderr = os.Stderr
return c.Run()
}
+29 -1
View File
@@ -1,20 +1,46 @@
package commands
import (
"encoding/json"
"fmt"
"time"
buildinfo "mediakit-cli/internal/build"
"mediakit-cli/internal/updatecheck"
"github.com/spf13/cobra"
)
func newVersionCmd() *cobra.Command {
return &cobra.Command{
var checkUpdate bool
cmd := &cobra.Command{
Use: "version",
Short: "Print CLI version information",
Args: cobra.NoArgs,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
if checkUpdate {
updatecheck.StartAsync()
r := updatecheck.WaitForResult(2 * time.Second)
payload := map[string]any{
"current": buildinfo.Version,
"date": buildinfo.Date,
}
if r != nil {
payload["latest"] = r.Latest
payload["has_update"] = r.HasUpdate
if r.Err != nil {
payload["error"] = r.Err.Error()
}
if r.HasUpdate {
payload["upgrade_command"] = fmt.Sprintf("npm install -g %s@latest", updatecheck.PackageName)
}
}
encoder := json.NewEncoder(cmd.OutOrStdout())
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
return encoder.Encode(payload)
}
_, err := fmt.Fprintf(
cmd.OutOrStdout(),
"mediakit-cli %s\nbuild date: %s\n",
@@ -24,4 +50,6 @@ func newVersionCmd() *cobra.Command {
return err
},
}
cmd.Flags().BoolVar(&checkUpdate, "check", false, "Check the npm registry for a newer release and report status")
return cmd
}
+32 -1
View File
@@ -12,9 +12,11 @@ import (
"github.com/spf13/cobra"
"mediakit-cli/internal/cliexit"
cliconfig "mediakit-cli/internal/config"
"mediakit-cli/internal/local/core"
"mediakit-cli/internal/output"
"mediakit-cli/internal/updatecheck"
)
// Executor coordinates local capability execution in later stages.
@@ -112,10 +114,39 @@ func Execute(cmd *cobra.Command, command string, params map[string]any) error {
}
func writeJSON(output io.Writer, value any) error {
if m, ok := value.(map[string]any); ok {
updatecheck.InjectNotice(m)
}
encoder := json.NewEncoder(output)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
return encoder.Encode(value)
if err := encoder.Encode(value); err != nil {
return err
}
if m, ok := value.(map[string]any); ok {
if errField, exists := m["error"]; exists && !isEmptyErrorValue(errField) {
return cliexit.ErrBusinessFailure
}
}
return nil
}
// isEmptyErrorValue 判断 error 字段是否实质为空:
// nil / 空字符串 / 空 map / 空 slice 都视为"无错误",不应触发 sentinel。
func isEmptyErrorValue(v any) bool {
if v == nil {
return true
}
switch typed := v.(type) {
case string:
return strings.TrimSpace(typed) == ""
case map[string]any:
return len(typed) == 0
case []any:
return len(typed) == 0
default:
return false
}
}
func cloneParams(params map[string]any) map[string]any {
+68
View File
@@ -0,0 +1,68 @@
package updatecheck
import (
"encoding/json"
"os"
"path/filepath"
"time"
cliconfig "mediakit-cli/internal/config"
)
const (
CacheFileName = "update-check.json"
CacheTTL = 24 * time.Hour
)
type Cache struct {
Current string `json:"current"`
Latest string `json:"latest"`
CheckedAt time.Time `json:"checked_at"`
HasUpdate bool `json:"has_update"`
NotifiedAt time.Time `json:"notified_at,omitempty"`
}
func CacheFile(home string) string {
return filepath.Join(cliconfig.ConfigDir(home), CacheFileName)
}
func LoadCache(home string) (*Cache, error) {
path := CacheFile(home)
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
c := &Cache{}
if err := json.Unmarshal(data, c); err != nil {
return nil, err
}
return c, nil
}
func SaveCache(home string, c *Cache) error {
dir := cliconfig.ConfigDir(home)
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
data, err := json.MarshalIndent(c, "", " ")
if err != nil {
return err
}
return os.WriteFile(CacheFile(home), data, 0o644)
}
func IsCacheFresh(c *Cache, runningVersion string) bool {
if c == nil {
return false
}
if c.Current != runningVersion {
return false
}
if time.Since(c.CheckedAt) > CacheTTL {
return false
}
return true
}
+226
View File
@@ -0,0 +1,226 @@
package updatecheck
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"sync/atomic"
"time"
cliconfig "mediakit-cli/internal/config"
"mediakit-cli/internal/build"
)
const (
NpmRegistryURL = "https://registry.npmjs.org/@volcengine/mediakit-cli/latest"
PackageName = "@volcengine/mediakit-cli"
EnvDisable = "MEDIAKIT_DISABLE_UPDATE_CHECK"
EnvCI = "CI"
)
type Result struct {
HasUpdate bool
Current string
Latest string
Err error
}
var checkResult atomic.Value // *Result
func StartAsync() {
if shouldSkip() {
return
}
if r := loadCachedResult(); r != nil {
checkResult.Store(r)
return
}
go func() {
r := runCheck()
if r != nil {
checkResult.Store(r)
}
}()
}
func loadCachedResult() *Result {
running := strings.TrimSpace(build.Version)
if running == "" {
return nil
}
home, err := cliconfig.ResolveHomeDir()
if err != nil {
return nil
}
cached, _ := LoadCache(home)
if cached == nil || !IsCacheFresh(cached, running) {
return nil
}
return &Result{
HasUpdate: cached.HasUpdate,
Current: cached.Current,
Latest: cached.Latest,
}
}
func GetResult() *Result {
v := checkResult.Load()
if v == nil {
return nil
}
r, ok := v.(*Result)
if !ok {
return nil
}
return r
}
func WaitForResult(timeout time.Duration) *Result {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if r := GetResult(); r != nil {
return r
}
time.Sleep(10 * time.Millisecond)
}
return GetResult()
}
func shouldSkip() bool {
if v := strings.TrimSpace(os.Getenv(EnvDisable)); v != "" && v != "0" && strings.ToLower(v) != "false" {
return true
}
if v := strings.TrimSpace(os.Getenv(EnvCI)); v != "" && v != "0" && strings.ToLower(v) != "false" {
return true
}
if strings.TrimSpace(build.Version) == "dev" {
return true
}
return false
}
func runCheck() *Result {
running := strings.TrimSpace(build.Version)
if running == "" {
return nil
}
home, err := cliconfig.ResolveHomeDir()
if err == nil {
if cached, _ := LoadCache(home); cached != nil && IsCacheFresh(cached, running) {
return &Result{
HasUpdate: cached.HasUpdate,
Current: cached.Current,
Latest: cached.Latest,
}
}
}
latest, err := fetchLatestVersion()
if err != nil {
return &Result{Current: running, Err: err}
}
latest = strings.TrimSpace(latest)
if latest == "" {
return &Result{Current: running, Err: fmt.Errorf("empty latest version")}
}
hasUpdate := compareVersions(running, latest) < 0
cache := &Cache{
Current: running,
Latest: latest,
CheckedAt: time.Now(),
HasUpdate: hasUpdate,
}
if home, herr := cliconfig.ResolveHomeDir(); herr == nil {
_ = SaveCache(home, cache)
}
return &Result{
HasUpdate: hasUpdate,
Current: running,
Latest: latest,
}
}
func fetchLatestVersion() (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 1500*time.Millisecond)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, NpmRegistryURL, nil)
if err != nil {
return "", err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("registry status %d", resp.StatusCode)
}
var payload struct {
Version string `json:"version"`
}
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
return "", err
}
return payload.Version, nil
}
// compareVersions returns -1 if a<b, 0 if equal, 1 if a>b. Best-effort semver.
func compareVersions(a, b string) int {
a = strings.TrimPrefix(strings.TrimSpace(a), "v")
b = strings.TrimPrefix(strings.TrimSpace(b), "v")
if a == b {
return 0
}
pa := strings.SplitN(a, "-", 2)
pb := strings.SplitN(b, "-", 2)
ca := splitNums(pa[0])
cb := splitNums(pb[0])
for i := 0; i < 3; i++ {
var va, vb int
if i < len(ca) {
va = ca[i]
}
if i < len(cb) {
vb = cb[i]
}
if va < vb {
return -1
}
if va > vb {
return 1
}
}
// Equal core. Pre-release < release.
if len(pa) == 2 && len(pb) == 1 {
return -1
}
if len(pa) == 1 && len(pb) == 2 {
return 1
}
if len(pa) == 2 && len(pb) == 2 {
return strings.Compare(pa[1], pb[1])
}
return 0
}
func splitNums(s string) []int {
parts := strings.Split(s, ".")
out := make([]int, 0, len(parts))
for _, p := range parts {
n := 0
for _, r := range p {
if r < '0' || r > '9' {
break
}
n = n*10 + int(r-'0')
}
out = append(out, n)
}
return out
}
+69
View File
@@ -0,0 +1,69 @@
package updatecheck
import (
"fmt"
"io"
"os"
)
// NoticePayload returns a map suitable to be embedded into the JSON stdout
// payload at the top-level `_notice.update` key, or nil if no update is needed.
func NoticePayload() map[string]any {
r := GetResult()
if r == nil || !r.HasUpdate {
return nil
}
return map[string]any{
"current": r.Current,
"latest": r.Latest,
"command": fmt.Sprintf("npm install -g %s@latest", PackageName),
"message": fmt.Sprintf("New %s release available: %s -> %s", PackageName, r.Current, r.Latest),
}
}
// InjectNotice adds `_notice.update` to a top-level JSON object map if an
// update is available. It is a no-op when the result is nil or no update.
func InjectNotice(result map[string]any) {
if result == nil {
return
}
payload := NoticePayload()
if payload == nil {
return
}
notice, _ := result["_notice"].(map[string]any)
if notice == nil {
notice = map[string]any{}
}
notice["update"] = payload
result["_notice"] = notice
}
// PrintStderrNag prints an unobtrusive update hint to stderr when an update is
// available and stderr is a TTY (character device).
func PrintStderrNag(w io.Writer) {
r := GetResult()
if r == nil || !r.HasUpdate {
return
}
f, ok := w.(*os.File)
if !ok {
return
}
if !isCharDevice(f) {
return
}
fmt.Fprintf(w, "\n[mediakit-cli] new version available: %s -> %s\n run: npm install -g %s@latest\n",
r.Current, r.Latest, PackageName)
}
func isCharDevice(f *os.File) bool {
if f == nil {
return false
}
info, err := f.Stat()
if err != nil {
return false
}
return (info.Mode() & os.ModeCharDevice) != 0
}
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@volcengine/mediakit-cli",
"version": "0.1.5",
"version": "0.1.6",
"description": "MediaKit CLI with multi-platform binary distribution via npm",
"license": "MIT",
"bin": {
+153
View File
@@ -0,0 +1,153 @@
#!/usr/bin/env node
const { spawnSync } = require("node:child_process");
const path = require("node:path");
const pkg = require("../package.json");
const PACKAGE_NAME = pkg.name || "@volcengine/mediakit-cli";
const SKILL_REPO = "volcengine/mediakit-cli";
function parseArgs(argv) {
const opts = {
cliOnly: false,
skillsOnly: false,
skills: [],
yes: false,
versionTag: "latest",
};
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
switch (a) {
case "--cli-only":
opts.cliOnly = true;
break;
case "--skills-only":
opts.skillsOnly = true;
break;
case "-y":
case "--yes":
opts.yes = true;
break;
case "-s":
case "--skill": {
const next = argv[i + 1];
if (next && !next.startsWith("-")) {
opts.skills.push(next);
i++;
}
break;
}
case "--version": {
const next = argv[i + 1];
if (next && !next.startsWith("-")) {
opts.versionTag = next;
i++;
}
break;
}
default:
if (a.startsWith("--version=")) {
opts.versionTag = a.slice("--version=".length);
} else if (a.startsWith("--skills=")) {
opts.skills.push(
...a.slice("--skills=".length).split(",").map((s) => s.trim()).filter(Boolean)
);
}
break;
}
}
return opts;
}
function log(msg) {
console.log(`[mediakit-cli install] ${msg}`);
}
function whichSync(cmd) {
const probe = process.platform === "win32" ? "where" : "command";
const probeArgs = process.platform === "win32" ? [cmd] : ["-v", cmd];
const result = spawnSync(probe, probeArgs, { stdio: "ignore", shell: true });
return result.status === 0;
}
function runNpmInstall(target) {
log(`installing ${target} via npm install -g`);
const result = spawnSync("npm", ["install", "-g", target], {
stdio: "inherit",
});
if (result.error) {
throw result.error;
}
if (result.status !== 0) {
throw new Error(`npm install -g ${target} failed with exit code ${result.status}`);
}
}
function runSkillsAdd(opts) {
const args = ["-y", "skills", "add", SKILL_REPO];
if (opts.skills.length === 0) {
args.push("-g");
} else {
for (const skill of opts.skills) {
args.push("-s", skill);
}
}
if (opts.yes) {
args.push("-y");
}
log(`installing skills via npx ${args.join(" ")}`);
const result = spawnSync("npx", args, { stdio: "inherit" });
if (result.error) {
throw result.error;
}
if (result.status !== 0) {
throw new Error(`npx skills add failed with exit code ${result.status}`);
}
}
function isCliAlreadyInstalled() {
// If MEDIAKIT_CLI_FORCE_REINSTALL=1, always reinstall.
if (
process.env.MEDIAKIT_CLI_FORCE_REINSTALL === "1" ||
process.env.MEDIKIT_CLI_FORCE_REINSTALL === "1"
) {
return false;
}
return whichSync("mediakit-cli");
}
async function runInstallWizard(rawArgs) {
const opts = parseArgs(rawArgs);
if (!whichSync("npm")) {
throw new Error("npm is required but not found in PATH");
}
if (!opts.skillsOnly) {
if (isCliAlreadyInstalled()) {
log("mediakit-cli already installed; skipping CLI install");
} else {
const target = `${PACKAGE_NAME}@${opts.versionTag}`;
runNpmInstall(target);
}
}
if (!opts.cliOnly) {
if (!whichSync("npx")) {
throw new Error("npx is required to install skills but not found in PATH");
}
runSkillsAdd(opts);
}
log("done.");
}
module.exports = { runInstallWizard };
if (require.main === module) {
runInstallWizard(process.argv.slice(2)).catch((error) => {
console.error(`[mediakit-cli install] ${error.message}`);
process.exit(1);
});
}
+24 -4
View File
@@ -11,8 +11,27 @@ const http = require("node:http");
const pkg = require("../package.json");
const PROJECT_NAME = "mediakit-cli";
// Deprecated env names (MEDIKIT_*) are still read for back-compat but emit a
// warning. New env names use the MEDIAKIT_* prefix (Rule 22). The 0.3.0
// release will drop the deprecated forms.
function readEnv(currentName, deprecatedName) {
const current = process.env[currentName];
if (current !== undefined && current !== "") {
return current;
}
const deprecated = process.env[deprecatedName];
if (deprecated !== undefined && deprecated !== "") {
console.warn(
`[mediakit-cli] env ${deprecatedName} is deprecated, please use ${currentName}`
);
return deprecated;
}
return undefined;
}
const RELEASE_BASE_URL =
process.env.MEDIKIT_CLI_RELEASE_BASE_URL ||
readEnv("MEDIAKIT_CLI_RELEASE_BASE_URL", "MEDIKIT_CLI_RELEASE_BASE_URL") ||
"https://github.com/volcengine/mediakit-cli/releases/download";
const TMP_PREFIX = "mediakit-cli-install-";
const packageRoot = path.resolve(__dirname, "..");
@@ -180,14 +199,15 @@ function findBinary(rootDir, targetName) {
}
async function install(options = {}) {
if (process.env.MEDIKIT_CLI_SKIP_DOWNLOAD === "1") {
console.log("[mediakit-cli] skip download because MEDIKIT_CLI_SKIP_DOWNLOAD=1");
if (readEnv("MEDIAKIT_CLI_SKIP_DOWNLOAD", "MEDIKIT_CLI_SKIP_DOWNLOAD") === "1") {
console.log("[mediakit-cli] skip download because MEDIAKIT_CLI_SKIP_DOWNLOAD=1");
return;
}
const platform = resolvePlatform();
const arch = resolveArch();
const version = process.env.MEDIKIT_CLI_VERSION || pkg.version;
const version =
readEnv("MEDIAKIT_CLI_VERSION", "MEDIKIT_CLI_VERSION") || pkg.version;
const targetBinary = path.join(binDir, binaryName());
if (!options.force && fs.existsSync(targetBinary)) {
+22 -2
View File
@@ -2,8 +2,28 @@
set -euo pipefail
PROJECT_NAME="mediakit-cli"
RELEASE_BASE_URL="${MEDIKIT_CLI_RELEASE_BASE_URL:-https://github.com/volcengine/mediakit-cli/releases/download}"
LATEST_API_URL="${MEDIKIT_CLI_LATEST_API_URL:-https://api.github.com/repos/volcengine/mediakit-cli/releases/latest}"
# Resolve env vars from MEDIAKIT_* (preferred) or MEDIKIT_* (deprecated).
resolve_env() {
local current="$1"
local legacy="$2"
local fallback="${3:-}"
local cur_val="${!current:-}"
local legacy_val="${!legacy:-}"
if [[ -n "${cur_val}" ]]; then
printf '%s' "${cur_val}"
return
fi
if [[ -n "${legacy_val}" ]]; then
echo "[mediakit-cli] env ${legacy} is deprecated, please use ${current}" >&2
printf '%s' "${legacy_val}"
return
fi
printf '%s' "${fallback}"
}
RELEASE_BASE_URL="$(resolve_env MEDIAKIT_CLI_RELEASE_BASE_URL MEDIKIT_CLI_RELEASE_BASE_URL "https://github.com/volcengine/mediakit-cli/releases/download")"
LATEST_API_URL="$(resolve_env MEDIAKIT_CLI_LATEST_API_URL MEDIKIT_CLI_LATEST_API_URL "https://api.github.com/repos/volcengine/mediakit-cli/releases/latest")"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
VERSION="${VERSION:-}"
+22 -1
View File
@@ -5,6 +5,7 @@ const path = require("node:path");
const { spawnSync } = require("node:child_process");
const { install } = require("./install");
const { runInstallWizard } = require("./install-wizard");
const packageRoot = path.resolve(__dirname, "..");
const binDir = path.join(packageRoot, "bin");
@@ -20,10 +21,30 @@ async function ensureBinary() {
await install({ force: true });
}
// Detect whether the CLI is being run via `npx @volcengine/mediakit-cli install`,
// where it should bootstrap a global installation rather than execute the
// embedded binary inside the npx cache. MEDIAKIT_CLI_RUN=1 is a re-entry guard.
function shouldInterceptInstall(args) {
if (!args || args.length === 0 || args[0] !== "install") {
return false;
}
if (process.env.MEDIAKIT_CLI_RUN === "1" || process.env.MEDIKIT_CLI_RUN === "1") {
return false;
}
return process.env.npm_command === "exec";
}
async function main() {
const argv = process.argv.slice(2);
if (shouldInterceptInstall(argv)) {
process.env.MEDIAKIT_CLI_RUN = "1";
await runInstallWizard(argv.slice(1));
return;
}
await ensureBinary();
const result = spawnSync(binary, process.argv.slice(2), {
const result = spawnSync(binary, argv, {
stdio: "inherit",
env: { ...process.env, MEDIAKIT_CLI_RUN: "1" },
});
if (result.error) {
+8 -2
View File
@@ -2,7 +2,7 @@
name: byted-mediakit-editing
version: "1.0.0"
license: "MIT"
description: "音视频剪辑涵盖音视频拼接、裁剪、合成等能力。包含能力add-image-to-video, add-subtitle-to-video, adjust-video-speed, concat-audio, concat-video, extract-audio, flip-video, image-to-video, mux-audio-video, trim-audio, trim-video。当用户需要使用 editing 域的 MediaKit CLI 能力时触发。"
description: "音视频剪辑涵盖音视频拼接、裁剪、合成等能力。包含能力add-image-to-video, add-subtitle-to-video, adjust-audio-speed, adjust-video-speed, adjust-video-volume, apply-video-filter, concat-audio, concat-video, extract-audio, fade-audio, fade-video-audio, flip-video, image-to-video, mix-audio, mux-audio-video, trim-audio, trim-video。当用户需要使用 editing 域的 MediaKit CLI 能力时触发。"
permissions:
- shell
metadata:
@@ -11,7 +11,7 @@ metadata:
cliHelp: "mediakit-cli editing --help"
product: mediakit-cli/skills
domain: editing
capability_count: 11
capability_count: 17
---
# Editing Skills
@@ -25,12 +25,18 @@ metadata:
|------|------|----------|----------|
| add-image-to-video | 视频加图片,可用作加图片水印。 | `video_url:string, sub_image_url:string, sub_image_height?:string, sub_image_width?:string, sub_image_pos_x?:string, sub_image_pos_y?:string, start_time?:number, end_time?:number, callback_args?:string, client_token?:string` | [reference/add-image-to-video.md](reference/add-image-to-video.md) |
| add-subtitle-to-video | 将字幕文件或文本内容,以指定样式压制到视频画面中,生成带内嵌字幕的新视频。 | `video_url:string, subtitle_url?:string, subtitles?:array<object{subtitle_text:string, start_time:number, end_time:number}>, subtitle_pos_preset?:string, subtitle_font_size?:integer, subtitle_font_color?:string, subtitle_font_type?:string, callback_args?:string, client_token?:string` | [reference/add-subtitle-to-video.md](reference/add-subtitle-to-video.md) |
| adjust-audio-speed | 调整音频的播放倍速,实现快放或慢放效果。 | `audio_url:string, speed?:number, callback_args?:string, client_token?:string` | [reference/adjust-audio-speed.md](reference/adjust-audio-speed.md) |
| adjust-video-speed | 调整视频的播放倍速,实现快放或慢放效果。 | `video_url:string, speed?:number, callback_args?:string, client_token?:string` | [reference/adjust-video-speed.md](reference/adjust-video-speed.md) |
| adjust-video-volume | 调整视频音量大小,支持静音;输出 mp4分辨率与原片一致。 | `video_url:string, volume?:number, callback_args?:string, client_token?:string` | [reference/adjust-video-volume.md](reference/adjust-video-volume.md) |
| apply-video-filter | 为视频添加指定滤镜效果输出mp4分辨率与原片一致。 | `video_url:string, filter_style?:string, callback_args?:string, client_token?:string` | [reference/apply-video-filter.md](reference/apply-video-filter.md) |
| concat-audio | 拼接多个音频片段。 | `audio_urls:array<string>, callback_args?:string, client_token?:string` | [reference/concat-audio.md](reference/concat-audio.md) |
| concat-video | 拼接多个视频片段,支持添加转场效果。 | `video_urls:array<string>, transitions?:array<string>, callback_args?:string, client_token?:string` | [reference/concat-video.md](reference/concat-video.md) |
| extract-audio | 将视频文件中的音频流分离并保存为独立的音频文件。 | `video_url:string, format?:string, callback_args?:string, client_token?:string` | [reference/extract-audio.md](reference/extract-audio.md) |
| fade-audio | 对输入音频实现淡入淡出效果,输出 mp3。 | `audio_url:string, fade_in_duration?:number, fade_out_duration?:number, callback_args?:string, client_token?:string` | [reference/fade-audio.md](reference/fade-audio.md) |
| fade-video-audio | 对输入视频的声轨实现淡入淡出效果。 输出 mp4分辨率与原片一致。 | `video_url:string, fade_in_duration?:number, fade_out_duration?:number, callback_args?:string, client_token?:string` | [reference/fade-video-audio.md](reference/fade-video-audio.md) |
| flip-video | 对视频画面进行上下或左右镜像翻转。 | `video_url:string, is_flip_vertical?:boolean, is_flip_horizontal?:boolean, callback_args?:string, client_token?:string` | [reference/flip-video.md](reference/flip-video.md) |
| image-to-video | 多张图片生成动画视频。 | `images:array<object{image_url:string, duration?:number, animation_type?:string, animation_in?:number, animation_out?:number}>, transitions?:array<string>, callback_args?:string, client_token?:string` | [reference/image-to-video.md](reference/image-to-video.md) |
| mix-audio | 将多个音频文件(如背景音乐、音效、人声)进行混音,生成一个新的音频文件。 处理耗时:处理耗时与视频时长正相关。视频时长越长,处理耗时越长。平均 RTF处理耗时/原片时长)为 1。 输出音频的时长以最长的音频为准。 输出视频格式mp3 | `audio_urls:array<string>, callback_args?:string, client_token?:string` | [reference/mix-audio.md](reference/mix-audio.md) |
| mux-audio-video | 音视频合成。 | `video_url:string, audio_url:string, is_audio_reserve?:boolean, is_video_audio_sync?:boolean, sync_mode?:string, sync_method?:string, callback_args?:string, client_token?:string` | [reference/mux-audio-video.md](reference/mux-audio-video.md) |
| trim-audio | 按起止时间点(秒级)裁剪音频,生成新片段。 | `audio_url:string, start_time?:number, end_time?:number, callback_args?:string, client_token?:string` | [reference/trim-audio.md](reference/trim-audio.md) |
| trim-video | 按起止时间点裁剪视频,生成新片段。 | `video_url:string, start_time?:number, end_time?:number, callback_args?:string, client_token?:string` | [reference/trim-video.md](reference/trim-video.md) |
+93 -93
View File
@@ -2,7 +2,7 @@
name: byted-mediakit-shared
version: '1.0.0'
license: 'MIT'
description: 'mediakit-cli 共享:环境检查、初始化配置、命令结构、认证配置、异步任务响应与错误处理。'
description: '1. mediakit-cli: supports a variety of operations such as audio/video processing, editing, and images, with some capabilities covering both cloud and local modes; 2. mediakit-cli shared: environment checks, initialization config, command structure, authentication config, async task responses, and error handling.'
permissions:
- shell
metadata:
@@ -14,48 +14,48 @@ metadata:
capability_count: 14
---
# MediaKit 共享规则
# MediaKit Shared Rules
本技能指导你如何通过 mediakit-cli 操作媒体资源,以及调用过程中的通用规则和注意事项。
This skill explains how to operate on media resources via `mediakit-cli`, and the shared rules and caveats that apply to every call.
## 前置检查
## Prerequisites
### 依赖安装
### Install the CLI
首次使用前,确认 CLI 已安装:
Before first use, confirm the CLI is installed:
```bash
# 安装
# Install
npm install -g @volcengine/mediakit-cli
# 验证
# Verify
mediakit-cli --version
```
### 鉴权信息检查
### Authentication check
优先级:环境变量 > 配置文件(文件路径 `~/.mediakit/config.json`
Priority: environment variables > config file (path: `~/.mediakit/config.json`).
#### 字段说明
#### Field reference
- 环境变量/配置文件:`MEDIAKIT_API_KEY``MEDIAKIT_ENDPOINT``MEDIAKIT_SURFACE``MEDIAKIT_RUNTIME`
- Environment variables / config file: `MEDIAKIT_API_KEY`, `MEDIAKIT_ENDPOINT`, `MEDIAKIT_SURFACE`, `MEDIAKIT_RUNTIME`
| 变量 | 必填 | 说明 |
| ------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `MEDIAKIT_API_KEY` | 云端模式必填 | API 认证 Token |
| `MEDIAKIT_ENDPOINT` | | API 访问点 |
| `MEDIAKIT_SURFACE` | | 请求来源 Header `x-surface`;默认 `cli`Skill 建议 `skill`Plugin 建议 `plugin`,最终上报 `cli/skill` `cli/plugin` |
| `MEDIAKIT_RUNTIME` | | 请求来源 Header `x-runtime`;按宿主设置为 `claude``arkclaw` 等,未配置时回退环境探测或 `unknown` |
| Variable | Required | Description |
| ------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MEDIAKIT_API_KEY` | Required (cloud mode) | API authentication token |
| `MEDIAKIT_ENDPOINT` | Optional | API endpoint |
| `MEDIAKIT_SURFACE` | Optional | Request-source Header `x-surface`. Defaults to `cli`. Skill should set `skill`, Plugin should set `plugin`; CLI reports them as `cli/skill` or `cli/plugin`. |
| `MEDIAKIT_RUNTIME` | Optional | Request-source Header `x-runtime`. Set to host name such as `claude` / `arkclaw`; falls back to environment probing or `unknown` when not configured. |
任一必填项缺失时,终止执行并输出所有缺失项的列表及修复建议。
When any required field is missing, stop execution and output the full list of missing items together with fix suggestions.
云端调用会自动携带 `x-surface` / `x-runtime`Header 优先级为:环境变量 > `~/.mediakit/config.json` > 默认值/环境探测。当本 Skill/Plugin 通过 `mediakit-cli` 调用云端能力时,运行环境应注入 `MEDIAKIT_SURFACE=skill|plugin` `MEDIAKIT_RUNTIME=<宿主>`CLI 会保留原始产物前缀并上报 `x-surface=cli/skill|cli/plugin`。若未显式配置CLI 默认按 `x-surface=cli``x-runtime` 依次回退 `IDENTITY_NAME` / `OPENCLAW_SERVICE_MARKER` 环境探测,最后为 `unknown`
Cloud calls automatically carry `x-surface` / `x-runtime`. Header priority: environment variables > `~/.mediakit/config.json` > defaults / environment probing. When this Skill/Plugin invokes cloud capabilities through `mediakit-cli`, the runtime should inject `MEDIAKIT_SURFACE=skill|plugin` and `MEDIAKIT_RUNTIME=<host>`. The CLI keeps the original product prefix and reports `x-surface=cli/skill` or `x-surface=cli/plugin`. If no explicit configuration is provided, the CLI defaults to `x-surface=cli`; `x-runtime` falls back to `IDENTITY_NAME` / `OPENCLAW_SERVICE_MARKER` environment probing, and finally to `unknown`.
### 来源上报约束
### Source-reporting constraints
- Skill 调用 `mediakit-cli` 时,必须显式设置 `MEDIAKIT_SURFACE=skill`,不能依赖用户已有环境变量。
- Plugin 调用 `mediakit-cli` 时,必须显式设置 `MEDIAKIT_SURFACE=plugin`,不能复用 Skill 的取值。
- 宿主环境标识建议同时显式设置 `MEDIAKIT_RUNTIME=<宿主>`若未设置CLI 会回退为环境探测值或 `unknown`
- When a Skill invokes `mediakit-cli`, it must explicitly set `MEDIAKIT_SURFACE=skill` and must not rely on the user's pre-existing environment variables.
- When a Plugin invokes `mediakit-cli`, it must explicitly set `MEDIAKIT_SURFACE=plugin` and must not reuse the Skill value.
- The host runtime identifier should also be explicitly set via `MEDIAKIT_RUNTIME=<host>`; if unset, the CLI falls back to environment probing or `unknown`.
```bash
MEDIAKIT_SURFACE=skill MEDIAKIT_RUNTIME=<runtime> mediakit-cli editing add-image-to-video
@@ -63,94 +63,94 @@ MEDIAKIT_SURFACE=skill MEDIAKIT_RUNTIME=<runtime> mediakit-cli editing add-image
MEDIAKIT_SURFACE=plugin MEDIAKIT_RUNTIME=<runtime> mediakit-cli editing add-image-to-video
```
## CLI 使用方式
## CLI usage
### 初始化配置
### Initialization
首次使用建议先运行初始化向导:
Run the init wizard before first use:
```bash
mediakit-cli init
```
Agent 非交互初始化可显式写入请求来源与运行时配置:
For non-interactive initialization (Agents), pass the request-source and runtime configuration explicitly:
```bash
mediakit-cli init --mode cloud-first --api-key <key> --runtime <runtime> --surface cli --yes
mediakit-cli init --mode local-first --api-key <key> --endpoint <url> --output-path ~/mediakit-output --runtime <runtime> --surface cli --credential-store config --yes
```
初始化后常用命令如下:
Common commands after initialization:
```bash
# 查看当前配置
# Show current configuration
mediakit-cli config show
# 切换默认模式到本地优先
# Switch the default mode to local-first
mediakit-cli config set mode local-first
# 切换默认模式到云端优先
# Switch the default mode to cloud-first
mediakit-cli config set mode cloud-first
# 刷新环境检查并查看依赖状态
# Refresh environment checks and view dependency status
mediakit-cli doctor
```
### 命令结构
### Command structure
MediaKit CLI 统一使用 `domain + tool` 的调用方式:
MediaKit CLI consistently uses the `domain + tool` invocation shape:
```bash
mediakit-cli {domain} {tool} [flags]
```
常见帮助命令:
Common help commands:
```bash
# 查看所有 domain
# List all domains
mediakit-cli --domains
# 查看某个分组下的工具列表
# List tools in a given domain
mediakit-cli {domain} --help
# 查看具体工具的参数
# Show parameters of a specific tool
mediakit-cli {domain} {tool} --help
# 动态发现工具能力与返回结构
# Dynamically discover tool capability and return structure
mediakit-cli {domain} {tool} --schema
mediakit-cli --local {domain} {tool} --schema
```
当前产物覆盖的 domain 包括:`editing`, `video`
Domains currently covered by this product: `editing`, `video`.
### Schema 发现
### Schema discovery
每个 capability 命令都支持 `--schema`,用于 Agent 动态读取工具能力,不要求传必填业务参数。
Every capability command supports `--schema`, used by Agents to dynamically read tool capabilities. Required business parameters are not enforced when `--schema` is used.
返回结构包含:
The returned structure contains:
- `name`:工具名,使用 snake_case,如 `add_image_to_video`
- `description`:工具描述,自动包含 `Mode` `Async` 信息
- `input_schema`:输入参数 JSON Schema
- `output_schema`:当前执行模式下的返回结构
- `name`: tool name in snake_case, e.g. `add_image_to_video`
- `description`: tool description, automatically including `Mode` and `Async` information
- `input_schema`: input parameter JSON Schema
- `output_schema`: return structure for the current execution mode
输出区分规则:
Output disambiguation rules:
- 默认按全局 `mode` 配置解析返回面
- `--local ... --schema` 输出本地模式返回面,本地模式直接返回最终结果字段
- 云端异步工具输出 `task_id` / `request_id`,并在 `final_result` 中描述 `query-task` 完成态结果
- `query-task` cloud onlyschema 描述任务状态与完成态结果
- By default the return surface is resolved against the global `mode` configuration
- `--local ... --schema` outputs the local-mode return surface; local mode returns the final result fields directly
- Cloud async tools output `task_id` / `request_id`, and describe the completed-state result of `query-task` under `final_result`
- `query-task` is cloud only; its schema describes task status plus the completed-state result
示例:
Examples:
```bash
mediakit-cli editing trim-video --schema
mediakit-cli --local editing trim-video --schema
```
### 单次调用模式覆盖
### Per-invocation mode override
`config set mode` 设置默认模式外,还支持仅对当前命令生效的临时覆盖:
In addition to `config set mode` for setting the default mode, you can override the mode for a single command only:
```bash
mediakit-cli --local editing add-image-to-video
@@ -158,61 +158,61 @@ mediakit-cli --local editing add-image-to-video
mediakit-cli --cloud editing add-image-to-video
```
补充规则:
Additional rules:
- `--local` / `--cloud` 只影响当前命令,不修改全局 `config.mode`
- `--local` `--cloud` 互斥,不能同时传入
- `--local` / `--cloud` only affect the current command and do not modify the global `config.mode`
- `--local` and `--cloud` are mutually exclusive and cannot be passed together
## 异步任务
## Async tasks
提交异步媒体处理任务成功后会返回 `task_id` 字段。通过 `shared query-task` 命令查询结果。
When an async media-processing task is accepted successfully, the response contains a `task_id` field. Use the `shared query-task` command to poll the result.
```bash
mediakit-cli shared query-task --task-id <task_id>
```
## local / cloud 约束
## local / cloud constraints
- `query-task` **cloud only** 工具
- local 模式下不支持 query-task
- 当前本轮能力以云端执行为主;如需显式声明,请优先使用 `--cloud`
- `query-task` is a **cloud only** tool
- local mode does not support `query-task`
- The current capability set runs primarily in cloud mode; when an explicit declaration is required, prefer `--cloud`
### Cloud 模式媒体输入补充
### Cloud-mode media input notes
- 当命令以 `--cloud` `cloud-first` 策略执行时,媒体输入参数(如 `video_url``audio_url``image_url``subtitle_url``sub_image_url` 及对应数组/对象子字段)可传入 `http://` / `https://` URL`mediakit://...` file_id 或本地文件路径
- `http://` / `https://` URL `mediakit://...` file_id 会原样提交;本地文件路径会由 CLI 先上传为 `mediakit://...` file_id,再提交给云端工具
- 各工具 reference 中的参数说明来自 APIHub/OpenAPI 原始字段描述;若其中写有公网 URL 或 HTTP/HTTPS URL表示云端 API 最终接收的资源形态,不限制 CLI cloud 模式的本地路径预处理能力
- When a command runs with `--cloud` or under the `cloud-first` strategy, media input parameters (`video_url`, `audio_url`, `image_url`, `subtitle_url`, `sub_image_url`, and their corresponding array / object sub-fields) accept `http://` / `https://` URLs, `mediakit://...` file_ids, or local file paths
- `http://` / `https://` URLs and `mediakit://...` file_ids are submitted as-is; local file paths are first uploaded by the CLI as `mediakit://...` file_ids and then submitted to the cloud tool
- Parameter descriptions in each tool's reference are sourced from the APIHub/OpenAPI raw field descriptions; even when they show public URLs or HTTP/HTTPS URLs, that only reflects the resource form the cloud API ultimately receives — it does not restrict the CLI's ability to pre-process local paths in cloud mode
### Local 模式补充
### Local-mode notes
- 本地输出目录优先级:`--output-path` > `MEDIAKIT_OUTPUT_PATH` > config `output_path` > `~/.mediakit/temp`
- `--output-path` 指向具体媒体文件名时,直接作为最终输出文件;否则按输入文件名生成 `{原文件名}_{工具名}.{ext}`,重复时追加 6 位随机数
- 无法从输入 URL 或路径提取文件名时,退回 `{工具名}-{UnixNano}.{ext}`
- local 模式依赖 `ffmpeg` / `ffprobe`,缺失时错误中会给出 `install_guide`
- local 模式媒体处理输出必须贴合接口 response schema禁止输出内部执行元数据
- Local output directory priority: `--output-path` > `MEDIAKIT_OUTPUT_PATH` > config `output_path` > `~/.mediakit/temp`
- When `--output-path` points to a concrete media filename, it is used as the final output file directly; otherwise the filename is built as `{source_name}_{tool_name}.{ext}`, with a 6-digit random suffix appended on collision
- When no filename can be extracted from the input URL or path, fall back to `{tool_name}-{UnixNano}.{ext}`
- Local mode depends on `ffmpeg` / `ffprobe`; when missing, the error response includes an `install_guide`
- Local-mode media processing output must conform to the corresponding interface response schema; internal execution metadata must not be emitted
### 错误响应
### Error responses
- CLI cloud 模式直接透传 API 返回的原始 error 对象,不提取 `message`
- CLI local 模式返回结构化错误:`{"error":{"type":"...","code":"...","message":"..."}}`
- MCP error_response 直接透传原始 error 内容dict 原样作为 `error` 字段值
- CLI cloud mode forwards the original error object returned by the API as-is, without extracting `message`
- CLI local mode returns a structured error: `{"error":{"type":"...","code":"...","message":"..."}}`
- MCP `error_response` forwards the original error content as-is; a dict is used directly as the `error` field value
## 幂等参数维护
## Idempotency parameter maintenance
| 参数 | 作用 | 维护建议 |
| --------------- | ------------ | ------------------------------------------------------ |
| `client_token` | 主动控制幂等 | 请求重试时复用同一值;强制重新执行时传新的唯一值 |
| `callback_args` | 透传回调参数 | 建议与 `client_token` 一起维护,便于回调对账与重试追踪 |
| Parameter | Purpose | Maintenance guidance |
| ---------------- | ---------------------- | ----------------------------------------------------------------------------------- |
| `client_token` | Explicit idempotency | Reuse the same value on request retries; use a new unique value to force re-execute |
| `callback_args` | Passthrough to callback | Maintain together with `client_token` to ease callback reconciliation and retry tracking |
补充规则:
Additional rules:
- `client_token` 长度不超过 64 个字符
- `callback_args` 可用于回调透传与对账追踪
- `client_token` must not exceed 64 characters
- `callback_args` is useful for callback passthrough and reconciliation tracking
## 轮询策略
## Polling strategy
| 参数 | 描述 | 默认值 |
| ----------------------- | ---------------------- | ------ |
| `poll-interval-seconds` | 轮询间隔 | 10s |
| `max-poll-attempts` | 轮询次数0 代表不查询 | 0 |
| `poll-complete` | 阻塞至终态 | - |
| Parameter | Description | Default |
| ----------------------- | --------------------------------- | ------- |
| `poll-interval-seconds` | Polling interval | 10s |
| `max-poll-attempts` | Polling attempts; 0 disables it | 0 |
| `poll-complete` | Block until terminal status | - |
@@ -1,28 +1,31 @@
# 查询任务
# Query Task
## 能力描述
异步任务结果查询通过task_id查询任务信息
## Capability
## 执行方式
Query async task information by `task_id`.
| 项目 | 说明 |
|------|------|
| Domain | `shared` |
| Tool | `query-task` |
| 是否异步 | `否` |
| 是否支持 local | `否` |
| 模式说明 | cloud only当前命令用于查询云端异步任务状态。 |
| 幂等行为 | 当前命令无额外幂等参数要求。 |
## Execution
## 参数
| 参数 | CLI flag | 类型 | 必填 | 默认值 | 说明 |
|------|----------|------|------|--------|------|
| task_id | `--task-id` | string | 是 | - | 需要查询的任务 ID。 |
| poll_interval_seconds | `--poll-interval-seconds` | number | 否 | 10 | 轮询间隔秒数。 |
| max_poll_attempts | `--max-poll-attempts` | integer | 否 | 0 | 最大轮询次数0 表示不自动轮询。 |
| poll_complete | `--poll-complete` | boolean | 否 | - | 是否轮询直到任务完成。 |
| Item | Value |
| --------------- | -------------------------------------------------------------------------- |
| Domain | `shared` |
| Tool | `query-task` |
| Async | `No` |
| Local supported | `No` |
| Mode notes | cloud only; this command is used to query the status of cloud async tasks. |
| Idempotency | No additional idempotency parameters required for this command. |
## Parameters
| Parameter | CLI flag | Type | Required | Default | Description |
| --------------------- | ------------------------- | ------- | -------- | ------- | -------------------------------------------------- |
| task_id | `--task-id` | string | Yes | - | Task ID to query. |
| poll_interval_seconds | `--poll-interval-seconds` | number | No | 10 | Polling interval in seconds. |
| max_poll_attempts | `--max-poll-attempts` | integer | No | 0 | Maximum polling attempts; 0 disables auto-polling. |
| poll_complete | `--poll-complete` | boolean | No | - | Whether to poll until the task is complete. |
## Example
## 调用示例
```bash
mediakit-cli shared query-task \
--task-id task_demo_001 \
@@ -30,8 +33,9 @@ mediakit-cli shared query-task \
--max-poll-attempts 12
```
## 受理响应
异步媒体处理命令提交成功后通常先返回如下受理结果:
## Acceptance response
Async media-processing commands typically return an acceptance response on successful submission:
```json
{
@@ -40,7 +44,8 @@ mediakit-cli shared query-task \
}
```
## 输出格式
## Output format
```json
{
"success": true,
@@ -54,7 +59,8 @@ mediakit-cli shared query-task \
}
```
## 任务结果查询
当前命令本身就是任务查询入口,无需再次查询。
## Task result lookup
- 当前命令:`mediakit-cli shared query-task`
This command is itself the task-query entry point; no further query is required.
- Current command: `mediakit-cli shared query-task`
+14 -2
View File
@@ -2,7 +2,7 @@
name: byted-mediakit-video
version: "1.0.0"
license: "MIT"
description: "视频处理,涵盖视频画质增强、视频理解、字幕擦除等能力。包含能力:enhance-video, erase-video-subtitle-pro。当用户需要使用 video 域的 MediaKit CLI 能力时触发。"
description: "视频处理,涵盖视频画质增强、视频理解、字幕擦除等能力。包含能力:analyze-video-highlights, analyze-video-storyline, asr-subtitles, enhance-video, enhance-video-generative, erase-video-subtitle, erase-video-subtitle-pro, generate-highlights-microdrama, generate-highlights-minigame, matte-greenscreen-video, matte-portrait-video, probe-video-metadata, segment-scenes, video-ocr。当用户需要使用 video 域的 MediaKit CLI 能力时触发。"
permissions:
- shell
metadata:
@@ -11,7 +11,7 @@ metadata:
cliHelp: "mediakit-cli video --help"
product: mediakit-cli/skills
domain: video
capability_count: 2
capability_count: 14
---
# Video Skills
@@ -23,5 +23,17 @@ metadata:
| 工具 | 说明 | 参数声明 | 参考文档 |
|------|------|----------|----------|
| analyze-video-highlights | 智能捕捉视频"情绪波峰"与"关键动作"输出精准时间戳、高光打分、OCR 文本和画面描述等元数据,供下游进行更灵活的二次开发。 支持短剧Miniseries和小游戏Game两种分析模型。 使用限制:单次最多 100 个视频,累计时长不超过 300 分钟。 | `video_urls:array<string>, model:string, mode:string, minigame_info?:object{name?:string, play_definition?:string, highlight_definition?:string}, callback_args?:string, client_token?:string` | [reference/analyze-video-highlights.md](reference/analyze-video-highlights.md) |
| analyze-video-storyline | 智能解析影视剧内容,生成结构化剧情线,供智能剪辑、内容检索与互动播放等场景使用。 基于大模型视频理解能力,对输入的单个或多个长视频(如电影、电视剧)进行分析,提取并组织成一份完整的故事线。 该故事线由一系列按时间顺序排列的剧情片段Clips和基于片段聚合的高光故事线Highlights组成。 使用限制:单次最多 30 个视频,单个视频时长不超过 2.5 小时。 | `video_urls:array<string>, enable_snapshot?:boolean, callback_args?:string, client_token?:string` | [reference/analyze-video-storyline.md](reference/analyze-video-storyline.md) |
| asr-subtitles | 对输入视频或音频进行语音识别,输出带时间戳的字幕片段。 支持格式主流音视频格式如mp4、mov、mp3、m4a、wav等。 输入video_url和audio_url二选一。 | `video_url?:string, audio_url?:string, content_type?:string, language?:string, enable_speaker_info?:boolean, enable_confidence?:boolean, callback_args?:string, client_token?:string` | [reference/asr-subtitles.md](reference/asr-subtitles.md) |
| enhance-video | 画质增强:针对 AIGC / UGC / 短剧 / 教育 / 游戏 / 老片修复等场景,提供画质提升 + 超分增强一站式解决方案。依托 AI MediaKit 智能媒体处理引擎,融合视频内容理解、画质指标智能决策、多维度增强原子算法,实现画质的全面优化。 支持格式主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。 使用限制单文件大小不超过100G。 | `video_url:string, scene?:string, tool_version?:string, resolution?:string, resolution_limit?:integer, fps?:number, callback_args?:string, client_token?:string` | [reference/enhance-video.md](reference/enhance-video.md) |
| enhance-video-generative | 生成式视频增强修复generative_video_restoration是基于扩散大模型Diffusion-based Large Model的生成式视频修复技术。不仅可以还原被破坏的像素更借助大规模预训练积累的丰富视觉先验主动补全细节、理解语义生成真实、自然、高保真的视频内容。 | `video_url:string, resolution?:string, bitrate_level?:string, fps?:number, callback_args?:string, client_token?:string` | [reference/enhance-video-generative.md](reference/enhance-video-generative.md) |
| erase-video-subtitle | 智能检测并擦除视频画面中已有的硬字幕,保留原始背景。 支持格式主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。 | `video_url:string, callback_args?:string, client_token?:string` | [reference/erase-video-subtitle.md](reference/erase-video-subtitle.md) |
| erase-video-subtitle-pro | 针对视频中的字幕,实现高质量的无痕擦除,最大程度的还原视频画面。 支持格式主流视频格式如mp4、flv、ts、avi、mov、wmv、mkv。 | `video_url:string, mode?:string, output_encode_mode?:string, erase_ratio_location?:array<object{top_left_x:number, top_left_y:number, bottom_right_x:number, bottom_right_y:number}>, callback_args?:string, client_token?:string` | [reference/erase-video-subtitle-pro.md](reference/erase-video-subtitle-pro.md) |
| generate-highlights-microdrama | 深度理解短剧角色、剧情与故事线,自动提取高光片段并混剪成投流视频。 支持故事线混剪模式StorylineCuts可选"短剧三要素"视觉模板,输出高光集锦、单集预告等。 支持输出详细分镜信息storyboard。 使用限制:单次最多 100 个视频,累计时长不超过 300 分钟。 | `video_urls:array<string>, mode?:string, enable_generate_video?:boolean, enable_return_poster?:boolean, edit_param?:object{mode:string, template_edit?:object{template?:string, title?:string, hint?:string}}, highlight_cuts_param?:object{enable_storyboard?:boolean, min_duration?:number, max_duration?:number, max_number?:integer, cut_mode?:string}, opening_hook_param?:object{enable_opening_hook?:boolean, min_duration?:number, max_duration?:number, min_clip_duration?:number, min_score?:number}, callback_args?:string, client_token?:string` | [reference/generate-highlights-microdrama.md](reference/generate-highlights-microdrama.md) |
| generate-highlights-minigame | 识别小游戏录屏视频中的核心玩法与高光事件(如连击、通关、极限操作等), 快速生成用于买量的视频素材。支持提供游戏名称、玩法描述、高光定义以辅助模型更精准识别。 使用限制:本期仅支持单视频输入。 | `video_urls:array<string>, mode?:string, enable_generate_video?:boolean, minigame_info?:object{name?:string, play_definition?:string, highlight_definition?:string}, callback_args?:string, client_token?:string` | [reference/generate-highlights-minigame.md](reference/generate-highlights-minigame.md) |
| matte-greenscreen-video | 对以绿幕或纯色为背景的视频进行抠图,自动识别主体(人物、物品、动物等),同时移除背景,生成背景透明的视频。 输出视频格式为 WEBM默认或 MOV分辨率与原片对齐。 支持的格式:主流视频格式如 mp4、flv、ts、avi、mov、mkv、wmv。 | `video_url:string, format?:string, callback_args?:string, client_token?:string` | [reference/matte-greenscreen-video.md](reference/matte-greenscreen-video.md) |
| matte-portrait-video | 自动识别人物主体,同时移除背景,生成背景透明的视频,适用于背景替换等场景。 输出格式为 WEBM默认或 MOV分辨率与原片对齐。 支持的格式:主流视频格式如 mp4、flv、ts、avi、mov、mkv、wmv。 | `video_url:string, format?:string, callback_args?:string, client_token?:string` | [reference/matte-portrait-video.md](reference/matte-portrait-video.md) |
| probe-video-metadata | 对输入视频 URL 进行探测输出标准化媒资元信息覆盖容器层format_meta、视频流层video_stream_meta与音频流层audio_stream_meta。 字段分类参考 ffprobe并对 VOD 原始返回做精简与统一,便于上层做分辨率/帧率/码率/编码等策略判断。 使用限制:仅支持公网 HTTP/HTTPS URL输入视频分辨率最高支持 4K。 | `video_url:string, callback_args?:string, client_token?:string` | [reference/probe-video-metadata.md](reference/probe-video-metadata.md) |
| segment-scenes | 依据视频转场与画面变化自动切分场景,输出切片时间轴和(可选)切片文件。 支持格式MP4、FLV、ASF、RM、RMVB、MPEG、MOV、AVI、MPEGTS、M4S、WMV、3GP、TS、MPG、WEBM、MKV、WM、MPE、VOB、DAT、MP4V、M4V、F4V、MXF、QT 等主流视频格式。 使用限制:单个视频时长不超过 2 小时。 | `video_url:string, enable_clip_fade?:boolean, segment_threshold?:number, min_duration?:number, max_duration?:number, callback_args?:string, client_token?:string` | [reference/segment-scenes.md](reference/segment-scenes.md) |
| video-ocr | 识别视频画面中的字幕/文字内容,输出带时间戳的字幕片段。 支持格式:主流视频格式如 mp4、flv、ts、avi、mov、wmv、mkv。 | `video_url:string, mode?:string, callback_args?:string, client_token?:string` | [reference/video-ocr.md](reference/video-ocr.md) |