siluzan-website-cli 1.0.1-beta.5 → 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
@@ -1,6 +1,6 @@
1
1
  # siluzan-website-cli
2
2
 
3
- Siluzan **WordPress 站点管理** Skill CLI。安装后由 AI Agent 列出站点、新增/编辑页面、安装 / 升级 / 卸载插件。
3
+ Siluzan **WordPress 站点开通与管理** Skill CLI。安装后由 AI Agent 选套餐/模板开通站点,以及列出站点、新增/编辑页面、安装 / 升级 / 卸载插件。
4
4
 
5
5
  ## 安装
6
6
 
@@ -12,7 +12,24 @@ siluzan-website login
12
12
 
13
13
  或使用 Skill 文档里的一键安装脚本。凭据与 `siluzan-cso` / `siluzan-tso` 共用 `~/.siluzan/config.json` 的 **API Key**(Skill 不使用 JWT)。
14
14
 
15
- ## 常用命令
15
+ ## 开通建站
16
+
17
+ 对话顺序:**先图文选模板,再一次性填站信息**(对齐 TSO)。
18
+
19
+ ```bash
20
+ siluzan-website templates catalog # 套餐+封面+预览,开站第一步
21
+ siluzan-website templates get --id <siteTemplateId>
22
+ siluzan-website trial status
23
+ siluzan-website bills create --package <packageId> --channel Monthly
24
+ siluzan-website bills get --id <billId>
25
+ siluzan-website sites create --name "Acme" --region US --package <id> --template <id> --site-lang en_US --admin-lang en_US [--bill <billId>] [--wait]
26
+ siluzan-website sites status --site <guid> --region US
27
+ siluzan-website sites bind --site <guid> --region US --name "Acme"
28
+ ```
29
+
30
+ 开通走主站 `/website-provision/*`(API Key)。对话 SOP 见 Skill `references/provision.md`。
31
+
32
+ ## 常用运维命令
16
33
 
