@seacloudai/seacloud-cli 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/README.zh.md +228 -0
- package/assets/seacloud-cli-image-en.png +0 -0
- package/assets/seacloud-cli-image-zh.png +0 -0
- package/npm-bundles/SHA256SUMS +5 -0
- package/npm-bundles/seacloud_0.0.3_darwin_amd64.tar.gz +0 -0
- package/npm-bundles/seacloud_0.0.3_darwin_arm64.tar.gz +0 -0
- package/npm-bundles/seacloud_0.0.3_linux_amd64.tar.gz +0 -0
- package/npm-bundles/seacloud_0.0.3_linux_arm64.tar.gz +0 -0
- package/npm-bundles/seacloud_0.0.3_windows_amd64.zip +0 -0
- package/package.json +41 -0
- package/scripts/postinstall.js +284 -0
- package/scripts/seacloud-skills.js +32 -0
- package/scripts/seacloud.js +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VtrixAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<p>
|
|
3
|
+
<img src="./assets/seacloud-cli-image-en.png" alt="SeaCloud CLI banner">
|
|
4
|
+
</p>
|
|
5
|
+
<h1>SeaCloud CLI</h1>
|
|
6
|
+
<h3>The official CLI for the SeaCloud AI Platform</h3>
|
|
7
|
+
<p>
|
|
8
|
+
Built for AI agents. Authenticate, browse models, submit multimodal tasks,
|
|
9
|
+
track task status, and manage SkillHub skills from any agent or terminal.
|
|
10
|
+
</p>
|
|
11
|
+
<p>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@seacloudai/seacloud-cli">
|
|
13
|
+
<img src="https://img.shields.io/npm/v/@seacloudai/seacloud-cli" alt="npm version">
|
|
14
|
+
</a>
|
|
15
|
+
<img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License">
|
|
16
|
+
<img src="https://img.shields.io/badge/node-%3E%3D18-339933" alt="Node.js >= 18">
|
|
17
|
+
<img src="https://img.shields.io/badge/go-%3E%3D1.26-00ADD8" alt="Go >= 1.26">
|
|
18
|
+
</p>
|
|
19
|
+
<p>
|
|
20
|
+
<a href="./README.zh.md">中文文档</a>
|
|
21
|
+
·
|
|
22
|
+
<a href="https://vtrix.ai/">Official Website</a>
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Authentication**: Sign in with the browser-based device flow and store credentials locally.
|
|
29
|
+
- **Model discovery**: List available models and inspect full parameter specs in human-readable or JSON form.
|
|
30
|
+
- **Task execution**: Submit multimodal generation tasks from the CLI with parameter validation and structured output options.
|
|
31
|
+
- **Task tracking**: Poll task status and print result URLs or full JSON responses.
|
|
32
|
+
- **SkillHub integration**: Search, install, and configure agent skills from SeaCloud SkillHub.
|
|
33
|
+
- **Agent-friendly UX**: Supports `--dry-run`, JSON output, stable command shapes, and copy-pasteable examples.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
### Install with npm
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g @seacloudai/seacloud-cli
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> Requires Node.js 18+
|
|
44
|
+
|
|
45
|
+
### Install from source
|
|
46
|
+
|
|
47
|
+
Default install:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/SeaCloudAI/seacloud-cli.git
|
|
51
|
+
cd seacloud-cli
|
|
52
|
+
make install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> Requires Go 1.26+
|
|
56
|
+
> The installed binary uses the default service endpoints for the public CLI. You can override them with `SEACLOUD_BASE_URL`, `SEACLOUD_MODELS_URL`, `SEACLOUD_GENERATION_URL`, and `SEACLOUD_SKILLHUB_URL`.
|
|
57
|
+
|
|
58
|
+
If `/usr/local/bin` requires elevated permissions:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
sudo make install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If you prefer a user-local install without `sudo`:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
make install PREFIX=$HOME/.local
|
|
68
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Download binaries
|
|
72
|
+
|
|
73
|
+
Prebuilt binaries are published on the [Releases](https://github.com/SeaCloudAI/seacloud-cli/releases) page for:
|
|
74
|
+
|
|
75
|
+
- macOS `amd64`
|
|
76
|
+
- macOS `arm64`
|
|
77
|
+
- Linux `amd64`
|
|
78
|
+
- Linux `arm64`
|
|
79
|
+
- Windows `amd64`
|
|
80
|
+
|
|
81
|
+
## Quick Start
|
|
82
|
+
|
|
83
|
+
### Authenticate
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
seacloud auth login
|
|
87
|
+
seacloud auth status
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Browse models
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
seacloud models list
|
|
94
|
+
seacloud models list --type video
|
|
95
|
+
seacloud models spec kirin_v2_6_i2v
|
|
96
|
+
seacloud models spec kirin_v2_6_i2v --output json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Run a task
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
seacloud run kirin_v2_6_i2v --param image=https://example.com/cat.jpg
|
|
103
|
+
seacloud run kirin_v2_6_i2v --param prompt="a cat running" --param duration=5
|
|
104
|
+
seacloud run kirin_v2_6_i2v --param mode=pro --output url
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Check task status
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
seacloud task status <task_id>
|
|
111
|
+
seacloud task status <task_id> --output url
|
|
112
|
+
seacloud task status <task_id> --output json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Manage skills
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
seacloud skills list
|
|
119
|
+
seacloud skills find prompt
|
|
120
|
+
seacloud skills add some-skill
|
|
121
|
+
seacloud skills config --show
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Commands
|
|
125
|
+
|
|
126
|
+
### `seacloud auth`
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
seacloud auth login
|
|
130
|
+
seacloud auth status
|
|
131
|
+
seacloud auth logout
|
|
132
|
+
seacloud auth set-key <api-key>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `seacloud models`
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
seacloud models list
|
|
139
|
+
seacloud models list --keywords kirin
|
|
140
|
+
seacloud models list --output id
|
|
141
|
+
seacloud models spec <model_id>
|
|
142
|
+
seacloud models spec <model_id> --output json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `seacloud run`
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
seacloud run <model_id> --param key=value
|
|
149
|
+
seacloud run <model_id> --param prompt="hello" --param duration=5
|
|
150
|
+
seacloud run <model_id> --output json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Nested fields use dot notation:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
seacloud run some_model \
|
|
157
|
+
--param camera_control.type=simple \
|
|
158
|
+
--param camera_control.speed=2
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### `seacloud task`
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
seacloud task status <task_id>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `seacloud skills`
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
seacloud skills list
|
|
171
|
+
seacloud skills find [query]
|
|
172
|
+
seacloud skills add <slug>
|
|
173
|
+
seacloud skills config --show
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### `seacloud version`
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
seacloud version
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Output and Automation
|
|
183
|
+
|
|
184
|
+
- Use `--output json` where supported for machine-readable responses.
|
|
185
|
+
- Use `--output url` on task commands to print only result URLs.
|
|
186
|
+
- Use global `--dry-run` to inspect execution without sending requests.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
seacloud --dry-run run kirin_v2_6_i2v --param prompt=test
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Release
|
|
195
|
+
|
|
196
|
+
Release assets are built from source and published to GitHub Releases.
|
|
197
|
+
The npm package downloads the matching prebuilt binary for the user platform during installation.
|
|
198
|
+
|
|
199
|
+
If you maintain releases manually, the repository includes:
|
|
200
|
+
|
|
201
|
+
- `scripts/build.sh`
|
|
202
|
+
- `.goreleaser.yml`
|
|
203
|
+
- `scripts/set-release-version.js`
|
|
204
|
+
|
|
205
|
+
## Repository Layout
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
seacloud-cli/
|
|
209
|
+
├── cmd/ # CLI command definitions
|
|
210
|
+
├── internal/auth/ # Auth client and login flow
|
|
211
|
+
├── internal/models/ # Model list and spec APIs
|
|
212
|
+
├── internal/generation/# Task submit and polling
|
|
213
|
+
├── internal/skillhub/ # SkillHub client and install logic
|
|
214
|
+
├── package.json # npm package manifest
|
|
215
|
+
├── scripts/ # Build, release, and npm wrapper scripts
|
|
216
|
+
└── skills/ # Built-in skill definitions
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Contributing
|
|
220
|
+
|
|
221
|
+
Issues and pull requests are welcome. Before sending larger changes, it is best to open an issue first so the scope can be discussed.
|
|
222
|
+
|
|
223
|
+
For local verification:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
go test ./...
|
|
227
|
+
go run . --help
|
|
228
|
+
```
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<p>
|
|
3
|
+
<img src="./assets/seacloud-cli-image-zh.png" alt="SeaCloud CLI banner">
|
|
4
|
+
</p>
|
|
5
|
+
<h1>SeaCloud CLI</h1>
|
|
6
|
+
<h3>SeaCloud AI 平台的官方命令行界面</h3>
|
|
7
|
+
<p>
|
|
8
|
+
专为人工智能代理而设计。可从任何代理或终端完成认证、模型查询、
|
|
9
|
+
多模态任务执行、任务状态追踪和 SkillHub 技能管理。
|
|
10
|
+
</p>
|
|
11
|
+
<p>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@seacloudai/seacloud-cli">
|
|
13
|
+
<img src="https://img.shields.io/npm/v/@seacloudai/seacloud-cli" alt="npm version">
|
|
14
|
+
</a>
|
|
15
|
+
<img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License">
|
|
16
|
+
<img src="https://img.shields.io/badge/node-%3E%3D18-339933" alt="Node.js >= 18">
|
|
17
|
+
<img src="https://img.shields.io/badge/go-%3E%3D1.26-00ADD8" alt="Go >= 1.26">
|
|
18
|
+
</p>
|
|
19
|
+
<p>
|
|
20
|
+
<a href="./README.md">English</a>
|
|
21
|
+
·
|
|
22
|
+
<a href="https://vtrix.ai/">SeaCloud官网</a>
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
## 功能特性
|
|
27
|
+
|
|
28
|
+
- **认证登录**:支持浏览器设备码登录,并将凭证安全保存在本地。
|
|
29
|
+
- **模型发现**:列出可用模型,并以可读文本或 JSON 查看完整参数规格。
|
|
30
|
+
- **任务执行**:通过 CLI 提交多模态生成任务,支持参数校验和结构化输出。
|
|
31
|
+
- **任务追踪**:轮询任务状态,输出结果 URL 或完整 JSON。
|
|
32
|
+
- **SkillHub 集成**:搜索、安装和配置 SeaCloud SkillHub 技能。
|
|
33
|
+
- **Agent 友好**:支持 `--dry-run`、JSON 输出、稳定命令结构和可直接复制的示例。
|
|
34
|
+
|
|
35
|
+
## 安装
|
|
36
|
+
|
|
37
|
+
### 使用 npm 安装
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g @seacloudai/seacloud-cli
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> 需要 Node.js 18+
|
|
44
|
+
|
|
45
|
+
### 从源码安装
|
|
46
|
+
|
|
47
|
+
默认安装方式:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/SeaCloudAI/seacloud-cli.git
|
|
51
|
+
cd seacloud-cli
|
|
52
|
+
make install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> 需要 Go 1.26+
|
|
56
|
+
> 安装后的二进制会注入公开版本使用的默认服务地址。你也可以通过 `SEACLOUD_BASE_URL`、`SEACLOUD_MODELS_URL`、`SEACLOUD_GENERATION_URL`、`SEACLOUD_SKILLHUB_URL` 覆盖这些地址。
|
|
57
|
+
|
|
58
|
+
如果 `/usr/local/bin` 需要更高权限:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
sudo make install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
如果你想在无 `sudo` 的情况下安装到用户目录:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
make install PREFIX=$HOME/.local
|
|
68
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 下载预编译二进制
|
|
72
|
+
|
|
73
|
+
预编译二进制发布在 [Releases](https://github.com/SeaCloudAI/seacloud-cli/releases) 页面,当前支持:
|
|
74
|
+
|
|
75
|
+
- macOS `amd64`
|
|
76
|
+
- macOS `arm64`
|
|
77
|
+
- Linux `amd64`
|
|
78
|
+
- Linux `arm64`
|
|
79
|
+
- Windows `amd64`
|
|
80
|
+
|
|
81
|
+
## 快速开始
|
|
82
|
+
|
|
83
|
+
### 登录认证
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
seacloud auth login
|
|
87
|
+
seacloud auth status
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 查询模型
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
seacloud models list
|
|
94
|
+
seacloud models list --type video
|
|
95
|
+
seacloud models spec kirin_v2_6_i2v
|
|
96
|
+
seacloud models spec kirin_v2_6_i2v --output json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 执行任务
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
seacloud run kirin_v2_6_i2v --param image=https://example.com/cat.jpg
|
|
103
|
+
seacloud run kirin_v2_6_i2v --param prompt="a cat running" --param duration=5
|
|
104
|
+
seacloud run kirin_v2_6_i2v --param mode=pro --output url
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 查询任务状态
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
seacloud task status <task_id>
|
|
111
|
+
seacloud task status <task_id> --output url
|
|
112
|
+
seacloud task status <task_id> --output json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 管理技能
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
seacloud skills list
|
|
119
|
+
seacloud skills find prompt
|
|
120
|
+
seacloud skills add some-skill
|
|
121
|
+
seacloud skills config --show
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 命令概览
|
|
125
|
+
|
|
126
|
+
### `seacloud auth`
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
seacloud auth login
|
|
130
|
+
seacloud auth status
|
|
131
|
+
seacloud auth logout
|
|
132
|
+
seacloud auth set-key <api-key>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `seacloud models`
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
seacloud models list
|
|
139
|
+
seacloud models list --keywords kirin
|
|
140
|
+
seacloud models list --output id
|
|
141
|
+
seacloud models spec <model_id>
|
|
142
|
+
seacloud models spec <model_id> --output json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `seacloud run`
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
seacloud run <model_id> --param key=value
|
|
149
|
+
seacloud run <model_id> --param prompt="hello" --param duration=5
|
|
150
|
+
seacloud run <model_id> --output json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
嵌套字段支持 dot notation:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
seacloud run some_model \
|
|
157
|
+
--param camera_control.type=simple \
|
|
158
|
+
--param camera_control.speed=2
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### `seacloud task`
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
seacloud task status <task_id>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `seacloud skills`
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
seacloud skills list
|
|
171
|
+
seacloud skills find [query]
|
|
172
|
+
seacloud skills add <slug>
|
|
173
|
+
seacloud skills config --show
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### `seacloud version`
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
seacloud version
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## 自动化与输出
|
|
183
|
+
|
|
184
|
+
- 在支持的命令上使用 `--output json` 获取机器可读输出。
|
|
185
|
+
- 在任务命令上使用 `--output url` 只打印结果 URL。
|
|
186
|
+
- 使用全局 `--dry-run` 在不发请求的前提下检查执行内容。
|
|
187
|
+
|
|
188
|
+
示例:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
seacloud --dry-run run kirin_v2_6_i2v --param prompt=test
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## 发布说明
|
|
195
|
+
|
|
196
|
+
发布产物由源码构建后上传到 GitHub Releases。
|
|
197
|
+
npm 包在安装时会自动下载当前平台对应的预编译二进制。
|
|
198
|
+
|
|
199
|
+
如果你需要手动维护发布流程,仓库中保留了这些文件:
|
|
200
|
+
|
|
201
|
+
- `scripts/build.sh`
|
|
202
|
+
- `.goreleaser.yml`
|
|
203
|
+
- `scripts/set-release-version.js`
|
|
204
|
+
|
|
205
|
+
## 仓库结构
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
seacloud-cli/
|
|
209
|
+
├── cmd/ # CLI 命令定义
|
|
210
|
+
├── internal/auth/ # 认证客户端与登录流程
|
|
211
|
+
├── internal/models/ # 模型列表与模型规格接口
|
|
212
|
+
├── internal/generation/ # 任务提交与轮询
|
|
213
|
+
├── internal/skillhub/ # SkillHub 客户端与安装逻辑
|
|
214
|
+
├── package.json # npm 包清单
|
|
215
|
+
├── scripts/ # 构建、发版与 npm 包装脚本
|
|
216
|
+
└── skills/ # 内置技能定义
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 参与贡献
|
|
220
|
+
|
|
221
|
+
欢迎提交 Issue 和 Pull Request。对于较大的改动,建议先开一个 Issue 讨论范围。
|
|
222
|
+
|
|
223
|
+
本地验证可使用:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
go test ./...
|
|
227
|
+
go run . --help
|
|
228
|
+
```
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
0526a5a3d950218a9f170521d194f70fbd592d1a878eb143ad04b4c9a9e3041e seacloud_0.0.3_darwin_amd64.tar.gz
|
|
2
|
+
8c42e932084982adde59795bb9a12494688e0394142c04721179a171b6381b88 seacloud_0.0.3_darwin_arm64.tar.gz
|
|
3
|
+
1cfb46f6d0c0c972749dae083ad32b8e5690c97b22bd47639d03a1af48514867 seacloud_0.0.3_linux_amd64.tar.gz
|
|
4
|
+
bdcd4a2a82375c2800cd657be46702e1b32a8abf0034997cca55b0c09f3dfc33 seacloud_0.0.3_linux_arm64.tar.gz
|
|
5
|
+
75a7787446c1c86ce7005b5657913f77144b9ccb03ed6c3420d66adfe8e44207 seacloud_0.0.3_windows_amd64.zip
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seacloudai/seacloud-cli",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "npm installer wrapper for the SeaCloud CLI binary",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"seacloud": "scripts/seacloud.js",
|
|
11
|
+
"seacloud-skills": "scripts/seacloud-skills.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"assets",
|
|
15
|
+
"npm-bundles",
|
|
16
|
+
"scripts/postinstall.js",
|
|
17
|
+
"scripts/seacloud.js",
|
|
18
|
+
"scripts/seacloud-skills.js",
|
|
19
|
+
"vendor"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"prepare:npm-bundles": "node ./scripts/prepare-npm-bundles.js",
|
|
23
|
+
"postinstall": "node ./scripts/postinstall.js"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"adm-zip": "^0.5.16",
|
|
30
|
+
"tar": "^7.4.3"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/SeaCloudAI/seacloud-cli.git"
|
|
35
|
+
},
|
|
36
|
+
"seacloud": {
|
|
37
|
+
"bundledAssetsDir": "npm-bundles",
|
|
38
|
+
"projectName": "seacloud",
|
|
39
|
+
"releaseBaseUrlTemplate": "https://github.com/SeaCloudAI/seacloud-cli/releases/download/v{version}"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const AdmZip = require("adm-zip");
|
|
8
|
+
const tar = require("tar");
|
|
9
|
+
|
|
10
|
+
const rootDir = path.resolve(__dirname, "..");
|
|
11
|
+
const pkg = require(path.join(rootDir, "package.json"));
|
|
12
|
+
|
|
13
|
+
const bundledAssetsDir = path.join(
|
|
14
|
+
rootDir,
|
|
15
|
+
pkg.seacloud?.bundledAssetsDir || "npm-bundles"
|
|
16
|
+
);
|
|
17
|
+
const vendorDir = path.join(rootDir, "vendor");
|
|
18
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "seacloud-npm-"));
|
|
19
|
+
|
|
20
|
+
const SUPPORTED_TARGETS = {
|
|
21
|
+
"darwin-arm64": { os: "darwin", arch: "arm64", ext: "tar.gz", bin: "seacloud" },
|
|
22
|
+
"darwin-x64": { os: "darwin", arch: "amd64", ext: "tar.gz", bin: "seacloud" },
|
|
23
|
+
"linux-arm64": { os: "linux", arch: "arm64", ext: "tar.gz", bin: "seacloud" },
|
|
24
|
+
"linux-x64": { os: "linux", arch: "amd64", ext: "tar.gz", bin: "seacloud" },
|
|
25
|
+
"win32-x64": { os: "windows", arch: "amd64", ext: "zip", bin: "seacloud.exe" }
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async function main() {
|
|
29
|
+
try {
|
|
30
|
+
if (process.env.SEACLOUD_SKIP_POSTINSTALL === "1") {
|
|
31
|
+
log("skip postinstall because SEACLOUD_SKIP_POSTINSTALL=1");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const target = resolveTarget();
|
|
36
|
+
const releaseSources = resolveReleaseSources();
|
|
37
|
+
const projectName = pkg.seacloud?.projectName || "seacloud";
|
|
38
|
+
const version = pkg.version;
|
|
39
|
+
|
|
40
|
+
const assetName = `${projectName}_${version}_${target.os}_${target.arch}.${target.ext}`;
|
|
41
|
+
const archivePath = path.join(tmpDir, assetName);
|
|
42
|
+
const extractDir = path.join(tmpDir, "extract");
|
|
43
|
+
|
|
44
|
+
fs.mkdirSync(vendorDir, { recursive: true });
|
|
45
|
+
fs.rmSync(vendorDir, { recursive: true, force: true });
|
|
46
|
+
fs.mkdirSync(vendorDir, { recursive: true });
|
|
47
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
48
|
+
|
|
49
|
+
log(`downloading ${assetName}`);
|
|
50
|
+
const expectedSha = await resolveExpectedSha(releaseSources, assetName);
|
|
51
|
+
await materializeArchive(releaseSources, assetName, archivePath, expectedSha);
|
|
52
|
+
|
|
53
|
+
await extractArchive(archivePath, extractDir, target.ext);
|
|
54
|
+
const extractedBinary = findFileRecursive(extractDir, target.bin);
|
|
55
|
+
if (!extractedBinary) {
|
|
56
|
+
throw new Error(`failed to locate ${target.bin} in extracted archive`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const finalBinary = path.join(vendorDir, target.bin);
|
|
60
|
+
fs.copyFileSync(extractedBinary, finalBinary);
|
|
61
|
+
if (process.platform !== "win32") {
|
|
62
|
+
fs.chmodSync(finalBinary, 0o755);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
log(`installed ${target.bin} to vendor directory`);
|
|
66
|
+
} finally {
|
|
67
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function resolveTarget() {
|
|
72
|
+
const key = `${process.platform}-${process.arch}`;
|
|
73
|
+
const target = SUPPORTED_TARGETS[key];
|
|
74
|
+
if (!target) {
|
|
75
|
+
throw new Error(`unsupported platform: ${process.platform}/${process.arch}`);
|
|
76
|
+
}
|
|
77
|
+
return target;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveReleaseBaseUrl() {
|
|
81
|
+
return resolveBaseUrl(
|
|
82
|
+
process.env.SEACLOUD_RELEASE_BASE_URL,
|
|
83
|
+
pkg.seacloud?.releaseBaseUrlTemplate,
|
|
84
|
+
"missing seacloud.releaseBaseUrlTemplate in package.json"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function resolveReleaseMirrorBaseUrl() {
|
|
89
|
+
return resolveBaseUrl(
|
|
90
|
+
process.env.SEACLOUD_RELEASE_MIRROR_BASE_URL,
|
|
91
|
+
pkg.seacloud?.releaseMirrorBaseUrlTemplate || null,
|
|
92
|
+
null
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function resolveBaseUrl(fromEnv, template, missingMessage) {
|
|
97
|
+
if (fromEnv) {
|
|
98
|
+
return stripTrailingSlash(fromEnv);
|
|
99
|
+
}
|
|
100
|
+
if (!template) {
|
|
101
|
+
if (missingMessage) {
|
|
102
|
+
throw new Error(missingMessage);
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return stripTrailingSlash(
|
|
107
|
+
template
|
|
108
|
+
.replaceAll("{version}", pkg.version)
|
|
109
|
+
.replaceAll("{tag}", `v${pkg.version}`)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function resolveReleaseSources() {
|
|
114
|
+
const sources = [
|
|
115
|
+
{ name: "GitHub Release", type: "remote", baseUrl: resolveReleaseBaseUrl() }
|
|
116
|
+
];
|
|
117
|
+
const mirrorBaseUrl = resolveReleaseMirrorBaseUrl();
|
|
118
|
+
|
|
119
|
+
if (mirrorBaseUrl && mirrorBaseUrl !== sources[0].baseUrl) {
|
|
120
|
+
sources.push({ name: "Release mirror", type: "remote", baseUrl: mirrorBaseUrl });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (fs.existsSync(bundledAssetsDir)) {
|
|
124
|
+
sources.push({ name: "bundled npm package", type: "local", basePath: bundledAssetsDir });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return sources;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getChecksumLocation(source) {
|
|
131
|
+
return source.type === "local"
|
|
132
|
+
? path.join(source.basePath, "SHA256SUMS")
|
|
133
|
+
: `${source.baseUrl}/SHA256SUMS`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getAssetLocation(source, assetName) {
|
|
137
|
+
return source.type === "local"
|
|
138
|
+
? path.join(source.basePath, assetName)
|
|
139
|
+
: `${source.baseUrl}/${assetName}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function resolveExpectedSha(sources, assetName) {
|
|
143
|
+
const errors = [];
|
|
144
|
+
|
|
145
|
+
for (const source of sources) {
|
|
146
|
+
const checksumLocation = getChecksumLocation(source);
|
|
147
|
+
try {
|
|
148
|
+
const checksumText = source.type === "local"
|
|
149
|
+
? fs.readFileSync(checksumLocation, "utf8")
|
|
150
|
+
: await fetchText(checksumLocation);
|
|
151
|
+
const expectedSha = parseChecksum(checksumText, assetName);
|
|
152
|
+
if (!expectedSha) {
|
|
153
|
+
throw new Error(`checksum for ${assetName} not found in SHA256SUMS`);
|
|
154
|
+
}
|
|
155
|
+
log(`using checksums from ${source.name}`);
|
|
156
|
+
return expectedSha;
|
|
157
|
+
} catch (err) {
|
|
158
|
+
errors.push(`${source.name}: ${err.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
throw new Error(`failed to resolve checksums for ${assetName}\n${errors.join("\n")}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function materializeArchive(sources, assetName, archivePath, expectedSha) {
|
|
166
|
+
const errors = [];
|
|
167
|
+
|
|
168
|
+
for (const source of sources) {
|
|
169
|
+
const assetLocation = getAssetLocation(source, assetName);
|
|
170
|
+
try {
|
|
171
|
+
log(`trying ${source.name}: ${assetLocation}`);
|
|
172
|
+
if (source.type === "local") {
|
|
173
|
+
fs.copyFileSync(assetLocation, archivePath);
|
|
174
|
+
} else {
|
|
175
|
+
await downloadFile(assetLocation, archivePath);
|
|
176
|
+
}
|
|
177
|
+
const actualSha = sha256File(archivePath);
|
|
178
|
+
if (actualSha !== expectedSha) {
|
|
179
|
+
throw new Error(`checksum mismatch for ${assetName}`);
|
|
180
|
+
}
|
|
181
|
+
log(`downloaded from ${source.name}`);
|
|
182
|
+
return;
|
|
183
|
+
} catch (err) {
|
|
184
|
+
fs.rmSync(archivePath, { force: true });
|
|
185
|
+
errors.push(`${source.name}: ${err.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
throw new Error(`failed to obtain ${assetName}\n${errors.join("\n")}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function stripTrailingSlash(value) {
|
|
193
|
+
return value.replace(/\/+$/, "");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function fetchText(url) {
|
|
197
|
+
try {
|
|
198
|
+
const response = await fetch(url);
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
throw new Error(`HTTP ${response.status}`);
|
|
201
|
+
}
|
|
202
|
+
return response.text();
|
|
203
|
+
} catch (err) {
|
|
204
|
+
throw new Error(`failed to download ${url}: ${err.message}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function downloadFile(url, destination) {
|
|
209
|
+
try {
|
|
210
|
+
const response = await fetch(url);
|
|
211
|
+
if (!response.ok) {
|
|
212
|
+
throw new Error(`HTTP ${response.status}`);
|
|
213
|
+
}
|
|
214
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
215
|
+
fs.writeFileSync(destination, buffer);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
throw new Error(`failed to download ${url}: ${err.message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function parseChecksum(content, fileName) {
|
|
222
|
+
for (const line of content.split(/\r?\n/)) {
|
|
223
|
+
const trimmed = line.trim();
|
|
224
|
+
if (!trimmed) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const match = trimmed.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
|
|
228
|
+
if (match && path.basename(match[2]) === fileName) {
|
|
229
|
+
return match[1].toLowerCase();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function sha256File(filePath) {
|
|
236
|
+
const hash = crypto.createHash("sha256");
|
|
237
|
+
hash.update(fs.readFileSync(filePath));
|
|
238
|
+
return hash.digest("hex");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function extractArchive(archivePath, destination, extension) {
|
|
242
|
+
if (extension === "zip") {
|
|
243
|
+
const zip = new AdmZip(archivePath);
|
|
244
|
+
zip.extractAllTo(destination, true);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (extension === "tar.gz") {
|
|
248
|
+
await tar.x({
|
|
249
|
+
file: archivePath,
|
|
250
|
+
cwd: destination
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
throw new Error(`unsupported archive format: ${extension}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function findFileRecursive(dir, fileName) {
|
|
258
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
259
|
+
for (const entry of entries) {
|
|
260
|
+
const fullPath = path.join(dir, entry.name);
|
|
261
|
+
if (entry.isFile() && entry.name === fileName) {
|
|
262
|
+
return fullPath;
|
|
263
|
+
}
|
|
264
|
+
if (entry.isDirectory()) {
|
|
265
|
+
const found = findFileRecursive(fullPath, fileName);
|
|
266
|
+
if (found) {
|
|
267
|
+
return found;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function log(message) {
|
|
275
|
+
console.log(`[seacloud installer] ${message}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
main().catch((err) => {
|
|
279
|
+
console.error(`[seacloud installer] ${err.message}`);
|
|
280
|
+
console.error(
|
|
281
|
+
"[seacloud installer] hint: GitHub download failed; package will fall back to bundled archives when available."
|
|
282
|
+
);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { spawn } = require("child_process");
|
|
6
|
+
|
|
7
|
+
const rootDir = path.resolve(__dirname, "..");
|
|
8
|
+
const exeName = process.platform === "win32" ? "seacloud.exe" : "seacloud";
|
|
9
|
+
const exePath = path.join(rootDir, "vendor", exeName);
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(exePath)) {
|
|
12
|
+
console.error("seacloud binary is not installed.");
|
|
13
|
+
console.error("Reinstall the package: npm install -g @seacloudai/seacloud-cli");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const child = spawn(exePath, ["skills", ...process.argv.slice(2)], {
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
child.on("error", (err) => {
|
|
22
|
+
console.error(`failed to start seacloud skills: ${err.message}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
child.on("exit", (code, signal) => {
|
|
27
|
+
if (signal) {
|
|
28
|
+
process.kill(process.pid, signal);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
process.exit(code ?? 0);
|
|
32
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { spawn } = require("child_process");
|
|
6
|
+
|
|
7
|
+
const rootDir = path.resolve(__dirname, "..");
|
|
8
|
+
const exeName = process.platform === "win32" ? "seacloud.exe" : "seacloud";
|
|
9
|
+
const exePath = path.join(rootDir, "vendor", exeName);
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(exePath)) {
|
|
12
|
+
console.error("seacloud binary is not installed.");
|
|
13
|
+
console.error("Reinstall the package: npm install -g @seacloudai/seacloud-cli");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const child = spawn(exePath, process.argv.slice(2), {
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
child.on("error", (err) => {
|
|
22
|
+
console.error(`failed to start seacloud: ${err.message}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
child.on("exit", (code, signal) => {
|
|
27
|
+
if (signal) {
|
|
28
|
+
process.kill(process.pid, signal);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
process.exit(code ?? 0);
|
|
32
|
+
});
|