siluzan-website-cli 1.0.1-beta.6 → 1.0.1-beta.7

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 CHANGED
@@ -14,9 +14,11 @@ siluzan-website login
14
14
 
15
15
  ## 开通建站
16
16
 
17
+ 对话顺序:**先图文选模板,再一次性填站信息**(对齐 TSO)。
18
+
17
19
  ```bash
18
- siluzan-website packages list
19
- siluzan-website templates list --package <packageId>
20
+ siluzan-website templates catalog # 套餐+封面+预览,开站第一步
21
+ siluzan-website templates get --id <siteTemplateId>
20
22
  siluzan-website trial status
21
23
  siluzan-website bills create --package <packageId> --channel Monthly
22
24
  siluzan-website bills get --id <billId>
package/dist/index.js CHANGED
@@ -3319,6 +3319,7 @@ var DEFAULT_API_BASE = "https://api-ci.siluzan.com";
3319
3319
  var DEFAULT_CSO_BASE = "https://cso-ci.siluzan.com";
3320
3320
  var DEFAULT_CSO_PLUGIN_API = "https://cso-api-ci.siluzan.com";
3321
3321
  var DEFAULT_WEB_BASE = "https://www-ci.siluzan.com";
3322
+ var DEFAULT_SITEMAN_BASE = "https://api-ussiteman-ci.mysiluzan.com";
3322
3323
  var DEFAULT_SITE_STAGE = "ci";
3323
3324
 
3324
3325
  // src/utils/version.ts
@@ -4189,8 +4190,115 @@ function unwrapList2(raw) {
4189
4190
  if (Array.isArray(obj.data)) {
4190
4191
  return obj.data.map(asRecord3).filter((x) => Boolean(x));
4191
4192
  }
4193
+ if (Array.isArray(obj.items)) {
4194
+ return obj.items.map(asRecord3).filter((x) => Boolean(x));
4195
+ }
4192
4196
  return [];
4193
4197
  }
4198
+ function siteManBase() {
4199
+ return (process.env.SILUZAN_SITEMAN_BASE ?? DEFAULT_SITEMAN_BASE).replace(/\/+$/, "");
4200
+ }
4201
+ function enrichTemplatePreview(t) {
4202
+ const out = { ...t };
4203
+ const base = siteManBase();
4204
+ const coverId = readString3(t, "cover") || readString3(t, "Cover");
4205
+ const imageId = readString3(t, "image") || readString3(t, "Image");
4206
+ if (!readString3(t, "coverUrl") && coverId) out.coverUrl = `${base}/api/Attachments/${coverId}`;
4207
+ if (!readString3(t, "imageUrl") && imageId) out.imageUrl = `${base}/api/Attachments/${imageId}`;
4208
+ const colors = Array.isArray(t.colors) ? t.colors : Array.isArray(t.Colors) ? t.Colors : [];
4209
+ const first = asRecord3(colors[0]);
4210
+ const preview = readString3(first, "colorSchemePreview") || readString3(first, "ColorSchemePreview");
4211
+ if (!readString3(t, "previewUrl") && preview) out.previewUrl = preview;
4212
+ const colorPreviewId = readString3(first, "colorPreview") || readString3(first, "ColorPreview");
4213
+ if (!readString3(t, "colorPreviewUrl") && colorPreviewId) {
4214
+ out.colorPreviewUrl = `${base}/api/Attachments/${colorPreviewId}`;
4215
+ }
4216
+ return out;
4217
+ }
4218
+ function templateBelongsToPackage(t, packageId) {
4219
+ const packages = readString3(t, "packages") || readString3(t, "Packages");
4220
+ if (!packages) return false;
4221
+ if (packages.toLowerCase().includes(`|${packageId.toLowerCase()}|`)) return true;
4222
+ return packages.split(/[|,;]/).map((s) => s.trim()).some((p) => p.toLowerCase() === packageId.toLowerCase());
4223
+ }
4224
+ function pickPreviewFields(t) {
4225
+ const e = enrichTemplatePreview(t);
4226
+ return {
4227
+ id: readString3(e, "siteTemplateId") || readString3(e, "SiteTemplateId"),
4228
+ name: readString3(e, "name") || readString3(e, "Name"),
4229
+ industry: readString3(e, "industrys") || readString3(e, "Industrys") || "-",
4230
+ coverUrl: readString3(e, "coverUrl") || readString3(e, "CoverUrl") || "",
4231
+ previewUrl: readString3(e, "previewUrl") || readString3(e, "PreviewUrl") || "",
4232
+ hot: String(e.hotTag ?? e.HotTag ?? "-"),
4233
+ neu: String(e.newTag ?? e.NewTag ?? "-"),
4234
+ version: readString3(e, "version") || readString3(e, "Version") || "-"
4235
+ };
4236
+ }
4237
+ function printCatalogMarkdown(groups) {
4238
+ console.log("\n# \u5EFA\u7AD9\u6A21\u677F\u76EE\u5F55\uFF08\u8BF7\u5148\u9009\u6A21\u677F\uFF09\n");
4239
+ console.log("\u5C55\u793A\u89C4\u5219\uFF1A\u6309\u5957\u9910\u5206\u7EC4\uFF1B\u6BCF\u6761\u542B\u5C01\u9762\u56FE\u4E0E\u5728\u7EBF\u9884\u89C8\u94FE\u63A5\uFF08\u4E0E TSO \u5EFA\u7AD9\u4E00\u81F4\uFF09\u3002");
4240
+ console.log("\u8BF7\u7528\u6237\u9009\u5B9A **siteTemplateId**\uFF08\u53CA\u6240\u5C5E\u5957\u9910\uFF09\u540E\uFF0C\u518D\u4E00\u6B21\u6027\u6536\u96C6\u5EFA\u7AD9\u8868\u5355\u5B57\u6BB5\u3002\n");
4241
+ for (const g of groups) {
4242
+ const pkgId = readString3(g, "packageId");
4243
+ const pkgName = readString3(g, "packageName") || pkgId;
4244
+ const month = g.monthPriceUSD ?? g.MonthPriceUSD ?? "-";
4245
+ const year = g.yearlyPriceUSD ?? g.YearlyPriceUSD ?? "-";
4246
+ const templates = Array.isArray(g.templates) ? g.templates : [];
4247
+ console.log(`## ${pkgName}\uFF08\`${pkgId}\`\uFF09`);
4248
+ console.log(`\u6708\u4ED8 USD ${month} / \u5E74\u4ED8\u6708\u4EF7 USD ${year} \xB7 \u6A21\u677F ${templates.length} \u4E2A
4249
+ `);
4250
+ if (templates.length === 0) {
4251
+ console.log("_\u672C\u5957\u9910\u6682\u65E0\u542F\u7528\u6A21\u677F_\n");
4252
+ continue;
4253
+ }
4254
+ for (const raw of templates) {
4255
+ const t = asRecord3(raw);
4256
+ if (!t) continue;
4257
+ const p = pickPreviewFields(t);
4258
+ const tags = [p.hot === "true" ? "\u70ED\u95E8" : "", p.neu === "true" ? "\u65B0\u54C1" : ""].filter(Boolean).join(" \xB7 ");
4259
+ console.log(`### ${p.name || p.id} \uFF08\`${p.id}\`\uFF09`);
4260
+ if (tags) console.log(`\u6807\u7B7E\uFF1A${tags}`);
4261
+ console.log(`\u884C\u4E1A\uFF1A${p.industry}`);
4262
+ if (p.coverUrl) console.log(`![${p.name || p.id}](${p.coverUrl})`);
4263
+ if (p.previewUrl) console.log(`\u5728\u7EBF\u9884\u89C8\uFF1A${p.previewUrl}`);
4264
+ console.log("");
4265
+ }
4266
+ }
4267
+ console.log("---");
4268
+ console.log("\u4E0B\u4E00\u6B65\uFF1A\u7528\u6237\u786E\u8BA4\u6A21\u677F\u540E\uFF0C\u7528\u4E00\u6B21\u6027\u8868\u5355\u6536\u96C6\uFF1A\u7AD9\u540D\u3001\u533A\u57DF(US|CN)\u3001\u524D\u53F0\u8BED\u8A00\u3001\u540E\u53F0\u8BED\u8A00\u3001\u8BD5\u7528\u6216\u4ED8\u8D39\u3002");
4269
+ console.log("\u786E\u8BA4\u8868\u5355\u540E\u6267\u884C\uFF1A`siluzan-website sites create ...`\n");
4270
+ }
4271
+ async function composeCatalogLocally(config, verbose) {
4272
+ const [pkgRaw, tplRaw] = await Promise.all([
4273
+ apiFetch2(`${config.apiBaseUrl}/website-provision/packages`, config, {}, verbose),
4274
+ apiFetch2(
4275
+ `${config.apiBaseUrl}/website-provision/templates?pageSize=500&pageIndex=1`,
4276
+ config,
4277
+ {},
4278
+ verbose
4279
+ )
4280
+ ]);
4281
+ const packages = unwrapList2(pkgRaw).filter((p) => !(p.sunset === true || p.Sunset === true));
4282
+ const templates = unwrapList2(tplRaw).map(enrichTemplatePreview);
4283
+ const groups = packages.map((p) => {
4284
+ const packageId = readString3(p, "packageId") || readString3(p, "PackageId");
4285
+ const matched = templates.filter((t) => templateBelongsToPackage(t, packageId));
4286
+ return {
4287
+ packageId,
4288
+ packageName: readString3(p, "packageName") || readString3(p, "PackageName"),
4289
+ monthPriceUSD: p.monthPriceUSD ?? p.MonthPriceUSD,
4290
+ yearlyPriceUSD: p.yearlyPriceUSD ?? p.YearlyPriceUSD,
4291
+ templates: matched,
4292
+ templateCount: matched.length
4293
+ };
4294
+ });
4295
+ return {
4296
+ packages,
4297
+ groups,
4298
+ totalTemplates: templates.length,
4299
+ source: "cli-fallback"
4300
+ };
4301
+ }
4194
4302
  async function runTemplatesList(opts) {
4195
4303
  const config = loadConfig();
4196
4304
  const params = new URLSearchParams({ pageSize: "100", pageIndex: "1" });
@@ -4208,7 +4316,7 @@ async function runTemplatesList(opts) {
4208
4316
  process.exit(1);
4209
4317
  return;
4210
4318
  }
4211
- const list = unwrapList2(raw);
4319
+ const list = unwrapList2(raw).map(enrichTemplatePreview);
4212
4320
  if (opts.json) {
4213
4321
  console.log(JSON.stringify({ total: list.length, templates: list }, null, 2));
4214
4322
  return;
@@ -4221,23 +4329,106 @@ async function runTemplatesList(opts) {
4221
4329
  { key: "id", header: "siteTemplateId" },
4222
4330
  { key: "name", header: "\u540D\u79F0" },
4223
4331
  { key: "industry", header: "\u884C\u4E1A" },
4224
- { key: "version", header: "\u7248\u672C" },
4332
+ { key: "preview", header: "\u9884\u89C8" },
4225
4333
  { key: "hot", header: "\u70ED\u95E8" },
4226
4334
  { key: "neu", header: "\u65B0\u54C1" }
4227
4335
  ];
4228
- const rows = list.map((t) => ({
4229
- id: readString3(t, "siteTemplateId") || readString3(t, "SiteTemplateId"),
4230
- name: readString3(t, "name") || readString3(t, "Name"),
4231
- industry: readString3(t, "industrys") || readString3(t, "Industrys") || "-",
4232
- version: readString3(t, "version") || readString3(t, "Version") || "-",
4233
- hot: String(t.hotTag ?? t.HotTag ?? "-"),
4234
- neu: String(t.newTag ?? t.NewTag ?? "-")
4235
- }));
4336
+ const rows = list.map((t) => {
4337
+ const p = pickPreviewFields(t);
4338
+ return {
4339
+ id: p.id,
4340
+ name: p.name,
4341
+ industry: p.industry,
4342
+ preview: p.previewUrl || (p.coverUrl ? "(\u6709\u5C01\u9762)" : "-"),
4343
+ hot: p.hot,
4344
+ neu: p.neu
4345
+ };
4346
+ });
4236
4347
  console.log(`
4237
4348
  \u6A21\u677F\u5217\u8868\uFF08\u5171 ${list.length} \u4E2A\uFF09
4238
4349
  `);
4239
4350
  printCliTable(rows, columns);
4240
- console.log("\n\u8BE6\u60C5\uFF1Asiluzan-website templates get --id <siteTemplateId>\n");
4351
+ console.log("\n\u56FE\u6587\u76EE\u5F55\uFF1Asiluzan-website templates catalog");
4352
+ console.log("\u8BE6\u60C5\uFF1Asiluzan-website templates get --id <siteTemplateId>\n");
4353
+ }
4354
+ async function runTemplatesCatalog(opts) {
4355
+ const config = loadConfig();
4356
+ const url = `${config.apiBaseUrl}/website-provision/template-catalog`;
4357
+ let raw;
4358
+ let usedFallback = false;
4359
+ try {
4360
+ raw = await apiFetch2(url, config, {}, opts.verbose);
4361
+ } catch (e) {
4362
+ const msg = e.message || "";
4363
+ if (/HTTP 404/.test(msg)) {
4364
+ console.error("\u2139\uFE0F \u4E3B\u7AD9\u5C1A\u672A\u90E8\u7F72 template-catalog\uFF0C\u6539\u7528 packages+templates \u672C\u5730\u62FC\u88C5\u76EE\u5F55\u2026");
4365
+ try {
4366
+ raw = await composeCatalogLocally(config, opts.verbose);
4367
+ usedFallback = true;
4368
+ } catch (e2) {
4369
+ console.error(`
4370
+ \u274C \u62FC\u88C5\u6A21\u677F\u76EE\u5F55\u5931\u8D25\uFF1A${e2.message}`);
4371
+ process.exit(1);
4372
+ return;
4373
+ }
4374
+ } else {
4375
+ console.error(`
4376
+ \u274C \u83B7\u53D6\u6A21\u677F\u76EE\u5F55\u5931\u8D25\uFF1A${msg}`);
4377
+ if (!opts.verbose) console.error(" \u52A0 --verbose \u53EF\u67E5\u770B\u8BE6\u7EC6\u9519\u8BEF");
4378
+ process.exit(1);
4379
+ return;
4380
+ }
4381
+ }
4382
+ const root = asRecord3(raw);
4383
+ let groups = (Array.isArray(root?.groups) ? root.groups : []).map(asRecord3).filter((x) => Boolean(x)).map((g) => ({
4384
+ ...g,
4385
+ templates: Array.isArray(g.templates) ? g.templates.map((t) => enrichTemplatePreview(asRecord3(t) ?? {})) : []
4386
+ }));
4387
+ if (opts.package?.trim()) {
4388
+ const pkg = opts.package.trim();
4389
+ groups = groups.filter((g) => readString3(g, "packageId") === pkg);
4390
+ }
4391
+ if (opts.json) {
4392
+ console.log(
4393
+ JSON.stringify(
4394
+ {
4395
+ totalGroups: groups.length,
4396
+ totalTemplates: root?.totalTemplates ?? groups.reduce((n, g) => n + (Array.isArray(g.templates) ? g.templates.length : 0), 0),
4397
+ source: usedFallback ? "cli-fallback" : root?.source ?? "api",
4398
+ groups
4399
+ },
4400
+ null,
4401
+ 2
4402
+ )
4403
+ );
4404
+ return;
4405
+ }
4406
+ if (opts.markdown !== false) {
4407
+ if (usedFallback) console.log("\uFF08\u6570\u636E\u6E90\uFF1ACLI \u56DE\u9000\u62FC\u88C5\uFF09");
4408
+ printCatalogMarkdown(groups);
4409
+ return;
4410
+ }
4411
+ for (const g of groups) {
4412
+ const pkgId = readString3(g, "packageId");
4413
+ const pkgName = readString3(g, "packageName") || pkgId;
4414
+ const templates = Array.isArray(g.templates) ? g.templates : [];
4415
+ console.log(`
4416
+ ## ${pkgName} (${pkgId}) \u2014 ${templates.length} \u4E2A\u6A21\u677F
4417
+ `);
4418
+ const columns = [
4419
+ { key: "id", header: "siteTemplateId" },
4420
+ { key: "name", header: "\u540D\u79F0" },
4421
+ { key: "industry", header: "\u884C\u4E1A" },
4422
+ { key: "preview", header: "\u9884\u89C8URL" }
4423
+ ];
4424
+ const rows = templates.map((item) => {
4425
+ const t = asRecord3(item) ?? {};
4426
+ const p = pickPreviewFields(t);
4427
+ return { id: p.id, name: p.name, industry: p.industry, preview: p.previewUrl || "-" };
4428
+ });
4429
+ printCliTable(rows, columns);
4430
+ }
4431
+ console.log();
4241
4432
  }
4242
4433
  async function runTemplatesGet(opts) {
4243
4434
  const id = opts.id?.trim();
@@ -4256,22 +4447,35 @@ async function runTemplatesGet(opts) {
4256
4447
  process.exit(1);
4257
4448
  return;
4258
4449
  }
4450
+ const enriched = enrichTemplatePreview(asRecord3(raw) ?? {});
4259
4451
  if (opts.json) {
4260
- console.log(JSON.stringify(raw, null, 2));
4452
+ console.log(JSON.stringify(enriched, null, 2));
4261
4453
  return;
4262
4454
  }
4263
- const t = asRecord3(raw);
4455
+ const p = pickPreviewFields(enriched);
4264
4456
  console.log("\n\u6A21\u677F\u8BE6\u60C5\n");
4265
- console.log(` siteTemplateId : ${readString3(t, "siteTemplateId") || readString3(t, "SiteTemplateId") || id}`);
4266
- console.log(` name : ${readString3(t, "name") || readString3(t, "Name")}`);
4267
- console.log(` description : ${readString3(t, "description") || readString3(t, "Description") || "-"}`);
4268
- console.log(` industrys : ${readString3(t, "industrys") || readString3(t, "Industrys") || "-"}`);
4269
- console.log(` version : ${readString3(t, "version") || readString3(t, "Version") || "-"}`);
4270
- console.log(` hasTheme : ${String(t?.hasTheme ?? "-")}`);
4457
+ console.log(` siteTemplateId : ${p.id || id}`);
4458
+ console.log(` name : ${p.name}`);
4459
+ console.log(` description : ${readString3(enriched, "description") || readString3(enriched, "Description") || "-"}`);
4460
+ console.log(` industrys : ${p.industry}`);
4461
+ console.log(` version : ${p.version}`);
4462
+ console.log(` hasTheme : ${String(enriched.hasTheme ?? "-")}`);
4463
+ console.log(` coverUrl : ${p.coverUrl || "-"}`);
4464
+ console.log(` previewUrl : ${p.previewUrl || "-"}`);
4465
+ if (p.coverUrl) console.log(`
4466
+ ![${p.name || p.id}](${p.coverUrl})`);
4271
4467
  console.log("\n\uFF08\u5B89\u88C5\u5305\u5730\u5740\u5DF2\u8131\u654F\uFF0C\u4E0D\u4F1A\u8F93\u51FA\uFF09\n");
4272
4468
  }
4273
4469
  function register7(program2) {
4274
4470
  const templates = program2.command("templates").description("\u5EFA\u7AD9\u6A21\u677F\uFF08website-provision\uFF09");
4471
+ templates.command("catalog").description("\u5957\u9910+\u6A21\u677F\u56FE\u6587\u76EE\u5F55\uFF08\u5BF9\u9F50 TSO\uFF1B\u5BF9\u8BDD\u5F00\u7AD9\u7B2C\u4E00\u6B65\uFF09").option("--package <packageId>", "\u53EA\u663E\u793A\u67D0\u4E00\u5957\u9910").option("--json", "\u8F93\u51FA JSON", false).option("--table", "\u8868\u683C\u8F93\u51FA\uFF08\u9ED8\u8BA4 Markdown \u56FE\u6587\u753B\u5ECA\uFF09", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4472
+ await runTemplatesCatalog({
4473
+ package: opts.package,
4474
+ json: opts.json,
4475
+ markdown: !opts.table && !opts.json,
4476
+ verbose: opts.verbose
4477
+ });
4478
+ });
4275
4479
  templates.command("list").description("\u6309\u5957\u9910\u7B49\u6761\u4EF6\u5217\u51FA\u6A21\u677F").option("--package <packageId>", "\u5957\u9910 ID \u8FC7\u6EE4").option("--industry <industry>", "\u884C\u4E1A\u8FC7\u6EE4").option("--keyword <keyword>", "\u5173\u952E\u8BCD").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4276
4480
  await runTemplatesList(opts);
4277
4481
  });