17
34
  ```bash
18
35
  siluzan-website sites list
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
@@ -3905,10 +3906,717 @@ async function runSitesList(options) {
3905
3906
  console.log("\n\u540E\u7EED\uFF1Asiluzan-website pages list --site <guid>\n");
3906
3907
  }
3907
3908
  function register5(program2) {
3908
- const sites = program2.command("sites").description("WordPress \u7AD9\u70B9\u67E5\u8BE2");
3909
+ const sites = program2.command("sites").description("WordPress \u7AD9\u70B9\u67E5\u8BE2\u4E0E\u5F00\u901A");
3909
3910
  sites.command("list").description("\u5217\u51FA\u5F53\u524D\u8D26\u53F7\u4E0B\u7684 WordPress \u7AD9\u70B9").option("-q, --query <keyword>", "\u6309\u540D\u79F0\u641C\u7D22").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
3910
3911
  await runSitesList(opts);
3911
3912
  });
3913
+ sites.command("create").description("\u805A\u5408\u521B\u5EFA\u7AD9\u70B9\uFF08website-provision\uFF0C\u5199\u64CD\u4F5C\uFF0CAgent \u987B\u5148\u83B7\u7528\u6237\u786E\u8BA4\uFF09").option("--name <advertiserName>", "\u7AD9\u70B9\u540D\u79F0").option("--region <US|CN>", "\u533A\u57DF", "US").option("--package <packageId>", "\u5957\u9910 ID").option("--template <siteTemplateId>", "\u6A21\u677F ID").option("--site-lang <lang>", "\u524D\u53F0\u8BED\u8A00\uFF0C\u5982 en_US / zh_CN").option("--admin-lang <lang>", "\u540E\u53F0\u8BED\u8A00").option("--bill <packageBillId>", "\u5DF2\u4ED8\u8D26\u5355\u53F7\uFF08\u4ED8\u8D39\u8DEF\u5F84\uFF1B\u8BD5\u7528\u53EF\u4E0D\u4F20\uFF09").option("--wait", "\u521B\u5EFA\u540E\u8F6E\u8BE2\u81F3 Online", false).option("--timeout <seconds>", "wait \u8D85\u65F6\u79D2\u6570", "600").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
3914
+ await runSitesCreate(opts);
3915
+ });
3916
+ sites.command("status").description("\u67E5\u8BE2\u5EFA\u7AD9\u72B6\u6001\uFF08website-provision\uFF09").option("--site <siteGuid>", "\u7AD9\u70B9 guid").option("--region <US|CN>", "\u533A\u57DF", "US").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
3917
+ await runSitesStatus(opts);
3918
+ });
3919
+ sites.command("bind").description("\u8D26\u53F7\u6302\u63A5\u8865\u507F\uFF08SiteMan \u5DF2\u6210\u529F\u4F46 list \u4E0D\u53EF\u89C1\u65F6\uFF09").option("--site <siteGuid>", "\u7AD9\u70B9 guid").option("--region <US|CN>", "\u533A\u57DF", "US").option("--name <advertiserName>", "\u7AD9\u70B9\u540D\u79F0\uFF08\u7528\u4E8E\u67E5\u627E/\u521B\u5EFA Mag\uFF09").option("--mag <magKey>", "\u5DF2\u6709 Mag Key\uFF08\u53EF\u9009\uFF09").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
3920
+ await runSitesBind(opts);
3921
+ });
3922
+ }
3923
+ function asRecordLocal(value) {
3924
+ return value && typeof value === "object" ? value : null;
3925
+ }
3926
+ function readStr2(obj, key) {
3927
+ if (!obj) return "";
3928
+ const v = obj[key];
3929
+ return typeof v === "string" ? v : v == null ? "" : String(v);
3930
+ }
3931
+ async function runSitesCreate(opts) {
3932
+ const name = opts.name?.trim();
3933
+ const packageId = opts.package?.trim();
3934
+ const templateId = opts.template?.trim();
3935
+ const region = (opts.region || "US").trim();
3936
+ const siteLang = opts.siteLang?.trim() || (region.toUpperCase() === "CN" ? "zh_CN" : "en_US");
3937
+ const adminLang = opts.adminLang?.trim() || siteLang;
3938
+ if (!name || !packageId || !templateId) {
3939
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --name --package --template\uFF08\u4EE5\u53CA\u53EF\u9009 --bill / --region / --site-lang / --admin-lang\uFF09\n");
3940
+ process.exit(1);
3941
+ }
3942
+ console.log("\n\u5373\u5C06\u521B\u5EFA WordPress \u7AD9\u70B9\uFF08\u4E3B\u7AD9 website-provision\uFF0C\u4E0D\u53EF\u76F2\u76EE\u91CD\u8BD5\uFF09\uFF1A");
3943
+ console.log(` name : ${name}`);
3944
+ console.log(` region : ${region}`);
3945
+ console.log(` packageId : ${packageId}`);
3946
+ console.log(` templateId : ${templateId}`);
3947
+ console.log(` siteLang : ${siteLang}`);
3948
+ console.log(` adminLang : ${adminLang}`);
3949
+ console.log(` billId : ${opts.bill?.trim() || "(\u8BD5\u7528)"}`);
3950
+ const config = loadConfig();
3951
+ const body = {
3952
+ advertiserName: name,
3953
+ region,
3954
+ packageId,
3955
+ siteTemplateId: templateId,
3956
+ siteLang,
3957
+ adminLang,
3958
+ packageBillId: opts.bill?.trim() || void 0
3959
+ };
3960
+ const url = `${config.apiBaseUrl}/website-provision/sites`;
3961
+ let raw;
3962
+ try {
3963
+ raw = await apiFetch2(
3964
+ url,
3965
+ config,
3966
+ { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) },
3967
+ opts.verbose
3968
+ );
3969
+ } catch (e) {
3970
+ console.error(`
3971
+ \u274C \u521B\u5EFA\u7AD9\u70B9\u5931\u8D25\uFF1A${e.message}`);
3972
+ process.exit(1);
3973
+ return;
3974
+ }
3975
+ const obj = asRecordLocal(raw);
3976
+ const siteGuid = readStr2(obj, "siteGuid") || readStr2(obj, "SiteGuid");
3977
+ const bound = obj?.bound === true || obj?.Bound === true;
3978
+ const code = readStr2(obj, "code") || readStr2(obj, "Code") || "ok";
3979
+ if (opts.json) {
3980
+ console.log(JSON.stringify(raw, null, 2));
3981
+ } else {
3982
+ console.log(code === "site_created_unbound" ? "\n\u26A0\uFE0F \u7AD9\u70B9\u5DF2\u521B\u5EFA\u4F46\u8D26\u53F7\u6302\u63A5\u672A\u5B8C\u6210\n" : "\n\u2705 \u7AD9\u70B9\u521B\u5EFA\u8BF7\u6C42\u5DF2\u53D7\u7406\n");
3983
+ console.log(` siteGuid : ${siteGuid}`);
3984
+ console.log(` region : ${readStr2(obj, "region") || region}`);
3985
+ console.log(` state : ${readStr2(obj, "state") || readStr2(obj, "State") || "-"}`);
3986
+ console.log(` platformUrl : ${readStr2(obj, "platformUrl") || readStr2(obj, "PlatformUrl") || "-"}`);
3987
+ console.log(` bound : ${bound}`);
3988
+ console.log(` magKey : ${readStr2(obj, "magKey") || readStr2(obj, "MagKey") || "-"}`);
3989
+ console.log(` code : ${code}`);
3990
+ if (code === "site_created_unbound") {
3991
+ console.log(`
3992
+ \u8865\u507F\uFF1Asiluzan-website sites bind --site ${siteGuid} --region ${region} --name "${name}"
3993
+ `);
3994
+ }
3995
+ }
3996
+ if (opts.wait && siteGuid) {
3997
+ await pollUntilOnline(config, siteGuid, region, Number(opts.timeout) || 600, opts.verbose);
3998
+ } else if (siteGuid && !opts.json) {
3999
+ console.log(`
4000
+ \u8F6E\u8BE2\u72B6\u6001\uFF1Asiluzan-website sites status --site ${siteGuid} --region ${region}`);
4001
+ console.log(`\u5217\u8868\u6838\u5BF9\uFF1Asiluzan-website sites list -q ${siteGuid}
4002
+ `);
4003
+ }
4004
+ }
4005
+ async function runSitesStatus(opts) {
4006
+ const siteGuid = opts.site?.trim();
4007
+ const region = (opts.region || "US").trim();
4008
+ if (!siteGuid) {
4009
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --site <siteGuid>\n");
4010
+ process.exit(1);
4011
+ }
4012
+ const config = loadConfig();
4013
+ const url = `${config.apiBaseUrl}/website-provision/sites/${encodeURIComponent(region)}/${encodeURIComponent(siteGuid)}/status`;
4014
+ let raw;
4015
+ try {
4016
+ raw = await apiFetch2(url, config, {}, opts.verbose);
4017
+ } catch (e) {
4018
+ console.error(`
4019
+ \u274C \u67E5\u8BE2\u72B6\u6001\u5931\u8D25\uFF1A${e.message}`);
4020
+ process.exit(1);
4021
+ return;
4022
+ }
4023
+ if (opts.json) {
4024
+ console.log(JSON.stringify(raw, null, 2));
4025
+ return;
4026
+ }
4027
+ const obj = asRecordLocal(raw);
4028
+ console.log("\n\u5EFA\u7AD9\u72B6\u6001\n");
4029
+ console.log(` siteGuid : ${readStr2(obj, "siteGuid") || siteGuid}`);
4030
+ console.log(` region : ${readStr2(obj, "region") || region}`);
4031
+ console.log(` statusCode : ${String(obj?.statusCode ?? obj?.StatusCode ?? "-")}`);
4032
+ console.log(` online : ${String(obj?.online ?? obj?.Online ?? "-")}`);
4033
+ console.log(` url : ${readStr2(obj, "url") || readStr2(obj, "Url") || "-"}`);
4034
+ console.log();
4035
+ }
4036
+ async function runSitesBind(opts) {
4037
+ const siteGuid = opts.site?.trim();
4038
+ if (!siteGuid) {
4039
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --site <siteGuid>\n");
4040
+ process.exit(1);
4041
+ }
4042
+ if (!opts.name?.trim() && !opts.mag?.trim()) {
4043
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --name <advertiserName> \u6216 --mag <magKey>\n");
4044
+ process.exit(1);
4045
+ }
4046
+ const config = loadConfig();
4047
+ const body = {
4048
+ region: opts.region || "US",
4049
+ advertiserName: opts.name?.trim(),
4050
+ magKey: opts.mag?.trim()
4051
+ };
4052
+ const url = `${config.apiBaseUrl}/website-provision/sites/${encodeURIComponent(siteGuid)}/bind`;
4053
+ let raw;
4054
+ try {
4055
+ raw = await apiFetch2(
4056
+ url,
4057
+ config,
4058
+ { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) },
4059
+ opts.verbose
4060
+ );
4061
+ } catch (e) {
4062
+ console.error(`
4063
+ \u274C \u6302\u63A5\u5931\u8D25\uFF1A${e.message}`);
4064
+ process.exit(1);
4065
+ return;
4066
+ }
4067
+ if (opts.json) {
4068
+ console.log(JSON.stringify(raw, null, 2));
4069
+ return;
4070
+ }
4071
+ const obj = asRecordLocal(raw);
4072
+ console.log("\n\u2705 \u6302\u63A5\u5B8C\u6210\n");
4073
+ console.log(` siteGuid : ${readStr2(obj, "siteGuid") || siteGuid}`);
4074
+ console.log(` bound : ${String(obj?.bound ?? true)}`);
4075
+ console.log(` magKey : ${readStr2(obj, "magKey") || "-"}`);
4076
+ console.log("\n\u8BF7\u518D\u8DD1 siluzan-website sites list \u6838\u5BF9\u3002\n");
4077
+ }
4078
+ async function pollUntilOnline(config, siteGuid, region, timeoutSec, verbose) {
4079
+ const deadline = Date.now() + timeoutSec * 1e3;
4080
+ console.log(`
4081
+ \u7B49\u5F85\u7AD9\u70B9 Online\uFF08\u6700\u957F ${timeoutSec}s\uFF09\u2026`);
4082
+ while (Date.now() < deadline) {
4083
+ try {
4084
+ const url = `${config.apiBaseUrl}/website-provision/sites/${encodeURIComponent(region)}/${encodeURIComponent(siteGuid)}/status`;
4085
+ const raw = await apiFetch2(url, config, {}, verbose);
4086
+ const obj = asRecordLocal(raw);
4087
+ const online = Boolean(obj?.online ?? obj?.Online);
4088
+ const code = obj?.statusCode ?? obj?.StatusCode;
4089
+ process.stdout.write(` statusCode=${code} online=${online}
4090
+ `);
4091
+ if (online) {
4092
+ console.log("\n\u2705 \u7AD9\u70B9\u5DF2\u53EF\u8BBF\u95EE\uFF08Online\uFF09\n");
4093
+ return;
4094
+ }
4095
+ } catch (e) {
4096
+ process.stdout.write(` \u8F6E\u8BE2\u5F02\u5E38\uFF1A${e.message}
4097
+ `);
4098
+ }
4099
+ await new Promise((r) => setTimeout(r, 1e4));
4100
+ }
4101
+ console.error("\n\u26A0\uFE0F \u7B49\u5F85\u8D85\u65F6\uFF0C\u7AD9\u70B9\u53EF\u80FD\u4ECD\u5728\u521B\u5EFA\u4E2D\u3002\u8BF7\u7A0D\u540E siluzan-website sites status \u518D\u67E5\u3002\n");
4102
+ }
4103
+
4104
+ // src/commands/packages.ts
4105
+ function asRecord2(value) {
4106
+ return value && typeof value === "object" ? value : null;
4107
+ }
4108
+ function readString2(obj, key) {
4109
+ if (!obj) return "";
4110
+ const v = obj[key];
4111
+ return typeof v === "string" ? v : v == null ? "" : String(v);
4112
+ }
4113
+ function unwrapList(raw) {
4114
+ if (Array.isArray(raw)) {
4115
+ return raw.map(asRecord2).filter((x) => Boolean(x));
4116
+ }
4117
+ const obj = asRecord2(raw);
4118
+ if (!obj) return [];
4119
+ if (Array.isArray(obj.data)) {
4120
+ return obj.data.map(asRecord2).filter((x) => Boolean(x));
4121
+ }
4122
+ return [];
4123
+ }
4124
+ async function runPackagesList(opts) {
4125
+ const config = loadConfig();
4126
+ const url = `${config.apiBaseUrl}/website-provision/packages`;
4127
+ let raw;
4128
+ try {
4129
+ raw = await apiFetch2(url, config, {}, opts.verbose);
4130
+ } catch (e) {
4131
+ console.error(`
4132
+ \u274C \u83B7\u53D6\u5957\u9910\u5217\u8868\u5931\u8D25\uFF1A${e.message}`);
4133
+ if (!opts.verbose) console.error(" \u52A0 --verbose \u53EF\u67E5\u770B\u8BE6\u7EC6\u9519\u8BEF");
4134
+ process.exit(1);
4135
+ return;
4136
+ }
4137
+ const list = unwrapList(raw);
4138
+ if (opts.json) {
4139
+ console.log(JSON.stringify({ total: list.length, packages: list }, null, 2));
4140
+ return;
4141
+ }
4142
+ if (list.length === 0) {
4143
+ console.log("\n\u6682\u65E0\u53EF\u7528\u5957\u9910\u3002\n");
4144
+ return;
4145
+ }
4146
+ const columns = [
4147
+ { key: "packageId", header: "packageId" },
4148
+ { key: "packageName", header: "\u540D\u79F0" },
4149
+ { key: "monthUsd", header: "\u6708\u4ED8(USD)" },
4150
+ { key: "yearUsd", header: "\u5E74\u4ED8\u6708\u4EF7(USD)" },
4151
+ { key: "enabled", header: "\u542F\u7528" },
4152
+ { key: "sunset", header: "\u4E0B\u67B6" }
4153
+ ];
4154
+ const rows = list.map((p) => ({
4155
+ packageId: readString2(p, "packageId") || readString2(p, "PackageId"),
4156
+ packageName: readString2(p, "packageName") || readString2(p, "PackageName"),
4157
+ monthUsd: readString2(p, "monthPriceUSD") || readString2(p, "MonthPriceUSD") || "-",
4158
+ yearUsd: readString2(p, "yearlyPriceUSD") || readString2(p, "YearlyPriceUSD") || "-",
4159
+ enabled: String(p.enabled ?? p.Enabled ?? "-"),
4160
+ sunset: String(p.sunset ?? p.Sunset ?? "-")
4161
+ }));
4162
+ console.log(`
4163
+ \u5957\u9910\u5217\u8868\uFF08\u5171 ${list.length} \u4E2A\uFF09
4164
+ `);
4165
+ printCliTable(rows, columns);
4166
+ console.log("\n\u4E0B\u4E00\u6B65\uFF1Asiluzan-website templates list --package <packageId>\n");
4167
+ }
4168
+ function register6(program2) {
4169
+ const packages = program2.command("packages").description("\u5EFA\u7AD9\u5957\u9910\uFF08website-provision\uFF09");
4170
+ packages.command("list").description("\u5217\u51FA\u53EF\u552E\u5957\u9910").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4171
+ await runPackagesList(opts);
4172
+ });
4173
+ }
4174
+
4175
+ // src/commands/templates.ts
4176
+ function asRecord3(value) {
4177
+ return value && typeof value === "object" ? value : null;
4178
+ }
4179
+ function readString3(obj, key) {
4180
+ if (!obj) return "";
4181
+ const v = obj[key];
4182
+ return typeof v === "string" ? v : v == null ? "" : String(v);
4183
+ }
4184
+ function unwrapList2(raw) {
4185
+ if (Array.isArray(raw)) {
4186
+ return raw.map(asRecord3).filter((x) => Boolean(x));
4187
+ }
4188
+ const obj = asRecord3(raw);
4189
+ if (!obj) return [];
4190
+ if (Array.isArray(obj.data)) {
4191
+ return obj.data.map(asRecord3).filter((x) => Boolean(x));
4192
+ }
4193
+ if (Array.isArray(obj.items)) {
4194
+ return obj.items.map(asRecord3).filter((x) => Boolean(x));
4195
+ }
4196
+ return [];
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
+ }
4302
+ async function runTemplatesList(opts) {
4303
+ const config = loadConfig();
4304
+ const params = new URLSearchParams({ pageSize: "100", pageIndex: "1" });
4305
+ if (opts.package) params.set("package", opts.package);
4306
+ if (opts.industry) params.set("industry", opts.industry);
4307
+ if (opts.keyword) params.set("keyword", opts.keyword);
4308
+ const url = `${config.apiBaseUrl}/website-provision/templates?${params.toString()}`;
4309
+ let raw;
4310
+ try {
4311
+ raw = await apiFetch2(url, config, {}, opts.verbose);
4312
+ } catch (e) {
4313
+ console.error(`
4314
+ \u274C \u83B7\u53D6\u6A21\u677F\u5217\u8868\u5931\u8D25\uFF1A${e.message}`);
4315
+ if (!opts.verbose) console.error(" \u52A0 --verbose \u53EF\u67E5\u770B\u8BE6\u7EC6\u9519\u8BEF");
4316
+ process.exit(1);
4317
+ return;
4318
+ }
4319
+ const list = unwrapList2(raw).map(enrichTemplatePreview);
4320
+ if (opts.json) {
4321
+ console.log(JSON.stringify({ total: list.length, templates: list }, null, 2));
4322
+ return;
4323
+ }
4324
+ if (list.length === 0) {
4325
+ console.log("\n\u6682\u65E0\u5339\u914D\u6A21\u677F\u3002\u8BF7\u68C0\u67E5 --package \u662F\u5426\u6B63\u786E\u3002\n");
4326
+ return;
4327
+ }
4328
+ const columns = [
4329
+ { key: "id", header: "siteTemplateId" },
4330
+ { key: "name", header: "\u540D\u79F0" },
4331
+ { key: "industry", header: "\u884C\u4E1A" },
4332
+ { key: "preview", header: "\u9884\u89C8" },
4333
+ { key: "hot", header: "\u70ED\u95E8" },
4334
+ { key: "neu", header: "\u65B0\u54C1" }
4335
+ ];
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
+ });
4347
+ console.log(`
4348
+ \u6A21\u677F\u5217\u8868\uFF08\u5171 ${list.length} \u4E2A\uFF09
4349
+ `);
4350
+ printCliTable(rows, columns);
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();
4432
+ }
4433
+ async function runTemplatesGet(opts) {
4434
+ const id = opts.id?.trim();
4435
+ if (!id) {
4436
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id <siteTemplateId>\n");
4437
+ process.exit(1);
4438
+ }
4439
+ const config = loadConfig();
4440
+ const url = `${config.apiBaseUrl}/website-provision/templates/${encodeURIComponent(id)}`;
4441
+ let raw;
4442
+ try {
4443
+ raw = await apiFetch2(url, config, {}, opts.verbose);
4444
+ } catch (e) {
4445
+ console.error(`
4446
+ \u274C \u83B7\u53D6\u6A21\u677F\u8BE6\u60C5\u5931\u8D25\uFF1A${e.message}`);
4447
+ process.exit(1);
4448
+ return;
4449
+ }
4450
+ const enriched = enrichTemplatePreview(asRecord3(raw) ?? {});
4451
+ if (opts.json) {
4452
+ console.log(JSON.stringify(enriched, null, 2));
4453
+ return;
4454
+ }
4455
+ const p = pickPreviewFields(enriched);
4456
+ console.log("\n\u6A21\u677F\u8BE6\u60C5\n");
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})`);
4467
+ console.log("\n\uFF08\u5B89\u88C5\u5305\u5730\u5740\u5DF2\u8131\u654F\uFF0C\u4E0D\u4F1A\u8F93\u51FA\uFF09\n");
4468
+ }
4469
+ function register7(program2) {
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
+ });
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) => {
4480
+ await runTemplatesList(opts);
4481
+ });
4482
+ templates.command("get").description("\u6A21\u677F\u8BE6\u60C5\uFF08\u4E0D\u542B\u5B89\u88C5\u5305 URI\uFF09").option("--id <siteTemplateId>", "\u6A21\u677F ID").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4483
+ await runTemplatesGet(opts);
4484
+ });
4485
+ }
4486
+
4487
+ // src/commands/trial.ts
4488
+ function asRecord4(value) {
4489
+ return value && typeof value === "object" ? value : null;
4490
+ }
4491
+ async function runTrialStatus(opts) {
4492
+ const config = loadConfig();
4493
+ const url = `${config.apiBaseUrl}/website-provision/trial-count`;
4494
+ let raw;
4495
+ try {
4496
+ raw = await apiFetch2(url, config, {}, opts.verbose);
4497
+ } catch (e) {
4498
+ console.error(`
4499
+ \u274C \u83B7\u53D6\u8BD5\u7528\u6B21\u6570\u5931\u8D25\uFF1A${e.message}`);
4500
+ process.exit(1);
4501
+ return;
4502
+ }
4503
+ if (opts.json) {
4504
+ console.log(JSON.stringify(raw, null, 2));
4505
+ return;
4506
+ }
4507
+ const obj = asRecord4(raw);
4508
+ const used = Number(obj?.used ?? obj?.Used ?? 0);
4509
+ const limit = Number(obj?.limit ?? obj?.Limit ?? 3);
4510
+ const remaining = Number(obj?.remaining ?? obj?.Remaining ?? Math.max(0, limit - used));
4511
+ console.log("\n\u8BD5\u7528\u5EFA\u7AD9\u989D\u5EA6\n");
4512
+ console.log(` \u5DF2\u7528 : ${used}`);
4513
+ console.log(` \u4E0A\u9650 : ${limit}`);
4514
+ console.log(` \u5269\u4F59 : ${remaining}`);
4515
+ console.log();
4516
+ }
4517
+ function register8(program2) {
4518
+ const trial = program2.command("trial").description("\u8BD5\u7528\u5EFA\u7AD9\u989D\u5EA6\uFF08website-provision\uFF09");
4519
+ trial.command("status").description("\u67E5\u770B\u5F53\u524D\u8D26\u53F7\u8BD5\u7528\u5DF2\u7528\u6B21\u6570\u4E0E\u4E0A\u9650").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4520
+ await runTrialStatus(opts);
4521
+ });
4522
+ }
4523
+
4524
+ // src/commands/bills.ts
4525
+ function asRecord5(value) {
4526
+ return value && typeof value === "object" ? value : null;
4527
+ }
4528
+ function readString4(obj, key) {
4529
+ if (!obj) return "";
4530
+ const v = obj[key];
4531
+ return typeof v === "string" ? v : v == null ? "" : String(v);
4532
+ }
4533
+ async function runBillsCreate(opts) {
4534
+ const packageId = opts.package?.trim();
4535
+ if (!packageId) {
4536
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --package <packageId>\n");
4537
+ process.exit(1);
4538
+ }
4539
+ const config = loadConfig();
4540
+ const body = {
4541
+ packageId,
4542
+ payChannel: opts.channel || "Monthly",
4543
+ currency: opts.currency || "USD",
4544
+ successUrl: opts.successUrl || "",
4545
+ cancelUrl: opts.cancelUrl || ""
4546
+ };
4547
+ console.log("\n\u5373\u5C06\u521B\u5EFA\u5957\u9910\u8D26\u5355\uFF08\u4E0D\u4F1A\u81EA\u52A8\u6263\u6B3E\uFF0C\u7528\u6237\u9700\u6253\u5F00\u6536\u94F6\u53F0\u5B8C\u6210\u652F\u4ED8\uFF09\uFF1A");
4548
+ console.log(` packageId : ${body.packageId}`);
4549
+ console.log(` payChannel : ${body.payChannel}`);
4550
+ console.log(` currency : ${body.currency}`);
4551
+ const url = `${config.apiBaseUrl}/website-provision/bills`;
4552
+ let raw;
4553
+ try {
4554
+ raw = await apiFetch2(
4555
+ url,
4556
+ config,
4557
+ { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) },
4558
+ opts.verbose
4559
+ );
4560
+ } catch (e) {
4561
+ console.error(`
4562
+ \u274C \u521B\u5EFA\u8D26\u5355\u5931\u8D25\uFF1A${e.message}`);
4563
+ process.exit(1);
4564
+ return;
4565
+ }
4566
+ if (opts.json) {
4567
+ console.log(JSON.stringify(raw, null, 2));
4568
+ return;
4569
+ }
4570
+ const obj = asRecord5(raw);
4571
+ const billId = readString4(obj, "billId") || readString4(obj, "BillId");
4572
+ const checkoutUrl = readString4(obj, "checkoutUrl") || readString4(obj, "CheckoutUrl");
4573
+ console.log("\n\u2705 \u8D26\u5355\u5DF2\u521B\u5EFA\n");
4574
+ console.log(` billId : ${billId}`);
4575
+ console.log(` state : ${readString4(obj, "state") || readString4(obj, "State") || "-"}`);
4576
+ console.log(` checkoutUrl : ${checkoutUrl || "(\u65E0\uFF0C\u8BF7\u68C0\u67E5 Stripe \u914D\u7F6E)"}`);
4577
+ console.log("\n\u8BF7\u5C06\u6536\u94F6\u53F0\u94FE\u63A5\u53D1\u7ED9\u7528\u6237\u5B8C\u6210\u652F\u4ED8\uFF0C\u7136\u540E\uFF1A");
4578
+ console.log(` siluzan-website bills get --id ${billId}`);
4579
+ console.log(` \u652F\u4ED8\u6210\u529F\u540E\u521B\u5EFA\u7AD9\u70B9\u65F6\u5E26 --bill ${billId}
4580
+ `);
4581
+ }
4582
+ async function runBillsGet(opts) {
4583
+ const id = opts.id?.trim();
4584
+ if (!id) {
4585
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id <billId>\n");
4586
+ process.exit(1);
4587
+ }
4588
+ const config = loadConfig();
4589
+ const url = `${config.apiBaseUrl}/website-provision/bills/${encodeURIComponent(id)}`;
4590
+ let raw;
4591
+ try {
4592
+ raw = await apiFetch2(url, config, {}, opts.verbose);
4593
+ } catch (e) {
4594
+ console.error(`
4595
+ \u274C \u67E5\u8BE2\u8D26\u5355\u5931\u8D25\uFF1A${e.message}`);
4596
+ process.exit(1);
4597
+ return;
4598
+ }
4599
+ if (opts.json) {
4600
+ console.log(JSON.stringify(raw, null, 2));
4601
+ return;
4602
+ }
4603
+ const obj = asRecord5(raw);
4604
+ console.log("\n\u8D26\u5355\u72B6\u6001\n");
4605
+ console.log(` billId : ${readString4(obj, "billId") || id}`);
4606
+ console.log(` state : ${readString4(obj, "state") || readString4(obj, "State")}`);
4607
+ console.log(` paid : ${String(obj?.paid ?? obj?.Paid ?? "-")}`);
4608
+ console.log(` packageId : ${readString4(obj, "packageId") || readString4(obj, "PackageId") || "-"}`);
4609
+ console.log(` siteId : ${readString4(obj, "siteId") || readString4(obj, "SiteId") || "(\u672A\u7ED1\u5B9A)"}`);
4610
+ console.log();
4611
+ }
4612
+ function register9(program2) {
4613
+ const bills = program2.command("bills").description("\u5EFA\u7AD9\u5957\u9910\u8D26\u5355 / \u652F\u4ED8\uFF08website-provision\uFF09");
4614
+ bills.command("create").description("\u521B\u5EFA\u5957\u9910\u8D26\u5355\u5E76\u8FD4\u56DE\u6536\u94F6\u53F0 URL\uFF08\u5199\u64CD\u4F5C\uFF0CAgent \u987B\u5148\u83B7\u7528\u6237\u786E\u8BA4\uFF09").option("--package <packageId>", "\u5957\u9910 ID").option("--channel <Monthly|Yearly>", "\u4ED8\u8D39\u5468\u671F", "Monthly").option("--currency <code>", "\u5E01\u79CD", "USD").option("--success-url <url>", "\u652F\u4ED8\u6210\u529F\u56DE\u8DF3 URL").option("--cancel-url <url>", "\u652F\u4ED8\u53D6\u6D88\u56DE\u8DF3 URL").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4615
+ await runBillsCreate(opts);
4616
+ });
4617
+ bills.command("get").description("\u67E5\u8BE2\u8D26\u5355\u652F\u4ED8\u4E0E\u7ED1\u5B9A\u72B6\u6001").option("--id <billId>", "\u8D26\u5355\u53F7").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4618
+ await runBillsGet(opts);
4619
+ });
3912
4620
  }
