siluzan-cso-cli 1.0.0-beta.29 → 1.0.0-beta.30

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
@@ -20,7 +20,7 @@ siluzan-cso init -d /path/to/skills # 写入自定义目录
20
20
  siluzan-cso init --force # 强制覆盖已存在文件
21
21
  ```
22
22
 
23
- > **注意**:当前为测试版(1.0.0-beta.29),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
23
+ > **注意**:当前为测试版(1.0.0-beta.30),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
24
24
 
25
25
  | 助手 | 建议 `--ai` |
26
26
  |------|-------------|
package/dist/index.js CHANGED
@@ -2971,7 +2971,7 @@ async function runUpdate(options) {
2971
2971
  console.log(`
2972
2972
  \u5F53\u524D\u7248\u672C\uFF1A${current}`);
2973
2973
  const npmTag = npmDistTagForBuildEnv(BUILD_ENV);
2974
- console.log(`\u6B63\u5728\u67E5\u8BE2 npm registry\uFF08\u6784\u5EFA\uFF1A${BUILD_ENV}\uFF0Cdist-tag\uFF1A${npmTag}\uFF09\u2026`);
2974
+ console.log("\u6B63\u5728\u67E5\u8BE2 npm registry\u2026");
2975
2975
  const latest = await fetchLatestVersion();
2976
2976
  if (!latest) {
2977
2977
  console.warn("\u26A0\uFE0F \u65E0\u6CD5\u8BBF\u95EE npm registry\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u3002");
@@ -3137,9 +3137,30 @@ function printCliTable(rows, columns, options) {
3137
3137
  }
3138
3138
 
3139
3139
  // src/commands/list-accounts.ts
3140
- function redact(value) {
3141
- if (value.length <= 8) return "****";
3142
- return `${value.slice(0, 4)}****${value.slice(-4)}`;
3140
+ var MEDIA_TYPE_ALIAS = {
3141
+ tiktok: "TikTokBusinessAccount",
3142
+ tiktokbusinessaccount: "TikTokBusinessAccount",
3143
+ douyin: "Douyin",
3144
+ \u6296\u97F3: "Douyin",
3145
+ youtube: "YouTube",
3146
+ wechat: "WeChatChannel",
3147
+ wechatchannel: "WeChatChannel",
3148
+ \u89C6\u9891\u53F7: "WeChatChannel",
3149
+ \u5FAE\u4FE1\u89C6\u9891\u53F7: "WeChatChannel",
3150
+ instagram: "Instagram",
3151
+ ig: "Instagram",
3152
+ facebook: "Facebook",
3153
+ fb: "Facebook",
3154
+ twitter: "Twitter",
3155
+ x: "Twitter",
3156
+ kuaishou: "Kuaishou",
3157
+ \u5FEB\u624B: "Kuaishou",
3158
+ linkedin: "Linkedin",
3159
+ kwai: "Kuaishou"
3160
+ };
3161
+ function normalizeMediaType(input) {
3162
+ if (!input) return input;
3163
+ return MEDIA_TYPE_ALIAS[input.toLowerCase()] ?? MEDIA_TYPE_ALIAS[input] ?? input;
3143
3164
  }
3144
3165
  function formatOwners(ownerInfo) {
3145
3166
  if (!ownerInfo || ownerInfo.length === 0) return "-";
@@ -3165,6 +3186,15 @@ function formatDate(iso) {
3165
3186
  if (!iso) return "-";
3166
3187
  return iso.slice(0, 10);
3167
3188
  }
3189
+ function groupByPlatform(list) {
3190
+ const map = /* @__PURE__ */ new Map();
3191
+ for (const account of list) {
3192
+ const platform = account.mediaAccountType || "\u672A\u77E5\u5E73\u53F0";
3193
+ if (!map.has(platform)) map.set(platform, []);
3194
+ map.get(platform).push(account);
3195
+ }
3196
+ return Array.from(map.entries());
3197
+ }
3168
3198
  async function runListAccounts(options) {
3169
3199
  const config = loadConfig(options.token);
3170
3200
  const isDomestic = options.domestic ?? false;
@@ -3189,7 +3219,7 @@ async function runListAccounts(options) {
3189
3219
  pageNo: String(pageNo),
3190
3220
  pageSize: String(pageSize),
3191
3221
  ...options.name ? { mediaCustomerName: options.name } : {},
3192
- ...options.mediaType ? { mediaType: options.mediaType } : {},
3222
+ ...options.mediaType ? { mediaType: normalizeMediaType(options.mediaType) } : {},
3193
3223
  ...options.owner ? { owner: options.owner } : {},
3194
3224
  ...stateParams
3195
3225
  });
@@ -3226,7 +3256,7 @@ async function runListAccounts(options) {
3226
3256
  groupName: a.groupName ?? "",
3227
3257
  imageUrl: a.imageUrl ?? null,
3228
3258
  overview: a.mediaAccountOverviewDataDto ?? null,
3229
- externalMediaAccountTokenId: options.verbose ? a.externalMediaAccountTokenId : redact(a.externalMediaAccountTokenId)
3259
+ externalMediaAccountTokenId: a.externalMediaAccountTokenId
3230
3260
  }));
3231
3261
  console.log(JSON.stringify(outputAccounts, null, 2));
3232
3262
  return;
@@ -3239,12 +3269,12 @@ async function runListAccounts(options) {
3239
3269
  const totalPages = Math.ceil(total / pageSize);
3240
3270
  const pageInfo = `\u7B2C ${pageNo} \u9875 / \u5171 ${totalPages} \u9875\uFF0C\u603B\u8BA1 ${total} \u6761`;
3241
3271
  const tableOpts2 = options.unicode ? { plain: false } : void 0;
3242
- if (options.overview) {
3272
+ const groups = groupByPlatform(list);
3273
+ const isOverview = options.overview ?? true;
3274
+ if (isOverview) {
3243
3275
  console.log(`