@@ -40,7 +40,8 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
40
40
  | `siluzan-website config show/set/clear` | 查看 / 写入凭据 | [references/setup.md](references/setup.md) |
41
41
  | `siluzan-website init` / `update` | 写入 / 刷新 Skill 文件 | [references/setup.md](references/setup.md) |
42
42
  | `siluzan-website packages list` | 可售套餐 | [references/provision.md](references/provision.md) |
43
- | `siluzan-website templates list/get` | 建站模板 | [references/provision.md](references/provision.md) |
43
+ | `siluzan-website templates catalog` | **图文选模目录**(套餐+封面+预览,开站第一步) | [references/provision.md](references/provision.md) |
44
+ | `siluzan-website templates list/get` | 建站模板列表 / 详情 | [references/provision.md](references/provision.md) |
44
45
  | `siluzan-website trial status` | 试用额度 | [references/provision.md](references/provision.md) |
45
46
  | `siluzan-website bills create/get` | 套餐账单与支付状态 | [references/provision.md](references/provision.md) |
46
47
  | `siluzan-website sites list` | 列出我的 WordPress 站点 | [references/sites.md](references/sites.md) |
@@ -61,7 +62,7 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
61
62
  | 用户在做什么 | 先读 | 再执行 |
62
63
  |--------------|------|--------|