3913
4621
 
3914
4622
  // src/commands/pages.ts
@@ -4202,7 +4910,7 @@ async function runPagesTrash(opts) {
4202
4910
  `
4203
4911
  );
4204
4912
  }
4205
- function register6(program2) {
4913
+ function register10(program2) {
4206
4914
  const pages = program2.command("pages").description("WordPress \u9875\u9762\uFF08zionbuilder visual-edit\uFF09");
4207
4915
  pages.command("list").description("\u5217\u51FA\u7AD9\u70B9\u9875\u9762").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740\uFF08\u8DF3\u8FC7 all-sites\uFF09").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4208
4916
  await runPagesList(opts);
@@ -4735,7 +5443,7 @@ function hintZaspIfNeeded(pluginOrSlug) {
4735
5443
  if (!pluginOrSlug.toLowerCase().includes("zasp-smart-recommender")) return;
4736
5444
  console.log(" \u63A5\u4E0B\u6765\u7528 `siluzan-website zasp settings / set / sync` \u914D\u7F6E AI \u63A8\u8350\uFF08\u89C1 references/zasp-recommender.md\uFF09\n");
4737
5445
  }
4738
- function register7(program2) {
5446
+ function register11(program2) {
4739
5447
  const zasp = program2.command("zasp").description("zasp-smart-recommender\uFF08AI \u63A8\u8350\uFF09\u914D\u7F6E\u4E0E\u9A8C\u6536\uFF0C\u8D70\u7AD9\u70B9 /wp-json/zasp-b2b/v2");
4740
5448
  zasp.command("settings").description("\u67E5\u770B\u5F53\u524D AI \u63A8\u8350\u914D\u7F6E\uFF08GET /settings\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--json", "\u8F93\u51FA\u5B8C\u6574 JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4741
5449
  await runZaspSettings(opts);
@@ -5247,7 +5955,7 @@ async function runPluginsUninstall(opts) {
5247
5955
  exitOnError: false
5248
5956
  });
5249
5957
  }
5250
- function register8(program2) {
5958
+ function register12(program2) {
5251
5959
  const plugins = program2.command("plugins").description("WordPress \u63D2\u4EF6\uFF1A\u5217\u51FA\u3001\u76EE\u5F55\u3001\u5B89\u88C5\u3001\u5347\u7EA7\u3001\u5378\u8F7D");
5252
5960
  plugins.command("list").description("\u5217\u51FA\u7AD9\u70B9\u5DF2\u5B89\u88C5\u7684\u5168\u90E8\u63D2\u4EF6").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5253
5961
  await runPluginsList(opts);
@@ -5534,7 +6242,7 @@ async function runContentTrash(opts) {
5534
6242
  \u2705 \u5DF2\u79FB\u5165\u56DE\u6536\u7AD9 ${restBase} #${id}\uFF08status=${item.status || "trash"}\uFF09
5535
6243
  `);
