siluzan-website-cli 1.0.1-beta.1 → 1.0.1-beta.2

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
 
@@ -21,9 +21,12 @@ siluzan-website pages add --site <guid> --title "关于我们" --content-file ab
21
21
  siluzan-website plugins list --site <guid>
22
22
  siluzan-website plugins catalog --site <guid>
23
23
  siluzan-website plugins install --site <guid> --slug <slug>
24
+ siluzan-website plugins install --site <guid> --zip ./plugin.zip
25
+ siluzan-website plugins update --site <guid> --plugin <plugin>
26
+ siluzan-website plugins helper-status --site <guid>
24
27
  ```
25
28
 
26
- 页面写入走站点上已有的 `zionbuilder/v1` visual-edit 接口(HTML → Builder)。插件安装 REST 尚未开放,本版只支持列出。
29
+ 页面写入走站点上已有的 `zionbuilder/v1` visual-edit 接口(HTML → Builder)。插件:目录用 `--slug`,外部用 `--zip`;helper 太旧时会先从镜像本地 zip 升级。
27
30
 
28
31
  ## 开发
29
32
 
package/dist/index.js CHANGED
@@ -3995,15 +3995,70 @@ function register6(program2) {
3995
3995
  }
3996
3996
 
3997
3997
  // src/commands/plugins.ts
3998
+ import { readFileSync as readFileSync6, existsSync as existsSync3 } from "fs";
3999
+ import { basename as basename2, resolve as resolve2 } from "path";
4000
+ var MAX_ZIP_BYTES = 20 * 1024 * 1024;
3998
4001
  function pluginName(item) {
3999
4002
  if (typeof item.name === "string") return item.name;
4000
4003
  return item.name?.raw || item.name?.rendered || item.plugin || "";
4001
4004
  }
4005
+ function pluginKey(value) {
4006
+ return value.replace(/\\/g, "/").replace(/\.php$/i, "").toLowerCase();
4007
+ }
4002
4008
  async function resolveTarget(opts) {
4003
4009
  const config = loadConfig();
4004
4010
  const site = await resolveSite(config, opts);
4005
4011
  return { config, site, base: trimTrailingSlash(site.url) };
4006
4012
  }
4013
+ function isRouteMissing(err) {
4014
+ const msg = err.message || "";
4015
+ return /HTTP 404/.test(msg) && /No route was found|rest_no_route/i.test(msg);
4016
+ }
4017
+ function readZipPayload(zipPath) {
4018
+ const abs = resolve2(zipPath);
4019
+ if (!existsSync3(abs)) {
4020
+ console.error(`
4021
+ \u274C zip \u4E0D\u5B58\u5728\uFF1A${abs}
4022
+ `);
4023
+ process.exit(1);
4024
+ }
4025
+ const buf = readFileSync6(abs);
4026
+ if (buf.length > MAX_ZIP_BYTES) {
4027
+ console.error("\n\u274C zip \u8D85\u8FC7 20MB\uFF0C\u8BF7\u6362\u66F4\u5C0F\u7684\u5305\n");
4028
+ process.exit(1);
4029
+ }
4030
+ return {
4031
+ filename: basename2(abs),
4032
+ zip_base64: buf.toString("base64")
4033
+ };
4034
+ }
4035
+ function printActionResult(result, site, base, okTitle) {
4036
+ if (!result.success) {
4037
+ console.error(`
4038
+ \u274C ${result.message || "\u7AD9\u70B9\u62D2\u7EDD\u4E86\u8FD9\u6B21\u64CD\u4F5C"}`);
4039
+ if (result.code) console.error(` code : ${result.code}`);
4040
+ process.exit(1);
4041
+ }
4042
+ console.log(result.already ? "\n\u2705 \u63D2\u4EF6\u5DF2\u5728\u7AD9\u70B9\u4E0A\uFF08\u5DF2\u542F\u7528\uFF09" : `
4043
+ \u2705 ${okTitle}`);
4044
+ if (result.plugin) console.log(` plugin : ${result.plugin}`);
4045
+ if (result.version) console.log(` \u7248\u672C : ${result.version}`);
4046
+ if (result.zip) console.log(` zip : ${result.zip}`);
4047
+ if (result.message) console.log(` \u8BF4\u660E : ${result.message}`);
4048
+ console.log(` \u7AD9\u70B9 : ${site.name} (${base})
4049
+ `);
4050
+ }
4051
+ async function withHelperBootstrap(opts, target, run) {
4052
+ try {
4053
+ return await run();
4054
+ } catch (e) {
4055
+ const err = e;
4056
+ if (!isRouteMissing(err)) throw err;
4057
+ console.log("\u7AD9\u70B9 helper REST \u4E0D\u53EF\u7528\uFF0C\u5148\u4ECE\u955C\u50CF\u672C\u5730 zip \u5347\u7EA7 helper\u2026");
4058
+ await runUpdateHelper({ ...opts, site: target.site.guid || opts.site, url: target.site.url }, true);
4059
+ return await run();
4060
+ }
4061
+ }
4007
4062
  async function runPluginsList(opts) {
4008
4063
  const { config, site, base } = await resolveTarget(opts);
4009
4064
  const api = `${base}/wp-json/wp/v2/plugins`;
@@ -4011,28 +4066,58 @@ async function runPluginsList(opts) {
4011
4066
  try {
4012
4067
  plugins = await apiFetch2(api, config, {}, opts.verbose);
4013
4068
  } catch (e) {
4069
+ const msg = e.message || "";
4014
4070
  console.error(`
