siluzan-tso-cli 1.1.26-beta.1 → 1.1.26-beta.3

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
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
- > **注意**:当前为测试版(1.1.26-beta.1),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.26-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -4014,7 +4014,7 @@ var init_cli_json = __esm({
4014
4014
  CLI_SNAPSHOT_MANIFEST_FILE = "cli-manifest.json";
4015
4015
  CLI_PACKAGE = "siluzan-tso-cli";
4016
4016
  SCHEMA_VERSION = 1;
4017
- OUTLINE_AGENT_HINT = "\u5904\u7406\u987A\u5E8F\uFF1A\u5148\u8BFB outlineFile\uFF08schema \u63CF\u8FF0\uFF0C\u975E\u6570\u636E\uFF09\u4E86\u89E3\u5B57\u6BB5\u7C7B\u578B\uFF0C\u518D\u7528\u811A\u672C\u8BFB writtenFiles[0]\uFF08\u771F\u5B9E JSON \u6570\u636E\uFF09\u505A\u7B5B\u9009/\u805A\u5408\uFF1B\u4E0D\u8981\u628A outline \u5F53\u6210\u6570\u636E\uFF0C\u4E5F\u4E0D\u8981\u628A JSON \u5F53\u6210 schema\u3002";
4017
+ OUTLINE_AGENT_HINT = "\u7981\u6B62\u7528 Read/cat \u6253\u5F00 writtenFiles \u4E2D\u7684\u5B8C\u6574\u4E1A\u52A1 *.json\u3002\u987A\u5E8F\uFF1A1) \u89E3\u6790\u672C\u884C stdout \u6458\u8981\uFF1B2) \u4EC5\u53EF\u8BFB outlineFile\uFF08*.outline.txt\uFF0Cschema\uFF09\uFF1B3) \u5FC5\u987B\u7528 node/python \u811A\u672C require/readFileSync \u8BFB JSON \u5E76\u6253\u5370\u6240\u9700\u5B57\u6BB5\u3002\u52FF\u628A JSON \u8D34\u8FDB\u5BF9\u8BDD\u3002";
4018
4018
  DEFAULT_FIELD_GUIDE = {
4019
4019
  markdownRefs: ["references/accounts/currency.md", "references/core/tips.md", "references/accounts/accounts.md"]
4020
4020
  };
@@ -120073,7 +120073,133 @@ init_auth();
120073
120073
  import * as fs9 from "fs";
120074
120074
  import * as path14 from "path";
120075
120075
  init_cli_json_snapshot();
120076
- init_cli_table();
120076
+
120077
+ // src/commands/open-account/bing-industry-dict.ts
120078
+ init_auth();
120079
+ var ORPHAN_PARENT_KEY = "__orphan__";
120080
+ function normalizeBingTradeReadList(readResponse) {
120081
+ let raw = readResponse;
120082
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
120083
+ const o = raw;
120084
+ if ("value" in o && Array.isArray(o.value)) {
120085
+ raw = o.value;
120086
+ } else if ("data" in o) {
120087
+ raw = o.data;
120088
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
120089
+ const inner = raw;
120090
+ if (Array.isArray(inner.data)) raw = inner.data;
120091
+ else if (Array.isArray(inner.value)) raw = inner.value;
120092
+ }
120093
+ }
120094
+ }
120095
+ return Array.isArray(raw) ? raw : [];
120096
+ }
120097
+ function applyBingTradeTranslations(records, translationRows) {
120098
+ for (const item of records) {
120099
+ for (const row of translationRows) {
120100
+ if (item.name == row.name) {
120101
+ item.TranslationLanguage = row.value;
120102
+ break;
120103
+ }
120104
+ }
120105
+ }
120106
+ }
120107
+ function extractTradeListRecords(raw) {
120108
+ if (Array.isArray(raw)) return raw;
120109
+ if (!raw || typeof raw !== "object") return [];
120110
+ const o = raw;
120111
+ const data = o.data;
120112
+ if (data && typeof data === "object" && !Array.isArray(data)) {
120113
+ const records = data.records;
120114
+ if (Array.isArray(records)) return records;
120115
+ }
120116
+ if (Array.isArray(o.records)) return o.records;
120117
+ return [];
120118
+ }
120119
+ function buildBingIndustryOptions(records) {
120120
+ const byId = new Map(records.map((r) => [String(r.id), r]));
120121
+ const level2 = records.filter(
120122
+ (r) => String(r.tradeLevel) === "2" || r.tradeLevel === 2
120123
+ );
120124
+ const byParent = /* @__PURE__ */ new Map();
120125
+ for (const row of level2) {
120126
+ const pid = row.parentId == null ? ORPHAN_PARENT_KEY : String(row.parentId);
120127
+ if (!byParent.has(pid)) byParent.set(pid, []);
120128
+ byParent.get(pid).push(row);
120129
+ }
120130
+ const cmpCode = (a, b) => String(a.code ?? "").localeCompare(String(b.code ?? ""), void 0, { numeric: true });
120131
+ const groups = [];
120132
+ for (const [parentKey, children] of byParent) {
120133
+ const parent = parentKey === ORPHAN_PARENT_KEY ? null : byId.get(parentKey);
120134
+ const label = parent?.TranslationLanguage || parent?.name || (parentKey === ORPHAN_PARENT_KEY ? "\u2014" : parentKey);
120135
+ groups.push({
120136
+ parentId: parentKey === ORPHAN_PARENT_KEY ? "" : parentKey,
120137
+ label,
120138
+ children: [...children].sort(cmpCode)
120139
+ });
120140
+ }
120141
+ const emptyParent = { id: "", code: "" };
120142
+ groups.sort((a, b) => {
120143
+ const pa = a.parentId ? byId.get(a.parentId) : null;
120144
+ const pb = b.parentId ? byId.get(b.parentId) : null;
120145
+ return cmpCode(pa ?? emptyParent, pb ?? emptyParent);
120146
+ });
120147
+ const out = [];
120148
+ for (const g of groups) {
120149
+ for (const row of g.children) {
120150
+ const id = String(row.id ?? "");
120151
+ const label = String(row.TranslationLanguage || row.name || id);
120152
+ out.push({
120153
+ id,
120154
+ label,
120155
+ group: g.label,
120156
+ name: String(row.name ?? "")
120157
+ });
120158
+ }
120159
+ }
120160
+ return out;
120161
+ }
120162
+ function filterBingIndustryOptions(options, keyword) {
120163
+ const kw = keyword.trim().toLowerCase();
120164
+ if (!kw) return options;
120165
+ return options.filter((o) => {
120166
+ const hay = `${o.id} ${o.label} ${o.group} ${o.name}`.toLowerCase();
120167
+ return hay.includes(kw);
120168
+ });
120169
+ }
120170
+ function resolveBingTradeIdForSubmit(tradeIdOrLabel, options) {
120171
+ const raw = tradeIdOrLabel.trim();
120172
+ if (!raw) return raw;
120173
+ const byId = options.find((o) => o.id === raw);
120174
+ if (byId) return byId.id;
120175
+ const byLabel = options.find(
120176
+ (o) => o.label === raw || o.name === raw || `${o.group}/${o.label}` === raw
120177
+ );
120178
+ if (byLabel) return byLabel.id;
120179
+ return raw;
120180
+ }
120181
+ async function fetchBingIndustryOptions(config, verbose) {
120182
+ const rawList = await apiFetch2(
120183
+ `${config.apiBaseUrl}/BingV2Account/Management/TradeList`,
120184
+ config,
120185
+ { method: "POST", body: JSON.stringify({ page: 1, pageSize: 9999 }) },
120186
+ verbose
120187
+ );
120188
+ const records = extractTradeListRecords(rawList).map((r) => ({
120189
+ ...r,
120190
+ id: String(r.id ?? "")
120191
+ }));
120192
+ const rawRead = await apiFetch2(
120193
+ `${config.apiBaseUrl}/query/media-account/bing/BingTradeList/Read`,
120194
+ config,
120195
+ {},
120196
+ verbose
120197
+ );
120198
+ applyBingTradeTranslations(records, normalizeBingTradeReadList(rawRead));
120199
+ return buildBingIndustryOptions(records);
120200
+ }
120201
+
120202
+ // src/commands/open-account/bing.ts
120077
120203
  async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