63
64
  | 首次安装 / 登录 | `references/setup.md` | `login` 或复用已有 CSO/TSO 凭据 |
64
- | **选套餐 / 选模板 / 试用或付费开站** | `references/provision.md` | packages → templates → trial/bills → 确认 → sites create |
65
+ | **开通新站 / 选模板建站** | `references/provision.md` | **先** `templates catalog` 图文选模 → **再**一次性表单(站名/区域/语言/试用或付费)→ trial/bills → 确认 → `sites create` |
65
66
  | 我有哪些网站 | `references/sites.md` | `sites list`,不要猜 guid |
66
67
  | 加一页 / 改一页正文 | `references/pages.md` | 先 `sites list` 再 `pages add/update` |
67
68
  | 改页面 slug / 状态 / 回收站 | `references/pages.md` | `pages meta` / `pages trash` |
@@ -84,7 +85,8 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
84
85
  - 写操作(`bills create`、`sites create/bind`、`pages add/update/meta/trash`、`content add/update/trash`、`media upload`、`terms add/update/delete`、`plugins install/update/uninstall/update-helper`、`zasp set/sync`)会立刻生效,执行前必须复述目标与内容,获得用户确认。
85
86
  - 只读(`packages/templates/trial`、`bills get`、`sites list/status`、`pages list/get`、`content types/list/get`、`media list/get`、`terms taxonomies/list`、`search`、`plugins list/catalog/helper-status`、`zasp settings/schema/status/diagnostics`、`config show`)可直接跑。