4015
- \u274C \u83B7\u53D6\u5DF2\u88C5\u63D2\u4EF6\u5931\u8D25\uFF1A${e.message}
4016
- `);
4071
+ \u274C \u83B7\u53D6\u5DF2\u88C5\u63D2\u4EF6\u5931\u8D25\uFF1A${msg}`);
4072
+ if (/HTTP 401/.test(msg)) {
4073
+ console.error(" \u7AD9\u70B9 oauth2 \u8FC7\u65E7\u6216\u4E0D\u8BA4 API Key\uFF0C\u9700\u8981\u5148\u505A SiteMan \u955C\u50CF\u5347\u7EA7\u3002\n");
4074
+ }
4075
+ if (opts.exitOnError === false) return;
4017
4076
  process.exit(1);
4018
4077
  return;
4019
4078
  }
4020
4079
  if (!Array.isArray(plugins)) {
4021
4080
  console.error("\n\u274C \u7AD9\u70B9\u8FD4\u56DE\u7684\u63D2\u4EF6\u5217\u8868\u683C\u5F0F\u5F02\u5E38\n");
4081
+ if (opts.exitOnError === false) return;
4022
4082
  process.exit(1);
4023
4083
  }
4084
+ let catalog = [];
4085
+ try {
4086
+ const res = await apiFetch2(
4087
+ `${base}/wp-json/siluzan-helper/v1/plugins/catalog`,
4088
+ config,
4089
+ {},
4090
+ opts.verbose
4091
+ );
4092
+ catalog = Array.isArray(res.items) ? res.items : [];
4093
+ } catch {
4094
+ }
4095
+ const catalogByKey = /* @__PURE__ */ new Map();
4096
+ for (const item of catalog) {
4097
+ if (item.plugin) catalogByKey.set(pluginKey(item.plugin), item);
4098
+ if (item.slug) catalogByKey.set(pluginKey(item.slug), item);
4099
+ }
4100
+ const rows = plugins.map((p) => {
4101
+ const plugin = p.plugin ?? "";
4102
+ const hit = catalogByKey.get(pluginKey(plugin));
4103
+ let updateSource = "none";
4104
+ if (hit?.update_available) updateSource = "catalog";
4105
+ return {
4106
+ plugin,
4107
+ name: pluginName(p),
4108
+ status: p.status ?? "",
4109
+ version: p.version ?? "",
4110
+ updateSource,
4111
+ catalogVersion: hit?.new_version ?? ""
4112
+ };
4113
+ });
4024
4114
  if (opts.json) {
4025
4115
  console.log(
4026
4116
  JSON.stringify(
4027
4117
  {
4028
4118
  site: { guid: site.guid, name: site.name, url: site.url },
4029
- total: plugins.length,
4030
- plugins: plugins.map((p) => ({
4031
- plugin: p.plugin ?? "",
4032
- name: pluginName(p),
4033
- status: p.status ?? "",
4034
- version: p.version ?? ""
4035
- }))
4119
+ total: rows.length,
4120
+ plugins: rows
4036
4121
  },
4037
4122
  null,
4038
4123
  2
@@ -4040,7 +4125,7 @@ async function runPluginsList(opts) {
4040
4125
  );
4041
4126
  return;
4042
4127
  }
4043
- if (plugins.length === 0) {
4128
+ if (rows.length === 0) {
4044
4129
  console.log(`
4045
4130
  \u7AD9\u70B9 ${site.name} \u672A\u8FD4\u56DE\u63D2\u4EF6\u3002
4046
4131
  `);
@@ -4050,32 +4135,45 @@ async function runPluginsList(opts) {
4050
4135
  { key: "plugin", header: "plugin" },
4051
4136
  { key: "name", header: "\u540D\u79F0" },
4052
4137
  { key: "status", header: "\u72B6\u6001" },
4053
- { key: "version", header: "\u7248\u672C" }
4138
+ { key: "version", header: "\u7248\u672C" },
4139
+ { key: "updateSource", header: "\u66F4\u65B0\u6E90" },
4140
+ { key: "catalogVersion", header: "\u76EE\u5F55\u7248\u672C" }
4054
4141
  ];
4055
4142
  console.log(`
4056
- ${site.name} \u5DF2\u88C5\u63D2\u4EF6\uFF08${plugins.length}\uFF09
4143
+ ${site.name} \u5DF2\u88C5\u63D2\u4EF6\uFF08${rows.length}\uFF09
4057
4144
  `);
4058
4145
  printCliTable(
4059
- plugins.map((p) => ({
4060
- plugin: p.plugin ?? "-",
4061
- name: pluginName(p) || "-",
4062
- status: p.status ?? "-",
4063
- version: p.version ?? "-"
4146
+ rows.map((r) => ({
4147
+ plugin: r.plugin || "-",
4148
+ name: r.name || "-",
4149
+ status: r.status || "-",
4150
+ version: r.version || "-",
4151
+ updateSource: r.updateSource,
4152
+ catalogVersion: r.catalogVersion || "-"
4064
4153
  })),
4065
4154
  columns
4066
4155
  );
4067
4156
  console.log();
4068
4157
  }
4069
4158
  async function runPluginsCatalog(opts) {
4070
- const { config, site, base } = await resolveTarget(opts);
4071
- const api = `${base}/wp-json/siluzan-helper/v1/plugins/catalog`;
4159
+ const target = await resolveTarget(opts);
4160
+ const { site, base } = target;
4072
4161
  let res;
4073
4162
  try {
4074
- res = await apiFetch2(api, config, {}, opts.verbose);
4163
+ res = await withHelperBootstrap(
4164
+ opts,
4165
+ target,
4166
+ () => apiFetch2(
4167
+ `${base}/wp-json/siluzan-helper/v1/plugins/catalog`,
4168
+ target.config,
4169
+ {},
4170
+ opts.verbose
4171
+ )
4172
+ );
4075
4173
  } catch (e) {
4076
4174
  console.error(`
4077
- \u274C \u83B7\u53D6\u63D2\u4EF6\u76EE\u5F55\u5931\u8D25\uFF1A${e.message}`);
4078
- console.error(" \u9700\u8981\u7AD9\u70B9\u5DF2\u90E8\u7F72\u5E26 REST \u7684 siluzan-helper-plugin\u3002\n");
4175
+ \u274C \u83B7\u53D6\u63D2\u4EF6\u76EE\u5F55\u5931\u8D25\uFF1A${e.message}
4176
+ `);
4079
4177
  process.exit(1);
4080
4178
  return;
4081
4179
  }
@@ -4101,7 +4199,8 @@ async function runPluginsCatalog(opts) {
4101
4199
  { key: "name", header: "\u540D\u79F0" },
4102
4200
  { key: "new_version", header: "\u76EE\u5F55\u7248\u672C" },
4103
4201
  { key: "installed", header: "\u5DF2\u88C5" },
4104
- { key: "active", header: "\u542F\u7528" }
4202
+ { key: "active", header: "\u542F\u7528" },
4203
+ { key: "update_available", header: "\u53EF\u66F4\u65B0" }
4105
4204
  ];
4106
4205
  console.log(`
4107
4206
  ${site.name} \u53EF\u5B89\u88C5\u76EE\u5F55\uFF08${items.length}\uFF09
@@ -4112,28 +4211,41 @@ ${site.name} \u53EF\u5B89\u88C5\u76EE\u5F55\uFF08${items.length}\uFF09
4112
4211
  name: p.name || "-",
4113
4212
  new_version: p.new_version || "-",
4114
4213
  installed: p.installed ? "yes" : "no",
4115
- active: p.active ? "yes" : "no"
4214
+ active: p.active ? "yes" : "no",
4215
+ update_available: p.update_available ? "yes" : "no"
4116
4216
  })),
4117
4217
  columns
4118
4218
  );
4119
- console.log("\n\u5B89\u88C5\uFF1Asiluzan-website plugins install --site <guid> --slug <slug>\n");
4219
+ console.log("\n\u5B89\u88C5 / \u5347\u7EA7\uFF1Asiluzan-website plugins install --site <guid> --slug <slug>\n");
4120
4220
  }
4121
4221
  async function runPluginsInstall(opts) {
4122
4222
  const slug = opts.slug?.trim() ?? "";
4123
- if (!slug) {
4124
- console.error("\n\u274C \u8BF7\u63D0\u4F9B --slug\uFF08\u5148 plugins catalog \u67E5\u770B\u53EF\u88C5\u5217\u8868\uFF09\n");
4223
+ const zipPath = opts.zip?.trim() ?? "";
4224
+ if (slug && zipPath || !slug && !zipPath) {
4225
+ console.error("\n\u274C \u76EE\u5F55\u63D2\u4EF6\u7528 --slug\uFF0C\u5916\u90E8\u63D2\u4EF6\u7528 --zip\uFF0C\u4E24\u8005\u53EA\u4F20\u4E00\u4E2A\n");
4125
4226
  process.exit(1);
4126
4227
  }
4127
- const { config, site, base } = await resolveTarget(opts);
4128
- const api = `${base}/wp-json/siluzan-helper/v1/plugins/install`;
4228
+ const target = await resolveTarget(opts);
4229
+ const { site, base } = target;
4129
4230
  let result;
4130
4231
  try {
4131
- result = await apiFetch2(
4132
- api,
4133
- config,
4134
- { method: "POST", body: JSON.stringify({ slug }) },
4135
- opts.verbose
4136
- );
4232
+ result = await withHelperBootstrap(opts, target, async () => {
4233
+ if (slug) {
4234
+ return apiFetch2(
4235
+ `${base}/wp-json/siluzan-helper/v1/plugins/install`,
4236
+ target.config,
4237
+ { method: "POST", body: JSON.stringify({ slug }) },
4238
+ opts.verbose
4239
+ );
4240
+ }
4241
+ const zip = readZipPayload(zipPath);
4242
+ return apiFetch2(
4243
+ `${base}/wp-json/siluzan-helper/v1/plugins/install-zip`,
4244
+ target.config,
4245
+ { method: "POST", body: JSON.stringify(zip) },
4246
+ opts.verbose
4247
+ );
4248
+ });
4137
4249
  } catch (e) {
4138
4250
  console.error(`
4139
4251
  \u274C \u5B89\u88C5\u5931\u8D25\uFF1A${e.message}
@@ -4141,39 +4253,151 @@ async function runPluginsInstall(opts) {
4141
4253
  process.exit(1);
4142
4254
  return;
4143
4255
  }
4144
- if (!result.success) {
4256
+ printActionResult(result, site, base, "\u63D2\u4EF6\u5DF2\u5B89\u88C5\u5E76\u542F\u7528");
4257
+ await runPluginsList({
4258
+ site: site.guid || opts.site,
4259
+ url: site.url,
4260
+ verbose: opts.verbose,
4261
+ exitOnError: false
4262
+ });
4263
+ }
4264
+ async function runPluginsUpdate(opts) {
4265
+ const plugin = opts.plugin?.trim() ?? "";
4266
+ if (!plugin) {
4267
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --plugin\uFF08\u5148 plugins list \u770B plugin \u5217\uFF09\n");
4268
+ process.exit(1);
4269
+ }
4270
+ const target = await resolveTarget(opts);
4271
+ const { site, base } = target;
4272
+ const zipPath = opts.zip?.trim() ?? "";
4273
+ const body = { plugin };
4274
+ if (zipPath) {
4275
+ const zip = readZipPayload(zipPath);
4276
+ body.zip_base64 = zip.zip_base64;
4277
+ body.filename = zip.filename;
4278
+ }
4279
+ let result;
4280
+ try {
4281
+ result = await withHelperBootstrap(
4282
+ opts,
4283
+ target,
4284
+ () => apiFetch2(
4285
+ `${base}/wp-json/siluzan-helper/v1/plugins/update`,
4286
+ target.config,
4287
+ { method: "POST", body: JSON.stringify(body) },
4288
+ opts.verbose
4289
+ )
4290
+ );
4291
+ } catch (e) {
4292
+ const msg = e.message || "";
4293
+ if (/HTTP 409/.test(msg) && /no_package/i.test(msg)) {
4294
+ console.error(`
4295
+ \u274C ${msg}`);
4296
+ console.error(" \u76EE\u5F55\u548C WordPress.org \u90FD\u6CA1\u6709\u5B89\u88C5\u5305\u3002\u5916\u90E8\u63D2\u4EF6\u8BF7\u5E26 --zip \u518D\u5347\u7EA7\u3002\n");
4297
+ process.exit(1);
4298
+ }
4145
4299
  console.error(`
4146
- \u274C ${result.message || "\u5B89\u88C5\u88AB\u7AD9\u70B9\u62D2\u7EDD"}
4300
+ \u274C \u5347\u7EA7\u5931\u8D25\uFF1A${msg}
4147
4301
  `);
4148
4302
  process.exit(1);
4303
+ return;
4149
4304
  }
4150
- console.log(result.already ? "\n\u2705 \u63D2\u4EF6\u5DF2\u5728\u7AD9\u70B9\u4E0A\uFF08\u5DF2\u542F\u7528\uFF09" : "\n\u2705 \u63D2\u4EF6\u5DF2\u5B89\u88C5\u5E76\u542F\u7528");
4151
- if (result.plugin) console.log(` plugin : ${result.plugin}`);
4152
- if (result.message) console.log(` \u8BF4\u660E : ${result.message}`);
4153
- console.log(` \u7AD9\u70B9 : ${site.name} (${base})
4305
+ printActionResult(result, site, base, "\u63D2\u4EF6\u5DF2\u5347\u7EA7");
4306
+ await runPluginsList({
4307
+ site: site.guid || opts.site,
4308
+ url: site.url,
4309
+ verbose: opts.verbose,
4310
+ exitOnError: false
4311
+ });
4312
+ }
4313
+ async function runHelperStatus(opts) {
4314
+ const { config, site, base } = await resolveTarget(opts);
4315
+ let status;
4316
+ try {
4317
+ status = await apiFetch2(
4318
+ `${base}/wp-json/wposso/v1/helper/status`,
4319
+ config,
4320
+ {},
4321
+ opts.verbose
4322
+ );
4323
+ } catch (e) {
4324
+ const msg = e.message || "";
4325
+ console.error(`
4326
+ \u274C \u65E0\u6CD5\u8BFB\u53D6 helper \u72B6\u6001\uFF1A${msg}`);
4327
+ if (isRouteMissing(e) || /HTTP 404/.test(msg)) {
4328
+ console.error(" \u7AD9\u70B9 oauth2 \u8FD8\u6CA1\u6709\u5F15\u5BFC\u5347\u7EA7\u63A5\u53E3\uFF0C\u9700\u8981\u5148\u505A SiteMan \u955C\u50CF\u5347\u7EA7\u3002\n");
4329
+ }
4330
+ process.exit(1);
4331
+ return;
4332
+ }
4333
+ if (opts.json) {
4334
+ console.log(JSON.stringify({ site: { guid: site.guid, name: site.name, url: site.url }, ...status }, null, 2));
4335
+ return;
4336
+ }
4337
+ console.log(`
4338
+ ${site.name} helper \u72B6\u6001`);
4339
+ console.log(` \u5DF2\u5B89\u88C5 : ${status.helper_installed ? "yes" : "no"}`);
4340
+ console.log(` \u5DF2\u542F\u7528 : ${status.helper_active ? "yes" : "no"}`);
4341
+ console.log(` \u7248\u672C : ${status.helper_version || "-"}`);
4342
+ console.log(` helper REST: ${status.helper_rest_available ? "yes" : "no"}`);
4343
+ console.log(` \u955C\u50CF zip : ${status.bootstrap_zip || "-"}`);
4344
+ console.log(` \u53EF\u5F15\u5BFC\u5347\u7EA7 : ${status.can_upgrade ? "yes" : "no"}
4154
4345
  `);
4346
+ }
4347
+ async function runUpdateHelper(opts, fromBootstrap = false) {
4348
+ const { config, site, base } = await resolveTarget(opts);
4349
+ let result;
4155
4350
  try {
4156
- await runPluginsList({ site: site.guid || opts.site, url: site.url, verbose: opts.verbose });
4157
- } catch {
4351
+ result = await apiFetch2(
4352
+ `${base}/wp-json/wposso/v1/helper/upgrade`,
4353
+ config,
4354
+ { method: "POST", body: JSON.stringify({}) },
4355
+ opts.verbose
4356
+ );
4357
+ } catch (e) {
4358
+ const msg = e.message || "";
4359
+ console.error(`
4360
+ \u274C helper \u5347\u7EA7\u5931\u8D25\uFF1A${msg}`);
4361
+ if (isRouteMissing(e)) {
4362
+ console.error(" \u7AD9\u70B9 oauth2 \u8FD8\u6CA1\u6709\u5F15\u5BFC\u5347\u7EA7\u63A5\u53E3\uFF0C\u9700\u8981\u5148\u505A SiteMan \u955C\u50CF\u5347\u7EA7\u3002");
4363
+ } else if (/HTTP 409/.test(msg)) {
4364
+ console.error(" \u5F53\u524D\u955C\u50CF\u91CC\u6CA1\u6709 siluzan-helper-plugin.1.0.*.zip\u3002");
4365
+ }
4366
+ console.error();
4367
+ process.exit(1);
4368
+ return;
4369
+ }
4370
+ printActionResult(result, site, base, "helper \u5DF2\u4ECE\u955C\u50CF\u672C\u5730 zip \u5347\u7EA7");
4371
+ if (!fromBootstrap) {
4372
+ await runHelperStatus({ site: site.guid || opts.site, url: site.url, verbose: opts.verbose });
4158
4373
  }
4159
4374
  }
4160
4375
  function register7(program2) {
4161
- const plugins = program2.command("plugins").description("WordPress \u63D2\u4EF6\uFF1A\u5217\u51FA\u3001\u76EE\u5F55\u3001\u5B89\u88C5");
4162
- plugins.command("list").description("\u5217\u51FA\u7AD9\u70B9\u5DF2\u5B89\u88C5\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) => {
4376
+ const plugins = program2.command("plugins").description("WordPress \u63D2\u4EF6\uFF1A\u5217\u51FA\u3001\u76EE\u5F55\u3001\u5B89\u88C5\u3001\u5347\u7EA7");
4377
+ 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) => {
4163
4378
  await runPluginsList(opts);
4164
4379
  });
4165
- plugins.command("catalog").description("\u5217\u51FA\u4E1D\u8DEF\u8D5E\u63D2\u4EF6\u76EE\u5F55\uFF08\u53EF\u5B89\u88C5\u5217\u8868\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) => {
4380
+ plugins.command("catalog").description("\u5217\u51FA\u4E1D\u8DEF\u8D5E\u63D2\u4EF6\u76EE\u5F55\uFF08\u53EF\u5B89\u88C5 / \u53EF\u5347\u7EA7\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) => {
4166
4381
  await runPluginsCatalog(opts);
4167
4382
  });
4168
- plugins.command("install").description("\u4ECE\u4E1D\u8DEF\u8D5E\u76EE\u5F55\u5B89\u88C5\u5E76\u542F\u7528\u63D2\u4EF6\uFF08\u5199\u64CD\u4F5C\uFF0C\u4F1A\u7ACB\u523B\u751F\u6548\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("--slug <slug>", "\u76EE\u5F55\u4E2D\u7684 slug \u6216 plugin \u8DEF\u5F84").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4383
+ plugins.command("install").description("\u5B89\u88C5\u5E76\u542F\u7528\u63D2\u4EF6\uFF1A\u76EE\u5F55\u7528 --slug\uFF0C\u5916\u90E8\u7528 --zip").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--slug <slug>", "\u4E1D\u8DEF\u8D5E\u76EE\u5F55\u4E2D\u7684 slug \u6216 plugin \u8DEF\u5F84").option("--zip <file>", "\u5916\u90E8\u63D2\u4EF6 zip\uFF08\u6839\u76EE\u5F55\u5FC5\u987B\u662F\u63D2\u4EF6\u76EE\u5F55\uFF09").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4169
4384
  await runPluginsInstall(opts);
4170
4385
  });
4386
+ plugins.command("update").description("\u5347\u7EA7\u5DF2\u88C5\u63D2\u4EF6\uFF1A\u6709\u76EE\u5F55/\u5B98\u65B9\u5305\u5219\u4E0D\u7528 zip\uFF0C\u5916\u90E8\u63D2\u4EF6\u5E26 --zip").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("--plugin <plugin>", "\u5DF2\u88C5\u63D2\u4EF6\u8DEF\u5F84\uFF0C\u5982 theme-manage/theme-manage").option("--zip <file>", "\u5916\u90E8\u63D2\u4EF6\u5347\u7EA7\u5305").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4387
+ await runPluginsUpdate(opts);
4388
+ });
4389
+ plugins.command("helper-status").description("\u67E5\u770B\u7AD9\u70B9 helper \u4E0E\u955C\u50CF\u672C\u5730 zip").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) => {
4390
+ await runHelperStatus(opts);
4391
+ });
4392
+ plugins.command("update-helper").description("\u7528\u955C\u50CF\u91CC\u7684 siluzan-helper-plugin.1.0.*.zip \u5347\u7EA7 helper\uFF08\u5199\u64CD\u4F5C\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4393
+ await runUpdateHelper(opts);
4394
+ });
4171
4395
  }
