u1s1-cli 1.0.0 → 1.2.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/dist/deploy.d.ts +8 -0
- package/dist/deploy.js +77 -19
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/deploy.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
import { type CliConfig } from "./config.js";
|
|
2
|
+
type SiteVisibility = "public" | "private";
|
|
3
|
+
interface DeployArgs {
|
|
4
|
+
name?: string;
|
|
5
|
+
dirArg?: string;
|
|
6
|
+
visibility?: SiteVisibility;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseDeployArgs(args: string[]): DeployArgs;
|
|
2
9
|
export declare function deployCommand(cfg: CliConfig, args: string[]): Promise<void>;
|
|
10
|
+
export {};
|
package/dist/deploy.js
CHANGED
|
@@ -4,8 +4,8 @@ import { createInterface } from "node:readline/promises";
|
|
|
4
4
|
import { VERSION, u1s1Dir } from "./config.js";
|
|
5
5
|
import { authorizedFetch } from "./device-auth.js";
|
|
6
6
|
/**
|
|
7
|
-
* u1s1 deploy
|
|
8
|
-
*
|
|
7
|
+
* u1s1 deploy publishes a static site to <project>.<account-code>.u1s1.app.
|
|
8
|
+
* Detect the site root, ask for a project slug, and remember it in ~/.u1s1/deploys.json.
|
|
9
9
|
* → 并发上传 → 网关原子切换生效,输出可分享的网址。
|
|
10
10
|
*/
|
|
11
11
|
const deploysFile = join(u1s1Dir, "deploys.json");
|
|
@@ -84,6 +84,37 @@ function fmtBytes(n) {
|
|
|
84
84
|
return `${Math.round(n / 1024)}KB`;
|
|
85
85
|
return `${n}B`;
|
|
86
86
|
}
|
|
87
|
+
export function parseDeployArgs(args) {
|
|
88
|
+
const parsed = {};
|
|
89
|
+
for (let i = 0; i < args.length; i++) {
|
|
90
|
+
const arg = args[i];
|
|
91
|
+
if (arg === "--name" || arg === "-n") {
|
|
92
|
+
const value = args[++i];
|
|
93
|
+
if (!value || value.startsWith("-"))
|
|
94
|
+
throw new Error(`${arg} 后面需要站点名`);
|
|
95
|
+
parsed.name = value.toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
else if (arg.startsWith("--name=")) {
|
|
98
|
+
parsed.name = arg.slice(7).toLowerCase();
|
|
99
|
+
if (!parsed.name)
|
|
100
|
+
throw new Error("--name 后面需要站点名");
|
|
101
|
+
}
|
|
102
|
+
else if (arg === "--public" || arg === "--private") {
|
|
103
|
+
const visibility = arg.slice(2);
|
|
104
|
+
if (parsed.visibility && parsed.visibility !== visibility) {
|
|
105
|
+
throw new Error("--public 和 --private 不能同时使用");
|
|
106
|
+
}
|
|
107
|
+
parsed.visibility = visibility;
|
|
108
|
+
}
|
|
109
|
+
else if (arg.startsWith("-")) {
|
|
110
|
+
throw new Error(`不认识的 deploy 参数:${arg}`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
parsed.dirArg = arg;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return parsed;
|
|
117
|
+
}
|
|
87
118
|
async function api(cfg, method, path, body) {
|
|
88
119
|
let resp;
|
|
89
120
|
try {
|
|
@@ -137,15 +168,33 @@ async function promptSiteName(def) {
|
|
|
137
168
|
return def;
|
|
138
169
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
139
170
|
try {
|
|
140
|
-
const answer = (await rl.question(`
|
|
171
|
+
const answer = (await rl.question(` 项目名(回车用 ${def},只需在你的账号内唯一):`)).trim().toLowerCase();
|
|
141
172
|
return answer || def;
|
|
142
173
|
}
|
|
143
174
|
finally {
|
|
144
175
|
rl.close();
|
|
145
176
|
}
|
|
146
177
|
}
|
|
178
|
+
async function promptVisibility() {
|
|
179
|
+
if (!process.stdin.isTTY)
|
|
180
|
+
return "private";
|
|
181
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
182
|
+
try {
|
|
183
|
+
while (true) {
|
|
184
|
+
const answer = (await rl.question(" 发布方式 [1] 公开(进入作品社区) [2] 私密(仅自己登录后可看,默认):")).trim().toLowerCase();
|
|
185
|
+
if (!answer || answer === "2" || answer === "private" || answer === "私密")
|
|
186
|
+
return "private";
|
|
187
|
+
if (answer === "1" || answer === "public" || answer === "公开")
|
|
188
|
+
return "public";
|
|
189
|
+
console.log(" 请输入 1 或 2");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
rl.close();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
147
196
|
export async function deployCommand(cfg, args) {
|
|
148
|
-
// 参数:[dir] [--name xxx];u1s1 deploy list 列出已有站点
|
|
197
|
+
// 参数:[dir] [--name xxx] [--public|--private];u1s1 deploy list 列出已有站点
|
|
149
198
|
if (args[0] === "list") {
|
|
150
199
|
const { sites } = await api(cfg, "GET", "/deploy/sites");
|
|
151
200
|
if (!sites.length) {
|
|
@@ -154,22 +203,14 @@ export async function deployCommand(cfg, args) {
|
|
|
154
203
|
}
|
|
155
204
|
console.log("");
|
|
156
205
|
for (const s of sites) {
|
|
157
|
-
|
|
206
|
+
const visibility = s.visibility === "private" ? "私密" : s.community_listed ? "公开(社区)" : "公开(未展示)";
|
|
207
|
+
console.log(` ${s.deployed ? "●" : "○"} ${s.url} ${visibility} · ${fmtBytes(s.total_bytes)} · ${s.updated_at} UTC`);
|
|
158
208
|
}
|
|
159
209
|
console.log("");
|
|
160
210
|
return;
|
|
161
211
|
}
|
|
162
|
-
|
|
163
|
-
let dirArg;
|
|
164
|
-
for (let i = 0; i < args.length; i++) {
|
|
165
|
-
const a = args[i];
|
|
166
|
-
if (a === "--name" || a === "-n")
|
|
167
|
-
name = args[++i]?.toLowerCase();
|
|
168
|
-
else if (a.startsWith("--name="))
|
|
169
|
-
name = a.slice(7).toLowerCase();
|
|
170
|
-
else if (!a.startsWith("-"))
|
|
171
|
-
dirArg = a;
|
|
172
|
-
}
|
|
212
|
+
const parsed = parseDeployArgs(args);
|
|
213
|
+
let { name, dirArg, visibility } = parsed;
|
|
173
214
|
const dir = resolveSiteDir(dirArg);
|
|
174
215
|
const files = collectFiles(dir);
|
|
175
216
|
if (!files.some((f) => f.path === "index.html")) {
|
|
@@ -187,6 +228,10 @@ export async function deployCommand(cfg, args) {
|
|
|
187
228
|
const projectName = BUILD_DIRS.includes(basename(dir)) ? basename(resolve(dir, "..")) : basename(dir);
|
|
188
229
|
name = await promptSiteName(slugify(projectName));
|
|
189
230
|
}
|
|
231
|
+
// 同一目录再次部署时保持上次设置;首次部署显式选择。CI/脚本没有交互时
|
|
232
|
+
// 取更安全的 private,用户仍可用 --public 明确公开。
|
|
233
|
+
if (!visibility && remembered !== name)
|
|
234
|
+
visibility = await promptVisibility();
|
|
190
235
|
let start;
|
|
191
236
|
for (let attempt = 0; !start; attempt++) {
|
|
192
237
|
try {
|
|
@@ -222,12 +267,25 @@ export async function deployCommand(cfg, args) {
|
|
|
222
267
|
const fin = await api(cfg, "POST", "/deploy/finish", {
|
|
223
268
|
site: start.site,
|
|
224
269
|
deploy_id: start.deploy_id,
|
|
270
|
+
visibility,
|
|
225
271
|
});
|
|
226
|
-
rememberSite(dir, start.site);
|
|
272
|
+
rememberSite(dir, start.slug || name || start.site);
|
|
227
273
|
console.log(` ✅ 部署完成,${fin.file_count} 个文件已上线`);
|
|
228
274
|
console.log("");
|
|
229
|
-
console.log(` 🌐 ${fin.url}`);
|
|
275
|
+
console.log(` ${fin.visibility === "private" ? "🔒" : "🌐"} ${fin.url}`);
|
|
230
276
|
console.log("");
|
|
231
|
-
|
|
277
|
+
if ((fin.hostname || start.hostname || "").split(".").length > 3) {
|
|
278
|
+
console.log(" 新 hostname 的 HTTPS 证书会由 Total TLS 自动签发,首次访问可能需要等待几分钟。");
|
|
279
|
+
}
|
|
280
|
+
if (fin.visibility === "private") {
|
|
281
|
+
console.log(" 私密站点仅你可见,请从 https://u1s1.io/dashboard#sec-sites 打开。");
|
|
282
|
+
}
|
|
283
|
+
else if (fin.community_listed) {
|
|
284
|
+
console.log(" 已进入 https://u1s1.io/community ,把网址发给朋友就能看。");
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
console.log(" 公开链接可直接访问;运行 u1s1 deploy --public 可明确加入作品社区。");
|
|
288
|
+
}
|
|
289
|
+
console.log(" 改完代码再跑一次 u1s1 deploy 即可更新。");
|
|
232
290
|
console.log("");
|
|
233
291
|
}
|
package/dist/index.js
CHANGED
|
@@ -314,7 +314,7 @@ async function run() {
|
|
|
314
314
|
}
|
|
315
315
|
if (cmd === "--help" || cmd === "-h") {
|
|
316
316
|
printConsoleBanner(VERSION);
|
|
317
|
-
console.log(" u1s1 命令:deploy(
|
|
317
|
+
console.log(" u1s1 命令:deploy(发布网页,可选 --public / --private)· login / logout · model · usage · update · import · bench");
|
|
318
318
|
console.log("");
|
|
319
319
|
}
|
|
320
320
|
if (cmd === "web") {
|