5536
6244
  }
5537
- function register9(program2) {
6245
+ function register13(program2) {
5538
6246
  const content = program2.command("content").description("\u901A\u7528\u5185\u5BB9\uFF1A\u6587\u7AE0/\u4EA7\u54C1\u7B49 REST \u7C7B\u578B\uFF08\u9875\u9762\u6B63\u6587\u8BF7\u7528 pages\uFF09");
5539
6247
  content.command("types").description("\u5217\u51FA\u7AD9\u70B9\u53EF REST \u7BA1\u7406\u7684\u5185\u5BB9\u7C7B\u578B").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5540
6248
  await runContentTypes(opts);
@@ -5724,7 +6432,7 @@ async function runMediaUpload(opts) {
5724
6432
  console.log(` \u6807\u9898 : ${wpRenderedText(item.title) || filename}`);
5725
6433
  console.log();
5726
6434
  }
5727
- function register10(program2) {
6435
+ function register14(program2) {
5728
6436
  const media = program2.command("media").description("\u5A92\u4F53\u5E93\uFF1A\u5217\u51FA\u3001\u67E5\u770B\u3001\u4E0A\u4F20");
5729
6437
  media.command("list").description("\u5217\u51FA\u5A92\u4F53\u5E93\uFF08\u6700\u8FD1 50 \u6761\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5730
6438
  await runMediaList(opts);
@@ -5905,7 +6613,7 @@ async function runTermsDelete(opts) {
5905
6613
  \u2705 \u5DF2\u5220\u9664 ${restBase} #${id}
5906
6614
  `);
5907
6615
  }
5908
- function register11(program2) {
6616
+ function register15(program2) {
5909
6617
  const terms = program2.command("terms").description("\u5206\u7C7B / \u6807\u7B7E / \u81EA\u5B9A\u4E49\u5206\u7C7B\u6CD5");
5910
6618
  terms.command("taxonomies").description("\u5217\u51FA\u5206\u7C7B\u6CD5").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5911
6619
  await runTermsTaxonomies(opts);
@@ -5985,7 +6693,7 @@ ${target.site.name} \u641C\u7D22\u300C${q}\u300D\uFF08${hits.length}\uFF09
5985
6693
  );
5986
6694
  console.log();
5987
6695
  }
5988
- function register12(program2) {
6696
+ function register16(program2) {
5989
6697
  program2.command("search").description("\u5168\u7AD9\u641C\u7D22\uFF08\u9875\u9762/\u6587\u7AE0/\u5A92\u4F53\u7B49\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("-q, --q <keyword>", "\u5173\u952E\u8BCD").option("--type <subtype>", "\u9650\u5B9A rest \u5B50\u7C7B\u578B\uFF0C\u5982 page / post").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5990
6698
  await runSearch(opts);
5991
6699
  });
@@ -5994,20 +6702,24 @@ function register12(program2) {
5994
6702
  // src/index.ts
5995
6703
  installProcessHandlers();
5996
6704
  var program = new Command();
5997
- program.name("siluzan-website").description("Siluzan WordPress \u7AD9\u70B9\u7BA1\u7406\uFF1A\u7AD9\u70B9\u3001\u9875\u9762\u3001\u6587\u7AE0\u3001\u5A92\u4F53\u3001\u5206\u7C7B\u3001\u63D2\u4EF6\u3001AI \u63A8\u8350").version(getCurrentVersion2());
6705
+ program.name("siluzan-website").description("Siluzan WordPress\uFF1A\u5F00\u901A\u5EFA\u7AD9\u3001\u7AD9\u70B9\u3001\u9875\u9762\u3001\u6587\u7AE0\u3001\u5A92\u4F53\u3001\u5206\u7C7B\u3001\u63D2\u4EF6\u3001AI \u63A8\u8350").version(getCurrentVersion2());
5998
6706
  var REGISTRARS = [
5999
6707
  register,
6000
6708
  register2,
6001
6709
  register3,
6002
6710
  register4,
6003
- register5,
6004
6711
  register6,
6712
+ register7,
6713
+ register8,
6005
6714
  register9,
6715
+ register5,
6006
6716
  register10,
6007
- register11,
6717
+ register13,
6718
+ register14,
6719
+ register15,
6720
+ register16,
6008
6721
  register12,
6009
- register8,
6010
- register7
6722
+ register11
6011
6723
  ];
6012
6724
  for (const reg of REGISTRARS) reg(program);
6013
6725
  program.parse();
@@ -1,17 +1,17 @@
1
1
  ---
2
2
  name: siluzan-website
3
3
  description: >-
4
- 管理丝路赞 WordPress 站点(wp-k8s 托管):列出站点、页面/文章/产品、媒体、分类、插件。
5
- 当用户提到 WordPress、建站后台、新增页面、发文章、上传图片、改官网、装插件、
4
+ 管理丝路赞 WordPress 站点(wp-k8s 托管):选套餐/模板开通新站、列出站点、页面/文章/产品、媒体、分类、插件。
5
+ 当用户提到 WordPress、建站、选套餐、选模板、试用站、创建网站、建站后台、新增页面、发文章、上传图片、改官网、装插件、
6
6
  AI 推荐、相关推荐、zasp、zasp-smart-recommender、B2B Site AI、
7
- 站点 guid、siluzan-website、zionbuilder、visual-edit 时使用。不是 LPS / AI Sites,也不是 SEO JSON 生成。
7
+ 站点 guid、siluzan-website、zionbuilder、visual-edit、website-provision 时使用。不是 LPS / AI Sites,也不是 SEO JSON 生成。
8
8
  ---
9
9
 
10
10
  # siluzan-website
11
11
 
12
- 管理用户账号下的 **WordPress 站点**(TSO 建站 / wp-k8s)。通过 CLI 调平台 all-sites 与站点 WP REST,不要手写 curl,不要猜 guid / 页面 ID。
12
+ 管理用户账号下的 **WordPress 站点**(TSO 建站 / wp-k8s)。通过 CLI 调主站 `website-provision` / `all-sites` 与站点 WP REST,不要手写 curl,不要猜 guid / 页面 ID,**不要直连 SiteMan**。
13
13
 
14
- **不是** `landing-page-system` 的 LPS Agent,也不是 `siluzan-seo` 的 schema 生成。用户要的是「我的官网加一页 / 发一篇资讯 / 上传图片 / 装插件 / 配 AI 推荐」才用本 Skill。
14
+ **不是** `landing-page-system` 的 LPS Agent,也不是 `siluzan-seo` 的 schema 生成。用户要「开通新站 / 我的官网加一页 / 发资讯 / 装插件」才用本 Skill。
15
15
 
16
16
  ## 一键安装
17
17
 
@@ -39,7 +39,13 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
39
39
  | `siluzan-website login` / `send-login-code` | 登录并保存 API Key | [references/setup.md](references/setup.md) |
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
+ | `siluzan-website packages list` | 可售套餐 | [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) |
45
+ | `siluzan-website trial status` | 试用额度 | [references/provision.md](references/provision.md) |
46
+ | `siluzan-website bills create/get` | 套餐账单与支付状态 | [references/provision.md](references/provision.md) |
42
47
  | `siluzan-website sites list` | 列出我的 WordPress 站点 | [references/sites.md](references/sites.md) |
48
+ | `siluzan-website sites create/status/bind` | 开通 / 状态 / 挂接补偿 | [references/provision.md](references/provision.md) |
43
49
  | `siluzan-website pages list/get/add/update/meta/trash` | Zion 页面 + 元数据 / 回收站 | [references/pages.md](references/pages.md) |
44
50
  | `siluzan-website content types/list/get/add/update/trash` | 文章/产品等 REST 类型 | [references/content.md](references/content.md) |
45
51
  | `siluzan-website media list/get/upload` | 媒体库 | [references/media.md](references/media.md) |
@@ -56,6 +62,7 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
56
62
  | 用户在做什么 | 先读 | 再执行 |
57
63
  |--------------|------|--------|
58
64
  | 首次安装 / 登录 | `references/setup.md` | `login` 或复用已有 CSO/TSO 凭据 |
65
+ | **开通新站 / 选模板建站** | `references/provision.md` | **先** `templates catalog` 图文选模 → **再**一次性表单(站名/区域/语言/试用或付费)→ trial/bills → 确认 → `sites create` |
59
66
  | 我有哪些网站 | `references/sites.md` | `sites list`,不要猜 guid |
60
67
  | 加一页 / 改一页正文 | `references/pages.md` | 先 `sites list` 再 `pages add/update` |
61
68
  | 改页面 slug / 状态 / 回收站 | `references/pages.md` | `pages meta` / `pages trash` |
@@ -64,30 +71,33 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
64
71
  | 分类标签 | `references/terms.md` | `terms list` 再 add/update |
65
72
  | 按关键词找内容 | `references/search.md` | `search --q` |
66
73
  | 这个站装了哪些插件 | `references/plugins.md` | `plugins list`(全部已装,不只是目录) |
67
- | 安装 / 启用目录插件 | `references/plugins.md` | `plugins catalog`(官方存储目录,含 AI 推荐)→ 确认 → `install --slug` |
74
+ | 安装 / 启用目录插件 | `references/plugins.md` | `plugins catalog` → 确认 → `install --slug` |
68
75
  | 安装外部插件 | `references/plugins.md` | 确认 → `install --zip` |
69
- | 升级已装插件 | `references/plugins.md` | `list` 拿 plugin → 确认 → `update --plugin`;外部再加 `--zip` |
70
- | 卸载插件 | `references/plugins.md` | `list` 拿 plugin → 确认 → `uninstall --plugin` |
71
- | 配置 / 验收 AI 推荐(zasp) | `references/zasp-recommender.md` | 先确认已装 `zasp-smart-recommender` → `zasp settings` → 确认后 `set`;改向量再 `sync` |
76
+ | 升级已装插件 | `references/plugins.md` | `list` → 确认 → `update --plugin` |
77
+ | 卸载插件 | `references/plugins.md` | `list` → 确认 → `uninstall --plugin` |
78
+ | 配置 / 验收 AI 推荐(zasp) | `references/zasp-recommender.md` | 确认已装 → `zasp settings` → `set` / `sync` |
72
79
 
73
80
  ---
74
81
 
75
82
  ## AI 行为规范
76
83
 
77
84
  1. **计划 → 确认 → 执行 → 验证**
78
- - 写操作(`pages add/update/meta/trash`、`content add/update/trash`、`media upload`、`terms add/update/delete`、`plugins install/update/uninstall/update-helper`、`zasp set/sync`)会立刻生效,执行前必须复述目标站点与内容,获得用户确认。
79
- - 只读(`sites list`、`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`)可直接跑。
80
- 2. **不猜 ID**:站点用 `sites list` 的 `guid`;页面用 `pages list` 的 `id`。
81
- 3. **先查再写**:改页面前先 `pages list` 确认目标页存在。
82
- 4. **只认 API Key**:所有命令走 `x-api-key`(`SILUZAN_API_KEY` 或 `login` 写入的 config)。Agent 沙箱只注入 Key,不要向用户要 JWT,也不要设 `SILUZAN_AUTH_TOKEN`。
83
- 5. **命令透明**:用一句话说明意图(「正在列出您的站点」),不要把整段命令当主回复;用户要细节时再给完整命令。
84
- 6. **失败先自救**:401 → 引导重新 `login`;站点找不到 → 再跑 `sites list`;helper REST 404 时命令会先升 helper;oauth2 401/404 → 说明要 SiteMan 镜像升级。不要编造成功。
85
- 7. **不传任意下载 URL**:外部插件只接受本地 `--zip`。helper 只从镜像 `1.0.*` zip 引导升级,不要上架 helper。
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`)会立刻生效,执行前必须复述目标与内容,获得用户确认。
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`)可直接跑。
87
+ 2. **不猜 ID**:站点用 `sites list` 的 `guid`;页面用 `pages list` 的 `id`;套餐/模板用 list 返回的 id。
88
+ 3. **先查再写**:改页面前先 `pages list`;**开站必须先 `templates catalog` 选定模板**,再一次性收集站信息表单;创建前再查 `trial status` 或 `bills get`。
89
+ 禁止未选模板就追问站名/区域/语言。
90
+ 4. **只认 API Key**:所有命令走 `x-api-key`。Agent 沙箱只注入 Key,不要向用户要 JWT,也不要设 `SILUZAN_AUTH_TOKEN`,不要直连 ussiteman。
91
+ 5. **命令透明**:用一句话说明意图;用户要细节时再给完整命令。
92
+ 6. **失败先自救**:401 → 重新 `login`;开站失败勿盲目重试 POST;`site_created_unbound` → `sites bind`;helper 404 → 自动升 helper。不要编造成功。
93
+ 7. **不传任意下载 URL**:外部插件只接受本地 `--zip`。
86
94
 
87
95
  ### 本版明确不做
88
96
 
89
- - 创建 / 删除 / 重启站点(SiteMan)
97
+ - CLI / Agent 直连 SiteMan(ussiteman)
98
+ - 删除 / 重启站点
90
99
  - 绑定域名 / CDN / SSL
91
100
  - 单独停用插件但留在磁盘(停用只作为卸载的一步)
92
101
  - 用 API 写入 `ZASP_PROXY_KEY`(K8s 环境变量)
93
102
  - LPS / AI Sites / SEO JSON 灌入
103
+ - Agent 静默代扣 / 伪造已支付账单
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "slug": "siluzan-website",
3
- "version": "1.0.1-beta.5",
4
- "publishedAt": 1789721509096,
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": [
@@ -0,0 +1,112 @@
1
+ # 对话开通站点(provision)
2
+
3
+ 通过主站 `website-provision` API 完成:**先选模板(图文)→ 一次性填站信息 → 试用/付费 → 创建**。
4
+ 对齐 TSO 建站选模体验。**禁止**直连 ussiteman,**禁止**向用户要 JWT。只使用 API Key + 本 Skill CLI。
5
+
6
+ ## 对话 SOP(必须按此顺序)
7
+
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
76
+
77
+ ## 写操作确认
78
+
79
+ `bills create`、`sites create`、`sites bind` 执行前必须复述参数并获得用户明确同意。
80
+ 创建站**不可盲目重试**;失败后先 `sites list` / `trial status` / `bills get` 排查。
81
+
82
+ ## 命令速查
83
+
84
+ ```bash
85
+ siluzan-website templates catalog
86
+ siluzan-website templates catalog --package BasicEdition
87
+ siluzan-website templates get --id <siteTemplateId>
88
+ siluzan-website trial status
89
+ siluzan-website bills create --package <packageId> --channel Monthly
90
+ siluzan-website bills get --id <billId>
91
+ siluzan-website sites create --name "Acme" --region US --package <id> --template <id> --site-lang en_US --admin-lang en_US
92
+ siluzan-website sites create ... --bill <billId> --wait
93
+ siluzan-website sites status --site <guid> --region US
94
+ siluzan-website sites bind --site <guid> --region US --name "Acme"
95
+ ```
96
+
97
+ ## 常见错误
98
+
99
+ | 现象 | 处理 |
100
+ |------|------|
101
+ | trial_exhausted / trials | 试用已满 3 次,引导付费账单路径 |
102
+ | bill_unpaid / bill_owner_mismatch | 查 `bills get`;勿用他人账单号 |
103
+ | site_created_unbound | 跑 `sites bind`,再 `sites list` |
104
+ | 503 disabled | 主站 WebsiteProvision 功能开关关闭 |
105
+ | template-catalog 404 | 主站未部署 catalog 接口;可暂用 `packages list` + `templates list --package` 拼目录 |
106
+
107
+ ## 明确不做
108
+
109
+ - CLI 直连 SiteMan / 注入 `SILUZAN_AUTH_TOKEN` 开通
110
+ - 未选模板就收集站名等建站字段
111
+ - 删除站、重启、绑域名、CDN、SSL
112
+ - AI Sites / LPS 静态建站
@@ -1,6 +1,6 @@
1
- # 站点列表
1
+ # 站点列表与开通
2
2
 
3
- ## 命令
3
+ ## 列表
4
4
 
5
5
  ```bash
6
6
  siluzan-website sites list
@@ -10,7 +10,11 @@ siluzan-website sites list --json
10
10
 
11
11
  走主站 `GET /query/profile/steward-individual/all-sites`(与 TSO 建站后台同一接口)。
12
12
 
13
- ## 返回字段
13
+ ## 开通 / 状态 / 挂接
14
+
15
+ 见 [provision.md](provision.md)。创建走 `POST /website-provision/sites`,**不**直连 SiteMan。
16
+
17
+ ## 返回字段(list)
14
18
 
15
19
  | 字段 | 含义 |
16
20
  |------|------|
@@ -33,3 +37,4 @@ siluzan-website sites list --json
33
37
  2. 多个站点时让用户选,不要猜。
34
38
  3. 后续页面 / 插件命令传 `--site <guid>`。名称唯一时 `--site 名称` 也可以。
35
39
  4. 需要对返回结果做筛选时加 `--json`,再在本地处理;不要编造 guid。
40
+ 5. 用户要「新建站」时走 [provision.md](provision.md),不要只用 list。
@@ -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.5'
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.5"
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.5",
3
+ "version": "1.0.1-beta.7",
4
4
  "description": "Siluzan WordPress 站点管理 Skill CLI — 列出站点、新增/编辑页面、安装/升级插件。",
5
5
  "keywords": [
6
6
  "ai-skill",