4172
4396
 
4173
4397
  // src/index.ts
4174
4398
  installProcessHandlers();
4175
4399
  var program = new Command();
4176
- program.name("siluzan-website").description("Siluzan WordPress \u7AD9\u70B9\u7BA1\u7406\uFF1A\u5217\u51FA\u7AD9\u70B9\u3001\u65B0\u589E/\u7F16\u8F91\u9875\u9762\u3001\u67E5\u770B\u63D2\u4EF6").version(getCurrentVersion2());
4400
+ program.name("siluzan-website").description("Siluzan WordPress \u7AD9\u70B9\u7BA1\u7406\uFF1A\u5217\u51FA\u7AD9\u70B9\u3001\u65B0\u589E/\u7F16\u8F91\u9875\u9762\u3001\u5B89\u88C5/\u5347\u7EA7\u63D2\u4EF6").version(getCurrentVersion2());
4177
4401
  var REGISTRARS = [
4178
4402
  register,
4179
4403
  register2,
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: siluzan-website
3
3
  description: >-
4
- 管理丝路赞 WordPress 站点(wp-k8s 托管):列出我的站点、新增/编辑页面、从丝路赞目录安装插件。
5
- 当用户提到 WordPress、建站后台、新增页面、改官网、装插件、站点 guid、
4
+ 管理丝路赞 WordPress 站点(wp-k8s 托管):列出我的站点、新增/编辑页面、安装/升级插件。
5
+ 当用户提到 WordPress、建站后台、新增页面、改官网、装插件、升级插件、站点 guid、
6
6
  siluzan-website、zionbuilder、visual-edit 时使用。不是 LPS / AI Sites,也不是 SEO JSON 生成。
7
7
  ---
8
8
 
@@ -10,7 +10,7 @@ description: >-
10
10
 
11
11
  管理用户账号下的 **WordPress 站点**(TSO 建站 / wp-k8s)。通过 CLI 调平台 all-sites 与站点 WP REST,不要手写 curl,不要猜 guid / 页面 ID。
12
12
 
13
- **不是** `landing-page-system` 的 LPS Agent,也不是 `siluzan-seo` 的 schema 生成。用户要的是「我的官网加一页 / 看插件」才用本 Skill。
13
+ **不是** `landing-page-system` 的 LPS Agent,也不是 `siluzan-seo` 的 schema 生成。用户要的是「我的官网加一页 / 看插件 / 装插件 / 升级插件」才用本 Skill。
14
14
 
15
15
  ## 一键安装
16
16
 
@@ -40,7 +40,8 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
40
40
  | `siluzan-website init` / `update` | 写入 / 刷新 Skill 文件 | [references/setup.md](references/setup.md) |
41
41
  | `siluzan-website sites list` | 列出我的 WordPress 站点 | [references/sites.md](references/sites.md) |
42
42
  | `siluzan-website pages list/add/update` | 列出 / 新增 / 覆盖页面 | [references/pages.md](references/pages.md) |
43
- | `siluzan-website plugins list/catalog/install` | 已装列表 / 目录 / 安装并启用 | [references/plugins.md](references/plugins.md) |
43
+ | `siluzan-website plugins list/catalog/install/update` | 已装列表 / 目录 / 安装 / 升级 | [references/plugins.md](references/plugins.md) |
44
+ | `siluzan-website plugins helper-status/update-helper` | 查看 / 用镜像 zip 升级 helper | [references/plugins.md](references/plugins.md) |
44
45
 
45
46
  ---
46
47
 
@@ -51,25 +52,29 @@ Windows 注意:部分 Agent 通过 PowerShell / cmd 代执行时可能失败
51
52
  | 首次安装 / 登录 | `references/setup.md` | `login` 或复用已有 CSO/TSO 凭据 |
52
53
  | 我有哪些网站 | `references/sites.md` | `sites list`,不要猜 guid |
53
54
  | 加一页 / 改一页 | `references/pages.md` | 先 `sites list` 再 `pages add/update` |
54
- | 这个站装了哪些插件 | `references/plugins.md` | `plugins list` |
55
- | 安装 / 启用插件 | `references/plugins.md` | `catalog` 找 slug → 确认 → `install` |
55
+ | 这个站装了哪些插件 | `references/plugins.md` | `plugins list`(全部已装,不只是目录) |
56
+ | 安装 / 启用目录插件 | `references/plugins.md` | `catalog` 找 slug → 确认 → `install --slug` |
57
+ | 安装外部插件 | `references/plugins.md` | 确认 → `install --zip` |
58
+ | 升级已装插件 | `references/plugins.md` | `list` 拿 plugin → 确认 → `update --plugin`;外部再加 `--zip` |
56
59
 
57
60
  ---
58
61
 
59
62
  ## AI 行为规范
60
63
 
61
64
  1. **计划 → 确认 → 执行 → 验证**
62
- - 写操作(`pages add` / `pages update` / `plugins install`)会立刻生效,执行前必须复述目标站点与内容,获得用户确认。
63
- - 只读(`sites list` / `pages list` / `plugins list` / `plugins catalog` / `config show`)可直接跑。
65
+ - 写操作(`pages add` / `pages update` / `plugins install` / `plugins update` / `plugins update-helper`)会立刻生效,执行前必须复述目标站点与内容,获得用户确认。
66
+ - 只读(`sites list` / `pages list` / `plugins list` / `plugins catalog` / `plugins helper-status` / `config show`)可直接跑。
64
67
  2. **不猜 ID**:站点用 `sites list` 的 `guid`;页面用 `pages list` 的 `id`。
65
68
  3. **先查再写**:改页面前先 `pages list` 确认目标页存在。
66
69
  4. **只认 API Key**:所有命令走 `x-api-key`(`SILUZAN_API_KEY` 或 `login` 写入的 config)。Agent 沙箱只注入 Key,不要向用户要 JWT,也不要设 `SILUZAN_AUTH_TOKEN`。
67
70
  5. **命令透明**:用一句话说明意图(「正在列出您的站点」),不要把整段命令当主回复;用户要细节时再给完整命令。
68
- 6. **失败先自救**:401 → 引导重新 `login`;站点找不到 → 再跑 `sites list`;不要编造成功。
71
+ 6. **失败先自救**:401 → 引导重新 `login`;站点找不到 → 再跑 `sites list`;helper REST 404 时命令会先升 helper;oauth2 401/404 → 说明要 SiteMan 镜像升级。不要编造成功。
72
+ 7. **不传任意下载 URL**:外部插件只接受本地 `--zip`。helper 只从镜像 `1.0.*` zip 引导升级,不要上架 helper。
69
73
 
70
74
  ### 本版明确不做
71
75
 
72
76
  - 创建 / 删除 / 重启站点(SiteMan)
73
77
  - 绑定域名 / CDN / SSL
74
- - 停用 / 删除插件;安装目录里没有的第三方插件
78
+ - 停用 / 删除插件
79
+ - 推荐插件的业务配置(选择器、Cron、模型)
75
80
  - LPS / AI Sites / SEO JSON 灌入
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "slug": "siluzan-website",
3
- "version": "1.0.1-beta.1",
4
- "publishedAt": 1789609097006,
3
+ "version": "1.0.1-beta.2",
4
+ "publishedAt": 1789633076915,
5
5
  "homepage": "https://www.siluzan.com",
6
6
  "source": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill",
7
7
  "requiredBinaries": [
@@ -1,6 +1,11 @@
1
1
  # 插件
2
2
 
3
- 鉴权一律 **API Key(x-api-key)**。安装走站点 `siluzan-helper-plugin` 的 REST,只允许丝路赞目录里的 zip,不接受任意 URL。
3
+ 鉴权一律 **API Key(x-api-key)**。
4
+
5
+ - **已装列表**走 WordPress 核心 `GET /wp-json/wp/v2/plugins`,能看到站点上的全部插件。
6
+ - **目录安装 / 无 zip 升级**走 `siluzan-helper-plugin` REST。目录包来自丝路赞目录,不接受任意 URL。
7
+ - **外部插件**首次安装和升级都带本地 `--zip`。zip 根目录必须是插件目录本身(`插件名/xxx.php`),不能再套一层版本号目录。
8
+ - helper 太旧、没有 REST 时,命令会先打 oauth2 `POST /wp-json/wposso/v1/helper/upgrade`,用镜像里的 `siluzan-helper-plugin.1.0.*.zip` 升 helper,再继续原来的操作。helper **不上架**。
4
9
 
5
10
  ## 已装列表
6
11
 
@@ -9,36 +14,62 @@ siluzan-website plugins list --site <guid>
9
14
  siluzan-website plugins list --site <guid> --json
10
15
  ```
11
16
 
12
- `GET /wp-json/wp/v2/plugins`。字段:`plugin`、名称、`status`、版本。
17
+ 字段:`plugin`、名称、`status`、版本、`updateSource`(有目录命中则为 `catalog`)、目录版本。
18
+
19
+ `plugin` 列是后续 `update --plugin` 要用的值,不要猜。
13
20
 
14
21
  ## 可安装目录
15
22
 
16
23
  ```bash
17
24
  siluzan-website plugins catalog --site <guid>
18
- siluzan-website plugins catalog --site <guid> --json
19
25
  ```
20
26
 
21
- `GET /wp-json/siluzan-helper/v1/plugins/catalog`。记下 `slug`,安装必须用这个值(或 `plugin` 路径)。不要猜。
27
+ 记下 `slug`。目录安装 / 目录升级都用这个值。
22
28
 
23
- ## 安装并启用
29
+ ## 安装
24
30
 
25
31
  写操作,执行前必须和用户确认目标站点与插件名。
26
32
 
33
+ 目录插件:
34
+
27
35
  ```bash
28
36
  siluzan-website plugins install --site <guid> --slug <slug>
29
37
  ```
30
38
 
31
- `POST /wp-json/siluzan-helper/v1/plugins/install`,body:`{ "slug": "..." }`。
39
+ 外部插件:
40
+
41
+ ```bash
42
+ siluzan-website plugins install --site <guid> --zip ./my-plugin.zip
43
+ ```
44
+
45
+ `--slug` 和 `--zip` 只传一个。`install --slug` 已装时会按目录包覆盖升级。
46
+
47
+ ## 升级已装插件
48
+
49
+ 不传 zip 时:先查目录包,再查 WordPress `update_plugins`(含 wordpress.org)。都没有包则失败,提示带 `--zip`。
50
+
51
+ ```bash
52
+ siluzan-website plugins update --site <guid> --plugin theme-manage/theme-manage
53
+ siluzan-website plugins update --site <guid> --plugin zasp-smart-recommender/zasp-smart-recommender --zip ./zasp.zip
54
+ ```
55
+
56
+ ## helper
57
+
58
+ ```bash
59
+ siluzan-website plugins helper-status --site <guid>
60
+ siluzan-website plugins update-helper --site <guid>
61
+ ```
32
62
 
33
- 成功后命令会再跑一次 `plugins list` 复核。已装且已启用会返回 `already: true`,视为成功,不要再装一遍。
63
+ `update-helper` 只装镜像本地 `1.0.*` 包,不会去目录拉 helper。
34
64
 
35
65
  ## Agent 流程
36
66
 
37
67
  1. `sites list` 确定 guid
38
- 2. `plugins catalog` 找到 slug(用户说中文名时按 `name` 匹配)
39
- 3. 复述「将在 {站点} 安装并启用 {名称}(slug)」并等确认
40
- 4. `plugins install --site <guid> --slug <slug>`
41
- 5. 看复核列表里是否 `active`
68
+ 2. 用户要「装了哪些插件」→ `plugins list`
69
+ 3. 用户要装目录插件 `catalog` 对上 slug → 确认 → `install --slug`
70
+ 4. 用户要升级已装插件 → `list` 拿到 `plugin` 确认 → `update --plugin`;外部插件再加 `--zip`
71
+ 5. catalog / install / update 报路由 404 → 命令会自动 `update-helper` 再重试;oauth2 本身 404 / 401 → 告诉用户要先 SiteMan 镜像升级
72
+ 6. 看复核列表里是否 `active`
42
73
 
43
74
  匹配到多个目录项时让用户选,不要猜。
44
75
 
@@ -46,9 +77,12 @@ siluzan-website plugins install --site <guid> --slug <slug>
46
77
 
47
78
  | 现象 | 处理 |
48
79
  |------|------|
49
- | catalog / install 404 或路由不存在 | 站点 helper 还没带 REST,需要更新 `siluzan-helper-plugin` |
50
- | 401 | API Key 无效或无权操作该站 |
51
- | Plugin not found | slug 不对,重新 `catalog` |
80
+ | list 401 | 站点 oauth2 不认 API Key,需要 SiteMan 镜像升级 |
81
+ | helper REST 404 | 先自动升 helper;oauth2 也没有引导接口则要镜像升级 |
82
+ | update-helper 409 | 当前镜像没有 `siluzan-helper-plugin.1.0.*.zip` |
83
+ | Plugin not found / not_in_catalog | slug 不对,重新 `catalog` |
84
+ | 409 no_package | 目录和官方都没包,外部插件带 `--zip` |
85
+ | zip 结构不对 | 重新打包,根目录必须是插件目录 |
52
86
  | 安装成功但激活失败 | 把站点返回的 message 原文告诉用户 |
53
87
 
54
- 本 Skill **不能**停用、删除插件,也不能装 wordpress.org 上目录里没有的插件。
88
+ 本 Skill **不能**停用、删除插件,也不能传任意下载 URL。
@@ -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.1'
12
+ $PKG_VERSION = '1.0.1-beta.2'
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.1"
12
+ readonly PKG_VERSION="1.0.1-beta.2"
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,7 +1,7 @@
1
1
  {
2
2
  "name": "siluzan-website-cli",
3
- "version": "1.0.1-beta.1",
4
- "description": "Siluzan WordPress 站点管理 Skill CLI — 列出站点、新增/编辑页面、查看插件。",
3
+ "version": "1.0.1-beta.2",
4
+ "description": "Siluzan WordPress 站点管理 Skill CLI — 列出站点、新增/编辑页面、安装/升级插件。",
5
5
  "keywords": [
6
6
  "ai-skill",
7
7
  "cli",