86
87
  2. **不猜 ID**:站点用 `sites list` 的 `guid`;页面用 `pages list` 的 `id`;套餐/模板用 list 返回的 id。
87
- 3. **先查再写**:改页面前先 `pages list`;开站前先 `trial status` 或 `bills get`。
88
+ 3. **先查再写**:改页面前先 `pages list`;**开站必须先 `templates catalog` 选定模板**,再一次性收集站信息表单;创建前再查 `trial status` 或 `bills get`。
89
+ 禁止未选模板就追问站名/区域/语言。
88
90
  4. **只认 API Key**:所有命令走 `x-api-key`。Agent 沙箱只注入 Key,不要向用户要 JWT,也不要设 `SILUZAN_AUTH_TOKEN`,不要直连 ussiteman。
89
91
  5. **命令透明**:用一句话说明意图;用户要细节时再给完整命令。
90
92
  6. **失败先自救**:401 → 重新 `login`;开站失败勿盲目重试 POST;`site_created_unbound` → `sites bind`;helper 404 → 自动升 helper。不要编造成功。
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "slug": "siluzan-website",
3
- "version": "1.0.1-beta.6",
4
- "publishedAt": 1790567480723,
3
+ "version": "1.0.1-beta.7",
4
+ "publishedAt": 1790594348812,
5
5
  "homepage": "https://www.siluzan.com",