120078
120204
  const fileName = path14.basename(filePath);
120079
120205
  const fileBuffer = fs9.readFileSync(filePath);
@@ -120152,13 +120278,19 @@ async function runOpenAccountBing(opts) {
120152
120278
  opts.advertiserId
120153
120279
  );
120154
120280
  const accountCount = Math.min(Math.max(opts.accountCount ?? 1, 1), 6);
120281
+ let tradeId = opts.tradeId;
120282
+ try {
120283
+ const industryOptions = await fetchBingIndustryOptions(config, opts.verbose);
120284
+ tradeId = resolveBingTradeIdForSubmit(opts.tradeId, industryOptions);
120285
+ } catch {
120286
+ }
120155
120287
  const body = {
120156
120288
  mediaAccountGroupId: magKey,
120157
120289
  accountCount,
120158
120290
  bingCustomerInfo: {
120159
120291
  name: opts.advertiserName,
120160
120292
  province: opts.province,
120161
- tradeId: opts.tradeId,
120293
+ tradeId,
120162
120294
  nameShort: opts.nameShort,
120163
120295
  postcode: opts.postcode ?? "",
120164
120296
  city: opts.city,
@@ -120178,7 +120310,7 @@ async function runOpenAccountBing(opts) {
120178
120310
  companyName: opts.advertiserName,
120179
120311
  postcode: opts.postcode ?? "",
120180
120312
  province: opts.province,
120181
- tradeId: opts.tradeId,
120313
+ tradeId,
120182
120314
  nameShort: opts.nameShort,
120183
120315
  fileId,
120184
120316
  fileName,
@@ -120207,13 +120339,7 @@ async function runOpenAccountBingIndustries(opts) {
120207
120339
  const config = loadConfig(opts.token);
120208
120340
  let list;
120209
120341
  try {
120210
- const raw = await apiFetch2(
120211
- `${config.apiBaseUrl}/query/media-account/bing/BingTradeList/Read`,
120212
- config,
120213
- {},
120214
- opts.verbose
120215
- );
120216
- list = Array.isArray(raw) ? raw : raw.value ?? [];
120342
+ list = await fetchBingIndustryOptions(config, opts.verbose);
120217
120343
  } catch (err) {
120218
120344
  console.error(
120219
120345
  `
@@ -120222,10 +120348,7 @@ async function runOpenAccountBingIndustries(opts) {
120222
120348
  );
120223
120349
  process.exit(1);
120224
120350
  }
120225
- const kw = (opts.keyword ?? "").trim().toLowerCase();
120226
- if (kw) {
120227
- list = list.filter((r) => r.name.toLowerCase().includes(kw));
120228
- }
120351
+ list = filterBingIndustryOptions(list, opts.keyword ?? "");
120229
120352
  const n = list.length;
120230
120353
  const bingIndPayload = wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items: list });
120231
120354
  if (await emitCliJsonOrSnapshot(opts, {
@@ -120237,17 +120360,21 @@ async function runOpenAccountBingIndustries(opts) {
120237
120360
  }
120238
120361
  console.log(
120239
120362
  `
120240
- BingV2 \u884C\u4E1A\u5217\u8868\uFF08\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${list.length} \u6761\uFF0C\u5171 ${list.length} \u6761\uFF0C\u5C06 name \u503C\u4F20\u7ED9 --trade-id\uFF09
120363
+ BingV2 \u884C\u4E1A\u5217\u8868\uFF08\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${list.length} \u6761\uFF0C\u5171 ${list.length} \u6761\uFF0C--trade-id \u4F20 id \u5217\uFF0C\u4E0E\u7F51\u9875\u4E0B\u62C9\u4E00\u81F4\uFF09
120241
120364
  `
120242
120365
  );
120243
120366
  if (list.length === 0) {
120244
- console.log(" \u65E0\u6570\u636E\u3002\n");
120367
+ console.log(" \u65E0\u6570\u636E\u3002\u53EF\u53BB\u6389 --keyword \u67E5\u770B\u5168\u91CF\uFF0C\u6216\u6362\u66F4\u77ED\u7684\u4E2D\u6587\u5173\u952E\u8BCD\u3002\n");
120245
120368
  return;
120246
120369
  }
120247
- printCliTable(
120248
- list.map((r) => ({ name: r.name })),
120249
- [{ key: "name", header: "\u884C\u4E1A\u540D\u79F0\uFF08--trade-id\uFF09" }]
120250
- );
120370
+ let lastGroup = "";
120371
+ for (const row of list) {
120372
+ if (row.group !== lastGroup) {
120373
+ console.log(` \u25B8 ${row.group}`);
120374
+ lastGroup = row.group;
120375
+ }
120376
+ console.log(` ${row.label} (id: ${row.id})`);
120377
+ }
120251
120378
  console.log();
120252
120379
  }
120253
120380
 
@@ -121106,7 +121233,7 @@ function register24(program2) {
121106
121233
  });
121107
121234
  }
121108
121235
  );
121109
- openAccountCmd.command("bing").description("\u63D0\u4EA4 Bing/BingV2 \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--pattern <Direct|Agency>", "\u76F4\u63A5/\u4EE3\u7406\uFF1ADirect\uFF08\u76F4\u63A5\uFF09\u6216 Agency\uFF08\u4EE3\u7406\uFF09").requiredOption("--advertiser-name <name>", "\u516C\u53F8\u540D\u79F0\uFF08\u5E7F\u544A\u4E3B\u5168\u79F0\uFF0C\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--name-short <short>", "\u516C\u53F8\u7B80\u79F0").requiredOption("--name-remark-list <name>", "\u5F00\u6237\u540D\u79F0\uFF08\u8D26\u6237\u663E\u793A\u540D\uFF09").requiredOption("--province <province>", "\u516C\u53F8\u6240\u5728\u7701\u4EFD").requiredOption("--city <city>", "\u516C\u53F8\u6240\u5728\u57CE\u5E02").requiredOption("--address <address>", "\u516C\u53F8\u8BE6\u7EC6\u5730\u5740").requiredOption("--postcode <code>", "\u516C\u53F8\u6240\u5728\u5730\u90AE\u7F16").requiredOption("--trade-id <name>", "\u884C\u4E1A\uFF08\u6765\u81EA open-account bing-industries \u7684 name \u5B57\u6BB5\uFF09").requiredOption("--promotion-link <url>", "\u5F00\u6237\u7F51\u5740\uFF08\u63A8\u5E7F\u94FE\u63A5\uFF09").requiredOption("--license-file <path>", "\u8425\u4E1A\u6267\u7167\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF08JPG/PNG/PDF\uFF09").option("--account-count <n>", "\u5F00\u6237\u6570\u91CF\uFF081~6\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).option("--advertiser-cid <cid>", "\u7ECF\u7406\u8D26\u53F7 ID\uFF08\u53EF\u9009\uFF09").option("--advertiser-name2 <name>", "\u7ECF\u7406\u8D26\u53F7\u540D\u79F0\uFF08\u53EF\u9009\uFF0C\u4E0E --advertiser-cid \u914D\u5BF9\uFF09").option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 magKey\uFF08\u4E00\u822C\u65E0\u9700\u586B\u5199\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
121236
+ openAccountCmd.command("bing").description("\u63D0\u4EA4 Bing/BingV2 \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--pattern <Direct|Agency>", "\u76F4\u63A5/\u4EE3\u7406\uFF1ADirect\uFF08\u76F4\u63A5\uFF09\u6216 Agency\uFF08\u4EE3\u7406\uFF09").requiredOption("--advertiser-name <name>", "\u516C\u53F8\u540D\u79F0\uFF08\u5E7F\u544A\u4E3B\u5168\u79F0\uFF0C\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--name-short <short>", "\u516C\u53F8\u7B80\u79F0").requiredOption("--name-remark-list <name>", "\u5F00\u6237\u540D\u79F0\uFF08\u8D26\u6237\u663E\u793A\u540D\uFF09").requiredOption("--province <province>", "\u516C\u53F8\u6240\u5728\u7701\u4EFD").requiredOption("--city <city>", "\u516C\u53F8\u6240\u5728\u57CE\u5E02").requiredOption("--address <address>", "\u516C\u53F8\u8BE6\u7EC6\u5730\u5740").requiredOption("--postcode <code>", "\u516C\u53F8\u6240\u5728\u5730\u90AE\u7F16").requiredOption("--trade-id <id>", "\u884C\u4E1A tradeId\uFF08\u6765\u81EA open-account bing-industries \u7684 id\uFF0C\u4E0E\u7F51\u9875\u4E0B\u62C9 value \u4E00\u81F4\uFF09").requiredOption("--promotion-link <url>", "\u5F00\u6237\u7F51\u5740\uFF08\u63A8\u5E7F\u94FE\u63A5\uFF09").requiredOption("--license-file <path>", "\u8425\u4E1A\u6267\u7167\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF08JPG/PNG/PDF\uFF09").option("--account-count <n>", "\u5F00\u6237\u6570\u91CF\uFF081~6\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).option("--advertiser-cid <cid>", "\u7ECF\u7406\u8D26\u53F7 ID\uFF08\u53EF\u9009\uFF09").option("--advertiser-name2 <name>", "\u7ECF\u7406\u8D26\u53F7\u540D\u79F0\uFF08\u53EF\u9009\uFF0C\u4E0E --advertiser-cid \u914D\u5BF9\uFF09").option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 magKey\uFF08\u4E00\u822C\u65E0\u9700\u586B\u5199\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
121110
121237
  async (opts) => {
121111
121238
  await runOpenAccountBing({
121112
121239
  token: opts.token,
@@ -121171,7 +121298,7 @@ function register24(program2) {
121171
121298
  });
121172
121299
  }
121173
121300
  );
121174
- openAccountCmd.command("bing-industries").description("\u5217\u51FA BingV2 \u884C\u4E1A\u5206\u7C7B\uFF08\u5C06 name \u503C\u4F20\u7ED9 open-account bing \u7684 --trade-id\uFF09").option("-k, --keyword <text>", "\u6309\u4E2D\u6587\u540D\u6216\u82F1\u6587\u540D\u8FC7\u6EE4").option(
121301
+ openAccountCmd.command("bing-industries").description("\u5217\u51FA BingV2 \u884C\u4E1A\u5206\u7C7B\uFF08\u4E0E\u7F51\u9875\u540C\u6E90\uFF1B\u5C06 id \u4F20\u7ED9 open-account bing \u7684 --trade-id\uFF09").option("-k, --keyword <text>", "\u6309\u4E2D\u6587\u884C\u4E1A\u540D\u3001\u5206\u7EC4\u540D\u3001\u82F1\u6587 name \u6216 id \u8FC7\u6EE4").option(
121175
121302
  "--json-out <path>",
121176
121303
  "\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
121177
121304
  void 0
@@ -15,7 +15,9 @@ allowed-tools: Bash(siluzan-tso:*) Read Write
15
15
 
16
16
  <!-- 注入到 SKILL.md.tmpl 的 {{AGENT_PREAMBLE}};构建时由 gen-skill-docs.mjs 合并 -->
17
17
 
18
- > **Agent 纪律(每个新任务必读)**:Read `references/core/agent-conventions.md`,再按 `SKILL.md` 任务表 Read 域文档。禁止跨话题复用参数记忆;数据类任务一律 `--json-out` + 脚本读盘(见 `references/core/tips.md`)。
18
+ > **Agent 纪律(每个新任务必读)**:Read `references/core/agent-conventions.md`,再按 `SKILL.md` 任务表 Read 域文档。禁止跨话题复用参数记忆;数据类任务一律 `--json-out` + **仅用代码**读落盘 JSON(见 `references/core/tips.md`)——**禁止**用 Read 工具打开 `writtenFiles` 里的完整 `*.json`。
19
+ >
20
+ > **报告/Excel 交付前**:Read `references/core/deliverable-preflight.md`,**Read 最终产物**并按自检表确认币种与章节完整;币种只认当次 `list-accounts` 的 `currencyCode`。
19
21
  >
20
22
  > **开户**:首次进入开户话题须先向用户罗列该媒体(或未指明媒体时六平台)**全部必填项**,见 `references/accounts/open-account-by-media.md` §「首次响应硬规范」。
21
23
  >
@@ -70,7 +72,7 @@ Windows:部分 Agent 通过 PowerShell 代执行时可能失败,改在 [Git
70
72
  宿主具备 **Task / subagent**(或内置 Bash/Explore)时:
71
73
 
72
74
  1. **复杂报告或批处理**(P5 / P6 OKKI / P7 询盘)、预计 CLI 输出很长 → 先 Read `references/core/subagent-orchestration.md`,按**决策矩阵**选择主会话或子会话。
73
- 2. Handoff 模板在 skill 根目录 `snippets/handoff-p{5,6,7}-*.md`;子会话只回传 manifest/exitCode 摘要,**禁止**把 MB 级 JSON 贴回主对话。
75
+ 2. Handoff 模板在 skill 根目录 `snippets/handoff-p{5,6,7}-*.md`;子会话只回传 manifest/exitCode 摘要,**禁止** Read 落盘业务 JSON、**禁止**把 MB 级 JSON 贴回主对话(须脚本读盘后只回传脚本输出)。
74
76
  3. **无 subagent 能力** → 跳过本节,按 `core/playbooks.md` 在主会话执行。
75
77
 
76
78
  写操作、`--commit`、401/batch `resume` 策略由**主 Agent**负责,不默认下放子会话。
@@ -83,7 +85,7 @@ Windows:部分 Agent 通过 PowerShell 代执行时可能失败,改在 [Git
83
85
  | ------------------ | ----------------------------- |
84
86
  | 任意任务(首次) | `core/agent-conventions.md` |
85
87
  | 账户列表 / 余额 / 消耗 / 分享 / MCC / 多账户汇总 | `accounts/accounts.md`;金额加 `accounts/currency.md` |
86
- | 拉数 / 报告 / 周报 / `google-analysis` | `analytics/account-analytics.md` + `core/tips.md`;多账户加 `analytics/google-analysis-batch.md` |
88
+ | 拉数 / 报告 / 周报 / `google-analysis` | `analytics/account-analytics.md` + `core/tips.md` + **`core/deliverable-preflight.md`**;多账户加 `analytics/google-analysis-batch.md` |
87
89
  | Meta/Facebook 周期或诊断报告 / `facebook-analysis` | `analytics/facebook-analysis-guide.md` + `report-templates/meta-period-report.md`(或 `meta-account-diagnosis-report.md`)+ `core/playbooks.md` P4-FB |
88
90
  | 网站诊断 / 落地页评分 / `website-diagnosis` | `analytics/website-diagnosis-guide.md` + `assets/website-diagnosis-rules.md` + `report-templates/website-diagnosis-report.md`(**终稿 HTML**)+ `core/playbooks.md` P8 |
89
91
  | Google 新建搜索系列 | `google-ads/google-ads-campaign-plan.md` |
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.26-beta.1",
4
- "publishedAt": 1780483757520
3
+ "version": "1.1.26-beta.3",
4
+ "publishedAt": 1780540125141
5
5
  }
@@ -8,7 +8,8 @@
8
8
  | ---- | ---- |
9
9
  | `core/setup.md` | 安装、登录(手机验证码优先)、配置、更新 |
10
10
  | `core/agent-conventions.md` | **Agent 必读**:加载纪律、硬规范、数据转换、时间/币种/批量 |
11
- | `core/tips.md` | `--json-out` 协议、outlineJSON 处理顺序 |
11
+ | `core/tips.md` | `--json-out` 协议、outline→脚本读 JSON |
12
+ | `core/deliverable-preflight.md` | **交付前审阅最终产物**:Agent Read 报告文件并对照自检表(币种/章节) |
12
13
  | `core/playbooks.md` | P1–P7 高频任务标准动作 |
13
14
  | `core/workflows.md` | 多命令串联业务流程 |
14
15
  | `core/skill-authoring.md` | Skill 文档维护最佳实践(维护者) |
@@ -517,7 +517,7 @@ siluzan-tso open-account yandex \
517
517
  ### Bing/BingV2 开户(需要营业执照图片)
518
518
 
519
519
  ```bash
520
- # 前置:查询 Bing 行业名称(--trade-id 传返回的 name 字段值)
520
+ # 前置:查询 Bing 行业(--trade-id bing-industries 输出的 id,与网页下拉 value 一致)
521
521
  siluzan-tso open-account bing-industries --keyword "科技"
522
522
 
523
523
  siluzan-tso open-account bing \
@@ -530,7 +530,7 @@ siluzan-tso open-account bing \
530
530
  --address "南山区科技园XX路XX号XX大厦" \
531
531
  --postcode "518000" \
532
532
  --promotion-link "https://www.brand-a.com" \
533
- --trade-id "IT/消费电子-其他" \
533
+ --trade-id "<bing-industries 输出的 id>" \
534
534
  --license-file "/path/to/license.jpg"
535
535
  ```
536
536
 
@@ -84,7 +84,7 @@
84
84
  | 城市 | `--city` | |
85
85
  | 详细地址 | `--address` | |
86
86
  | 邮编 | `--postcode` | |
87
- | 行业 | `--trade-id` | `bing-industries` 返回的 **name** 字段 |
87
+ | 行业 | `--trade-id` | `bing-industries`;传输出 **id**(与网页下拉 value 一致),勿猜中文名 |
88
88
  | 推广链接 | `--promotion-link` | |
89
89
  | 执照图片 | `--license-file` | JPG/PNG/PDF 本地路径 |
90
90
 
@@ -199,6 +199,8 @@ siluzan-tso account-history -m Yandex
199
199
 
200
200
  **必填**:见上表 § BingV2。
201
201
 
202
+ > **行业(Agent 硬规范)**:与网页一致,数据源为 `TradeList` + `BingTradeList/Read` 合并后的**二级**选项。必须先执行 `open-account bing-industries`(`--keyword` 可匹配中文行业名、分组名、英文 name 或 id),从输出取 **`id`** 填入 `--trade-id`。**禁止**凭记忆写行业名或编造「一级/二级」拼接字符串。
203
+
202
204
  ```bash
203
205
  siluzan-tso open-account bing-industries --keyword "科技"
204
206
 
@@ -212,7 +214,7 @@ siluzan-tso open-account bing \
212
214
  --address "南山区科技园XX路XX号" \
213
215
  --postcode "518000" \
214
216
  --promotion-link "https://www.brand-a.com" \
215
- --trade-id "IT/消费电子-其他" \
217
+ --trade-id "<bing-industries 输出的 id>" \
216
218
  --license-file "/path/to/license.jpg"
217
219
 
218
220
  siluzan-tso account-history -m BingV2
@@ -32,8 +32,8 @@
32
32
  1. **先确认统计区间**:用户未给明确起止日期时**必须反问**(参见 SKILL.md 时间范围强制反问)。
33
33
  2. 确定报告维度(默认含:执行摘要、每日趋势、月度汇总、系列表现、设备分布、地域分布、关键词表现、优化建议),详见 `report-templates/README.md`。若用户使用 **「使用 okki 周报模板…」「OKKI 周报」** 等固定话术发客户,改走 **`report-templates/okki-weekly-google-client.md`**(与 SKILL **P6** 一致),不再按 `google-period-report.md` 的默认 8 维追问。
34
34
  3. **拉数**:使用 `google-analysis … --json-out <dir>`(Google)或对应 `report <media>-*` 命令落盘。
35
- 4. **编写并执行代码**从磁盘读取 `manifest-<accountId>.json` 与各 `<section>-<accountId>.json` 来完成筛选、聚合、排序等计算;**禁止**用 `Read` JSON 后在对话里心算或手填报告数字。
36
- - **写脚本前先读 `<section>-<accountId>.outline.txt`**(与 JSON stem 的纯文本,最后一行是 TS 式类型字面量)了解字段结构;它体积只有几百字节、不含数据,比直接 `Read` 整个 `*.json`(动辄几 MB / 几万行)省**两到三个数量级**的上下文,不要 `require()`,用 `fs.readFileSync(outlineFile,'utf8')` 读最后一行即可。
35
+ 4. **编写并执行代码**从磁盘读取 `manifest-<accountId>.json` 与各 `<section>-<accountId>.json` 来完成筛选、聚合、排序等计算;**禁止**用宿主 `Read` 打开业务 `*.json`;**禁止**在对话里心算或手填报告数字。
36
+ - **写脚本前**用 Read 仅打开 `<section>-<accountId>.outline.txt`(或 `node -e` 读最后一行类型字面量)了解字段结构;**业务 JSON 只许在脚本里** `require` / `readFileSync`,脚本 `console.log` 打印所需汇总。
37
37
  - 真实数据始终从 `<section>-<accountId>.json` 通过代码获取,**不要**把 outline 当作业务数据贴给用户。
38
38
  5. **由代码写出最终文件**(HTML/Excel/PDF/PPT/Markdown/word 等)。**禁止**在报告脚本中以源码字面量写死应从 JSON 读取的业务数据(消耗金额、系列名、日期区间等)。允许的常量仅限:快照目录路径、JSON 字段键名、版式/结构占位。
39
39
  6. **报告首行**须标注:`统计区间:YYYY-MM-DD ~ YYYY-MM-DD(货币:XXX)`
@@ -24,7 +24,8 @@
24
24
 
25
25
  - **禁止**「上次已经读过 `accounts/accounts.md` / `google-ads/google-ads.md`,本任务直接 `siluzan-tso …`」
26
26
  - **禁止**用对话记忆中的示例 ID、金额、命令 flags 代替当次 Read 文档
27
- - **禁止**跳过 `references/core/tips.md` 里的 outlineJSON 顺序
27
+ - **禁止**跳过 `references/core/tips.md` 里的 outline→脚本读 JSON 顺序
28
+ - **禁止**用 Read / `cat` / `type` 打开 `--json-out` 落盘的**业务数据** `*.json`(`campaigns-*.json`、`list-accounts-*.json` 等);只允许 Read 体积小的 `*.outline.txt` 与 `references/` 文档
28
29
 
29
30
  ---
30
31
 
@@ -36,7 +37,8 @@
36
37
  2. 涉及写入/修改/删除的操作必须与用户确认
37
38
  3. 按计划执行,说明每步意图
38
39
  4. 用成对的读命令复核写入结果;异步任务每 5s 轮询直到完成
39
- 5. 全部完成后预测用户下一步操作
40
+ 5. **交付前**(报告/Excel/含金额话术):Read `references/core/deliverable-preflight.md`,**Read 最终产物文件**并按自检表 A/B/C 逐项确认后再发给用户
41
+ 6. 全部完成后预测用户下一步操作
40
42
 
41
43
  ### 执行模式速查
42
44
 
@@ -77,15 +79,27 @@
77
79
 
78
80
  本 Skill 下**所有**业务数据均以 CLI `--json-out`(或用户提供的同构 JSON)落盘为唯一真相源。Agent **不得**在对话或脚本里「代替」磁盘数据。
79
81
 
82
+ ### 禁止 Read 完整业务 JSON(最高优先级)
83
+
84
+ `--json-out` 落盘的 `writtenFiles[0]`(及同目录 `campaigns-*.json`、`keywords-*.json` 等)往往为 **MB 级**。Agent **永远不得**用宿主 **Read** 工具、`cat`、`type`、`Get-Content` 等方式把整文件读进对话上下文。
85
+
86
+ | 允许 Read 的文件 | 必须用代码读取的文件 |
87
+ | ---------------- | -------------------- |
88
+ | `references/**/*.md`、`assets/**/*.md`(Skill 文档) | 所有 `--json-out` 业务 `*.json` |
89
+ | 当次 `*.outline.txt`(通常小于 2KB,仅 schema) | `cli-manifest*.json` / `manifest-*.json` 中的路径索引(应用脚本 `JSON.parse` 读,勿 Read 整份 manifest 若很大) |
90
+ | stdout **一行**摘要 JSON(命令回显,体积小) | 用户提供的同构大 JSON 文件 |
91
+
92
+ **正确流程**:解析 stdout 摘要 → Read **仅** `outlineFile` 了解字段类型 → **编写并执行** `node -e` / `node script.mjs` / `python`:`require` 或 `readFileSync` 读 JSON → `console.log` / 写 xlsx/html **只输出所需字段** → 用脚本打印结果回答用户。
93
+
80
94
  | 必须 | 禁止 |
81
95
  | ---- | ---- |
82
- | 编写并**执行** Node/Python 脚本:`readFile` / `require` 读落盘 JSON → 计算 → 写出 | 用 `Read` 读大 JSON 后在回复里手填、改数、心算汇总 |
83
- | **先 outline JSON**:`outlineFile`(`.outline.txt`)→ `writtenFiles[0]` | 跳过 outline 猜字段名;把 outline 当数据贴给用户 |
96
+ | 编写并**执行** Node/Python 脚本读落盘 JSON → 计算 → 写出 | 用 **Read** 打开 `writtenFiles` 业务 JSON;在回复里手填、改数、心算汇总 |
97
+ | **先 outline 后脚本读 JSON**:`outlineFile` → 代码读 `writtenFiles[0]` | 跳过 outline 猜字段名;把 outline 当数据贴给用户 |
84
98
  | 字段路径以 `outlineFile` + 当次 `writtenFiles` / manifest 为准 | 把国家名、ID、金额、词表等**业务值**写成源码字面量 |
85
99
  | 映射表 / 模板契约在运行时加载(`analytics/geo-continents.json`、`campaign-create-template.json`) | 复制配置条目进源码常量 |
86
100
  | 脚本输出与落盘 JSON 可复核 | 用「上次对话记住的数」「占位数」写入交付物 |
87
101
 
88
- **允许的字面量**:输出目录、Sheet/列标题、技术格式、`focusCountries` 等**用户当轮明确给出**的配置(建议落盘为 `config.json` 再读)。
102
+ **允许的字面量**:输出目录、Sheet/列标题、技术格式、`focusCountries` 等**用户当轮明确给出**的配置(建议落盘为 `config.json` 再**用脚本**读)。
89
103
 
90
104
  完整协议见 `references/core/tips.md` § 处理顺序、§ 已有 JSON 时。
91
105
 
@@ -122,6 +136,7 @@
122
136
  - **符号**:`CNY` → **¥**;`USD` → **$**。多账户按 `currencyCode` 分表,**禁止**跨币种求和。
123
137
  - **金额单位统一为「元」**:报告保留 2 位小数。
124
138
  - **Google 广告诊断报告**:`report-templates/google-ads-diagnosis.md` § 撰写硬约束 — 每日趋势金额/CPA 必须 2 位小数;每个模块除表格外须有「分析」+「建议」,禁止只贴数据。
139
+ - **交付前审阅产物**:报告/Excel 交付前 Agent **Read 最终文件**并对照 `deliverable-preflight.md` 自检表;未审阅禁止交付。
125
140
  - **品牌名优先级**:(1) 用户明确提供 → (2) `list-accounts.mag.advertiserName` → (3) 网址域名占位 `[待确认品牌名]`。**严禁**把英文域名翻译为虚构中文品牌。
126
141
 
127
142
  ---
@@ -0,0 +1,106 @@
1
+ # 交付前审阅最终产物(Agent 自检)
2
+
3
+ > 用户反馈:产出物常出现**币种错误**、**报告缺章/缺数**。
4
+ > **不靠外部校验脚本**——由 **Agent 在交付用户前,亲自查看已生成的最终文件**(HTML / Markdown / Excel 等),对照清单结论后再发送。
5
+
6
+ ---
7
+
8
+ ## 原则
9
+
10
+ | 必须 | 禁止 |
11
+ | ---- | ---- |
12
+ | **Read 最终交付文件**(你刚写出的 `.html` / `.md` 等) | 用 Read 打开 `--json-out` 落盘的业务 `*.json` 来「核对」(见 `tips.md`) |
13
+ | 对照当次 `report-templates/*.md` 章节逐项勾选 | 未打开产物就声称「报告已好」 |
14
+ | 币种与**当次对话**里 `list-accounts` / CLI 表格已确认的 `currencyCode` 一致 | 凭媒体猜 USD;CNY 用 `$` |
15
+ | 缺数据的章节写 `[ 数据不可用:… ]` | 空白章节、编造数字填坑 |
16
+
17
+ 拉数阶段仍用 `--json-out` + **代码**读 JSON 生成产物;**审阅阶段**只看**最终产物** + 记忆中的账户元数据(账户 ID、区间、币种),不跑 `check-*.mjs` 类脚本。
18
+
19
+ ---
20
+
21
+ ## 审阅时机
22
+
23
+ 在以下动作**之后、发给用户之前**:
24
+
25
+ 1. 已用脚本/CLI 拉数并生成报告文件(或 Excel)。
26
+ 2. 文件已写入磁盘路径(你知道路径)。
27
+
28
+ **然后必须 Read 该文件**(或 Excel 时对用户说明各 Sheet 名称与首行摘要——若无法 Read 二进制 xlsx,须在对话中贴出**自检表**逐条勾选,并说明依据来自生成脚本的 stdout 摘要,而非猜)。
29
+
30
+ ---
31
+
32
+ ## 自检表 A · 币种(每条必勾)
33
+
34
+ 对照 `references/accounts/currency.md`:
35
+
36
+ - [ ] 报告**封面/首行**含 `统计区间:…(货币:CNY)` 或 `(货币:USD)`(或等价英文标注)
37
+ - [ ] 代码与**一致**:`CNY` → 全文金额用 **¥**;`USD` → **$**;未混用
38
+ - [ ] 与当次 `list-accounts -k …` 得到的 **`currencyCode` 相同**(回顾对话中 CLI 表格或你记录的「币种」一行)
39
+ - [ ] 多账户报告:分币种分表,**无** CNY+USD 直接相加的一行「总计」
40
+ - [ ] 未出现「Google 账户所以是美金」类表述
41
+
42
+ **不通过** → 改产物文件后**重新 Read** 再审,不得交付。
43
+
44
+ ---
45
+
46
+ ## 自检表 B · 结构完整(对照当次模板)
47
+
48
+ 先确认当次用的是哪份模板(如 `google-period-report.md`、`okki-weekly-google-client.md`、`google-inquiry-analysis.md`),再逐项:
49
+
50
+ - [ ] 模板要求的**每一章/Sheet**在产物里都存在(标题或 Sheet 名可对应)
51
+ - [ ] **无**整章空白(除非该章标注 `[ 数据不可用:… ]`)
52
+ - [ ] 执行摘要 / 概览:有消耗、展示、点击等**至少一项非空数字**(或明确写无数据)
53
+ - [ ] 周期报告默认维(P4):概览、日趋势、系列、设备、地域、关键词等**均有对应段落**(Meta 无接口的维度按模板写「未提供」)
54
+ - [ ] 优化建议:独立一节,**不是**只有表格没有文字
55
+ - [ ] Google 诊断:每模块除表格外有「分析」+「建议」(见 `google-ads-diagnosis.md`)
56
+
57
+ **不通过** → 补拉 CLI 维度、重跑生成逻辑、**重写产物**后再 Read 审阅。
58
+
59
+ ---
60
+
61
+ ## 自检表 C · 数字可信(抽样,不读大 JSON)
62
+
63
+ 不打开落盘 JSON,用以下**已掌握信息**对照产物:
64
+
65
+ - [ ] 报告总消耗/CPA 数量级与你在生成过程中 **CLI/脚本 stdout 打印的汇总**一致(同一会话里应有过一行 summary;若无,先补跑一次极小 `node -e` 只打印 totals 再对照——**仅用于核对,不把 JSON 贴进对话**)
66
+ - [ ] 账户 ID、账户名、统计区间与用户需求一致
67
+ - [ ] 无「示例账户」「占位 123456」等模板残留
68
+ - [ ] 表格行数与预期同一(如 P3 要求每个 `-a` ID 都有一行)
69
+
70
+ **不通过** → 修正产物或重新生成,禁止手改数字糊弄。
71
+
72
+ ---
73
+
74
+ ## 向用户交付时的格式(建议)
75
+
76
+ 审阅通过后,消息中附带简短**自检结论**(证明你看过产物):
77
+
78
+ ```text
79
+ 交付前自检(已通过):
80
+ - 产物:./out/report-xxx.html
81
+ - 币种:CNY(来自 list-accounts,与报告首行一致)
82
+ - 章节:8/8 默认维度齐全;关键词章 [ 数据不可用:接口超时 ] 已标注
83
+ - 区间:2026-04-01 ~ 2026-04-30
84
+ ```
85
+
86
+ 若某项未通过,**不要**写「已通过」,应说明正在补全。
87
+
88
+ ---
89
+
90
+ ## 与 Playbook 的关系
91
+
92
+ | Playbook | 审阅时重点 |
93
+ | -------- | ---------- |
94
+ | P1 | 画像是否含 stats + 系列要点;币种 |
95
+ | P4 | `google-period-report.md` 8 块是否齐 |
96
+ | P4-FB | 无接口维度是否写「未提供」 |
97
+ | P6 / P7 | 按各自 `report-templates/*.md` Sheet/章节表逐项 |
98
+
99
+ ---
100
+
101
+ ## 相关文档
102
+
103
+ - `references/accounts/currency.md`
104
+ - `report-templates/REPORT-WORKFLOW.md`
105
+ - `references/core/tips.md`(拉数阶段)
106
+ - `references/core/playbooks.md`
@@ -4,10 +4,10 @@
4
4
 
5
5
  | 编号 | 场景 | 必读 references |
6
6
  | ---- | ---- | --------------- |
7
- | P1 | 单账户投放画像 | `analytics/account-analytics.md`、`accounts/currency.md`、`core/tips.md` |
7
+ | P1 | 单账户投放画像 | `analytics/account-analytics.md`、`accounts/currency.md`、`core/tips.md`、`core/deliverable-preflight.md` |
8
8
  | P2 | 多账户余额扫描 | `accounts/accounts.md`(`balance-scan`)、`accounts/currency.md` |
9
9
  | P3 | 多账户投放画像汇总 | `accounts/accounts.md` § accounts-digest、`accounts/currency.md`、`core/tips.md` |
10
- | P4 | Google 账户周期报告 | `report-templates/google-period-report.md`、`analytics/account-analytics.md`、`core/tips.md` |
10
+ | P4 | Google 账户周期报告 | `report-templates/google-period-report.md`、`analytics/account-analytics.md`、`core/tips.md`、`core/deliverable-preflight.md` |
11
11
  | P4-FB | Meta/Facebook 周期报告 | `report-templates/meta-period-report.md`、`analytics/facebook-analysis-guide.md`、`analytics/account-analytics.md`、`core/tips.md` |
12
12
  | P5 | 多账户多维度报告 | `analytics/google-analysis-batch.md`、`analytics/account-analytics.md`、`core/tips.md`;可选 `core/subagent-orchestration.md` |
13
13
  | P6 | OKKI 周报 | `report-templates/okki-weekly-google-client.md`(全文)、`analytics/account-analytics.md`、`core/tips.md`;可选 `core/subagent-orchestration.md` |
@@ -24,6 +24,7 @@
24
24
  4. `ad campaigns -a <mediaCustomerId> --start <S> --end <D> --json-out ./snap-p1`
25
25
  5. 诊断/画像报告:Read `report-templates/google-ads-diagnosis.md`(**硬约束:每日趋势 2 位小数、每模块必有分析**)+ `google-account-diagnosis-report.md`(章节与 CLI);`google-analysis --json-out` 须含 `daily-metrics`(按日趋势)。
26
26
  6. 首行标注统计区间和货币;HTML 可对齐 `report-template.html` + `section-*` 区块。
27
+ 7. 交付前:**Read 最终产物**,按 `deliverable-preflight.md` 自检表确认币种与章节。
27
28
 
28
29
  ---
29
30
 
@@ -66,6 +67,7 @@ siluzan-tso accounts-digest -m Google -a id1,id2,... --start <S> --end <D> --jso
66
67
  1. 确认时间范围;区间 > 3 个月时分段(季度/月)。
67
68
  2. 按 `google-period-report.md` 默认维度执行 `google-analysis --json-out <dir>`。
68
69
  3. 报告须含:账户概览、投放趋势、Top 关键词/系列/地区分布、优化建议。
70
+ 4. 交付前:**Read 最终 HTML/报告文件**,对照 `google-period-report.md` 与 `deliverable-preflight.md` 自检。
69
71
 
70
72
  ---
71
73
 
@@ -58,7 +58,7 @@
58
58
  | `mediaCustomerId` | 来自当次 `list-accounts`,禁止猜 |
59
59
  | `dateRange` | `start` / `end`(YYYY-MM-DD);P7 另附 `m1`/`m2`/`m3` |
60
60
  | `commands[]` | 可复制 bash 块(来自 playbook 或 report-template) |
61
- | `forbidden[]` | 至少包含:禁止 Read 全量 MB JSON 进回复;禁止编造 ID/金额;禁止 batch 重新 `run`(仅 `resume`) |
61
+ | `forbidden[]` | 至少包含:禁止 Read/cat 打开落盘业务 `*.json`(须 node/python 脚本读盘);禁止把 JSON 贴进回复;禁止编造 ID/金额;禁止 batch 重新 `run`(仅 `resume`) |
62
62
  | `returnSchema` | 子会话**只**回传下列内容 |
63
63
 
64
64
  ### returnSchema(子会话结束时的回复格式)
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **文档重读(与落盘无关,但同样硬)**:**每个新的用户任务**或**同对话内换话题**时,须按 `references/core/agent-conventions.md` § 文档加载纪律 **重新 Read** 当次任务对应的 `references/*.md` / `report-templates/*.md`,再执行 CLI。
4
4
 
5
- > **AI 助手(本 Skill)**:凡需结构化数据,一律使用 **`--json-out <路径>`**(**目录** **`*.json` 具体文件**)将 JSON 写入磁盘(并更新 **`cli-manifest[-<查询id>].json`**,与 `google-analysis` **`manifest-<accountId>.json`**、`report …` **`report-manifest[-<accountId>].json`** 并存于各自目录),再用 **脚本读文件**(`fs.readFileSync` / `require`)做筛选与聚合;**不要**依赖对话里粘贴的 stdout 整包 JSON。命令成功时 **stdout 仅一行摘要 JSON**(体积小),其中 **`outlineFile`** 指向与当次 `*.json` **同 stem** 的 **`*.outline.txt`**(纯文本单行,接近 **TypeScript 类型字面量**:`{ a: number; b: string }`、`(T | U)[]` 等;数组由**前 8 项**去重推断并集;环 / 同对象再出现为 `any`)。完整数据仍以落盘 `*.json` 为准,类型扫一眼读 outline 文件即可。
5
+ > **AI 助手(本 Skill)**:凡需结构化数据,一律 **`--json-out <路径>`** 落盘,再用 **Node/Python 脚本**(`require` / `readFileSync`)读 JSON 做筛选与聚合。**禁止**用宿主 **Read** 工具打开落盘业务 `*.json`(会占满上下文且易截断)。命令成功时 **stdout 仅一行摘要 JSON**(体积小);**`outlineFile`**(`*.outline.txt`,通常小于 2KB)是唯一允许用 Read 查看的「数据结构」文件——最后一行是 TS 类型字面量。业务数据**只**通过脚本访问,脚本 stdout 打印你需要的行/汇总即可。
6
6
  >
7
7
  > **文件命名规则(重要)**:当一条命令具备明确的"查询 id"(mediaCustomerId / entityId / ruleId / auditId 等)时,目录模式落盘的 `*.json`、`*.outline.txt` 与对应 manifest **都会带上 `-<查询id>` 后缀**:
8
8
  >
@@ -21,23 +21,26 @@
21
21
 
22
22
  ---
23
23
 
24
- ## 处理顺序(**Agent 必读**):先 outline JSON → 再脚本聚合
24
+ ## 处理顺序(**Agent 必读**):摘要 outline(schema)→ **脚本**读 JSON
25
25
 
26
26
  每条 `--json-out` 命令成功后,**必须**按以下顺序处理,不要跳步、不要把 outline 当数据:
27
27
 
28
- 1. **先读 stdout 一行摘要 JSON**:拿到 `outlineFile`、`writtenFiles[0]`、`manifestFile`,以及固定回显的 **`agentHint`** 一行说明(即"先读 outline 再读 JSON"的提示)。**不要硬编码 `<section>.json` 文件名**。
29
- 2. **再 `fs.readFileSync(outlineFile, 'utf8')` 读 outline 文件**(即 `*.outline.txt`):
28
+ 1. **解析 stdout 一行摘要 JSON**(禁止 Read 打开 `writtenFiles[0]` 的 `.json`):拿到 `outlineFile`、`writtenFiles[0]`、`manifestFile`、`agentHint`。**不要硬编码 `<section>.json` 文件名**。
29
+ 2. **了解字段结构**(二选一,推荐 A):
30
+ - **A**:宿主 **Read** 仅 `outlineFile`(`*.outline.txt`,体积小);或
31
+ - **B**:`node -e "const fs=require('fs'); const o=fs.readFileSync(process.argv[1],'utf8').trimEnd().split('\\n').filter(l=>!l.startsWith('//')); console.log(o.pop())" <outlineFile>`
30
32
  - 第 1 行(注释):`// outline of \`<xxx>.json\` — schema-only, NOT the data.` —— **明确告诉你它是"数据结构描述文件"**,不要把它当业务数据使用。
31
33
  - 第 2 行(注释):中文用法说明。
32
34
  - 第 3 行(注释):类型推断口径(数组取前 8 项去重并集;环或重复对象记为 any)。
33
35
  - 其后**可能**还有若干行 `//` 字段提示(例如 `google-analysis` 的 **`campaigns-*.outline.txt`** 会插入金额字段已统一为元的说明)。
34
36
  - **类型字面量**:**最后一个不以 `//` 开头的行**(单行 TS,如 `{ a: number; items: { ... }[] }`)—— 写筛选/聚合脚本时取这一行即可。
35
37
  - 提取写法:`outlineRaw.trimEnd().split('\n').filter(l => !l.startsWith('//')).pop()`(**不要**用 `lines[lines.length - 1]`:当最后一行是空行时会错)。
36
- 3. **再 `require(writtenFiles[0])` / `fs.readFileSync` 读真实 JSON 数据**做筛选、聚合、计算。**禁止**:
37
- - outline 文件 `require` 进来当 JSON 解析(它是 `.txt` 不是 JSON);
38
- - 跳过 outline 直接猜 JSON 字段名;
39
- - outline 当成"摘要数据"贴给用户当结论。
40
- 4. **交付物**用代码(Node / Python + 数据处理库)写出对应格式(HTML / Excel / PDF / PPT / Markdown 等),不要在对话里手填数。
38
+ 3. **必须用 Bash 执行脚本**读 `writtenFiles[0]`(及 manifest 中的路径)做筛选、聚合、计算——脚本内 `require` / `readFileSync`,**禁止**宿主 Read / cat / type 打开该 `.json`。**禁止**:
39
+ - **Read** `campaigns-*.json`、`list-accounts-*.json` 等落盘业务 JSON
40
+ - outline JSON `require`(它是 `.txt`);
41
+ - 跳过 outline 猜字段名;
42
+ - outline 或整段 JSON 贴进对话当结论。
43
+ 4. **交付物**用代码写出(HTML / Excel / PDF / PPT / Markdown 等);向用户展示的数字须来自**脚本 stdout**,不要在对话里手填数。
41
44
 
42
45
  > 这个顺序对所有三类快照统一生效:通用业务命令(`cli-manifest[-<id>].json`)、`google-analysis`(`manifest-<accountId>.json`)、`report …` 分析(`report-manifest[-<accountId>].json`)。三种 summary 都带 `outlineFile`、`agentHint` 字段。
43
46
 
@@ -324,8 +327,8 @@ console.log('可用字段:', first && Object.keys(first));
324
327
 
325
328
  ## AI Agent 使用规范
326
329
 
327
- - **优先用 `--json-out` 与清单文件**:同一运行目录、同一"查询 id"的多次拉数会合并到 `cli-manifest-<id>.json` 的 `artifacts[]`;不同账户/实体写入各自的 manifest,互不覆盖。读具体数据请打开 stdout 摘要 `writtenFiles[0]` 或对应 `<section>-<id>.json`。
328
- - **中间结果一律落盘**:跨步骤数据不要靠对话记忆,写到工作目录或临时目录并在后续脚本中 `require` / `readFile`。
330
+ - **优先用 `--json-out` 与清单文件**:同一运行目录、同一"查询 id"的多次拉数会合并到 `cli-manifest-<id>.json` 的 `artifacts[]`;不同账户/实体写入各自的 manifest,互不覆盖。读具体数据**只**在脚本里 `require(writtenFiles[0])`,**禁止 Read 工具**打开该 json
331
+ - **中间结果一律落盘**:跨步骤数据不要靠对话记忆,写到工作目录并在后续 **`node`/`python` 脚本**中读盘。
329
332
  - **Windows 上避免直接管道 JSON**:PowerShell 管道可能改变编码;优先 `--json-out` + `node -e` 读文件。
330
333
  - **用 `process.stdout.write` 而不是 `console.log` 提取单个值**:前者不带换行符,方便直接赋给 shell 变量。
331
334
  - **节点代码复杂时拆分写法**:不要写超过 10 行的 `node -e` 单行,改用 `node -e "$(cat <<'EOF'\n...\nEOF\n)"` 或多行 `.mjs` 脚本文件。
@@ -135,7 +135,7 @@ siluzan-tso account-history -m Yandex
135
135
  **必填清单**:见 `open-account-by-media.md` § BingV2(含 `--pattern`、`--name-remark-list`、`--postcode`)。
136
136
 
137
137
  ```bash
138
- # 前置:查询行业名称(将输出的 name 值传给 --trade-id
138
+ # 前置:查询行业(将 bing-industries 输出的 id 传给 --trade-id,勿猜行业名)
139
139
  siluzan-tso open-account bing-industries --keyword "科技"
140
140
 
141
141
  # 提交开户(CLI 自动按公司名创建/关联广告主组,无需 magKey)
@@ -149,7 +149,7 @@ siluzan-tso open-account bing \
149
149
  --address "南山区科技园XX路XX号XX大厦" \
150
150
  --postcode "518000" \
151
151
  --promotion-link "https://www.brand-a.com" \
152
- --trade-id "IT/消费电子-其他" \
152
+ --trade-id "<bing-industries 输出的 id>" \
153
153
  --license-file "/path/to/license.jpg"
154
154
 
155
155
  # 轮询审核进度
@@ -51,6 +51,14 @@
51
51
 
52
52
  ---
53
53
 
54
+ ### 步骤 4b:交付前审阅最终产物(**必做**)
55
+
56
+ - Read `references/core/deliverable-preflight.md` 自检表 A/B/C。
57
+ - **Read 刚生成的报告文件**(HTML/Markdown 等),对照当次 `report-templates/*.md` 逐项确认章节与币种。
58
+ - 未通过:**修改产物或补拉数据后重新生成**,再次 Read 审阅;不得在未审阅时交付。
59
+
60
+ ---
61
+
54
62
  ### 步骤 5:撰写报告
55
63
 
56
64
  - 按 `*.md` 章节结构组织内容。
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  $PKG_NAME = 'siluzan-tso-cli'
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- $PKG_VERSION = '1.1.26-beta.1'
12
+ $PKG_VERSION = '1.1.26-beta.3'
13
13
  $CLI_BIN = 'siluzan-tso'
14
14
  $SKILL_LABEL = 'Siluzan TSO'
15
15
  $INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
@@ -9,7 +9,7 @@ set -euo pipefail
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  readonly PKG_NAME="siluzan-tso-cli"
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- readonly PKG_VERSION="1.1.26-beta.1"
12
+ readonly PKG_VERSION="1.1.26-beta.3"
13
13
  readonly CLI_BIN="siluzan-tso"
14
14
  readonly SKILL_LABEL="Siluzan TSO"
15
15
  readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
@@ -6,5 +6,5 @@
6
6
  "writtenFiles": ["meta-overview-3995107670753101.json", "meta-overview-3995107670753101.outline.txt"],
7
7
  "section": "meta-overview",
8
8
  "outlineFile": "meta-overview-3995107670753101.outline.txt",
9
- "agentHint": "Read outline first, then JSON."
9
+ "agentHint": "禁止 Read 业务 *.json;先 outline schema,再用脚本读 JSON"
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.26-beta.1",
3
+ "version": "1.1.26-beta.3",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",