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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1960,6 +1960,7 @@ import * as fs from "fs";
1960
1960
  import * as path from "path";
1961
1961
  import * as os from "os";
1962
1962
  import lockfile from "proper-lockfile";
1963
+ import { randomBytes } from "crypto";
1963
1964
  import * as https from "https";
1964
1965
  import * as http from "http";
1965
1966
  import * as zlib from "zlib";
@@ -2460,7 +2461,13 @@ function rawRequest(url, options) {
2460
2461
  }
2461
2462
  reject(err);
2462
2463
  });
2463
- if (options.body) req.write(options.body);
2464
+ if (options.body !== void 0) {
2465
+ const payload = typeof options.body === "string" ? Buffer.from(options.body) : options.body;
2466
+ if (!headers["Content-Length"] && !headers["content-length"]) {
2467
+ req.setHeader("Content-Length", payload.length);
2468
+ }
2469
+ req.write(payload);
2470
+ }
2464
2471
  req.end();
2465
2472
  });
2466
2473
  }
@@ -2557,6 +2564,54 @@ function formatHttpError(status, rawBody, opts) {
2557
2564
  function buildAuthHeaders(config) {
2558
2565
  return buildSiluzanAuthHeaders(config);
2559
2566
  }
2567
+ function buildMultipartBody(fields, files) {
2568
+ const boundary = `----SiluzanForm${randomBytes(16).toString("hex")}`;
2569
+ const chunks = [];
2570
+ for (const [name, value] of Object.entries(fields)) {
2571
+ chunks.push(
2572
+ Buffer.from(
2573
+ `--${boundary}\r
2574
+ Content-Disposition: form-data; name="${name}"\r
2575
+ \r
2576
+ ${value}\r
2577
+ `
2578
+ )
2579
+ );
2580
+ }
2581
+ for (const file of files) {
2582
+ chunks.push(
2583
+ Buffer.from(
2584
+ `--${boundary}\r
2585
+ Content-Disposition: form-data; name="${file.field}"; filename="${file.filename}"\r
2586
+ Content-Type: ${file.contentType}\r
2587
+ \r
2588
+ `
2589
+ )
2590
+ );
2591
+ chunks.push(file.data);
2592
+ chunks.push(Buffer.from("\r\n"));
2593
+ }
2594
+ chunks.push(Buffer.from(`--${boundary}--\r
2595
+ `));
2596
+ return {
2597
+ body: Buffer.concat(chunks),
2598
+ contentType: `multipart/form-data; boundary=${boundary}`
2599
+ };
2600
+ }
2601
+ async function parseApiResponse(status, text, verbose) {
2602
+ if (status < 200 || status >= 300) {
2603
+ throw new Error(formatHttpError(status, text, { verbose }));
2604
+ }
2605
+ if (!text.trim()) return null;
2606
+ try {
2607
+ return parseJsonWithStringIds(text);
2608
+ } catch {
2609
+ if (verbose) {
2610
+ console.error(` \u26A0\uFE0F \u54CD\u5E94\u975E JSON\uFF0C\u539F\u59CB\u5185\u5BB9\uFF1A${redactSensitive(text).slice(0, 200)}`);
2611
+ }
2612
+ return text;
2613
+ }
2614
+ }
2560
2615
  async function apiFetch(url, config, options = {}, verbose = false) {
2561
2616
  const method = options.method ?? "GET";
2562
2617
  const reqHeaders = {
@@ -2575,19 +2630,23 @@ async function apiFetch(url, config, options = {}, verbose = false) {
2575
2630
  headers: reqHeaders,
2576
2631
  body
2577
2632
  });
2578
- const text = res.text;
2579
- if (res.status < 200 || res.status >= 300) {
2580
- throw new Error(formatHttpError(res.status, text, { verbose }));
2581
- }
2582
- if (!text.trim()) return null;
2583
- try {
2584
- return parseJsonWithStringIds(text);
2585
- } catch {
2586
- if (verbose) {
2587
- console.error(` \u26A0\uFE0F \u54CD\u5E94\u975E JSON\uFF0C\u539F\u59CB\u5185\u5BB9\uFF1A${redactSensitive(text).slice(0, 200)}`);
2588
- }
2589
- return text;
2590
- }
2633
+ return parseApiResponse(res.status, res.text, verbose);
2634
+ }
2635
+ async function apiFetchMultipart(url, config, payload, verbose = false) {
2636
+ const packed = buildMultipartBody(payload.fields ?? {}, payload.files);
2637
+ const reqHeaders = {
2638
+ "Content-Type": packed.contentType,
2639
+ "Accept-Language": "zh-CN",
2640
+ "Accept-Encoding": "gzip, deflate, br",
2641
+ ...buildAuthHeaders(config),
2642
+ Datapermission: config.dataPermission ?? ""
2643
+ };
2644
+ const res = await rawRequest(url, {
2645
+ method: "POST",
2646
+ headers: reqHeaders,
2647
+ body: packed.body
2648
+ });
2649
+ return parseApiResponse(res.status, res.text, verbose);
2591
2650
  }
2592
2651
  function getCurrentVersion(importMetaUrl) {
2593
2652
  try {
@@ -3258,6 +3317,7 @@ import { spawnSync } from "child_process";
3258
3317
  var BUILD_ENV = "test";
3259
3318
  var DEFAULT_API_BASE = "https://api-ci.siluzan.com";
3260
3319
  var DEFAULT_CSO_BASE = "https://cso-ci.siluzan.com";
3320
+ var DEFAULT_CSO_PLUGIN_API = "https://cso-api-ci.siluzan.com";
3261
3321
  var DEFAULT_WEB_BASE = "https://www-ci.siluzan.com";
3262
3322
  var DEFAULT_SITE_STAGE = "ci";
3263
3323
 
@@ -3668,6 +3728,7 @@ function loadConfig() {
3668
3728
  }
3669
3729
  const apiBaseUrl = process.env.SILUZAN_WEBSITE_API_BASE ?? DEFAULT_API_BASE;
3670
3730
  const csoBaseUrl = process.env.SILUZAN_CSO_BASE ?? DEFAULT_CSO_BASE;
3731
+ const csoPluginApiBaseUrl = process.env.SILUZAN_CSO_PLUGIN_API ?? DEFAULT_CSO_PLUGIN_API;
3671
3732
  const apiErr = validateBaseUrl(apiBaseUrl);
3672
3733
  if (apiErr) {
3673
3734
  console.error(`
@@ -3680,9 +3741,16 @@ function loadConfig() {
3680
3741
  \u274C csoBaseUrl \u4E0D\u5408\u6CD5\uFF1A${csoErr}`);
3681
3742
  process.exit(1);
3682
3743
  }
3744
+ const pluginApiErr = validateBaseUrl(csoPluginApiBaseUrl);
3745
+ if (pluginApiErr) {
3746
+ console.error(`
3747
+ \u274C csoPluginApiBaseUrl \u4E0D\u5408\u6CD5\uFF1A${pluginApiErr}`);
3748
+ process.exit(1);
3749
+ }
3683
3750
  return {
3684
3751
  apiBaseUrl,
3685
3752
  csoBaseUrl,
3753
+ csoPluginApiBaseUrl,
3686
3754
  authToken: "",
3687
3755
  apiKey,
3688
3756
  dataPermission: process.env.SILUZAN_DATA_PERMISSION ?? shared.dataPermission
@@ -3691,6 +3759,9 @@ function loadConfig() {
3691
3759
  function apiFetch2(url, config, options = {}, verbose = false) {
3692
3760
  return apiFetch(url, config, options, verbose);
3693
3761
  }
3762
+ function apiFetchMultipart2(url, config, payload, verbose = false) {
3763
+ return apiFetchMultipart(url, config, payload, verbose);
3764
+ }
3694
3765
 
3695
3766
  // src/utils/site-url.ts
3696
3767
  function normalizeRegion(raw) {
@@ -3842,6 +3913,57 @@ function register5(program2) {
3842
3913
 
3843
3914
  // src/commands/pages.ts
3844
3915
  import * as fs6 from "fs";
3916
+
3917
+ // src/utils/wp-site.ts
3918
+ async function resolveTarget(opts) {
3919
+ const config = loadConfig();
3920
+ const site = await resolveSite(config, opts);
3921
+ return { config, site, base: trimTrailingSlash(site.url) };
3922
+ }
3923
+ function wpV2(base, path6) {
3924
+ const suffix = path6.startsWith("/") ? path6 : `/${path6}`;
3925
+ return `${base}/wp-json/wp/v2${suffix}`;
3926
+ }
3927
+ async function resolveTypeRestBase(target, typeArg, verbose = false) {
3928
+ const needle = typeArg.trim().toLowerCase();
3929
+ if (!needle) {
3930
+ throw new Error("\u8BF7\u63D0\u4F9B --type\uFF08\u5148 content types \u770B rest_base\uFF09");
3931
+ }
3932
+ const types = await apiFetch2(
3933
+ wpV2(target.base, "/types?context=edit"),
3934
+ target.config,
3935
+ {},
3936
+ verbose
3937
+ );
3938
+ for (const [slug, info] of Object.entries(types || {})) {
3939
+ const restBase = (info.rest_base || slug).toLowerCase();
3940
+ if (slug.toLowerCase() === needle || restBase === needle) {
3941
+ return info.rest_base || slug;
3942
+ }
3943
+ }
3944
+ return typeArg.trim();
3945
+ }
3946
+ async function resolveTaxonomyRestBase(target, taxonomyArg, verbose = false) {
3947
+ const needle = taxonomyArg.trim().toLowerCase();
3948
+ if (!needle) {
3949
+ throw new Error("\u8BF7\u63D0\u4F9B --taxonomy\uFF08\u5982 category\u3001post_tag\uFF09");
3950
+ }
3951
+ const taxes = await apiFetch2(
3952
+ wpV2(target.base, "/taxonomies?context=edit"),
3953
+ target.config,
3954
+ {},
3955
+ verbose
3956
+ );
3957
+ for (const [slug, info] of Object.entries(taxes || {})) {
3958
+ const restBase = (info.rest_base || slug).toLowerCase();
3959
+ if (slug.toLowerCase() === needle || restBase === needle) {
3960
+ return info.rest_base || slug;
3961
+ }
3962
+ }
3963
+ return taxonomyArg.trim();
3964
+ }
3965
+
3966
+ // src/commands/pages.ts
3845
3967
  function pageTitle(page) {
3846
3968
  if (typeof page.title === "string") return page.title;
3847
3969
  return page.title?.rendered ?? "";
@@ -3909,6 +4031,7 @@ async function runPagesList(opts) {
3909
4031
  { key: "id", header: "id" },
3910
4032
  { key: "title", header: "\u6807\u9898" },
3911
4033
  { key: "status", header: "\u72B6\u6001" },
4034
+ { key: "slug", header: "slug" },
3912
4035
  { key: "link", header: "\u94FE\u63A5" }
3913
4036
  ];
3914
4037
  console.log(`
@@ -3919,6 +4042,7 @@ ${site.name} \u9875\u9762\uFF08${pages.length}\uFF09
3919
4042
  id: String(p.id),
3920
4043
  title: pageTitle(p) || "(\u65E0\u6807\u9898)",
3921
4044
  status: p.status ?? "-",
4045
+ slug: p.slug ?? "-",
3922
4046
  link: p.link ?? "-"
3923
4047
  })),
3924
4048
  columns
@@ -3981,6 +4105,103 @@ async function runPagesWrite(mode, opts) {
3981
4105
  console.log(` \u7AD9\u70B9 : ${base}
3982
4106
  `);
3983
4107
  }
4108
+ async function runPagesGet(opts) {
4109
+ const id = Number(opts.id);
4110
+ if (!Number.isFinite(id) || id <= 0) {
4111
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id\n");
4112
+ process.exit(1);
4113
+ }
4114
+ const target = await resolveTarget(opts);
4115
+ const page = await apiFetch2(
4116
+ wpV2(target.base, `/pages/${id}?context=edit`),
4117
+ target.config,
4118
+ {},
4119
+ opts.verbose
4120
+ ).catch((e) => {
4121
+ console.error(`
4122
+ \u274C \u83B7\u53D6\u9875\u9762\u5931\u8D25\uFF1A${e.message}
4123
+ `);
4124
+ process.exit(1);
4125
+ });
4126
+ if (opts.json) {
4127
+ console.log(JSON.stringify(page, null, 2));
4128
+ return;
4129
+ }
4130
+ console.log(`
4131
+ \u9875\u9762 #${page.id}`);
4132
+ console.log(` \u6807\u9898 : ${pageTitle(page) || "-"}`);
4133
+ console.log(` \u72B6\u6001 : ${page.status || "-"}`);
4134
+ console.log(` slug : ${page.slug || "-"}`);
4135
+ console.log(` \u94FE\u63A5 : ${page.link || "-"}`);
4136
+ if (page.parent) console.log(` \u7236\u9875 : ${page.parent}`);
4137
+ if (page.featured_media) console.log(` \u7279\u8272\u56FE : ${page.featured_media}`);
4138
+ console.log();
4139
+ }
4140
+ async function runPagesMeta(opts) {
4141
+ const id = Number(opts.id);
4142
+ if (!Number.isFinite(id) || id <= 0) {
4143
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id\n");
4144
+ process.exit(1);
4145
+ }
4146
+ const body = {};
4147
+ if (opts.title?.trim()) body.title = opts.title.trim();
4148
+ if (opts.slug?.trim()) body.slug = opts.slug.trim();
4149
+ if (opts.status?.trim()) body.status = opts.status.trim();
4150
+ const parent = Number(opts.parent);
4151
+ if (opts.parent !== void 0 && Number.isFinite(parent)) body.parent = parent;
4152
+ const featured = Number(opts.featuredMedia);
4153
+ if (Number.isFinite(featured) && featured > 0) body.featured_media = featured;
4154
+ if (Object.keys(body).length === 0) {
4155
+ console.error("\n\u274C \u6CA1\u6709\u8981\u6539\u7684\u5B57\u6BB5\u3002\u6B63\u6587\u8BF7\u7528 pages update\u3002\n");
4156
+ process.exit(1);
4157
+ }
4158
+ const target = await resolveTarget(opts);
4159
+ const page = await apiFetch2(
4160
+ wpV2(target.base, `/pages/${id}`),
4161
+ target.config,
4162
+ { method: "POST", body: JSON.stringify(body) },
4163
+ opts.verbose
4164
+ ).catch((e) => {
4165
+ console.error(`
4166
+ \u274C \u66F4\u65B0\u9875\u9762\u5143\u6570\u636E\u5931\u8D25\uFF1A${e.message}
4167
+ `);
4168
+ process.exit(1);
4169
+ });
4170
+ console.log(`
4171
+ \u2705 \u9875\u9762 #${page.id} \u5143\u6570\u636E\u5DF2\u66F4\u65B0`);
4172
+ console.log(` \u6807\u9898 : ${pageTitle(page) || "-"}`);
4173
+ console.log(` \u72B6\u6001 : ${page.status || "-"}`);
4174
+ console.log(` slug : ${page.slug || "-"}`);
4175
+ if (page.link) console.log(` \u94FE\u63A5 : ${page.link}`);
4176
+ console.log();
4177
+ }
4178
+ async function runPagesTrash(opts) {
4179
+ const id = Number(opts.id);
4180
+ if (!Number.isFinite(id) || id <= 0) {
4181
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id\n");
4182
+ process.exit(1);
4183
+ }
4184
+ const target = await resolveTarget(opts);
4185
+ const qs = opts.force ? "?force=true" : "";
4186
+ const page = await apiFetch2(
4187
+ wpV2(target.base, `/pages/${id}${qs}`),
4188
+ target.config,
4189
+ { method: "DELETE" },
4190
+ opts.verbose
4191
+ ).catch((e) => {
4192
+ console.error(`
4193
+ \u274C \u5220\u9664\u9875\u9762\u5931\u8D25\uFF1A${e.message}
4194
+ `);
4195
+ process.exit(1);
4196
+ });
4197
+ console.log(
4198
+ opts.force ? `
4199
+ \u2705 \u5DF2\u6C38\u4E45\u5220\u9664\u9875\u9762 #${id}
4200
+ ` : `
4201
+ \u2705 \u9875\u9762 #${id} \u5DF2\u8FDB\u56DE\u6536\u7AD9\uFF08status=${page.status || "trash"}\uFF09
4202
+ `
4203
+ );
4204
+ }
3984
4205
  function register6(program2) {
3985
4206
  const pages = program2.command("pages").description("WordPress \u9875\u9762\uFF08zionbuilder visual-edit\uFF09");
3986
4207
  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) => {
@@ -3992,12 +4213,188 @@ function register6(program2) {
3992
4213
  pages.command("update").description("\u7528 HTML \u8986\u76D6\u5DF2\u6709\u9875\u9762\uFF08\u8D70 save-visual-edit\uFF0C\u4F1A\u76F4\u63A5\u53D1\u5E03\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("--id <id>", "\u9875\u9762 ID").option("-t, --title <title>", "\u65B0\u6807\u9898\uFF08\u53EF\u7701\u7565\uFF09").option("-c, --content <html>", "HTML \u6B63\u6587").option("--content-file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6 HTML \u6B63\u6587").option("--css <css>", "\u9644\u52A0 CSS").option("--css-file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6 CSS").option("--js <js>", "\u9644\u52A0 JS").option("--js-file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6 JS").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
3993
4214
  await runPagesWrite("update", opts);
3994
4215
  });
4216
+ pages.command("get").description("\u67E5\u770B\u5355\u4E2A\u9875\u9762\u7684\u5143\u6570\u636E").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("--id <id>", "\u9875\u9762 ID").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4217
+ await runPagesGet(opts);
4218
+ });
4219
+ pages.command("meta").description("\u6539\u9875\u9762 slug / \u72B6\u6001 / \u7236\u9875 / \u7279\u8272\u56FE\uFF08\u4E0D\u6539 Zion \u6B63\u6587\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("--id <id>", "\u9875\u9762 ID").option("-t, --title <title>", "\u6807\u9898").option("--slug <slug>", "\u522B\u540D").option("--status <status>", "publish / draft / private / pending").option("--parent <id>", "\u7236\u9875\u9762 ID\uFF0C0 \u8868\u793A\u53D6\u6D88").option("--featured-media <id>", "\u7279\u8272\u56FE\u5A92\u4F53 ID").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4220
+ await runPagesMeta(opts);
4221
+ });
4222
+ pages.command("trash").description("\u9875\u9762\u8FDB\u56DE\u6536\u7AD9\uFF1B\u52A0 --force \u6C38\u4E45\u5220\u9664").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("--id <id>", "\u9875\u9762 ID").option("--force", "\u6C38\u4E45\u5220\u9664", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4223
+ await runPagesTrash(opts);
4224
+ });
3995
4225
  }
3996
4226
 
3997
4227
  // src/commands/plugins.ts
3998
4228
  import { readFileSync as readFileSync6, existsSync as existsSync3 } from "fs";
3999
4229
  import { basename as basename2, resolve as resolve2 } from "path";
4000
- var MAX_ZIP_BYTES = 20 * 1024 * 1024;
4230
+
4231
+ // src/utils/wp-plugin-catalog.ts
4232
+ import * as http2 from "http";
4233
+ import * as https2 from "https";
4234
+ function trimSlash(url) {
4235
+ return url.replace(/\/+$/, "");
4236
+ }
4237
+ function pluginApiBases(config) {
4238
+ const seen = /* @__PURE__ */ new Set();
4239
+ const out = [];
4240
+ for (const raw of [config.csoPluginApiBaseUrl, config.csoBaseUrl]) {
4241
+ const base = trimSlash(raw || "");
4242
+ if (!base || seen.has(base)) continue;
4243
+ seen.add(base);
4244
+ out.push(base);
4245
+ }
4246
+ return out;
4247
+ }
4248
+ function isNetworkish(err) {
4249
+ const msg = err.message || "";
4250
+ return /fetch failed|ENOTFOUND|ECONNREFUSED|ETIMEDOUT|ECONNRESET|EPROTO|TLS|socket disconnected|certificate|HTTP 5\d\d/i.test(
4251
+ msg
4252
+ );
4253
+ }
4254
+ function compareVersion(a, b) {
4255
+ const pa = a.split(".").map((p) => Number(p) || 0);
4256
+ const pb = b.split(".").map((p) => Number(p) || 0);
4257
+ const n = Math.max(pa.length, pb.length);
4258
+ for (let i = 0; i < n; i++) {
4259
+ const da = pa[i] ?? 0;
4260
+ const db = pb[i] ?? 0;
4261
+ if (da !== db) return da - db;
4262
+ }
4263
+ return 0;
4264
+ }
4265
+ function versionFromPath(path6, filename) {
4266
+ const parts = path6.replace(/\\/g, "/").split("/").filter(Boolean);
4267
+ if (parts.length >= 2) return parts[1];
4268
+ const m = filename.match(/\.((?:\d+\.)*\d+)\.zip$/i);
4269
+ return m?.[1] ?? "";
4270
+ }
4271
+ function collectZips(nodes, out = []) {
4272
+ for (const node of nodes || []) {
4273
+ if (!node.isDirectory && /\.zip$/i.test(node.name || "") && node.url) {
4274
+ out.push(node);
4275
+ }
4276
+ if (node.children?.length) collectZips(node.children, out);
4277
+ }
4278
+ return out;
4279
+ }
4280
+ function flattenOfficialPlugins(items) {
4281
+ const best = /* @__PURE__ */ new Map();
4282
+ for (const zip of collectZips(items)) {
4283
+ const path6 = (zip.path || "").replace(/\\/g, "/");
4284
+ const filename = zip.name || path6.split("/").pop() || "";
4285
+ const slug = path6.split("/").filter(Boolean)[0] || filename.replace(/\.zip$/i, "");
4286
+ if (!slug) continue;
4287
+ const candidate = {
4288
+ slug,
4289
+ version: versionFromPath(path6, filename),
4290
+ filename,
4291
+ path: path6,
4292
+ size: Number(zip.size) || 0,
4293
+ url: zip.url || "",
4294
+ lastModified: zip.lastModified || ""
4295
+ };
4296
+ const prev = best.get(slug);
4297
+ if (!prev || compareVersion(candidate.version, prev.version) > 0) {
4298
+ best.set(slug, candidate);
4299
+ }
4300
+ }
4301
+ return [...best.values()].sort((a, b) => a.slug.localeCompare(b.slug));
4302
+ }
4303
+ async function fetchOfficialPluginList(config, opts = {}) {
4304
+ const params = new URLSearchParams();
4305
+ if (opts.prefix?.trim()) params.set("prefix", opts.prefix.trim());
4306
+ const qs = params.toString();
4307
+ const path6 = `/cso/v1/wp-plugin/list${qs ? `?${qs}` : ""}`;
4308
+ const bases = pluginApiBases(config);
4309
+ let lastErr;
4310
+ for (let i = 0; i < bases.length; i++) {
4311
+ const url = `${bases[i]}${path6}`;
4312
+ try {
4313
+ const raw = await apiFetch2(url, config, {}, opts.verbose);
4314
+ const code = raw.code;
4315
+ if (code !== void 0 && code !== 1) {
4316
+ throw new Error(raw.message || `\u76EE\u5F55\u63A5\u53E3\u8FD4\u56DE code=${code}`);
4317
+ }
4318
+ const data = raw.data || {};
4319
+ return {
4320
+ container: data.container || "wp-plugins",
4321
+ items: flattenOfficialPlugins(data.items)
4322
+ };
4323
+ } catch (e) {
4324
+ lastErr = e;
4325
+ if (i < bases.length - 1 && isNetworkish(lastErr)) {
4326
+ console.log(`\u76EE\u5F55\u63A5\u53E3 ${bases[i]} \u4E0D\u53EF\u8FBE\uFF0C\u6539\u8D70 ${bases[i + 1]} \u2026`);
4327
+ continue;
4328
+ }
4329
+ throw lastErr;
4330
+ }
4331
+ }
4332
+ throw lastErr || new Error("\u76EE\u5F55\u63A5\u53E3\u4E0D\u53EF\u7528");
4333
+ }
4334
+ async function findOfficialPlugin(config, slugOrPlugin, verbose = false) {
4335
+ const needle = slugOrPlugin.replace(/\\/g, "/").replace(/\.php$/i, "").toLowerCase();
4336
+ if (!needle) return null;
4337
+ const { items } = await fetchOfficialPluginList(config, { verbose });
4338
+ return items.find((p) => p.slug.toLowerCase() === needle) || items.find((p) => needle === p.slug.toLowerCase() || needle.startsWith(`${p.slug.toLowerCase()}/`)) || null;
4339
+ }
4340
+ function downloadOfficialZip(url) {
4341
+ return new Promise((resolve4, reject) => {
4342
+ let parsed;
4343
+ try {
4344
+ parsed = new URL(url);
4345
+ } catch {
4346
+ reject(new Error("\u76EE\u5F55\u5305\u5730\u5740\u4E0D\u5408\u6CD5"));
4347
+ return;
4348
+ }
4349
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
4350
+ reject(new Error("\u76EE\u5F55\u5305\u5730\u5740\u5FC5\u987B\u662F http(s)"));
4351
+ return;
4352
+ }
4353
+ const transport = parsed.protocol === "https:" ? https2 : http2;
4354
+ const req = transport.get(url, { timeout: 12e4 }, (res) => {
4355
+ const status = res.statusCode ?? 0;
4356
+ if (status >= 300 && status < 400 && res.headers.location) {
4357
+ res.resume();
4358
+ downloadOfficialZip(res.headers.location).then(resolve4, reject);
4359
+ return;
4360
+ }
4361
+ if (status < 200 || status >= 300) {
4362
+ res.resume();
4363
+ reject(new Error(`\u4E0B\u8F7D\u76EE\u5F55\u5305\u5931\u8D25\uFF1AHTTP ${status}`));
4364
+ return;
4365
+ }
4366
+ const chunks = [];
4367
+ let size = 0;
4368
+ res.on("data", (chunk) => {
4369
+ size += chunk.length;
4370
+ if (size > 40 * 1024 * 1024) {
4371
+ res.destroy();
4372
+ reject(new Error("\u76EE\u5F55\u5305\u8D85\u8FC7 40MB"));
4373
+ return;
4374
+ }
4375
+ chunks.push(chunk);
4376
+ });
4377
+ res.on("end", () => resolve4(Buffer.concat(chunks)));
4378
+ res.on("error", reject);
4379
+ });
4380
+ req.on("timeout", () => {
4381
+ req.destroy();
4382
+ reject(new Error("\u4E0B\u8F7D\u76EE\u5F55\u5305\u8D85\u65F6"));
4383
+ });
4384
+ req.on("error", reject);
4385
+ });
4386
+ }
4387
+
4388
+ // src/commands/plugins.ts
4389
+ var MAX_ZIP_BYTES = 40 * 1024 * 1024;
4390
+ var PROTECTED_PLUGIN_PREFIXES = ["siluzan-helper-plugin", "wp-oauth2"];
4391
+ function isProtectedPlugin(plugin) {
4392
+ const key = pluginKey(plugin);
4393
+ return PROTECTED_PLUGIN_PREFIXES.some((prefix) => key === prefix || key.startsWith(`${prefix}/`));
4394
+ }
4395
+ function wpPluginRestPath(plugin) {
4396
+ return plugin.replace(/\\/g, "/").split("/").filter(Boolean).map((part) => encodeURIComponent(part)).join("/");
4397
+ }
4001
4398
  function pluginName(item) {
4002
4399
  if (typeof item.name === "string") return item.name;
4003
4400
  return item.name?.raw || item.name?.rendered || item.plugin || "";
@@ -4005,7 +4402,13 @@ function pluginName(item) {
4005
4402
  function pluginKey(value) {
4006
4403
  return value.replace(/\\/g, "/").replace(/\.php$/i, "").toLowerCase();
4007
4404
  }
4008
- async function resolveTarget(opts) {
4405
+ function formatBytes(n) {
4406
+ if (!n) return "-";
4407
+ if (n < 1024) return `${n} B`;
4408
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
4409
+ return `${(n / 1024 / 1024).toFixed(1)} MB`;
4410
+ }
4411
+ async function resolveTarget2(opts) {
4009
4412
  const config = loadConfig();
4010
4413
  const site = await resolveSite(config, opts);
4011
4414
  return { config, site, base: trimTrailingSlash(site.url) };
@@ -4060,7 +4463,7 @@ async function withHelperBootstrap(opts, target, run) {
4060
4463
  }
4061
4464
  }
4062
4465
  async function runPluginsList(opts) {
4063
- const { config, site, base } = await resolveTarget(opts);
4466
+ const { config, site, base } = await resolveTarget2(opts);
4064
4467
  const api = `${base}/wp-json/wp/v2/plugins`;
4065
4468
  let plugins;
4066
4469
  try {
@@ -4081,34 +4484,23 @@ async function runPluginsList(opts) {
4081
4484
  if (opts.exitOnError === false) return;
4082
4485
  process.exit(1);
4083
4486
  }
4084
- let catalog = [];
4487
+ let official = [];
4085
4488
  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 : [];
4489
+ official = (await fetchOfficialPluginList(config, { verbose: opts.verbose })).items;
4093
4490
  } catch {
4094
4491
  }
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
- }
4492
+ const officialBySlug = new Map(official.map((item) => [item.slug.toLowerCase(), item]));
4100
4493
  const rows = plugins.map((p) => {
4101
4494
  const plugin = p.plugin ?? "";
4102
- const hit = catalogByKey.get(pluginKey(plugin));
4103
- let updateSource = "none";
4104
- if (hit?.update_available) updateSource = "catalog";
4495
+ const slug = pluginKey(plugin).split("/")[0];
4496
+ const hit = officialBySlug.get(slug);
4105
4497
  return {
4106
4498
  plugin,
4107
4499
  name: pluginName(p),
4108
4500
  status: p.status ?? "",
4109
4501
  version: p.version ?? "",
4110
- updateSource,
4111
- catalogVersion: hit?.new_version ?? ""
4502
+ updateSource: hit ? "catalog" : "none",
4503
+ catalogVersion: hit?.version ?? ""
4112
4504
  };
4113
4505
  });
4114
4506
  if (opts.json) {
@@ -4156,20 +4548,10 @@ ${site.name} \u5DF2\u88C5\u63D2\u4EF6\uFF08${rows.length}\uFF09
4156
4548
  console.log();
4157
4549
  }
4158
4550
  async function runPluginsCatalog(opts) {
4159
- const target = await resolveTarget(opts);
4160
- const { site, base } = target;
4161
- let res;
4551
+ const config = loadConfig();
4552
+ let pack;
4162
4553
  try {
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
- );
4554
+ pack = await fetchOfficialPluginList(config, { prefix: opts.prefix, verbose: opts.verbose });
4173
4555
  } catch (e) {
4174
4556
  console.error(`
4175
4557
  \u274C \u83B7\u53D6\u63D2\u4EF6\u76EE\u5F55\u5931\u8D25\uFF1A${e.message}
@@ -4177,46 +4559,78 @@ async function runPluginsCatalog(opts) {
4177
4559
  process.exit(1);
4178
4560
  return;
4179
4561
  }
4180
- const items = Array.isArray(res.items) ? res.items : [];
4562
+ const installedBySlug = /* @__PURE__ */ new Map();
4563
+ if (opts.site || opts.url) {
4564
+ try {
4565
+ const target = await resolveTarget2(opts);
4566
+ const plugins = await apiFetch2(
4567
+ `${target.base}/wp-json/wp/v2/plugins`,
4568
+ target.config,
4569
+ {},
4570
+ opts.verbose
4571
+ );
4572
+ if (Array.isArray(plugins)) {
4573
+ for (const p of plugins) {
4574
+ const slug = pluginKey(p.plugin || "").split("/")[0];
4575
+ if (slug) installedBySlug.set(slug, { version: p.version || "", status: p.status || "" });
4576
+ }
4577
+ }
4578
+ } catch {
4579
+ }
4580
+ }
4581
+ const rows = pack.items.map((p) => {
4582
+ const onSite = installedBySlug.get(p.slug.toLowerCase());
4583
+ return {
4584
+ slug: p.slug,
4585
+ version: p.version,
4586
+ size: formatBytes(p.size),
4587
+ installed: onSite ? "yes" : opts.site || opts.url ? "no" : "-",
4588
+ filename: p.filename
4589
+ };
4590
+ });
4181
4591
  if (opts.json) {
4182
4592
  console.log(
4183
- JSON.stringify(
4184
- { site: { guid: site.guid, name: site.name, url: site.url }, total: items.length, items },
4185
- null,
4186
- 2
4187
- )
4593
+ JSON.stringify({ container: pack.container, total: pack.items.length, items: pack.items }, null, 2)
4188
4594
  );
4189
4595
  return;
4190
4596
  }
4191
- if (items.length === 0) {
4192
- console.log(`
4193
- \u7AD9\u70B9 ${site.name} \u7684\u4E1D\u8DEF\u8D5E\u63D2\u4EF6\u76EE\u5F55\u4E3A\u7A7A\u3002
4194
- `);
4597
+ if (rows.length === 0) {
4598
+ console.log("\n\u5B98\u65B9\u63D2\u4EF6\u76EE\u5F55\u4E3A\u7A7A\u3002\n");
4195
4599
  return;
4196
4600
  }
4197
- const columns = [
4198
- { key: "slug", header: "slug" },
4199
- { key: "name", header: "\u540D\u79F0" },
4200
- { key: "new_version", header: "\u76EE\u5F55\u7248\u672C" },
4201
- { key: "installed", header: "\u5DF2\u88C5" },
4202
- { key: "active", header: "\u542F\u7528" },
4203
- { key: "update_available", header: "\u53EF\u66F4\u65B0" }
4204
- ];
4205
4601
  console.log(`
4206
- ${site.name} \u53EF\u5B89\u88C5\u76EE\u5F55\uFF08${items.length}\uFF09
4602
+ \u5B98\u65B9\u63D2\u4EF6\u76EE\u5F55 ${pack.container}\uFF08${rows.length}\uFF09
4207
4603
  `);
4208
- printCliTable(
4209
- items.map((p) => ({
4210
- slug: p.slug || p.plugin || "-",
4211
- name: p.name || "-",
4212
- new_version: p.new_version || "-",
4213
- installed: p.installed ? "yes" : "no",
4214
- active: p.active ? "yes" : "no",
4215
- update_available: p.update_available ? "yes" : "no"
4216
- })),
4217
- columns
4604
+ printCliTable(rows, [
4605
+ { key: "slug", header: "slug" },
4606
+ { key: "version", header: "\u76EE\u5F55\u7248\u672C" },
4607
+ { key: "size", header: "\u5927\u5C0F" },
4608
+ { key: "installed", header: "\u672C\u7AD9\u5DF2\u88C5" },
4609
+ { key: "filename", header: "zip" }
4610
+ ]);
4611
+ console.log("\n\u5B89\u88C5\uFF1Asiluzan-website plugins install --site <guid> --slug <slug>\n");
4612
+ }
4613
+ async function installOfficialZip(target, official, verbose = false) {
4614
+ if (official.size > MAX_ZIP_BYTES) {
4615
+ throw new Error(`\u76EE\u5F55\u5305 ${official.filename} \u8D85\u8FC7 40MB`);
4616
+ }
4617
+ console.log(`\u4ECE\u5B98\u65B9\u76EE\u5F55\u4E0B\u8F7D ${official.slug} ${official.version} \u2026`);
4618
+ const buf = await downloadOfficialZip(official.url);
4619
+ if (buf.length > MAX_ZIP_BYTES) {
4620
+ throw new Error(`\u76EE\u5F55\u5305 ${official.filename} \u8D85\u8FC7 40MB`);
4621
+ }
4622
+ return apiFetch2(
4623
+ `${target.base}/wp-json/siluzan-helper/v1/plugins/install-zip`,
4624
+ target.config,
4625
+ {
4626
+ method: "POST",
4627
+ body: JSON.stringify({
4628
+ filename: official.filename,
4629
+ zip_base64: buf.toString("base64")
4630
+ })
4631
+ },
4632
+ verbose
4218
4633
  );
4219
- console.log("\n\u5B89\u88C5 / \u5347\u7EA7\uFF1Asiluzan-website plugins install --site <guid> --slug <slug>\n");
4220
4634
  }
4221
4635
  async function runPluginsInstall(opts) {
4222
4636
  const slug = opts.slug?.trim() ?? "";
@@ -4225,12 +4639,16 @@ async function runPluginsInstall(opts) {
4225
4639
  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");
4226
4640
  process.exit(1);
4227
4641
  }
4228
- const target = await resolveTarget(opts);
4642
+ const target = await resolveTarget2(opts);
4229
4643
  const { site, base } = target;
4230
4644
  let result;
4231
4645
  try {
4232
4646
  result = await withHelperBootstrap(opts, target, async () => {
4233
4647
  if (slug) {
4648
+ const official = await findOfficialPlugin(target.config, slug, opts.verbose);
4649
+ if (official) {
4650
+ return installOfficialZip(target, official, opts.verbose);
4651
+ }
4234
4652
  return apiFetch2(
4235
4653
  `${base}/wp-json/siluzan-helper/v1/plugins/install`,
4236
4654
  target.config,
@@ -4267,7 +4685,7 @@ async function runPluginsUpdate(opts) {
4267
4685
  console.error("\n\u274C \u8BF7\u63D0\u4F9B --plugin\uFF08\u5148 plugins list \u770B plugin \u5217\uFF09\n");
4268
4686
  process.exit(1);
4269
4687
  }
4270
- const target = await resolveTarget(opts);
4688
+ const target = await resolveTarget2(opts);
4271
4689
  const { site, base } = target;
4272
4690
  const zipPath = opts.zip?.trim() ?? "";
4273
4691
  const body = { plugin };
@@ -4278,16 +4696,20 @@ async function runPluginsUpdate(opts) {
4278
4696
  }
4279
4697
  let result;
4280
4698
  try {
4281
- result = await withHelperBootstrap(
4282
- opts,
4283
- target,
4284
- () => apiFetch2(
4699
+ result = await withHelperBootstrap(opts, target, async () => {
4700
+ if (!zipPath) {
4701
+ const official = await findOfficialPlugin(target.config, plugin, opts.verbose);
4702
+ if (official) {
4703
+ return installOfficialZip(target, official, opts.verbose);
4704
+ }
4705
+ }
4706
+ return apiFetch2(
4285
4707
  `${base}/wp-json/siluzan-helper/v1/plugins/update`,
4286
4708
  target.config,
4287
4709
  { method: "POST", body: JSON.stringify(body) },
4288
4710
  opts.verbose
4289
- )
4290
- );
4711
+ );
4712
+ });
4291
4713
  } catch (e) {
4292
4714
  const msg = e.message || "";
4293
4715
  if (/HTTP 409/.test(msg) && /no_package/i.test(msg)) {
@@ -4311,7 +4733,7 @@ async function runPluginsUpdate(opts) {
4311
4733
  });
4312
4734
  }
4313
4735
  async function runHelperStatus(opts) {
4314
- const { config, site, base } = await resolveTarget(opts);
4736
+ const { config, site, base } = await resolveTarget2(opts);
4315
4737
  let status;
4316
4738
  try {
4317
4739
  status = await apiFetch2(
@@ -4345,7 +4767,7 @@ ${site.name} helper \u72B6\u6001`);
4345
4767
  `);
4346
4768
  }
4347
4769
  async function runUpdateHelper(opts, fromBootstrap = false) {
4348
- const { config, site, base } = await resolveTarget(opts);
4770
+ const { config, site, base } = await resolveTarget2(opts);
4349
4771
  let result;
4350
4772
  try {
4351
4773
  result = await apiFetch2(
@@ -4372,32 +4794,830 @@ async function runUpdateHelper(opts, fromBootstrap = false) {
4372
4794
  await runHelperStatus({ site: site.guid || opts.site, url: site.url, verbose: opts.verbose });
4373
4795
  }
4374
4796
  }
4375
- function register7(program2) {
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) => {
4378
- await runPluginsList(opts);
4379
- });
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) => {
4381
- await runPluginsCatalog(opts);
4382
- });
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) => {
4384
- await runPluginsInstall(opts);
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);
4797
+ async function findInstalledPlugin(base, config, pluginArg, verbose = false) {
4798
+ const plugins = await apiFetch2(`${base}/wp-json/wp/v2/plugins`, config, {}, verbose);
4799
+ if (!Array.isArray(plugins)) {
4800
+ throw new Error("\u7AD9\u70B9\u8FD4\u56DE\u7684\u63D2\u4EF6\u5217\u8868\u683C\u5F0F\u5F02\u5E38");
4801
+ }
4802
+ const needle = pluginKey(pluginArg);
4803
+ const exact = plugins.find((p) => pluginKey(p.plugin || "") === needle);
4804
+ if (exact?.plugin) return exact;
4805
+ const hits = plugins.filter((p) => {
4806
+ const key = pluginKey(p.plugin || "");
4807
+ return key === needle || key.startsWith(`${needle}/`) || key.endsWith(`/${needle}`);
4391
4808
  });
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);
4809
+ if (hits.length === 1 && hits[0].plugin) return hits[0];
4810
+ if (hits.length > 1) {
4811
+ const names = hits.map((p) => p.plugin).join("\u3001");
4812
+ throw new Error(`\u300C${pluginArg}\u300D\u5339\u914D\u5230\u591A\u4E2A\u63D2\u4EF6\uFF0C\u8BF7\u6539\u7528\u5B8C\u6574 --plugin\uFF1A${names}`);
4813
+ }
4814
+ throw new Error(`\u7AD9\u70B9\u4E0A\u6CA1\u6709\u63D2\u4EF6\u300C${pluginArg}\u300D\uFF0C\u5148 plugins list \u770B plugin \u5217`);
4815
+ }
4816
+ async function runPluginsUninstall(opts) {
4817
+ const pluginArg = opts.plugin?.trim() ?? "";
4818
+ if (!pluginArg) {
4819
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --plugin\uFF08\u5148 plugins list \u770B plugin \u5217\uFF09\n");
4820
+ process.exit(1);
4821
+ }
4822
+ if (isProtectedPlugin(pluginArg)) {
4823
+ console.error("\n\u274C \u4E0D\u80FD\u5378\u8F7D siluzan-helper-plugin \u6216 wp-oauth2\uFF0C\u5378\u6389\u540E\u7AD9\u70B9\u65E0\u6CD5\u518D\u7528 Skill \u7BA1\u7406\u3002\n");
4824
+ process.exit(1);
4825
+ }
4826
+ const target = await resolveTarget2(opts);
4827
+ const { site, base } = target;
4828
+ let current;
4829
+ try {
4830
+ current = await findInstalledPlugin(base, target.config, pluginArg, opts.verbose);
4831
+ } catch (e) {
4832
+ console.error(`
4833
+ \u274C ${e.message}
4834
+ `);
4835
+ process.exit(1);
4836
+ return;
4837
+ }
4838
+ const plugin = current.plugin || pluginArg;
4839
+ if (isProtectedPlugin(plugin)) {
4840
+ console.error("\n\u274C \u4E0D\u80FD\u5378\u8F7D siluzan-helper-plugin \u6216 wp-oauth2\uFF0C\u5378\u6389\u540E\u7AD9\u70B9\u65E0\u6CD5\u518D\u7528 Skill \u7BA1\u7406\u3002\n");
4841
+ process.exit(1);
4842
+ }
4843
+ const restPath = `${base}/wp-json/wp/v2/plugins/${wpPluginRestPath(plugin)}`;
4844
+ try {
4845
+ if (current.status === "active") {
4846
+ console.log(`\u5148\u505C\u7528 ${plugin} \u2026`);
4847
+ await apiFetch2(
4848
+ restPath,
4849
+ target.config,
4850
+ { method: "POST", body: JSON.stringify({ status: "inactive" }) },
4851
+ opts.verbose
4852
+ );
4853
+ }
4854
+ await apiFetch2(restPath, target.config, { method: "DELETE" }, opts.verbose);
4855
+ } catch (e) {
4856
+ console.error(`
4857
+ \u274C \u5378\u8F7D\u5931\u8D25\uFF1A${e.message}
4858
+ `);
4859
+ process.exit(1);
4860
+ return;
4861
+ }
4862
+ console.log(`
4863
+ \u2705 \u5DF2\u5378\u8F7D ${plugin}`);
4864
+ console.log(` \u7AD9\u70B9 : ${site.name} (${base})
4865
+ `);
4866
+ await runPluginsList({
4867
+ site: site.guid || opts.site,
4868
+ url: site.url,
4869
+ verbose: opts.verbose,
4870
+ exitOnError: false
4871
+ });
4872
+ }
4873
+ function register7(program2) {
4874
+ const plugins = program2.command("plugins").description("WordPress \u63D2\u4EF6\uFF1A\u5217\u51FA\u3001\u76EE\u5F55\u3001\u5B89\u88C5\u3001\u5347\u7EA7\u3001\u5378\u8F7D");
4875
+ 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) => {
4876
+ await runPluginsList(opts);
4877
+ });
4878
+ plugins.command("catalog").description("\u5217\u51FA\u5B98\u65B9\u63D2\u4EF6\u76EE\u5F55\uFF08sammk8s / wp-plugins\uFF0C\u4E0D\u4F9D\u8D56\u7AD9\u70B9\uFF09").option("-s, --site <guidOrName>", "\u53EF\u9009\uFF0C\u5BF9\u7167\u8BE5\u7AD9\u662F\u5426\u5DF2\u88C5").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--prefix <prefix>", "\u53EA\u5217\u67D0\u4E2A\u76EE\u5F55\uFF0C\u5982 zasp-smart-recommender/").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4879
+ await runPluginsCatalog(opts);
4880
+ });
4881
+ plugins.command("install").description("\u5B89\u88C5\u5E76\u542F\u7528\u63D2\u4EF6\uFF1A\u5B98\u65B9\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) => {
4882
+ await runPluginsInstall(opts);
4883
+ });
4884
+ 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) => {
4885
+ await runPluginsUpdate(opts);
4886
+ });
4887
+ plugins.command("uninstall").description("\u5378\u8F7D\u5DF2\u88C5\u63D2\u4EF6\uFF08\u5148\u505C\u7528\u518D\u5220\u9664\uFF1Bhelper / oauth2 \u4E0D\u5141\u8BB8\uFF09").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 dummy-e2e-plugin/dummy-e2e-plugin").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
4888
+ await runPluginsUninstall(opts);
4889
+ });
4890
+ 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) => {
4891
+ await runHelperStatus(opts);
4892
+ });
4893
+ 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) => {
4894
+ await runUpdateHelper(opts);
4895
+ });
4896
+ }
4897
+
4898
+ // src/commands/content.ts
4899
+ import * as fs7 from "fs";
4900
+
4901
+ // src/types/wp-rest.ts
4902
+ function wpRenderedText(value) {
4903
+ if (!value) return "";
4904
+ if (typeof value === "string") return value;
4905
+ return value.raw || value.rendered || "";
4906
+ }
4907
+
4908
+ // src/commands/content.ts
4909
+ function parseIdList(raw) {
4910
+ if (!raw?.trim()) return void 0;
4911
+ return raw.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
4912
+ }
4913
+ function printPost(item, restBase) {
4914
+ console.log(`
4915
+ \u2705 ${restBase} #${item.id}`);
4916
+ console.log(` \u6807\u9898 : ${wpRenderedText(item.title) || "-"}`);
4917
+ console.log(` \u72B6\u6001 : ${item.status || "-"}`);
4918
+ console.log(` slug : ${item.slug || "-"}`);
4919
+ if (item.link) console.log(` \u94FE\u63A5 : ${item.link}`);
4920
+ console.log();
4921
+ }
4922
+ async function runContentTypes(opts) {
4923
+ const target = await resolveTarget(opts);
4924
+ let types;
4925
+ try {
4926
+ types = await apiFetch2(
4927
+ wpV2(target.base, "/types?context=edit"),
4928
+ target.config,
4929
+ {},
4930
+ opts.verbose
4931
+ );
4932
+ } catch (e) {
4933
+ console.error(`
4934
+ \u274C \u83B7\u53D6\u5185\u5BB9\u7C7B\u578B\u5931\u8D25\uFF1A${e.message}
4935
+ `);
4936
+ process.exit(1);
4937
+ return;
4938
+ }
4939
+ const rows = Object.entries(types || {}).map(([slug, info]) => ({
4940
+ slug,
4941
+ rest_base: info.rest_base || slug,
4942
+ name: info.name || slug
4943
+ }));
4944
+ if (opts.json) {
4945
+ console.log(JSON.stringify({ site: target.site, types: rows }, null, 2));
4946
+ return;
4947
+ }
4948
+ console.log(`
4949
+ ${target.site.name} \u53EF REST \u7BA1\u7406\u7684\u5185\u5BB9\u7C7B\u578B\uFF08${rows.length}\uFF09
4950
+ `);
4951
+ printCliTable(rows, [
4952
+ { key: "slug", header: "slug" },
4953
+ { key: "rest_base", header: "rest_base\uFF08--type\uFF09" },
4954
+ { key: "name", header: "\u540D\u79F0" }
4955
+ ]);
4956
+ console.log("\n\u9875\u9762\u6B63\u6587\u8BF7\u7528 pages add/update\uFF1B\u5176\u5B83\u7C7B\u578B\u7528 content add --type <rest_base>\n");
4957
+ }
4958
+ async function runContentList(opts) {
4959
+ const typeArg = opts.type?.trim() || "posts";
4960
+ const target = await resolveTarget(opts);
4961
+ let restBase;
4962
+ try {
4963
+ restBase = await resolveTypeRestBase(target, typeArg, opts.verbose);
4964
+ } catch (e) {
4965
+ console.error(`
4966
+ \u274C ${e.message}
4967
+ `);
4968
+ process.exit(1);
4969
+ return;
4970
+ }
4971
+ const status = opts.status?.trim() || "publish,draft,private,pending";
4972
+ const api = wpV2(target.base, `/${restBase}?per_page=100&status=${encodeURIComponent(status)}`);
4973
+ let items;
4974
+ try {
4975
+ items = await apiFetch2(api, target.config, {}, opts.verbose);
4976
+ } catch (e) {
4977
+ console.error(`
4978
+ \u274C \u83B7\u53D6 ${restBase} \u5931\u8D25\uFF1A${e.message}
4979
+ `);
4980
+ process.exit(1);
4981
+ return;
4982
+ }
4983
+ if (!Array.isArray(items)) {
4984
+ console.error("\n\u274C \u7AD9\u70B9\u8FD4\u56DE\u7684\u5217\u8868\u683C\u5F0F\u5F02\u5E38\n");
4985
+ process.exit(1);
4986
+ }
4987
+ if (opts.json) {
4988
+ console.log(
4989
+ JSON.stringify(
4990
+ {
4991
+ site: { guid: target.site.guid, name: target.site.name, url: target.site.url },
4992
+ type: restBase,
4993
+ total: items.length,
4994
+ items: items.map((p) => ({
4995
+ id: p.id,
4996
+ title: wpRenderedText(p.title),
4997
+ status: p.status ?? "",
4998
+ slug: p.slug ?? "",
4999
+ link: p.link ?? "",
5000
+ modified: p.modified ?? ""
5001
+ }))
5002
+ },
5003
+ null,
5004
+ 2
5005
+ )
5006
+ );
5007
+ return;
5008
+ }
5009
+ if (items.length === 0) {
5010
+ console.log(`
5011
+ ${target.site.name} \u6CA1\u6709 ${restBase}\u3002
5012
+ `);
5013
+ return;
5014
+ }
5015
+ const columns = [
5016
+ { key: "id", header: "id" },
5017
+ { key: "title", header: "\u6807\u9898" },
5018
+ { key: "status", header: "\u72B6\u6001" },
5019
+ { key: "slug", header: "slug" },
5020
+ { key: "link", header: "\u94FE\u63A5" }
5021
+ ];
5022
+ console.log(`
5023
+ ${target.site.name} ${restBase}\uFF08${items.length}\uFF09
5024
+ `);
5025
+ printCliTable(
5026
+ items.map((p) => ({
5027
+ id: String(p.id),
5028
+ title: wpRenderedText(p.title) || "(\u65E0\u6807\u9898)",
5029
+ status: p.status ?? "-",
5030
+ slug: p.slug ?? "-",
5031
+ link: p.link ?? "-"
5032
+ })),
5033
+ columns
5034
+ );
5035
+ console.log();
5036
+ }
5037
+ async function runContentGet(opts) {
5038
+ const id = Number(opts.id);
5039
+ if (!Number.isFinite(id) || id <= 0) {
5040
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B\u6709\u6548 --id\n");
5041
+ process.exit(1);
5042
+ }
5043
+ const target = await resolveTarget(opts);
5044
+ const restBase = await resolveTypeRestBase(target, opts.type?.trim() || "posts", opts.verbose);
5045
+ const item = await apiFetch2(
5046
+ wpV2(target.base, `/${restBase}/${id}?context=edit`),
5047
+ target.config,
5048
+ {},
5049
+ opts.verbose
5050
+ ).catch((e) => {
5051
+ console.error(`
5052
+ \u274C \u83B7\u53D6\u5931\u8D25\uFF1A${e.message}
5053
+ `);
5054
+ process.exit(1);
5055
+ });
5056
+ if (opts.json) {
5057
+ console.log(JSON.stringify(item, null, 2));
5058
+ return;
5059
+ }
5060
+ console.log(`
5061
+ ${restBase} #${item.id}`);
5062
+ console.log(` \u6807\u9898 : ${wpRenderedText(item.title) || "-"}`);
5063
+ console.log(` \u72B6\u6001 : ${item.status || "-"}`);
5064
+ console.log(` slug : ${item.slug || "-"}`);
5065
+ console.log(` \u94FE\u63A5 : ${item.link || "-"}`);
5066
+ if (item.featured_media) console.log(` \u7279\u8272\u56FE : ${item.featured_media}`);
5067
+ const excerpt = wpRenderedText(item.excerpt);
5068
+ if (excerpt) console.log(` \u6458\u8981 : ${excerpt.slice(0, 120)}`);
5069
+ console.log();
5070
+ }
5071
+ async function runContentWrite(mode, opts) {
5072
+ const typeArg = opts.type?.trim() || "posts";
5073
+ if (typeArg === "page" || typeArg === "pages") {
5074
+ console.error("\n\u274C \u9875\u9762\u6B63\u6587\u8BF7\u7528 pages add / pages update\uFF08Zion visual-edit\uFF09\u3002");
5075
+ console.error(" \u53EA\u6539 slug/\u72B6\u6001/\u56DE\u6536\u7AD9\u7528 pages meta / pages trash\u3002\n");
5076
+ process.exit(1);
5077
+ }
5078
+ const target = await resolveTarget(opts);
5079
+ const restBase = await resolveTypeRestBase(target, typeArg, opts.verbose);
5080
+ const content = opts.contentFile ? fs7.readFileSync(opts.contentFile, "utf8") : opts.content ?? "";
5081
+ if (mode === "add" && !opts.title?.trim() && !content.trim()) {
5082
+ console.error("\n\u274C \u65B0\u589E\u81F3\u5C11\u8981 --title \u6216 --content / --content-file\n");
5083
+ process.exit(1);
5084
+ }
5085
+ const body = {};
5086
+ if (opts.title?.trim()) body.title = opts.title.trim();
5087
+ if (content.trim()) body.content = content;
5088
+ if (opts.status?.trim()) body.status = opts.status.trim();
5089
+ if (opts.slug?.trim()) body.slug = opts.slug.trim();
5090
+ if (opts.excerpt?.trim()) body.excerpt = opts.excerpt.trim();
5091
+ const featured = Number(opts.featuredMedia);
5092
+ if (Number.isFinite(featured) && featured > 0) body.featured_media = featured;
5093
+ const cats = parseIdList(opts.categories);
5094
+ if (cats) body.categories = cats;
5095
+ const tags = parseIdList(opts.tags);
5096
+ if (tags) body.tags = tags;
5097
+ if (mode === "update") {
5098
+ const id = Number(opts.id);
5099
+ if (!Number.isFinite(id) || id <= 0) {
5100
+ console.error("\n\u274C \u66F4\u65B0\u5FC5\u987B\u63D0\u4F9B --id\uFF08\u5148 content list\uFF09\n");
5101
+ process.exit(1);
5102
+ }
5103
+ if (Object.keys(body).length === 0) {
5104
+ console.error("\n\u274C \u6CA1\u6709\u8981\u6539\u7684\u5B57\u6BB5\n");
5105
+ process.exit(1);
5106
+ }
5107
+ const item2 = await apiFetch2(
5108
+ wpV2(target.base, `/${restBase}/${id}`),
5109
+ target.config,
5110
+ { method: "POST", body: JSON.stringify(body) },
5111
+ opts.verbose
5112
+ ).catch((e) => {
5113
+ console.error(`
5114
+ \u274C \u66F4\u65B0\u5931\u8D25\uFF1A${e.message}
5115
+ `);
5116
+ process.exit(1);
5117
+ });
5118
+ printPost(item2, restBase);
5119
+ return;
5120
+ }
5121
+ const item = await apiFetch2(
5122
+ wpV2(target.base, `/${restBase}`),
5123
+ target.config,
5124
+ { method: "POST", body: JSON.stringify(body) },
5125
+ opts.verbose
5126
+ ).catch((e) => {
5127
+ console.error(`
5128
+ \u274C \u65B0\u589E\u5931\u8D25\uFF1A${e.message}
5129
+ `);
5130
+ process.exit(1);
5131
+ });
5132
+ printPost(item, restBase);
5133
+ }
5134
+ async function runContentTrash(opts) {
5135
+ const id = Number(opts.id);
5136
+ if (!Number.isFinite(id) || id <= 0) {
5137
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id\n");
5138
+ process.exit(1);
5139
+ }
5140
+ const target = await resolveTarget(opts);
5141
+ const restBase = await resolveTypeRestBase(target, opts.type?.trim() || "posts", opts.verbose);
5142
+ const qs = opts.force ? "?force=true" : "";
5143
+ const item = await apiFetch2(
5144
+ wpV2(target.base, `/${restBase}/${id}${qs}`),
5145
+ target.config,
5146
+ { method: "DELETE" },
5147
+ opts.verbose
5148
+ ).catch((e) => {
5149
+ console.error(`
5150
+ \u274C \u5220\u9664\u5931\u8D25\uFF1A${e.message}
5151
+ `);
5152
+ process.exit(1);
5153
+ });
5154
+ console.log(opts.force ? `
5155
+ \u2705 \u5DF2\u6C38\u4E45\u5220\u9664 ${restBase} #${id}
5156
+ ` : `
5157
+ \u2705 \u5DF2\u79FB\u5165\u56DE\u6536\u7AD9 ${restBase} #${id}\uFF08status=${item.status || "trash"}\uFF09
5158
+ `);
5159
+ }
5160
+ function register8(program2) {
5161
+ 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");
5162
+ 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) => {
5163
+ await runContentTypes(opts);
5164
+ });
5165
+ content.command("list").description("\u5217\u51FA\u67D0\u4E00\u7C7B\u578B\u7684\u5185\u5BB9").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--type <restBase>", "\u7C7B\u578B rest_base\uFF0C\u9ED8\u8BA4 posts", "posts").option("--status <status>", "\u72B6\u6001\uFF0C\u9017\u53F7\u5206\u9694", "publish,draft,private,pending").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5166
+ await runContentList(opts);
5167
+ });
5168
+ content.command("get").description("\u67E5\u770B\u5355\u6761\u5185\u5BB9").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--type <restBase>", "\u7C7B\u578B rest_base\uFF0C\u9ED8\u8BA4 posts", "posts").requiredOption("--id <id>", "\u5185\u5BB9 ID").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5169
+ await runContentGet(opts);
5170
+ });
5171
+ content.command("add").description("\u65B0\u589E\u6587\u7AE0\u6216\u5176\u5B83 CPT\uFF08\u4E0D\u8981\u7528\u6765\u52A0 Zion \u9875\u9762\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--type <restBase>", "\u7C7B\u578B rest_base\uFF0C\u9ED8\u8BA4 posts", "posts").option("-t, --title <title>", "\u6807\u9898").option("-c, --content <html>", "\u6B63\u6587 HTML").option("--content-file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u6B63\u6587").option("--status <status>", "publish / draft / private", "draft").option("--slug <slug>", "\u522B\u540D").option("--excerpt <text>", "\u6458\u8981").option("--featured-media <id>", "\u7279\u8272\u56FE\u5A92\u4F53 ID").option("--categories <ids>", "\u5206\u7C7B ID\uFF0C\u9017\u53F7\u5206\u9694").option("--tags <ids>", "\u6807\u7B7E ID\uFF0C\u9017\u53F7\u5206\u9694").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5172
+ await runContentWrite("add", opts);
5173
+ });
5174
+ content.command("update").description("\u66F4\u65B0\u5DF2\u6709\u5185\u5BB9\u5B57\u6BB5").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--type <restBase>", "\u7C7B\u578B rest_base\uFF0C\u9ED8\u8BA4 posts", "posts").requiredOption("--id <id>", "\u5185\u5BB9 ID").option("-t, --title <title>", "\u6807\u9898").option("-c, --content <html>", "\u6B63\u6587 HTML").option("--content-file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u6B63\u6587").option("--status <status>", "publish / draft / private / pending").option("--slug <slug>", "\u522B\u540D").option("--excerpt <text>", "\u6458\u8981").option("--featured-media <id>", "\u7279\u8272\u56FE\u5A92\u4F53 ID").option("--categories <ids>", "\u5206\u7C7B ID\uFF0C\u9017\u53F7\u5206\u9694").option("--tags <ids>", "\u6807\u7B7E ID\uFF0C\u9017\u53F7\u5206\u9694").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5175
+ await runContentWrite("update", opts);
5176
+ });
5177
+ content.command("trash").description("\u79FB\u5165\u56DE\u6536\u7AD9\uFF1B\u52A0 --force \u6C38\u4E45\u5220\u9664").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--type <restBase>", "\u7C7B\u578B rest_base\uFF0C\u9ED8\u8BA4 posts", "posts").requiredOption("--id <id>", "\u5185\u5BB9 ID").option("--force", "\u6C38\u4E45\u5220\u9664", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5178
+ await runContentTrash(opts);
5179
+ });
5180
+ }
5181
+
5182
+ // src/commands/media.ts
5183
+ import { existsSync as existsSync4, readFileSync as readFileSync8 } from "fs";
5184
+ import { basename as basename3, extname, resolve as resolve3 } from "path";
5185
+ var MIME_BY_EXT = {
5186
+ ".jpg": "image/jpeg",
5187
+ ".jpeg": "image/jpeg",
5188
+ ".png": "image/png",
5189
+ ".gif": "image/gif",
5190
+ ".webp": "image/webp",
5191
+ ".svg": "image/svg+xml",
5192
+ ".pdf": "application/pdf",
5193
+ ".mp4": "video/mp4",
5194
+ ".zip": "application/zip"
5195
+ };
5196
+ var MAX_MEDIA_BYTES = 20 * 1024 * 1024;
5197
+ function guessMime(filename) {
5198
+ return MIME_BY_EXT[extname(filename).toLowerCase()] || "application/octet-stream";
5199
+ }
5200
+ async function runMediaList(opts) {
5201
+ const target = await resolveTarget(opts);
5202
+ let items;
5203
+ try {
5204
+ items = await apiFetch2(
5205
+ wpV2(target.base, "/media?per_page=50"),
5206
+ target.config,
5207
+ {},
5208
+ opts.verbose
5209
+ );
5210
+ } catch (e) {
5211
+ console.error(`
5212
+ \u274C \u83B7\u53D6\u5A92\u4F53\u5E93\u5931\u8D25\uFF1A${e.message}
5213
+ `);
5214
+ process.exit(1);
5215
+ return;
5216
+ }
5217
+ if (!Array.isArray(items)) {
5218
+ console.error("\n\u274C \u7AD9\u70B9\u8FD4\u56DE\u7684\u5A92\u4F53\u5217\u8868\u683C\u5F0F\u5F02\u5E38\n");
5219
+ process.exit(1);
5220
+ }
5221
+ if (opts.json) {
5222
+ console.log(
5223
+ JSON.stringify(
5224
+ {
5225
+ site: { guid: target.site.guid, name: target.site.name, url: target.site.url },
5226
+ total: items.length,
5227
+ items: items.map((m) => ({
5228
+ id: m.id,
5229
+ title: wpRenderedText(m.title),
5230
+ mime: m.mime_type ?? "",
5231
+ url: m.source_url ?? ""
5232
+ }))
5233
+ },
5234
+ null,
5235
+ 2
5236
+ )
5237
+ );
5238
+ return;
5239
+ }
5240
+ if (items.length === 0) {
5241
+ console.log(`
5242
+ ${target.site.name} \u5A92\u4F53\u5E93\u4E3A\u7A7A\u3002
5243
+ `);
5244
+ return;
5245
+ }
5246
+ console.log(`
5247
+ ${target.site.name} \u5A92\u4F53\uFF08\u6700\u8FD1 ${items.length}\uFF09
5248
+ `);
5249
+ printCliTable(
5250
+ items.map((m) => ({
5251
+ id: String(m.id),
5252
+ title: wpRenderedText(m.title) || "-",
5253
+ mime: m.mime_type || "-",
5254
+ url: m.source_url || "-"
5255
+ })),
5256
+ [
5257
+ { key: "id", header: "id" },
5258
+ { key: "title", header: "\u6807\u9898" },
5259
+ { key: "mime", header: "\u7C7B\u578B" },
5260
+ { key: "url", header: "\u5730\u5740" }
5261
+ ]
5262
+ );
5263
+ console.log();
5264
+ }
5265
+ async function runMediaGet(opts) {
5266
+ const id = Number(opts.id);
5267
+ if (!Number.isFinite(id) || id <= 0) {
5268
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id\n");
5269
+ process.exit(1);
5270
+ }
5271
+ const target = await resolveTarget(opts);
5272
+ const item = await apiFetch2(
5273
+ wpV2(target.base, `/media/${id}`),
5274
+ target.config,
5275
+ {},
5276
+ opts.verbose
5277
+ ).catch((e) => {
5278
+ console.error(`
5279
+ \u274C \u83B7\u53D6\u5A92\u4F53\u5931\u8D25\uFF1A${e.message}
5280
+ `);
5281
+ process.exit(1);
5282
+ });
5283
+ if (opts.json) {
5284
+ console.log(JSON.stringify(item, null, 2));
5285
+ return;
5286
+ }
5287
+ console.log(`
5288
+ \u5A92\u4F53 #${item.id}`);
5289
+ console.log(` \u6807\u9898 : ${wpRenderedText(item.title) || "-"}`);
5290
+ console.log(` \u7C7B\u578B : ${item.mime_type || "-"}`);
5291
+ console.log(` \u5730\u5740 : ${item.source_url || "-"}`);
5292
+ if (item.alt_text) console.log(` alt : ${item.alt_text}`);
5293
+ console.log();
5294
+ }
5295
+ async function runMediaUpload(opts) {
5296
+ const filePath = opts.file?.trim() ?? "";
5297
+ if (!filePath) {
5298
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --file\n");
5299
+ process.exit(1);
5300
+ }
5301
+ const abs = resolve3(filePath);
5302
+ if (!existsSync4(abs)) {
5303
+ console.error(`
5304
+ \u274C \u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${abs}
5305
+ `);
5306
+ process.exit(1);
5307
+ }
5308
+ const data = readFileSync8(abs);
5309
+ if (data.length > MAX_MEDIA_BYTES) {
5310
+ console.error("\n\u274C \u6587\u4EF6\u8D85\u8FC7 20MB\n");
5311
+ process.exit(1);
5312
+ }
5313
+ const filename = basename3(abs);
5314
+ const target = await resolveTarget(opts);
5315
+ const fields = {};
5316
+ if (opts.title?.trim()) fields.title = opts.title.trim();
5317
+ if (opts.alt?.trim()) fields.alt_text = opts.alt.trim();
5318
+ if (opts.caption?.trim()) fields.caption = opts.caption.trim();
5319
+ let item;
5320
+ try {
5321
+ item = await apiFetchMultipart2(
5322
+ wpV2(target.base, "/media"),
5323
+ target.config,
5324
+ {
5325
+ fields,
5326
+ files: [
5327
+ {
5328
+ field: "file",
5329
+ filename,
5330
+ contentType: guessMime(filename),
5331
+ data
5332
+ }
5333
+ ]
5334
+ },
5335
+ opts.verbose
5336
+ );
5337
+ } catch (e) {
5338
+ console.error(`
5339
+ \u274C \u4E0A\u4F20\u5931\u8D25\uFF1A${e.message}
5340
+ `);
5341
+ process.exit(1);
5342
+ return;
5343
+ }
5344
+ console.log(`
5345
+ \u2705 \u5DF2\u4E0A\u4F20\u5A92\u4F53 #${item.id}`);
5346
+ console.log(` \u5730\u5740 : ${item.source_url || "-"}`);
5347
+ console.log(` \u6807\u9898 : ${wpRenderedText(item.title) || filename}`);
5348
+ console.log();
5349
+ }
5350
+ function register9(program2) {
5351
+ const media = program2.command("media").description("\u5A92\u4F53\u5E93\uFF1A\u5217\u51FA\u3001\u67E5\u770B\u3001\u4E0A\u4F20");
5352
+ 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) => {
5353
+ await runMediaList(opts);
5354
+ });
5355
+ media.command("get").description("\u67E5\u770B\u5355\u4E2A\u5A92\u4F53").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").requiredOption("--id <id>", "\u5A92\u4F53 ID").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5356
+ await runMediaGet(opts);
5357
+ });
5358
+ media.command("upload").description("\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230\u5A92\u4F53\u5E93\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").requiredOption("--file <path>", "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84").option("-t, --title <title>", "\u5A92\u4F53\u6807\u9898").option("--alt <text>", "\u56FE\u7247 alt").option("--caption <text>", "\u8BF4\u660E").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5359
+ await runMediaUpload(opts);
5360
+ });
5361
+ }
5362
+
5363
+ // src/commands/terms.ts
5364
+ async function runTermsTaxonomies(opts) {
5365
+ const target = await resolveTarget(opts);
5366
+ let taxes;
5367
+ try {
5368
+ taxes = await apiFetch2(
5369
+ wpV2(target.base, "/taxonomies?context=edit"),
5370
+ target.config,
5371
+ {},
5372
+ opts.verbose
5373
+ );
5374
+ } catch (e) {
5375
+ console.error(`
5376
+ \u274C \u83B7\u53D6\u5206\u7C7B\u6CD5\u5931\u8D25\uFF1A${e.message}
5377
+ `);
5378
+ process.exit(1);
5379
+ return;
5380
+ }
5381
+ const rows = Object.entries(taxes || {}).map(([slug, info]) => ({
5382
+ slug,
5383
+ rest_base: info.rest_base || slug,
5384
+ name: info.name || slug
5385
+ }));
5386
+ if (opts.json) {
5387
+ console.log(JSON.stringify({ site: target.site, taxonomies: rows }, null, 2));
5388
+ return;
5389
+ }
5390
+ console.log(`
5391
+ ${target.site.name} \u5206\u7C7B\u6CD5\uFF08${rows.length}\uFF09
5392
+ `);
5393
+ printCliTable(rows, [
5394
+ { key: "slug", header: "slug" },
5395
+ { key: "rest_base", header: "--taxonomy" },
5396
+ { key: "name", header: "\u540D\u79F0" }
5397
+ ]);
5398
+ console.log();
5399
+ }
5400
+ async function runTermsList(opts) {
5401
+ const target = await resolveTarget(opts);
5402
+ const restBase = await resolveTaxonomyRestBase(target, opts.taxonomy || "category", opts.verbose);
5403
+ let items;
5404
+ try {
5405
+ items = await apiFetch2(
5406
+ wpV2(target.base, `/${restBase}?per_page=100`),
5407
+ target.config,
5408
+ {},
5409
+ opts.verbose
5410
+ );
5411
+ } catch (e) {
5412
+ console.error(`
5413
+ \u274C \u83B7\u53D6 ${restBase} \u5931\u8D25\uFF1A${e.message}
5414
+ `);
5415
+ process.exit(1);
5416
+ return;
5417
+ }
5418
+ if (!Array.isArray(items)) {
5419
+ console.error("\n\u274C \u7AD9\u70B9\u8FD4\u56DE\u7684\u672F\u8BED\u5217\u8868\u683C\u5F0F\u5F02\u5E38\n");
5420
+ process.exit(1);
5421
+ }
5422
+ if (opts.json) {
5423
+ console.log(JSON.stringify({ taxonomy: restBase, total: items.length, items }, null, 2));
5424
+ return;
5425
+ }
5426
+ if (items.length === 0) {
5427
+ console.log(`
5428
+ \u6CA1\u6709 ${restBase}\u3002
5429
+ `);
5430
+ return;
5431
+ }
5432
+ console.log(`
5433
+ ${target.site.name} ${restBase}\uFF08${items.length}\uFF09
5434
+ `);
5435
+ printCliTable(
5436
+ items.map((t) => ({
5437
+ id: String(t.id),
5438
+ name: t.name || "-",
5439
+ slug: t.slug || "-",
5440
+ count: String(t.count ?? 0)
5441
+ })),
5442
+ [
5443
+ { key: "id", header: "id" },
5444
+ { key: "name", header: "\u540D\u79F0" },
5445
+ { key: "slug", header: "slug" },
5446
+ { key: "count", header: "\u6570\u91CF" }
5447
+ ]
5448
+ );
5449
+ console.log();
5450
+ }
5451
+ async function runTermsAdd(opts) {
5452
+ const name = opts.name?.trim() ?? "";
5453
+ if (!name) {
5454
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --name\n");
5455
+ process.exit(1);
5456
+ }
5457
+ const target = await resolveTarget(opts);
5458
+ const restBase = await resolveTaxonomyRestBase(target, opts.taxonomy || "category", opts.verbose);
5459
+ const body = { name };
5460
+ if (opts.slug?.trim()) body.slug = opts.slug.trim();
5461
+ const parent = Number(opts.parent);
5462
+ if (Number.isFinite(parent) && parent > 0) body.parent = parent;
5463
+ const item = await apiFetch2(
5464
+ wpV2(target.base, `/${restBase}`),
5465
+ target.config,
5466
+ { method: "POST", body: JSON.stringify(body) },
5467
+ opts.verbose
5468
+ ).catch((e) => {
5469
+ console.error(`
5470
+ \u274C \u65B0\u589E\u5931\u8D25\uFF1A${e.message}
5471
+ `);
5472
+ process.exit(1);
5473
+ });
5474
+ console.log(`
5475
+ \u2705 \u5DF2\u65B0\u589E ${restBase} #${item.id} ${item.name || name}
5476
+ `);
5477
+ }
5478
+ async function runTermsUpdate(opts) {
5479
+ const id = Number(opts.id);
5480
+ if (!Number.isFinite(id) || id <= 0) {
5481
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id\n");
5482
+ process.exit(1);
5483
+ }
5484
+ const body = {};
5485
+ if (opts.name?.trim()) body.name = opts.name.trim();
5486
+ if (opts.slug?.trim()) body.slug = opts.slug.trim();
5487
+ if (Object.keys(body).length === 0) {
5488
+ console.error("\n\u274C \u6CA1\u6709\u8981\u6539\u7684\u5B57\u6BB5\n");
5489
+ process.exit(1);
5490
+ }
5491
+ const target = await resolveTarget(opts);
5492
+ const restBase = await resolveTaxonomyRestBase(target, opts.taxonomy || "category", opts.verbose);
5493
+ const item = await apiFetch2(
5494
+ wpV2(target.base, `/${restBase}/${id}`),
5495
+ target.config,
5496
+ { method: "POST", body: JSON.stringify(body) },
5497
+ opts.verbose
5498
+ ).catch((e) => {
5499
+ console.error(`
5500
+ \u274C \u66F4\u65B0\u5931\u8D25\uFF1A${e.message}
5501
+ `);
5502
+ process.exit(1);
5503
+ });
5504
+ console.log(`
5505
+ \u2705 \u5DF2\u66F4\u65B0 ${restBase} #${item.id} ${item.name || ""}
5506
+ `);
5507
+ }
5508
+ async function runTermsDelete(opts) {
5509
+ const id = Number(opts.id);
5510
+ if (!Number.isFinite(id) || id <= 0) {
5511
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --id\n");
5512
+ process.exit(1);
5513
+ }
5514
+ const target = await resolveTarget(opts);
5515
+ const restBase = await resolveTaxonomyRestBase(target, opts.taxonomy || "category", opts.verbose);
5516
+ await apiFetch2(
5517
+ wpV2(target.base, `/${restBase}/${id}?force=true`),
5518
+ target.config,
5519
+ { method: "DELETE" },
5520
+ opts.verbose
5521
+ ).catch((e) => {
5522
+ console.error(`
5523
+ \u274C \u5220\u9664\u5931\u8D25\uFF1A${e.message}
5524
+ `);
5525
+ process.exit(1);
5526
+ });
5527
+ console.log(`
5528
+ \u2705 \u5DF2\u5220\u9664 ${restBase} #${id}
5529
+ `);
5530
+ }
5531
+ function register10(program2) {
5532
+ const terms = program2.command("terms").description("\u5206\u7C7B / \u6807\u7B7E / \u81EA\u5B9A\u4E49\u5206\u7C7B\u6CD5");
5533
+ 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) => {
5534
+ await runTermsTaxonomies(opts);
5535
+ });
5536
+ terms.command("list").description("\u5217\u51FA\u67D0\u5206\u7C7B\u6CD5\u4E0B\u7684\u672F\u8BED").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--taxonomy <slug>", "category / post_tag / \u5176\u5B83 rest_base", "category").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5537
+ await runTermsList(opts);
5538
+ });
5539
+ terms.command("add").description("\u65B0\u589E\u672F\u8BED\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("--taxonomy <slug>", "\u9ED8\u8BA4 category", "category").requiredOption("-n, --name <name>", "\u540D\u79F0").option("--slug <slug>", "\u522B\u540D").option("--parent <id>", "\u7236\u7EA7 ID").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5540
+ await runTermsAdd(opts);
5541
+ });
5542
+ terms.command("update").description("\u66F4\u65B0\u672F\u8BED\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("--taxonomy <slug>", "\u9ED8\u8BA4 category", "category").requiredOption("--id <id>", "\u672F\u8BED ID").option("-n, --name <name>", "\u540D\u79F0").option("--slug <slug>", "\u522B\u540D").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5543
+ await runTermsUpdate(opts);
5544
+ });
5545
+ terms.command("delete").description("\u5220\u9664\u672F\u8BED\uFF08\u5199\u64CD\u4F5C\uFF0C\u4E0D\u53EF\u8FDB\u56DE\u6536\u7AD9\uFF09").option("-s, --site <guidOrName>", "\u7AD9\u70B9 guid \u6216\u540D\u79F0").option("--url <url>", "\u76F4\u63A5\u6307\u5B9A\u7AD9\u70B9\u5730\u5740").option("--taxonomy <slug>", "\u9ED8\u8BA4 category", "category").requiredOption("--id <id>", "\u672F\u8BED ID").option("--verbose", "\u8F93\u51FA\u8BF7\u6C42\u8BE6\u60C5", false).action(async (opts) => {
5546
+ await runTermsDelete(opts);
5547
+ });
5548
+ }
5549
+
5550
+ // src/commands/search.ts
5551
+ async function runSearch(opts) {
5552
+ const q = opts.q?.trim() ?? "";
5553
+ if (!q) {
5554
+ console.error("\n\u274C \u8BF7\u63D0\u4F9B --q\n");
5555
+ process.exit(1);
5556
+ }
5557
+ const target = await resolveTarget(opts);
5558
+ const params = new URLSearchParams({
5559
+ search: q,
5560
+ per_page: "20"
5561
+ });
5562
+ if (opts.type?.trim()) params.set("subtype", opts.type.trim());
5563
+ let hits;
5564
+ try {
5565
+ hits = await apiFetch2(
5566
+ wpV2(target.base, `/search?${params.toString()}`),
5567
+ target.config,
5568
+ {},
5569
+ opts.verbose
5570
+ );
5571
+ } catch (e) {
5572
+ console.error(`
5573
+ \u274C \u641C\u7D22\u5931\u8D25\uFF1A${e.message}
5574
+ `);
5575
+ process.exit(1);
5576
+ return;
5577
+ }
5578
+ if (!Array.isArray(hits)) {
5579
+ console.error("\n\u274C \u7AD9\u70B9\u8FD4\u56DE\u7684\u641C\u7D22\u7ED3\u679C\u683C\u5F0F\u5F02\u5E38\n");
5580
+ process.exit(1);
5581
+ }
5582
+ if (opts.json) {
5583
+ console.log(JSON.stringify({ q, total: hits.length, hits }, null, 2));
5584
+ return;
5585
+ }
5586
+ if (hits.length === 0) {
5587
+ console.log(`
5588
+ \u6CA1\u6709\u5339\u914D\u300C${q}\u300D\u7684\u7ED3\u679C\u3002
5589
+ `);
5590
+ return;
5591
+ }
5592
+ console.log(`
5593
+ ${target.site.name} \u641C\u7D22\u300C${q}\u300D\uFF08${hits.length}\uFF09
5594
+ `);
5595
+ printCliTable(
5596
+ hits.map((h) => ({
5597
+ id: h.id != null ? String(h.id) : "-",
5598
+ title: h.title || "-",
5599
+ type: [h.type, h.subtype].filter(Boolean).join("/") || "-",
5600
+ url: h.url || "-"
5601
+ })),
5602
+ [
5603
+ { key: "id", header: "id" },
5604
+ { key: "title", header: "\u6807\u9898" },
5605
+ { key: "type", header: "\u7C7B\u578B" },
5606
+ { key: "url", header: "\u94FE\u63A5" }
5607
+ ]
5608
+ );
5609
+ console.log();
5610
+ }
5611
+ function register11(program2) {
5612
+ 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) => {
5613
+ await runSearch(opts);
4394
5614
  });
4395
5615
  }
4396
5616
 
4397
5617
  // src/index.ts
4398
5618
  installProcessHandlers();
4399
5619
  var program = new Command();
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());
5620
+ 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").version(getCurrentVersion2());
4401
5621
  var REGISTRARS = [
4402
5622
  register,
4403
5623
  register2,
@@ -4405,6 +5625,10 @@ var REGISTRARS = [
4405
5625
  register4,
4406
5626
  register5,
4407
5627
  register6,
5628
+ register8,
5629
+ register9,
5630
+ register10,
5631
+ register11,
4408
5632
  register7
4409
5633
  ];
4410
5634
  for (const reg of REGISTRARS) reg(program);