6
6
  "source": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill",
7
7
  "requiredBinaries": [
@@ -1,18 +1,78 @@
1
1
  # 对话开通站点(provision)
2
2
 
3
- 通过主站 `website-provision` API 完成选套餐 → 支付/试用 → 选模板 → 创建站。
4
- **禁止**直连 ussiteman,**禁止**向用户要 JWT。只使用 API Key + 本 Skill CLI。
3
+ 通过主站 `website-provision` API 完成:**先选模板(图文)→ 一次性填站信息 → 试用/付费 → 创建**。
4
+ 对齐 TSO 建站选模体验。**禁止**直连 ussiteman,**禁止**向用户要 JWT。只使用 API Key + 本 Skill CLI。
5
5
 
6
- ## 对话 SOP
6
+ ## 对话 SOP(必须按此顺序)
7
7
 
8
- 1. `packages list` — 列套餐并与用户选定(或确认走试用)
9
- 2. 付费:`bills create --package <id>` → 把 `checkoutUrl` 给用户 → 用户付完后 `bills get --id <billId>` 确认 Paid
10
- 3. `templates list --package <id>` — 选定模板;需要时 `templates get --id`
11
- 4. `trial status` — 试用路径先看剩余次数(上限 3)
12
- 5. **复述计划并获用户确认**(站名、区域 US/CN、语种、套餐、模板、试用或账单号)
13
- 6. `sites create --name … --region … --package … --template … --site-lang … --admin-lang … [--bill …] [--wait]`
14
- 7. 若返回 `site_created_unbound`:`sites bind --site <guid> --name <同名> --region …`
15
- 8. `sites status` / `sites list` 核对 Online 后,再交给 pages / plugins 等运维命令
8
+ ### 1. 图文选模板(先于任何站信息)
9
+
10
+ ```bash
11
+ siluzan-website templates catalog
12
+ # 或:siluzan-website templates catalog --json
13
+ # 或只看某套餐:siluzan-website templates catalog --package BasicEdition
14
+ ```
15
+
16
+ Agent **必须**把目录以图文形式展示给用户(CLI 默认已输出 Markdown 画廊):
17
+
18
+ - 按**套餐**分组(套餐名、价格、模板数)
19
+ - 每个模板:`![封面](coverUrl)` + 名称 + `siteTemplateId` + 行业 + `previewUrl`(在线预览,与 TSO 一致)
20
+ - **不要**先问站名/区域/语言;先让用户选定模板
21
+
22
+ 用户确认后记下:
23
+
24
+ | 字段 | 来源 |
25
+ |------|------|
26
+ | `siteTemplateId` | 用户点选的模板 |
27
+ | `packageId` | 该模板所属套餐分组 |
28
+
29
+ 可用 `templates get --id <id>` 再确认封面/预览(可选)。
30
+
31
+ ### 2. 一次性建站表单(确认模板之后)
32
+
33
+ 选定模板后,**一次性**向用户展示表单(可复制为 Markdown 表格让用户填),字段如下:
34
+
35
+ | 字段 | 必填 | 说明 | 示例 |
36
+ |------|------|------|------|
37
+ | 站点名称 `name` | 是 | 广告主/站点显示名 | Acme Trading |
38
+ | 区域 `region` | 是 | `US` 或 `CN` | US |
39
+ | 前台语言 `siteLang` | 是 | 如 `en_US` / `zh_CN` | en_US |
40
+ | 后台语言 `adminLang` | 是 | 通常与前台相同 | en_US |
41
+ | 开通方式 | 是 | `试用` 或 `付费` | 试用 |
42
+
43
+ 规则:
44
+
45
+ - 套餐与模板**已由第 1 步锁定**,表单里不要再改(可只读展示)
46
+ - 区域默认:未填时 `US` → 语言默认 `en_US`;`CN` → 默认 `zh_CN`
47
+ - **禁止**拆成多轮零散追问(不要先问站名、再问区域、再问语言);一张表单收齐
48
+
49
+ 付费路径额外字段:
50
+
51
+ | 字段 | 说明 |
52
+ |------|------|
53
+ | 付费周期 | `Monthly` / `Yearly`(对应 `bills create --channel`) |
54
+
55
+ ### 3. 额度 / 支付
56
+
57
+ - 试用:`trial status`;若 `remaining=0` 引导付费,勿强行 create
58
+ - 付费:`bills create --package <已锁定的 packageId>` → 把 `checkoutUrl` 给用户 → `bills get` 确认 Paid
59
+
60
+ ### 4. 复述计划并确认后创建
61
+
62
+ 复述:站名、区域、语种、套餐、模板、试用或账单号 → 用户明确同意后:
63
+
64
+ ```bash
65
+ siluzan-website sites create \
66
+ --name "…" --region US|CN \
67
+ --package <packageId> --template <siteTemplateId> \
68
+ --site-lang … --admin-lang … \
69
+ [--bill <billId>] [--wait]
70
+ ```
71
+
72
+ ### 5. 收尾
73
+
74
+ - `site_created_unbound` → `sites bind --site <guid> --name <同名> --region …`
75
+ - `sites status` / `sites list` 核对 Online 后,再交给 pages / plugins
16
76
 
17
77
  ## 写操作确认
18
78
 
@@ -22,8 +82,8 @@
22
82
  ## 命令速查
23
83
 
24
84
  ```bash
25
- siluzan-website packages list
26
- siluzan-website templates list --package <packageId>
85
+ siluzan-website templates catalog
86
+ siluzan-website templates catalog --package BasicEdition
27
87
  siluzan-website templates get --id <siteTemplateId>
28
88
  siluzan-website trial status
29
89
  siluzan-website bills create --package <packageId> --channel Monthly
@@ -42,9 +102,11 @@ siluzan-website sites bind --site <guid> --region US --name "Acme"
42
102
  | bill_unpaid / bill_owner_mismatch | 查 `bills get`;勿用他人账单号 |
43
103
  | site_created_unbound | 跑 `sites bind`,再 `sites list` |
44
104
  | 503 disabled | 主站 WebsiteProvision 功能开关关闭 |
105
+ | template-catalog 404 | 主站未部署 catalog 接口;可暂用 `packages list` + `templates list --package` 拼目录 |
45
106
 
46
107
  ## 明确不做
47
108
 
48
109
  - CLI 直连 SiteMan / 注入 `SILUZAN_AUTH_TOKEN` 开通
110
+ - 未选模板就收集站名等建站字段
49
111
  - 删除站、重启、绑域名、CDN、SSL
50
112
  - AI Sites / LPS 静态建站
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  $PKG_NAME = 'siluzan-website-cli'
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- $PKG_VERSION = '1.0.1-beta.6'
12
+ $PKG_VERSION = '1.0.1-beta.7'
13
13
  $CLI_BIN = 'siluzan-website'
14
14
  $SKILL_LABEL = 'Siluzan Website'
15
15
  $INSTALL_CMD = 'npm install -g siluzan-website-cli@beta'
@@ -9,7 +9,7 @@ set -euo pipefail
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  readonly PKG_NAME="siluzan-website-cli"
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- readonly PKG_VERSION="1.0.1-beta.6"
12
+ readonly PKG_VERSION="1.0.1-beta.7"
13
13
  readonly CLI_BIN="siluzan-website"
14
14
  readonly SKILL_LABEL="Siluzan Website"
15
15
  readonly INSTALL_CMD="npm install -g siluzan-website-cli@beta"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-website-cli",
3
- "version": "1.0.1-beta.6",
3
+ "version": "1.0.1-beta.7",
4
4
  "description": "Siluzan WordPress 站点管理 Skill CLI — 列出站点、新增/编辑页面、安装/升级插件。",
5
5
  "keywords": [
6
6
  "ai-skill",