@mediaio/cli 0.1.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 ADDED
@@ -0,0 +1,220 @@
1
+ # media-plugin-cli
2
+
3
+ Media.io CLI 的 npm 安装与启动层。该包本身不实现 Media.io API,而是在
4
+ `postinstall` 阶段下载与当前操作系统、CPU 架构匹配的 `media-plugin-bin` Go
5
+ binary,并通过 JavaScript launcher 透传参数、标准输入输出、signal 和退出码。
6
+
7
+ 整体技术方案参考:
8
+ `media-plugin-docs/architecture/MCP、CLI与Agent插件技术方案-v2.md`。
9
+
10
+ > 正式发布包名为 `@mediaio/cli`。安装、排障和发布均以 `package.json.name` 为准。
11
+
12
+ ## 架构位置
13
+
14
+ ```text
15
+ npm install -g @mediaio/cli
16
+ ↓ postinstall
17
+ install.js 下载 vendor/mediaio(Windows 为 vendor/mediaio.exe)
18
+
19
+ mediaio 命令 → bin/mediaio.js → bin/run.js → Go binary
20
+
21
+ Media.io 公网 API
22
+ ```
23
+
24
+ `media-plugin-main` 中的 Agent Skills 可以复用这套 CLI/binary 基座;本仓库不包含
25
+ Skills、MCP 服务或 Media.io API client 实现。
26
+
27
+ ## 环境要求
28
+
29
+ - Node.js 14 或更高版本。
30
+ - npm、pnpm、Yarn 或 Bun;安装器会记录检测到的包管理器。
31
+ - 系统提供 `tar` 命令。当前安装器使用 `tar` 解压 binary,Windows 环境也必须可用。
32
+ - 能访问当前配置的 GitHub Release 下载地址。
33
+
34
+ ## 安装
35
+
36
+ ```bash
37
+ npm install -g @mediaio/cli
38
+ ```
39
+
40
+ 安装完成后:
41
+
42
+ ```bash
43
+ mediaio --help
44
+ mediaio auth login
45
+ mediaio generate list
46
+ ```
47
+
48
+ CLI 只提供 `mediaio` 命令,不提供缩写别名。命令、binary、Release asset 和安装目录
49
+ 均使用完整的 `mediaio` 名称。
50
+
51
+ ## postinstall 做了什么
52
+
53
+ `npm install` 会执行:
54
+
55
+ ```text
56
+ node install.js
57
+ ```
58
+
59
+ 当前安装流程:
60
+
61
+ 1. 将 Node 平台映射为 binary 平台名:`darwin`、`linux`、`windows`。
62
+ 2. 将 Node 架构映射为 Go 架构名:`x64 → amd64`、`arm64 → arm64`。
63
+ 3. 读取 npm 包版本作为 binary 版本。
64
+ 4. 下载对应的 `.tar.gz` Release asset。
65
+ 5. 从压缩包根目录提取 `mediaio` 或 `mediaio.exe` 到 `vendor/`。
66
+ 6. Unix 平台为 binary 增加可执行权限。
67
+ 7. 写入 `vendor/install.json`,记录安装方式、包管理器、包名和版本。
68
+
69
+ 当前下载规则:
70
+
71
+ ```text
72
+ https://github.com/media-io/cli/releases/download/v<version>/mediaio_<version>_<os>_<arch>.tar.gz
73
+ ```
74
+
75
+ 例如 npm 包版本为 `1.0.3`、运行环境为 Apple Silicon macOS 时,会下载:
76
+
77
+ ```text
78
+ https://github.com/media-io/cli/releases/download/v1.0.3/mediaio_1.0.3_darwin_arm64.tar.gz
79
+ ```
80
+
81
+ archive 根目录必须直接包含 `mediaio`;Windows archive 必须直接包含 `mediaio.exe`。
82
+
83
+ ## launcher 行为
84
+
85
+ `bin/run.js` 启动 `vendor/mediaio` 或 `vendor/mediaio.exe`,并执行以下透传:
86
+
87
+ - 原样传递 CLI 参数。
88
+ - `stdin`、`stdout`、`stderr` 使用 `inherit`。
89
+ - 子进程被 signal 终止时,将 signal 传递给当前 Node 进程。
90
+ - 正常退出时返回 Go binary 的 exit code。
91
+ - 向 binary 注入 `mediaio_INSTALL_METHOD=npm`。
92
+ - 向 binary 注入 `mediaio_PACKAGE_MANAGER=<npm|pnpm|yarn|bun>`。
93
+
94
+ ## 本地开发
95
+
96
+ 只检查 JavaScript 语法,不触发 binary 下载:
97
+
98
+ ```bash
99
+ node --check install.js
100
+ node --check bin/mediaio.js
101
+ node --check bin/run.js
102
+ npm pack --dry-run
103
+ ```
104
+
105
+ 使用相邻的 `media-plugin-bin` 本地构建产物联调:
106
+
107
+ ```bash
108
+ # 先在 ../media-plugin-bin 中构建
109
+ cd ../media-plugin-bin
110
+ mkdir -p dist
111
+ go build -trimpath -o dist/mediaio .
112
+
113
+ # 回到本仓库,跳过 postinstall 并放入本地 binary
114
+ cd ../media-plugin-cli
115
+ npm install --ignore-scripts
116
+ mkdir -p vendor
117
+ cp ../media-plugin-bin/dist/mediaio vendor/mediaio
118
+ chmod +x vendor/mediaio
119
+
120
+ node bin/mediaio.js --help
121
+ node bin/mediaio.js generate list
122
+ ```
123
+
124
+ Windows 请复制 `mediaio.exe`:
125
+
126
+ ```powershell
127
+ New-Item -ItemType Directory -Force vendor
128
+ Copy-Item ..\media-plugin-bin\dist\mediaio.exe vendor\mediaio.exe
129
+ node bin\mediaio.js --help
130
+ ```
131
+
132
+ ## 发布
133
+
134
+ npm 包与 Go binary 当前使用同一个版本号,必须成套发布。
135
+
136
+ 1. 在 `media-plugin-bin` 中完成测试和多平台构建。
137
+ 2. 创建 `v<version>` Release,并上传对应的 binary archives。
138
+ 3. 确认每个 archive 的名称和根目录文件符合安装器约定。
139
+ 4. 将本仓库 `package.json.version` 设置为相同版本。
140
+ 5. 检查 npm 包内容并发布。
141
+
142
+ ```bash
143
+ npm pack --dry-run
144
+ npm pack
145
+ npm publish --access public
146
+ ```
147
+
148
+ 发布后应在干净环境验证:
149
+
150
+ ```bash
151
+ npm install -g @mediaio/cli@<version>
152
+ mediaio --help
153
+ ```
154
+
155
+ v2 方案首期 binary matrix 为:
156
+
157
+ ```text
158
+ darwin/amd64
159
+ darwin/arm64
160
+ linux/amd64
161
+ linux/arm64
162
+ windows/amd64
163
+ ```
164
+
165
+ 注意:当前 `package.json` 的 `os` 与 `cpu` 字段、`install.js` 的映射逻辑也会允许
166
+ `windows/arm64` 进入安装流程。正式发布前必须二选一:提供
167
+ `mediaio_<version>_windows_arm64.tar.gz`,或收紧安装器/包元数据,避免用户安装后得到 404。
168
+
169
+ ## 排障
170
+
171
+ ### binary 不存在
172
+
173
+ 如果看到 `binary not found at .../vendor/mediaio`,说明 `postinstall` 未执行或执行失败。
174
+
175
+ ```bash
176
+ npm uninstall -g @mediaio/cli
177
+ npm install -g @mediaio/cli
178
+ ```
179
+
180
+ 使用 `npm install --ignore-scripts` 安装时不会下载 binary;当前版本尚未提供独立的
181
+ `mediaio install` 修复命令。
182
+
183
+ ### 下载返回 404
184
+
185
+ 检查以下三项是否完全一致:
186
+
187
+ - `package.json.version`;
188
+ - GitHub Release tag `v<version>`;
189
+ - asset 名称 `mediaio_<version>_<os>_<arch>.tar.gz`。
190
+
191
+ ### 解压失败
192
+
193
+ 确认系统存在 `tar`,并确认 archive 根目录直接包含 `mediaio` 或 `mediaio.exe`。
194
+
195
+ ### 平台不支持
196
+
197
+ 当前安装器只识别:
198
+
199
+ ```text
200
+ darwin | linux | windows
201
+ amd64 | arm64
202
+ ```
203
+
204
+ Node 报告的其他 `process.platform` 或 `process.arch` 会直接终止安装。
205
+
206
+ ## 当前实现与 v2 目标的差异
207
+
208
+ | 领域 | 当前实现 | v2 目标 |
209
+ |---|---|---|
210
+ | npm 包名 | `@mediaio/cli` | `@mediaio/cli` |
211
+ | 命令入口 | 仅 `mediaio` | 仅 `mediaio`,不提供缩写别名 |
212
+ | 版本锁定 | npm version 直接拼接下载 URL | 独立 binary manifest 固定精确版本 |
213
+ | 完整性校验 | 尚无 checksum/signature 校验 | SHA-256、签名和 binary version 校验 |
214
+ | 下载安全 | 直接写目标 tarball,未配置 timeout | 随机临时文件、timeout、原子安装和统一清理 |
215
+ | 平台识别 | OS/CPU;未识别 libc | Linux 明确 glibc/musl 策略 |
216
+ | 安装修复 | 重新安装 npm 包 | 显式 install/repair/upgrade/offline 入口 |
217
+ | metadata | 写入 `install.json`,launcher 损坏时降级 | metadata 与 binary 原子安装,损坏时明确失败 |
218
+
219
+ 这些目标完成前,README 和发布说明应明确当前能力边界,不能声称安装器已经验证
220
+ checksum、签名或 binary version。
package/bin/mediaio.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require("./run.js")();
package/bin/run.js ADDED
@@ -0,0 +1,51 @@
1
+ const { spawn } = require("child_process");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+
5
+ function detectPackageManager() {
6
+ const ua = process.env.npm_config_user_agent || "";
7
+ if (ua.startsWith("pnpm/")) return "pnpm";
8
+ if (ua.startsWith("yarn/")) return "yarn";
9
+ if (ua.startsWith("bun/")) return "bun";
10
+ return "";
11
+ }
12
+
13
+ function readInstallMetadata(vendorDir) {
14
+ try {
15
+ return JSON.parse(fs.readFileSync(path.join(vendorDir, "install.json"), "utf8"));
16
+ } catch (_) {
17
+ return {};
18
+ }
19
+ }
20
+
21
+ module.exports = function run() {
22
+ const binName = process.platform === "win32" ? "mediaio.exe" : "mediaio";
23
+ const vendorDir = path.join(__dirname, "..", "vendor");
24
+ const bin = path.join(vendorDir, binName);
25
+ if (!fs.existsSync(bin)) {
26
+ console.error(
27
+ "@mediaio/cli: binary not found at " +
28
+ bin +
29
+ ". Reinstall: npm i -g @mediaio/cli"
30
+ );
31
+ process.exit(1);
32
+ }
33
+ const metadata = readInstallMetadata(vendorDir);
34
+ const child = spawn(bin, process.argv.slice(2), {
35
+ stdio: "inherit",
36
+ env: {
37
+ ...process.env,
38
+ mediaio_INSTALL_METHOD: "npm",
39
+ mediaio_PACKAGE_MANAGER:
40
+ metadata.package_manager || detectPackageManager() || "npm",
41
+ },
42
+ });
43
+ child.on("exit", (code, signal) => {
44
+ if (signal) process.kill(process.pid, signal);
45
+ else process.exit(code ?? 0);
46
+ });
47
+ child.on("error", (err) => {
48
+ console.error("@mediaio/cli: failed to exec —", err.message);
49
+ process.exit(1);
50
+ });
51
+ };
package/install.js ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const https = require("https");
5
+ const { execFileSync } = require("child_process");
6
+
7
+ const pkg = require("./package.json");
8
+ const VERSION = pkg.version;
9
+
10
+ const PLATFORM_MAP = { darwin: "darwin", linux: "linux", win32: "windows" };
11
+ const ARCH_MAP = { x64: "amd64", arm64: "arm64" };
12
+
13
+ const platform = PLATFORM_MAP[process.platform];
14
+ const arch = ARCH_MAP[process.arch];
15
+
16
+ if (!platform || !arch) {
17
+ console.error(
18
+ `@mediaio/cli: unsupported platform ${process.platform}/${process.arch}`
19
+ );
20
+ console.error("Supported: darwin|linux|windows × x64|arm64");
21
+ process.exit(1);
22
+ }
23
+
24
+ const binName = platform === "windows" ? "mediaio.exe" : "mediaio";
25
+ const tarball = `mediaio_${VERSION}_${platform}_${arch}.tar.gz`;
26
+ const url = `https://github.com/media-io/cli/releases/download/v${VERSION}/${tarball}`;
27
+
28
+ const vendorDir = path.join(__dirname, "vendor");
29
+ fs.mkdirSync(vendorDir, { recursive: true });
30
+ const tarballPath = path.join(vendorDir, tarball);
31
+ const metadataPath = path.join(vendorDir, "install.json");
32
+
33
+ function detectPackageManager() {
34
+ const ua = process.env.npm_config_user_agent || "";
35
+ if (ua.startsWith("pnpm/")) return "pnpm";
36
+ if (ua.startsWith("yarn/")) return "yarn";
37
+ if (ua.startsWith("bun/")) return "bun";
38
+ return "npm";
39
+ }
40
+
41
+ function download(targetUrl, dest, redirects = 0) {
42
+ return new Promise((resolve, reject) => {
43
+ if (redirects > 5) return reject(new Error("too many redirects"));
44
+ const file = fs.createWriteStream(dest);
45
+ https
46
+ .get(targetUrl, (res) => {
47
+ if (
48
+ res.statusCode >= 300 &&
49
+ res.statusCode < 400 &&
50
+ res.headers.location
51
+ ) {
52
+ file.close();
53
+ fs.unlinkSync(dest);
54
+ return resolve(download(res.headers.location, dest, redirects + 1));
55
+ }
56
+ if (res.statusCode !== 200) {
57
+ file.close();
58
+ try {
59
+ fs.unlinkSync(dest);
60
+ } catch (_) {}
61
+ return reject(new Error(`HTTP ${res.statusCode} for ${targetUrl}`));
62
+ }
63
+ res.pipe(file);
64
+ file.on("finish", () => file.close(() => resolve()));
65
+ })
66
+ .on("error", (err) => {
67
+ try {
68
+ fs.unlinkSync(dest);
69
+ } catch (_) {}
70
+ reject(err);
71
+ });
72
+ });
73
+ }
74
+
75
+ (async () => {
76
+ console.log(`@mediaio/cli: downloading ${url}`);
77
+ await download(url, tarballPath);
78
+ console.log(`downloaded ${url}`);
79
+ // pipe via stdin: bsdtar on Windows misparses "-f" paths under "@scope" dirs as user@host remote syntax
80
+ execFileSync("tar", ["-xz", "-C", vendorDir, binName], {
81
+ input: fs.readFileSync(tarballPath),
82
+ });
83
+ if (platform !== "windows") {
84
+ fs.chmodSync(path.join(vendorDir, binName), 0o755);
85
+ }
86
+ fs.writeFileSync(
87
+ metadataPath,
88
+ JSON.stringify(
89
+ {
90
+ install_method: "npm",
91
+ package_manager: detectPackageManager(),
92
+ package_name: pkg.name,
93
+ version: VERSION,
94
+ },
95
+ null,
96
+ 2
97
+ ) + "\n"
98
+ );
99
+ fs.unlinkSync(tarballPath);
100
+ console.log("@mediaio/cli: installed");
101
+ })().catch((err) => {
102
+ console.error("@mediaio/cli: install failed —", err.message);
103
+ process.exit(1);
104
+ });
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@mediaio/cli",
3
+ "version": "0.1.0",
4
+ "description": "mediaio AI CLI — generate images and videos from the terminal.",
5
+ "bin": {
6
+ "mediaio": "bin/mediaio.js"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node install.js"
10
+ },
11
+ "files": [
12
+ "bin/",
13
+ "install.js",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=14"
18
+ },
19
+ "license": "MIT",
20
+ "homepage": "https://media.io",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/media-io/cli.git"
24
+ },
25
+ "os": [
26
+ "darwin",
27
+ "linux",
28
+ "win32"
29
+ ],
30
+ "cpu": [
31
+ "x64",
32
+ "arm64"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ }
37
+ }