3244
- \u8D26\u53F7\u6570\u636E\u603B\u89C8\uFF08${tradeLabel}\uFF0C${pageInfo}\uFF09
3245
- `);
3276
+ \u8D26\u53F7\u6570\u636E\u603B\u89C8\uFF08${tradeLabel}\uFF0C${pageInfo}\uFF09`);
3246
3277
  const overviewColumns = [
3247
- { key: "mediaAccountType", header: "\u5E73\u53F0" },
3248
3278
  { key: "mediaCustomerName", header: "\u8D26\u53F7\u540D\u79F0" },
3249
3279
  { key: "state", header: "\u72B6\u6001" },
3250
3280
  { key: "fansCount", header: "\u7C89\u4E1D\u6570" },
@@ -3253,26 +3283,28 @@ async function runListAccounts(options) {
3253
3283
  { key: "commentCount", header: "\u8BC4\u8BBA\u6570" },
3254
3284
  { key: "diggCount", header: "\u83B7\u8D5E\u6570" }
3255
3285
  ];
3256
- const overviewRows = list.map((a) => {
3257
- const ov = a.mediaAccountOverviewDataDto;
3258
- return {
3259
- mediaAccountType: a.mediaAccountType,
3260
- mediaCustomerName: a.mediaCustomerName,
3261
- state: formatState(a.mediaAccountState, a.invalidOAuthToken),
3262
- fansCount: formatCount(ov?.fansCount),
3263
- videoCount: formatCount(ov?.videoCount),
3264
- playCount: formatCount(ov?.playCount),
3265
- commentCount: formatCount(ov?.commentCount),
3266
- diggCount: formatCount(ov?.diggCount)
3267
- };
3268
- });
3269
- printCliTable(overviewRows, overviewColumns, tableOpts2);
3286
+ for (const [platform, accounts] of groups) {
3287
+ console.log(`
3288
+ \u25AA ${platform}\uFF08${accounts.length} \u4E2A\u8D26\u53F7\uFF09
3289
+ `);
3290
+ const rows = accounts.map((a) => {
3291
+ const ov = a.mediaAccountOverviewDataDto;
3292
+ return {
3293
+ mediaCustomerName: a.mediaCustomerName,
3294
+ state: formatState(a.mediaAccountState, a.invalidOAuthToken),
3295
+ fansCount: formatCount(ov?.fansCount),
3296
+ videoCount: formatCount(ov?.videoCount),
3297
+ playCount: formatCount(ov?.playCount),
3298
+ commentCount: formatCount(ov?.commentCount),
3299
+ diggCount: formatCount(ov?.diggCount)
3300
+ };
3301
+ });
3302
+ printCliTable(rows, overviewColumns, tableOpts2);
3303
+ }
3270
3304
  } else {
3271
3305
  console.log(`
3272
- \u5A92\u4F53\u8D26\u53F7\u5217\u8868\uFF08${tradeLabel}\uFF0C${pageInfo}\uFF09
3273
- `);
3306
+ \u5A92\u4F53\u8D26\u53F7\u5217\u8868\uFF08${tradeLabel}\uFF0C${pageInfo}\uFF09`);
3274
3307
  const columns = [
3275
- { key: "mediaAccountType", header: "\u5E73\u53F0" },
3276
3308
  { key: "mediaCustomerName", header: "\u8D26\u53F7\u540D\u79F0" },
3277
3309
  { key: "mediaCustomerId", header: "\u8D26\u53F7ID" },
3278
3310
  { key: "state", header: "\u72B6\u6001" },
@@ -3280,28 +3312,27 @@ async function runListAccounts(options) {
3280
3312
  { key: "tokenExpiry", header: "Token\u5230\u671F" },
3281
3313
  { key: "owners", header: "\u8D1F\u8D23\u4EBA" }
3282
3314
  ];
3283
- const rows = list.map((a) => ({
3284
- mediaAccountType: a.mediaAccountType,
3285
- mediaCustomerName: a.mediaCustomerName,
3286
- mediaCustomerId: a.mediaCustomerId,
3287
- state: formatState(a.mediaAccountState, a.invalidOAuthToken),
3288
- lastAuthTime: formatDate(a.lastAuthorizationTime),
3289
- // expiresOn 比 tokenTime 更准确,优先使用
3290
- tokenExpiry: formatDate(a.expiresOn ?? a.tokenTime),
3291
- owners: formatOwners(a.ownerInfo)
3292
- }));
3293
- printCliTable(rows, columns, tableOpts2);
3315
+ for (const [platform, accounts] of groups) {
3316
+ console.log(`
3317
+ \u25AA ${platform}\uFF08${accounts.length} \u4E2A\u8D26\u53F7\uFF09
3318
+ `);
3319
+ const rows = accounts.map((a) => ({
3320
+ mediaCustomerName: a.mediaCustomerName,
3321
+ mediaCustomerId: a.mediaCustomerId,
3322
+ state: formatState(a.mediaAccountState, a.invalidOAuthToken),
3323
+ lastAuthTime: formatDate(a.lastAuthorizationTime),
3324
+ // expiresOn 比 tokenTime 更准确,优先使用
3325
+ tokenExpiry: formatDate(a.expiresOn ?? a.tokenTime),
3326
+ owners: formatOwners(a.ownerInfo)
3327
+ }));
3328
+ printCliTable(rows, columns, tableOpts2);
3329
+ }
3294
3330
  }
3295
3331
  if (total > pageNo * pageSize) {
3296
3332
  console.log(`
3297
3333
  \u63D0\u793A\uFF1A\u8FD8\u6709\u66F4\u591A\u8D26\u53F7\uFF0C\u4F7F\u7528 --page ${pageNo + 1} \u53EF\u67E5\u770B\u4E0B\u4E00\u9875\u3002`);
3298
3334
  }
3299
- if (options.overview) {
3300
- console.log("\u63D0\u793A\uFF1A\u4F7F\u7528 --json \u53EF\u83B7\u53D6\u5B8C\u6574 JSON \u6570\u636E\uFF08\u542B mediaCustomerId \u7B49\u5B57\u6BB5\uFF09\u3002");
3301
- } else {
3302
- console.log("\u63D0\u793A\uFF1A\u4F7F\u7528 --overview \u53EF\u67E5\u770B\u8D26\u53F7\u7684\u7C89\u4E1D\u6570/\u4F5C\u54C1\u6570/\u64AD\u653E\u6570\u7B49\u6570\u636E\u603B\u89C8\u3002");
3303
- console.log("\u63D0\u793A\uFF1A\u4F7F\u7528 --json \u53EF\u83B7\u53D6\u5B8C\u6574 JSON \u6570\u636E\uFF0C\u7528\u4E8E\u53D1\u5E03\u914D\u7F6E\u7B49\u573A\u666F\u3002");
3304
- }
3335
+ console.log("\u63D0\u793A\uFF1A\u4F7F\u7528 --json \u53EF\u83B7\u53D6\u5B8C\u6574 JSON \u6570\u636E\uFF08\u542B mediaCustomerId \u7B49\u5B57\u6BB5\uFF09\u3002");
3305
3336
  }
3306
3337
 
3307
3338
  // src/commands/list-members.ts
@@ -3545,6 +3576,9 @@ function nowUtc() {
3545
3576
  return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
3546
3577
  }
3547
3578
  var guid = randomUUID;
3579
+ function isGuidFormat(s) {
3580
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);
3581
+ }
3548
3582
  function defaultTaskName() {
3549
3583
  const d = /* @__PURE__ */ new Date();
3550
3584
  const pad = (n) => String(n).padStart(2, "0");
@@ -3619,6 +3653,7 @@ function finalizeChannels(channels, sentKey) {
3619
3653
  ch.publishAts = chSendType > 1 && chPublishAt ? [chPublishAt] : [];
3620
3654
  }
3621
3655
  }
3656
+ var UUID_HINT = "\uFF08\u8BF7\u5148\u8FD0\u884C `siluzan-cso list-accounts --json` \u83B7\u53D6\u6B63\u786E\u7684 UUID \u503C\uFF0C\u4E0D\u8981\u4F7F\u7528\u5360\u4F4D\u7B26\u6587\u672C\uFF09";
3622
3657
  function buildVideoChannels(videos, currentUserId) {
3623
3658
  const channels = {};
3624
3659
  const contents = [];
@@ -3628,8 +3663,23 @@ function buildVideoChannels(videos, currentUserId) {
3628
3663
  if (!item.accounts?.length) errorList.push(`\u89C6\u9891 "${item.videoName}" \u672A\u8BBE\u7F6E\u8D26\u53F7`);
3629
3664
  if (!item.title) errorList.push(`\u89C6\u9891 "${item.videoName}" \u672A\u586B\u5199\u6807\u9898`);
3630
3665
  if (!item.cover?.imageUrl) errorList.push(`\u89C6\u9891 "${item.videoName}" \u5C01\u9762\u53C2\u6570\u7F3A\u5931`);
3666
+ if (!isGuidFormat(item.videoId)) {
3667
+ errorList.push(
3668
+ `\u89C6\u9891 "${item.videoName}" \u7684 videoId "${item.videoId}" \u4E0D\u662F\u5408\u6CD5 UUID \u683C\u5F0F ${UUID_HINT}`
3669
+ );
3670
+ }
3631
3671
  let itemSendType = 1;
3632
3672
  for (const account of item.accounts ?? []) {
3673
+ if (!isGuidFormat(account.entityId)) {
3674
+ errorList.push(
3675
+ `\u8D26\u53F7 "${account.mediaCustomerName}" \u7684 entityId "${account.entityId}" \u4E0D\u662F\u5408\u6CD5 UUID \u683C\u5F0F ${UUID_HINT}`
3676
+ );
3677
+ }
3678
+ if (!isGuidFormat(account.externalMediaAccountTokenId)) {
3679
+ errorList.push(
3680
+ `\u8D26\u53F7 "${account.mediaCustomerName}" \u7684 externalMediaAccountTokenId "${account.externalMediaAccountTokenId}" \u4E0D\u662F\u5408\u6CD5 UUID \u683C\u5F0F ${UUID_HINT}`
3681
+ );
3682
+ }
3633
3683
  const mediaType = account.mediaAccountType;
3634
3684
  const sendType = toSendType(item.publishMode);
3635
3685
  const publishAtUtc = item.publishAt ? new Date(item.publishAt).toISOString() : null;
@@ -3729,6 +3779,16 @@ function buildImageChannels(posts, currentUserId) {
3729
3779
  if (!item.accounts?.length) errorList.push("\u56FE\u6587\u5E16\u5B50\u672A\u8BBE\u7F6E\u8D26\u53F7");
3730
3780
  let itemSendType = 1;
3731
3781
  for (const account of item.accounts ?? []) {
3782
+ if (!isGuidFormat(account.entityId)) {
3783
+ errorList.push(
3784
+ `\u8D26\u53F7 "${account.mediaCustomerName}" \u7684 entityId "${account.entityId}" \u4E0D\u662F\u5408\u6CD5 UUID \u683C\u5F0F ${UUID_HINT}`
3785
+ );
3786
+ }
3787
+ if (!isGuidFormat(account.externalMediaAccountTokenId)) {
3788
+ errorList.push(
3789
+ `\u8D26\u53F7 "${account.mediaCustomerName}" \u7684 externalMediaAccountTokenId "${account.externalMediaAccountTokenId}" \u4E0D\u662F\u5408\u6CD5 UUID \u683C\u5F0F ${UUID_HINT}`
3790
+ );
3791
+ }
3732
3792
  const mediaType = account.mediaAccountType;
3733
3793
  const sendType = toSendType(item.publishMode);
3734
3794
  const publishAtUtc = item.publishAt ? new Date(item.publishAt).toISOString() : null;
@@ -3777,6 +3837,127 @@ function buildImageChannels(posts, currentUserId) {
3777
3837
  finalizeChannels(channels, "imageTextToBeSent");
3778
3838
  return { channels, videoType: globalSendType, contents, errorList };
3779
3839
  }
3840
+ function formatTopics(topics) {
3841
+ if (!topics?.length) return "";
3842
+ return topics.map((t) => `#${t}`).join(" ");
3843
+ }
3844
+ function formatPublishMode(mode, publishAt) {
3845
+ if (mode === "scheduled") {
3846
+ return publishAt ? `\u5B9A\u65F6\u53D1\u5E03\uFF08${publishAt}\uFF09` : "\u5B9A\u65F6\u53D1\u5E03\uFF08\u26A0\uFE0F \u672A\u586B\u5199 publishAt\uFF09";
3847
+ }
3848
+ return "\u7ACB\u5373\u53D1\u5E03";
3849
+ }
3850
+ function printDryRun(input, taskName, isDomestic, body, verbose) {
3851
+ const SEP = "\u2500".repeat(54);
3852
+ console.log("\n\u2705 [dry-run] \u53D1\u5E03\u53C2\u6570\u9884\u89C8\uFF08\u672A\u5B9E\u9645\u63D0\u4EA4\uFF09\n");
3853
+ console.log(`\u4EFB\u52A1\u540D \uFF1A${taskName}`);
3854
+ console.log(`\u5185\u5BB9\u7C7B\u578B \uFF1A${input.contentType === 1 ? "\u89C6\u9891" : "\u56FE\u6587"}`);
3855
+ console.log(`\u5E73\u53F0\u6A21\u5F0F \uFF1A${isDomestic ? "\u5185\u8D38" : "\u5916\u8D38"}`);
3856
+ if (input.contentType === 1) {
3857
+ const items = input.videos;
3858
+ console.log(`\u89C6\u9891\u6570\u91CF \uFF1A${items.length} \u4E2A`);
3859
+ for (let i = 0; i < items.length; i++) {
3860
+ const item = items[i];
3861
+ console.log(`
3862
+ ${SEP}`);
3863
+ console.log(` \u89C6\u9891 ${i + 1} / ${items.length}\uFF1A${item.videoName}`);
3864
+ console.log(SEP);
3865
+ console.log(` \u89C6\u9891 ID \uFF1A${item.videoId}`);
3866
+ console.log(` \u6807\u9898 \uFF1A${item.title || "\u26A0\uFE0F \u672A\u586B\u5199"}`);
3867
+ if (item.description) {
3868
+ console.log(` \u63CF\u8FF0 \uFF1A${item.description}`);
3869
+ }
3870
+ const topicsStr = formatTopics(item.topics);
3871
+ if (topicsStr) {
3872
+ console.log(` \u6807\u7B7E \uFF1A${topicsStr}`);
3873
+ }
3874
+ console.log(` \u53D1\u5E03\u6A21\u5F0F \uFF1A${formatPublishMode(item.publishMode, item.publishAt)}`);
3875
+ console.log(` \u5C01\u9762 URL \uFF1A${item.cover?.imageUrl || "\u26A0\uFE0F \u672A\u8BBE\u7F6E"}`);
3876
+ if (item.cover?.sourceImageId) {
3877
+ console.log(` \u5C01\u9762 ID \uFF1A${item.cover.sourceImageId}`);
3878
+ }
3879
+ const byPlatform = /* @__PURE__ */ new Map();
3880
+ for (const acc of item.accounts ?? []) {
3881
+ const list = byPlatform.get(acc.mediaAccountType) ?? [];
3882
+ list.push(acc);
3883
+ byPlatform.set(acc.mediaAccountType, list);
3884
+ }
3885
+ const totalAccounts = item.accounts?.length ?? 0;
3886
+ console.log(`
3887
+ \u8D26\u53F7\uFF08${totalAccounts} \u4E2A\uFF0C${byPlatform.size} \u4E2A\u5E73\u53F0\uFF09\uFF1A`);
3888
+ for (const [platform, accounts] of byPlatform) {
3889
+ const override = item.platformOverrides?.[platform];
3890
+ console.log(`
3891
+ \u25AA ${platform}`);
3892
+ if (override) {
3893
+ const pt = buildPublishTitle(item, platform, override);
3894
+ if (override.title) console.log(` \u6807\u9898\u8986\u76D6 \uFF1A${pt.title}`);
3895
+ if (override.description) console.log(` \u63CF\u8FF0\u8986\u76D6 \uFF1A${pt.description}`);
3896
+ const ovTopics = formatTopics(override.topics);
3897
+ if (ovTopics) console.log(` \u6807\u7B7E\u8986\u76D6 \uFF1A${ovTopics}`);
3898
+ }
3899
+ for (const acc of accounts) {
3900
+ console.log(` \xB7 ${acc.mediaCustomerName}\uFF08${acc.mediaCustomerId}\uFF09`);
3901
+ }
3902
+ }
3903
+ }
3904
+ console.log(`
3905
+ ${SEP}`);
3906
+ } else {
3907
+ const items = input.posts;
3908
+ console.log(`\u56FE\u6587\u6570\u91CF \uFF1A${items.length} \u4E2A`);
3909
+ for (let i = 0; i < items.length; i++) {
3910
+ const item = items[i];
3911
+ console.log(`
3912
+ ${SEP}`);
3913
+ console.log(` \u56FE\u6587 ${i + 1} / ${items.length}`);
3914
+ console.log(SEP);
3915
+ console.log(` \u6807\u9898 \uFF1A${item.title || "\u26A0\uFE0F \u672A\u586B\u5199"}`);
3916
+ if (item.description) {
3917
+ console.log(` \u63CF\u8FF0 \uFF1A${item.description}`);
3918
+ }
3919
+ const topicsStr = formatTopics(item.topics);
3920
+ if (topicsStr) {
3921
+ console.log(` \u6807\u7B7E \uFF1A${topicsStr}`);
3922
+ }
3923
+ console.log(` \u56FE\u7247\u6570\u91CF \uFF1A${item.images?.length ?? 0} \u5F20`);
3924
+ console.log(` \u53D1\u5E03\u6A21\u5F0F \uFF1A${formatPublishMode(item.publishMode, item.publishAt)}`);
3925
+ const byPlatform = /* @__PURE__ */ new Map();
3926
+ for (const acc of item.accounts ?? []) {
3927
+ const list = byPlatform.get(acc.mediaAccountType) ?? [];
3928
+ list.push(acc);
3929
+ byPlatform.set(acc.mediaAccountType, list);
3930
+ }
3931
+ const totalAccounts = item.accounts?.length ?? 0;
3932
+ console.log(`
3933
+ \u8D26\u53F7\uFF08${totalAccounts} \u4E2A\uFF0C${byPlatform.size} \u4E2A\u5E73\u53F0\uFF09\uFF1A`);
3934
+ for (const [platform, accounts] of byPlatform) {
3935
+ const override = item.platformOverrides?.[platform];
3936
+ console.log(`
3937
+ \u25AA ${platform}`);
3938
+ if (override) {
3939
+ const pt = buildPublishTitle(item, platform, override, true);
3940
+ if (override.title) console.log(` \u6807\u9898\u8986\u76D6 \uFF1A${pt.title}`);
3941
+ if (override.description) console.log(` \u63CF\u8FF0\u8986\u76D6 \uFF1A${pt.description}`);
3942
+ const ovTopics = formatTopics(override.topics);
3943
+ if (ovTopics) console.log(` \u6807\u7B7E\u8986\u76D6 \uFF1A${ovTopics}`);
3944
+ }
3945
+ for (const acc of accounts) {
3946
+ console.log(` \xB7 ${acc.mediaCustomerName}\uFF08${acc.mediaCustomerId}\uFF09`);
3947
+ }
3948
+ }
3949
+ }
3950
+ console.log(`
3951
+ ${SEP}`);
3952
+ }
3953
+ if (verbose) {
3954
+ const verboseBody = { ...body, contents: JSON.parse(body.contents) };
3955
+ console.log("\n\u5B8C\u6574 JSON \u8BF7\u6C42\u4F53\uFF08--verbose\uFF0Ccontents \u5DF2\u5C55\u5F00\uFF09\uFF1A\n");
3956
+ console.log(JSON.stringify(verboseBody, null, 2));
3957
+ } else {
3958
+ console.log("\n\u2139\uFE0F \u52A0 --verbose \u53C2\u6570\u53EF\u67E5\u770B\u5B8C\u6574 JSON \u8BF7\u6C42\u4F53\u3002");
3959
+ }
3960
+ }
3780
3961
  async function runPublish(options) {
3781
3962
  const configPath = path6.resolve(options.config);
3782
3963
  if (!fs6.existsSync(configPath)) {
@@ -3840,24 +4021,7 @@ async function runPublish(options) {
3840
4021
  version: "v3"
3841
4022
  };
3842
4023
  if (options.dryRun) {
3843
- console.log("\n\u2705 [dry-run] \u53D1\u5E03\u53C2\u6570\u9884\u89C8\uFF08\u672A\u5B9E\u9645\u63D0\u4EA4\uFF09\uFF1A\n");
3844
- if (options.verbose) {
3845
- console.log(JSON.stringify(body, null, 2));
3846
- } else {
3847
- const redacted = {
3848
- ...body,
3849
- uniqueActionId: "[\u5DF2\u9690\u85CF\uFF0C\u52A0 --verbose \u67E5\u770B]",
3850
- channels: body.channels.map((ch) => ({
3851
- ...ch,
3852
- mediaAccountInfos: ch.mediaAccountInfos.map((acc) => ({
3853
- ...acc,
3854
- externalMediaAccountTokenId: `${acc.externalMediaAccountTokenId.slice(0, 4)}****`
3855
- }))
3856
- }))
3857
- };
3858
- console.log(JSON.stringify(redacted, null, 2));
3859
- console.log("\n\u26A0\uFE0F \u90E8\u5206\u5B57\u6BB5\u5DF2\u8131\u654F\uFF0C\u52A0 --verbose \u53C2\u6570\u67E5\u770B\u5B8C\u6574\u8BF7\u6C42\u4F53\u3002");
3860
- }
4024
+ printDryRun(input, taskName, isDomestic, body, options.verbose);
3861
4025
  return;
3862
4026
  }
3863
4027
  console.log(`
@@ -5633,27 +5797,29 @@ async function runTaskDetail(options) {
5633
5797
  console.log(JSON.stringify({ publishId: options.publishId, page, size, ...data }, null, 2));
5634
5798
  return;
5635
5799
  }
5800
+ const groupSummary = Array.isArray(data.publishGroupCounts) && data.publishGroupCounts.length > 0 ? " " + data.publishGroupCounts.map((g) => `${g.mediaType}\xD7${g.publishCount}`).join(" ") : "";
5636
5801
  console.log(`
5637
- \u4EFB\u52A1\u8BE6\u60C5\uFF08publishId=${options.publishId}\uFF0C\u603B\u53D1\u5E03\u9879 ${data.totalPublishCount ?? 0}\uFF09\uFF1A`);
5802
+ \u4EFB\u52A1\u8BE6\u60C5\uFF08publishId=${options.publishId}\uFF0C\u603B\u53D1\u5E03\u9879 ${data.totalPublishCount ?? 0}\uFF09${groupSummary ? `
5803
+ \u5404\u5E73\u53F0\uFF1A${groupSummary}` : ""}\uFF1A`);
5638
5804
  if (list.length === 0) {
5639
5805
  console.log(" \u5F53\u524D\u9875\u6682\u65E0\u53D1\u5E03\u9879\u3002\n");
5640
5806
  return;
5641
5807
  }
5642
5808
  const columns = [
5643
- { key: "id", header: "\u53D1\u5E03\u9879ID" },
5644
5809
  { key: "mediaType", header: "\u5A92\u4F53" },
5645
5810
  { key: "account", header: "\u8D26\u53F7" },
5811
+ { key: "title", header: "\u6807\u9898" },
5646
5812
  { key: "status", header: "\u72B6\u6001" },
5647
- { key: "publishAt", header: "\u8BA1\u5212\u53D1\u5E03\u65F6\u95F4" },
5648
- { key: "publishTime", header: "\u5B9E\u9645\u53D1\u5E03\u65F6\u95F4" }
5813
+ { key: "publishTime", header: "\u53D1\u5E03\u65F6\u95F4" },
5814
+ { key: "remark", header: "\u5907\u6CE8/\u5931\u8D25\u539F\u56E0" }
5649
5815
  ];
5650
5816
  const rows = list.map((item) => ({
5651
- id: String(item.id ?? "-"),
5652
5817
  mediaType: String(item.mediaType ?? "-"),
5653
5818
  account: String(item.mediaCustomerName ?? item.mediaCustomerId ?? "-"),
5819
+ title: item.title ? item.title.length > 20 ? item.title.slice(0, 20) + "\u2026" : item.title : "-",
5654
5820
  status: itemStatusLabel(typeof item.videoStatus === "number" ? item.videoStatus : void 0),
5655
- publishAt: String(item.publishAt ?? "-"),
5656
- publishTime: String(item.publishTime ?? "-")
5821
+ publishTime: item.publishTime ? typeof item.publishTime === "number" ? new Date(item.publishTime * 1e3).toLocaleString("zh-CN") : String(item.publishTime) : "-",
5822
+ remark: item.remark ? item.remark.length > 30 ? item.remark.slice(0, 30) + "\u2026" : item.remark : "-"
5657
5823
  }));
5658
5824
  printCliTable(rows, columns, tableOpts(options.unicode));
5659
5825
  console.log();
@@ -5783,6 +5949,35 @@ async function runTaskCommentList(options) {
5783
5949
  console.log();
5784
5950
  }
5785
5951
 
5952
+ // src/commands/authorize.ts
5953
+ import { exec } from "child_process";
5954
+ function openBrowser(url) {
5955
+ const platform = process.platform;
5956
+ let cmd;
5957
+ if (platform === "win32") cmd = `start "" "${url}"`;
5958
+ else if (platform === "darwin") cmd = `open "${url}"`;
5959
+ else cmd = `xdg-open "${url}"`;
5960
+ exec(cmd);
5961
+ }
5962
+ async function runAuthorize(opts) {
5963
+ const config = loadConfig(opts.token);
5964
+ const tradeSegment = opts.domestic ? "domestic_trade" : "foreign_trade";
5965
+ const returnUrl = `${DEFAULT_WEB_BASE}/v3/${tradeSegment}/cso/ManageAccounts`;
5966
+ const url = `${config.apiBaseUrl}/command/media-account/link/${encodeURIComponent(opts.mediaType)}?returnUrl=${encodeURIComponent(returnUrl)}`;
5967
+ let redirectUrl;
5968
+ try {
5969
+ const data = await apiFetch2(url, config, {}, opts.verbose);
5970
+ redirectUrl = data.redirectUrl;
5971
+ } catch {
5972
+ process.exit(1);
5973
+ }
5974
+ console.log(`\u6B63\u5728\u6253\u5F00 ${opts.mediaType} \u6388\u6743\u9875\u9762...`);
5975
+ console.log(`\u6388\u6743\u94FE\u63A5\uFF1A${redirectUrl}`);
5976
+ openBrowser(redirectUrl);
5977
+ console.log(`
5978
+ \u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u6210\u529F\u540E\uFF0C\u9875\u9762\u5C06\u81EA\u52A8\u8DF3\u56DE\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u7BA1\u7406\u9875\u9762\u3002`);
5979
+ }
5980
+
5786
5981
  // src/commands/config.ts
5787
5982
  import * as fs10 from "fs";
5788
5983
  function cmdConfigShow() {
@@ -5797,7 +5992,6 @@ function cmdConfigShow() {
5797
5992
  const csoBaseUrl = DEFAULT_CSO_BASE;
5798
5993
  const apiKey = shared.apiKey ?? "";
5799
5994
  console.log("\n\u5F53\u524D\u914D\u7F6E\uFF08~/.siluzan/config.json\uFF09\uFF1A");
5800
- console.log(` \u6784\u5EFA\u73AF\u5883 : ${BUILD_ENV}`);
5801
5995
  console.log(` apiBaseUrl : ${apiBaseUrl}`);
5802
5996
  console.log(` csoBaseUrl : ${csoBaseUrl}`);
5803
5997
  if (apiKey) {
@@ -5879,7 +6073,10 @@ program.command("init").description("\u5C06 Skill \u6587\u4EF6\u5199\u5165\u6307
5879
6073
  apiBaseUrl: opts.apiBase ?? "https://api.siluzan.com"
5880
6074
  });
5881
6075
  });
5882
- program.command("list-accounts").description("\u5217\u51FA\u5A92\u4F53\u8D26\u53F7\u5217\u8868\uFF0C\u652F\u6301\u5206\u9875\u3001\u5E73\u53F0/\u540D\u79F0/\u72B6\u6001/\u8D1F\u8D23\u4EBA\u8FC7\u6EE4\uFF1B--overview \u5C55\u793A\u7C89\u4E1D\u6570/\u4F5C\u54C1\u6570\u7B49\u6570\u636E\u603B\u89C8").option("--domestic", "\u5185\u8D38\u6A21\u5F0F\uFF08mediaGroup=1\uFF09\uFF0C\u9ED8\u8BA4\u5916\u8D38", false).option("--name <name>", "\u6309\u8D26\u53F7\u540D\u79F0\u6A21\u7CCA\u641C\u7D22").option("--media-type <type>", "\u6309\u5E73\u53F0\u7C7B\u578B\u8FC7\u6EE4\uFF08\u5982 \u6296\u97F3\u3001YouTube\u3001TikTok \u7B49\uFF09").option("--state <state>", "\u6309\u72B6\u6001\u8FC7\u6EE4\uFF1Aall\uFF08\u5168\u90E8\uFF09| normal\uFF08\u6B63\u5E38\uFF09| abnormal\uFF08\u5F02\u5E38/\u8FC7\u671F\uFF09\uFF0C\u9ED8\u8BA4 all", "all").option("--owner <owner>", "\u6309\u8D1F\u8D23\u4EBA\u8FC7\u6EE4\uFF08\u4F20\u5165\u7528\u6237 ID\uFF0C\u53EF\u4ECE --json \u8FD4\u56DE\u7684 ownerInfo[].id \u5B57\u6BB5\u83B7\u53D6\uFF09").option("--page <n>", "\u9875\u7801\uFF08\u9ED8\u8BA4 1\uFF09", "1").option("--page-size <n>", "\u6BCF\u9875\u6761\u6570\uFF08\u9ED8\u8BA4 20\uFF09", "20").option("--overview", "\u603B\u89C8\u6A21\u5F0F\uFF1A\u5C55\u793A\u7C89\u4E1D\u6570/\u4F5C\u54C1\u6570/\u64AD\u653E\u6570/\u8BC4\u8BBA\u6570/\u83B7\u8D5E\u6570\u7B49\u6570\u636E\u6307\u6807", false).option("--json", "\u4EE5 JSON \u8F93\u51FA\u8D26\u53F7\u5217\u8868\uFF08\u542B\u603B\u89C8\u6570\u636E\uFF0C\u7528\u4E8E\u811A\u672C\u6216\u914D\u7F6E\u6587\u4EF6\uFF09", false).option("--unicode", "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08\u9ED8\u8BA4 ASCII +-|\uFF09", false).option("--verbose", "\u663E\u793A\u5B8C\u6574\u4EE4\u724C\u6807\u8BC6\uFF08\u9ED8\u8BA4\u8131\u654F\uFF09", false).action(async (opts) => {
6076
+ program.command("authorize").description("\u53D1\u8D77\u5916\u8D38\u5A92\u4F53\u5E73\u53F0 OAuth \u6388\u6743\uFF08YouTube/TikTok/Instagram/Facebook/LinkedIn \u7B49\uFF09\uFF0C\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u540E\u8DF3\u56DE\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u7BA1\u7406\u9875").requiredOption("--media-type <type>", "\u5A92\u4F53\u5E73\u53F0\u7C7B\u578B\uFF0C\u5982 YouTube / TikTok / Instagram / Facebook / LinkedIn").option("--domestic", "\u5185\u8D38\u6A21\u5F0F\uFF0C\u6388\u6743\u5B8C\u6210\u540E\u8DF3\u56DE\u5185\u8D38\u8D26\u53F7\u7BA1\u7406\u9875\uFF08\u9ED8\u8BA4\u5916\u8D38\uFF09", false).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
6077
+ await runAuthorize(opts);
6078
+ });
6079
+ program.command("list-accounts").description("\u5217\u51FA\u5A92\u4F53\u8D26\u53F7\u5217\u8868\uFF0C\u9ED8\u8BA4\u5C55\u793A\u7C89\u4E1D\u6570/\u4F5C\u54C1\u6570/\u64AD\u653E\u6570\u7B49\u6570\u636E\u603B\u89C8\uFF1B\u652F\u6301\u5206\u9875\u3001\u5E73\u53F0/\u540D\u79F0/\u72B6\u6001/\u8D1F\u8D23\u4EBA\u8FC7\u6EE4").option("--domestic", "\u5185\u8D38\u6A21\u5F0F\uFF08mediaGroup=1\uFF09\uFF0C\u9ED8\u8BA4\u5916\u8D38", false).option("--name <name>", "\u6309\u8D26\u53F7\u540D\u79F0\u6A21\u7CCA\u641C\u7D22").option("--media-type <type>", "\u6309\u5E73\u53F0\u7C7B\u578B\u8FC7\u6EE4\uFF08\u5982 \u6296\u97F3\u3001YouTube\u3001TikTok \u7B49\uFF09").option("--state <state>", "\u6309\u72B6\u6001\u8FC7\u6EE4\uFF1Aall\uFF08\u5168\u90E8\uFF09| normal\uFF08\u6B63\u5E38\uFF09| abnormal\uFF08\u5F02\u5E38/\u8FC7\u671F\uFF09\uFF0C\u9ED8\u8BA4 all", "all").option("--owner <owner>", "\u6309\u8D1F\u8D23\u4EBA\u8FC7\u6EE4\uFF08\u4F20\u5165\u7528\u6237 ID\uFF0C\u53EF\u4ECE --json \u8FD4\u56DE\u7684 ownerInfo[].id \u5B57\u6BB5\u83B7\u53D6\uFF09").option("--page <n>", "\u9875\u7801\uFF08\u9ED8\u8BA4 1\uFF09", "1").option("--page-size <n>", "\u6BCF\u9875\u6761\u6570\uFF08\u9ED8\u8BA4 20\uFF09", "20").option("--no-overview", "\u5173\u95ED\u603B\u89C8\u6A21\u5F0F\uFF0C\u6539\u4E3A\u5C55\u793A\u57FA\u7840\u8D26\u53F7\u4FE1\u606F\uFF08\u8D26\u53F7ID/\u6388\u6743\u72B6\u6001/Token\u5230\u671F\u7B49\uFF09").option("--json", "\u4EE5 JSON \u8F93\u51FA\u8D26\u53F7\u5217\u8868\uFF08\u542B\u603B\u89C8\u6570\u636E\uFF0C\u7528\u4E8E\u811A\u672C\u6216\u914D\u7F6E\u6587\u4EF6\uFF09", false).option("--unicode", "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08\u9ED8\u8BA4 ASCII +-|\uFF09", false).option("--verbose", "\u663E\u793A\u5B8C\u6574\u4EE4\u724C\u6807\u8BC6\uFF08\u9ED8\u8BA4\u8131\u654F\uFF09", false).action(async (opts) => {
5883
6080
  const state = opts.state;
5884
6081
  await runListAccounts({
5885
6082
  token: opts.token,
@@ -14,9 +14,9 @@ description_en: >-
14
14
  |------|------|
15
15
  | `siluzan-cso login` | **首次使用:交互引导登录并保存 Token(推荐,Token 不进入 shell history)** |
16
16
  | `siluzan-cso config show` | 查看当前保存的配置 |
17
- | `siluzan-cso list-accounts` | 列出所有可用媒体账号 |
18
- | `siluzan-cso list-accounts --overview` | **账号数据总览:粉丝数/作品数/播放数/评论数/获赞数横向对比** |
19
- | `siluzan-cso list-accounts --media-type <平台> --overview` | 按平台筛选账号并查看数据总览 |
17
+ | `siluzan-cso list-accounts` | 列出所有可用媒体账号(按平台分组,默认展示数据总览) |
18
+ | `siluzan-cso list-accounts --name <名称> --media-type <平台> --json` | **精准获取单个账号完整信息(entityId / mediaCustomerId / Token 等)** |
19
+ | `siluzan-cso list-accounts --media-type <平台>` | 按平台筛选账号 |
20
20
  | `siluzan-cso report fetch --media <平台>` | 拉取指定平台的运营报表(核心指标 + 视频排行) |
21
21
  | `siluzan-cso report fetch --media <平台> --maids <账号ID>` | 查指定账号的运营数据 |
22
22
  | `siluzan-cso report fetch --media <平台> --days <N>` | 查近 N 天数据(默认 30 天) |
@@ -78,12 +78,13 @@ AI 需根据当前日期自动推算"明天"、"后天"等相对时间,转换
78
78
 
79
79
  | 用户问 | 应使用命令 |
80
80
  |--------|-----------|
81
- | 看所有账号对比/账号层面数据 | `list-accounts --overview` |
82
- | 只看某平台(如 TikTok)的账号 | `list-accounts --media-type TikTokBusinessAccount --overview` |
81
+ | 帮我找一下 xxx 这个账号 / 获取账号 ID | `list-accounts --name <名称> --media-type <平台> --json` |
82
+ | 看所有账号对比/账号层面数据 | `list-accounts`(默认总览模式) |
83
+ | 只看某平台(如 TikTok)的账号 | `list-accounts --media-type TikTokBusinessAccount` |
83
84
  | 某个账号最近表现 | `report fetch --media <平台> --maids <账号ID>` |
84
85
  | 近 30 天 / 近 N 天 | `report fetch --media <平台> --days <N>` |
85
86
  | 哪条视频/作品最好 | `report fetch --media <平台> --include works --order play` |
86
- | 按账号和平台分别看 | 逐个调用 `report fetch --maids <id>`,或先用 `list-accounts --overview` |
87
+ | 按账号和平台分别看 | 逐个调用 `report fetch --maids <id>`,或先用 `list-accounts` |
87
88
 
88
89
  **平台名称速查(--media / --media-type 参数):**
89
90
  `Douyin`(抖音)· `TikTokBusinessAccount`(TikTok)· `YouTube` · `Wechat`(视频号)· `Instagram` · `Facebook` · `Twitter`
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-cso",
3
- "version": "1.0.0-beta.29",
4
- "publishedAt": 1775526147685
3
+ "version": "1.0.0-beta.30",
4
+ "publishedAt": 1775544354663
5
5
  }
@@ -15,6 +15,7 @@
15
15
  "imageUrl": "https://cdn.example.com/img1.jpg",
16
16
  "imageName": "img1.jpg",
17
17
  "imageSourceType": 1,
18
+ "sourceImageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
18
19
  "width": 1080,
19
20
  "height": 1080
20
21
  }
@@ -22,11 +23,11 @@
22
23
 
23
24
  "accounts": [
24
25
  {
25
- "entityId": "从 siluzan-cso list-accounts 获取",
26
+ "entityId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
26
27
  "mediaAccountType": "Instagram",
27
- "mediaCustomerId": "xxx",
28
+ "mediaCustomerId": "平台账号ID(非UUID)",
28
29
  "mediaCustomerName": "我的 Instagram",
29
- "externalMediaAccountTokenId": ""
30
+ "externalMediaAccountTokenId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
30
31
  }
31
32
  ],
32
33
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  "videos": [
8
8
  {
9
- "videoId": "从素材库获取的 videoId",
9
+ "videoId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
10
10
  "videoUrl": "https://cdn.example.com/video.mp4",
11
11
  "videoName": "视频文件名.mp4",
12
12
  "isMaterialCenter": true,
@@ -21,23 +21,24 @@
21
21
  "cover": {
22
22
  "imageUrl": "https://cdn.example.com/cover.jpg",
23
23
  "imageName": "cover.jpg",
24
- "imageSourceType": 1
24
+ "imageSourceType": 1,
25
+ "sourceImageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
25
26
  },
26
27
 
27
28
  "accounts": [
28
29
  {
29
- "entityId": "从 siluzan-cso list-accounts 获取",
30
+ "entityId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
30
31
  "mediaAccountType": "Douyin",
31
- "mediaCustomerId": "xxx",
32
+ "mediaCustomerId": "抖音平台账号ID(非UUID,如数字字符串)",
32
33
  "mediaCustomerName": "我的抖音账号",
33
- "externalMediaAccountTokenId": ""
34
+ "externalMediaAccountTokenId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
34
35
  },
35
36
  {
36
- "entityId": "另一个账号 entityId",
37
+ "entityId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
37
38
  "mediaAccountType": "YouTube",
38
- "mediaCustomerId": "yyy",
39
+ "mediaCustomerId": "YouTube频道ID(非UUID,如UCxxx)",
39
40
  "mediaCustomerName": "我的 YouTube 频道",
40
- "externalMediaAccountTokenId": ""
41
+ "externalMediaAccountTokenId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
41
42
  }
42
43
  ],
43
44
 
@@ -37,6 +37,47 @@
37
37
 
38
38
  ---
39
39
 
40
+ ## 场景零:精准获取某一个账号的完整信息
41
+
42
+ **用户问**:"帮我查一下这个账号的信息"、"我想获取 xxx 账号的 ID"、"帮我找一下这个 YouTube 频道的账号信息"
43
+
44
+ ### 推荐方式:名称 + 平台组合过滤
45
+
46
+ ```bash
47
+ # 精确定位单个账号:同时传 --name(名称模糊搜索)和 --media-type(平台),加 --json 获取完整字段
48
+ siluzan-cso list-accounts --name "账号名" --media-type YouTube --json
49
+
50
+ # 只知道名称、不确定平台时,先不传 --media-type
51
+ siluzan-cso list-accounts --name "账号名" --json
52
+
53
+ # 只知道平台时,列出该平台所有账号
54
+ siluzan-cso list-accounts --media-type TikTokBusinessAccount --json
55
+ ```
56
+
57
+ ### JSON 输出中各字段的用途
58
+
59
+ | 字段 | 用途 |
60
+ |------|------|
61
+ | `entityId` | 发布配置 `accounts[].entityId` 的值 |
62
+ | `mediaCustomerId` | `report fetch --maids <id>` 的参数 |
63
+ | `mediaAccountType` | 发布配置 `accounts[].mediaAccountType`、`--media-type` 参数 |
64
+ | `mediaCustomerName` | 发布配置 `accounts[].mediaCustomerName` |
65
+ | `externalMediaAccountTokenId` | 发布配置 `accounts[].externalMediaAccountTokenId` |
66
+ | `invalidOAuthToken` | `true` 表示 Token 已失效,需要重新授权 |
67
+ | `expiresOn` | Token 到期时间 |
68
+ | `overview.fansCount` | 当前粉丝数 |
69
+ | `overview.videoCount` | 发布作品总数 |
70
+ | `overview.playCount` | 历史总播放数 |
71
+
72
+ ### AI 行为规则
73
+
74
+ - 用户要找一个具体账号时,**优先用 `--name` + `--media-type` 双重过滤**,通常可直接得到唯一结果,无需让用户翻页。
75
+ - 如果用户只说了账号名称(没说平台),先不加 `--media-type`,看结果是否唯一;不唯一时再让用户确认平台。
76
+ - `--json` 返回的 `entityId` + `externalMediaAccountTokenId` + `mediaCustomerId` 是构造发布配置 `accounts[]` 所需的全部字段,**找到账号后可以直接填入发布配置,不必再多问用户**。
77
+ - 账号的 `overview` 字段就是概览数据(粉丝数/播放数等),找到账号后无需再单独调用 `report fetch` 就能回答粉丝数之类的基本问题。
78
+
79
+ ---
80
+
40
81
  ## 场景一:查所有账号的数据对比
41
82
 
42
83
  **用户问**:"能不能看所有账号的对比?"、"不同账号谁表现更好?"、"账号层面的数据在哪里看?"
@@ -185,6 +185,21 @@ AI 可以直接根据以下方向帮助润色:
185
185
  - **视频配置文件结构:**
186
186
  请阅读:`assets/publish-config.example.json`
187
187
 
188
+ **⚠️ UUID 字段必须用真实值,不能用占位符文本**
189
+
190
+ 以下字段后端会做严格的 GUID 格式校验,填写错误将导致 HTTP 500 报错:
191
+
192
+ | 字段 | 来源 | 格式示例 |
193
+ |------|------|---------|
194
+ | `accounts[].entityId` | `list-accounts --json` 的 `entityId` 字段 | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
195
+ | `accounts[].externalMediaAccountTokenId` | `list-accounts --json` 的 `externalMediaAccountTokenId` 字段 | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
196
+ | `videos[].videoId` | `upload` 命令返回的 `videoId`,或素材库中的 UUID | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
197
+ | `cover.sourceImageId` | `upload` 命令返回的 `sourceImageId` | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
198
+
199
+ > **注意**:`mediaCustomerId` 不是 UUID,是各平台的账号 ID(如抖音的数字 ID、YouTube 的 UC 开头字符串),可以直接填写。
200
+
201
+ CLI 会在提交前校验所有 UUID 字段,如有非法值会给出明确错误提示,告知哪个账号的哪个字段需要修正。
202
+
188
203
  根据用户确认的信息,生成 `publish-config.json`,然后先预览:
189
204
 
190
205
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-cso-cli",
3
- "version": "1.0.0-beta.29",
3
+ "version": "1.0.0-beta.30",
4
4
  "description": "Siluzan platform AI Skill CLI — multi-platform content publishing (video/image-text) for Cursor, Claude Code, and OpenClaw.",
5
5
  "type": "module",
6
6
  "bin": {