siluzan-tso-cli 1.1.26 → 1.1.27-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.js +721 -102
- package/dist/skill/AGENTS.md +3 -1
- package/dist/skill/SKILL.md +5 -4
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/market-analysis-rules.md +134 -0
- package/dist/skill/references/README.md +3 -1
- package/dist/skill/references/accounts/accounts.md +18 -4
- package/dist/skill/references/accounts/finance.md +32 -32
- package/dist/skill/references/accounts/open-account-google-ui.md +1 -1
- package/dist/skill/references/analytics/market-analysis-guide.md +118 -0
- package/dist/skill/references/analytics/rag.md +1 -1
- package/dist/skill/references/analytics/reporting.md +5 -5
- package/dist/skill/references/analytics/website-diagnosis-guide.md +3 -3
- package/dist/skill/references/core/agent-conventions.md +1 -1
- package/dist/skill/references/core/cli-enums.md +140 -0
- package/dist/skill/references/core/playbooks.md +35 -2
- package/dist/skill/references/core/setup.md +5 -5
- package/dist/skill/references/core/skill-authoring.md +1 -1
- package/dist/skill/references/core/tips.md +1 -1
- package/dist/skill/references/core/workflows.md +4 -4
- package/dist/skill/references/misc/tso-home.md +2 -2
- package/dist/skill/report-templates/market-analysis-report.md +40 -0
- package/dist/skill/report-templates/website-diagnosis-report.html +1869 -420
- package/dist/skill/report-templates/website-diagnosis-report.md +11 -1
- package/dist/skill/report-templates/website-diagnosis-report.runtime.js +0 -0
- package/dist/skill/scripts/install.ps1 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- package/eval/cases/no-legacy-json-flag.scenario.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3328,7 +3328,7 @@ var DEFAULT_API_BASE;
|
|
|
3328
3328
|
var init_defaults = __esm({
|
|
3329
3329
|
"src/config/defaults.ts"() {
|
|
3330
3330
|
"use strict";
|
|
3331
|
-
DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
3331
|
+
DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
|
|
3332
3332
|
}
|
|
3333
3333
|
});
|
|
3334
3334
|
|
|
@@ -101225,8 +101225,8 @@ var init_http_retry = __esm({
|
|
|
101225
101225
|
});
|
|
101226
101226
|
|
|
101227
101227
|
// src/utils/batch-manifest.ts
|
|
101228
|
-
import * as
|
|
101229
|
-
import * as
|
|
101228
|
+
import * as fs14 from "fs/promises";
|
|
101229
|
+
import * as path19 from "path";
|
|
101230
101230
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
101231
101231
|
function generateRunId(now = /* @__PURE__ */ new Date()) {
|
|
101232
101232
|
const pad = (n) => String(n).padStart(2, "0");
|
|
@@ -101240,26 +101240,26 @@ function isValidRunId(id) {
|
|
|
101240
101240
|
}
|
|
101241
101241
|
function resolveBatchPaths(baseDir, runId) {
|
|
101242
101242
|
if (!isValidRunId(runId)) throw new Error(`\u975E\u6CD5 runId\uFF1A${runId}`);
|
|
101243
|
-
const runDir =
|
|
101244
|
-
const stateDir =
|
|
101243
|
+
const runDir = path19.resolve(baseDir, runId);
|
|
101244
|
+
const stateDir = path19.join(runDir, "state");
|
|
101245
101245
|
return {
|
|
101246
101246
|
runDir,
|
|
101247
|
-
runManifestFile:
|
|
101248
|
-
progressFile:
|
|
101249
|
-
accountsFile:
|
|
101250
|
-
resultsDir:
|
|
101251
|
-
errorsDir:
|
|
101247
|
+
runManifestFile: path19.join(runDir, "run-manifest.json"),
|
|
101248
|
+
progressFile: path19.join(runDir, "progress.json"),
|
|
101249
|
+
accountsFile: path19.join(runDir, "accounts.json"),
|
|
101250
|
+
resultsDir: path19.join(runDir, "results"),
|
|
101251
|
+
errorsDir: path19.join(runDir, "errors"),
|
|
101252
101252
|
stateDir,
|
|
101253
|
-
tasksLogFile:
|
|
101254
|
-
lockFile:
|
|
101253
|
+
tasksLogFile: path19.join(stateDir, "tasks.jsonl"),
|
|
101254
|
+
lockFile: path19.join(stateDir, "tasks.lock")
|
|
101255
101255
|
};
|
|
101256
101256
|
}
|
|
101257
101257
|
async function ensureAccountDirs(paths, accountId) {
|
|
101258
101258
|
const safe = sanitizeAccountSegment(accountId);
|
|
101259
|
-
const results =
|
|
101260
|
-
const errors =
|
|
101261
|
-
await
|
|
101262
|
-
await
|
|
101259
|
+
const results = path19.join(paths.resultsDir, safe);
|
|
101260
|
+
const errors = path19.join(paths.errorsDir, safe);
|
|
101261
|
+
await fs14.mkdir(results, { recursive: true });
|
|
101262
|
+
await fs14.mkdir(errors, { recursive: true });
|
|
101263
101263
|
return { results, errors };
|
|
101264
101264
|
}
|
|
101265
101265
|
function sanitizeAccountSegment(accountId) {
|
|
@@ -101274,20 +101274,20 @@ function sanitizeAccountSegment(accountId) {
|
|
|
101274
101274
|
return slug;
|
|
101275
101275
|
}
|
|
101276
101276
|
async function atomicWriteFile(target, content) {
|
|
101277
|
-
const dir =
|
|
101278
|
-
await
|
|
101279
|
-
const tmp =
|
|
101277
|
+
const dir = path19.dirname(target);
|
|
101278
|
+
await fs14.mkdir(dir, { recursive: true });
|
|
101279
|
+
const tmp = path19.join(dir, `.tmp-${randomUUID2()}-${path19.basename(target)}`);
|
|
101280
101280
|
try {
|
|
101281
|
-
await
|
|
101282
|
-
await
|
|
101281
|
+
await fs14.writeFile(tmp, content, "utf8");
|
|
101282
|
+
await fs14.rename(tmp, target);
|
|
101283
101283
|
} catch (e) {
|
|
101284
|
-
await
|
|
101284
|
+
await fs14.rm(tmp, { force: true }).catch(() => void 0);
|
|
101285
101285
|
throw e;
|
|
101286
101286
|
}
|
|
101287
101287
|
}
|
|
101288
101288
|
async function readJsonIfExists(file) {
|
|
101289
101289
|
try {
|
|
101290
|
-
const raw = await
|
|
101290
|
+
const raw = await fs14.readFile(file, "utf8");
|
|
101291
101291
|
return JSON.parse(raw);
|
|
101292
101292
|
} catch (e) {
|
|
101293
101293
|
if (e.code === "ENOENT") return null;
|
|
@@ -101317,12 +101317,12 @@ function readProgress(paths) {
|
|
|
101317
101317
|
return readJsonIfExists(paths.progressFile);
|
|
101318
101318
|
}
|
|
101319
101319
|
async function appendTaskLog(paths, entry) {
|
|
101320
|
-
await
|
|
101321
|
-
await
|
|
101320
|
+
await fs14.mkdir(paths.stateDir, { recursive: true });
|
|
101321
|
+
await fs14.appendFile(paths.tasksLogFile, JSON.stringify(entry) + "\n", "utf8");
|
|
101322
101322
|
}
|
|
101323
101323
|
async function readTaskLog(paths) {
|
|
101324
101324
|
try {
|
|
101325
|
-
const raw = await
|
|
101325
|
+
const raw = await fs14.readFile(paths.tasksLogFile, "utf8");
|
|
101326
101326
|
return raw.split("\n").filter((s) => s.trim() !== "").map((line) => JSON.parse(line));
|
|
101327
101327
|
} catch (e) {
|
|
101328
101328
|
if (e.code === "ENOENT") return [];
|
|
@@ -101377,9 +101377,9 @@ function emptyProgress(runId, totalTasks, accountConcurrency, sectionConcurrency
|
|
|
101377
101377
|
};
|
|
101378
101378
|
}
|
|
101379
101379
|
async function tryAcquireLock(paths) {
|
|
101380
|
-
await
|
|
101380
|
+
await fs14.mkdir(paths.stateDir, { recursive: true });
|
|
101381
101381
|
try {
|
|
101382
|
-
const handle = await
|
|
101382
|
+
const handle = await fs14.open(paths.lockFile, "wx");
|
|
101383
101383
|
await handle.writeFile(`${process.pid}@${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
101384
101384
|
await handle.close();
|
|
101385
101385
|
return true;
|
|
@@ -101389,7 +101389,7 @@ async function tryAcquireLock(paths) {
|
|
|
101389
101389
|
}
|
|
101390
101390
|
}
|
|
101391
101391
|
async function releaseLock(paths) {
|
|
101392
|
-
await
|
|
101392
|
+
await fs14.rm(paths.lockFile, { force: true }).catch(() => void 0);
|
|
101393
101393
|
}
|
|
101394
101394
|
var BATCH_SCHEMA_VERSION;
|
|
101395
101395
|
var init_batch_manifest = __esm({
|
|
@@ -101401,8 +101401,8 @@ var init_batch_manifest = __esm({
|
|
|
101401
101401
|
|
|
101402
101402
|
// src/utils/batch-runner.ts
|
|
101403
101403
|
import { performance as performance2 } from "perf_hooks";
|
|
101404
|
-
import * as
|
|
101405
|
-
import * as
|
|
101404
|
+
import * as path20 from "path";
|
|
101405
|
+
import * as fs15 from "fs/promises";
|
|
101406
101406
|
async function runPool(items, concurrencyRef, worker, signal) {
|
|
101407
101407
|
let cursor = 0;
|
|
101408
101408
|
const launch = () => {
|
|
@@ -101541,7 +101541,7 @@ async function runBatch(opts) {
|
|
|
101541
101541
|
}
|
|
101542
101542
|
const inner = {
|
|
101543
101543
|
...emptyProgress(
|
|
101544
|
-
opts.paths.runDir.split(
|
|
101544
|
+
opts.paths.runDir.split(path20.sep).pop() ?? "run",
|
|
101545
101545
|
allTasks.length,
|
|
101546
101546
|
opts.accountConcurrency,
|
|
101547
101547
|
opts.sectionConcurrency
|
|
@@ -101671,7 +101671,7 @@ async function runBatch(opts) {
|
|
|
101671
101671
|
}
|
|
101672
101672
|
);
|
|
101673
101673
|
await writer({
|
|
101674
|
-
snapshotDir:
|
|
101674
|
+
snapshotDir: path20.join(opts.paths.resultsDir, sanitizeAccountSegment(task.accountId)),
|
|
101675
101675
|
section: task.section,
|
|
101676
101676
|
accountId: task.accountId,
|
|
101677
101677
|
dateRange: result.dateRange,
|
|
@@ -101744,7 +101744,7 @@ async function runBatch(opts) {
|
|
|
101744
101744
|
};
|
|
101745
101745
|
}
|
|
101746
101746
|
async function writeTaskError(paths, errorsDir, task, err, cls) {
|
|
101747
|
-
const file =
|
|
101747
|
+
const file = path20.join(errorsDir, `${sanitizeSection(task.section)}.error.json`);
|
|
101748
101748
|
const payload = {
|
|
101749
101749
|
schemaVersion: 1,
|
|
101750
101750
|
taskId: task.id,
|
|
@@ -101790,10 +101790,10 @@ async function persistRunMetadata(input) {
|
|
|
101790
101790
|
accountsTotal: input.accounts.length,
|
|
101791
101791
|
invocation: input.invocation
|
|
101792
101792
|
};
|
|
101793
|
-
await
|
|
101794
|
-
await
|
|
101795
|
-
await
|
|
101796
|
-
await
|
|
101793
|
+
await fs15.mkdir(paths.runDir, { recursive: true });
|
|
101794
|
+
await fs15.mkdir(paths.stateDir, { recursive: true });
|
|
101795
|
+
await fs15.mkdir(paths.resultsDir, { recursive: true });
|
|
101796
|
+
await fs15.mkdir(paths.errorsDir, { recursive: true });
|
|
101797
101797
|
await writeRunManifest(paths, manifest);
|
|
101798
101798
|
await writeAccounts(paths, input.accounts);
|
|
101799
101799
|
return paths;
|
|
@@ -101856,7 +101856,7 @@ __export(google_analysis_batch_exports, {
|
|
|
101856
101856
|
parseAccountIds: () => parseAccountIds,
|
|
101857
101857
|
registerGoogleAnalysisBatchCommands: () => registerGoogleAnalysisBatchCommands
|
|
101858
101858
|
});
|
|
101859
|
-
import * as
|
|
101859
|
+
import * as path21 from "path";
|
|
101860
101860
|
import { performance as performance3 } from "perf_hooks";
|
|
101861
101861
|
function parseSections(input) {
|
|
101862
101862
|
if (!input || !input.trim()) return DEFAULT_SECTIONS;
|
|
@@ -102403,7 +102403,7 @@ var init_google_analysis_batch = __esm({
|
|
|
102403
102403
|
});
|
|
102404
102404
|
|
|
102405
102405
|
// src/commands/google-analysis/register-cli.ts
|
|
102406
|
-
import * as
|
|
102406
|
+
import * as path22 from "path";
|
|
102407
102407
|
import { performance as performance4 } from "perf_hooks";
|
|
102408
102408
|
function resolveSectionList(sections, exclude) {
|
|
102409
102409
|
const allNames = SECTIONS.map((s) => s.name);
|
|
@@ -102493,7 +102493,7 @@ async function runAllSections(opts) {
|
|
|
102493
102493
|
const results = await runWithConcurrency(tasks, concurrency);
|
|
102494
102494
|
const succeeded = results.filter((r) => r.ok).length;
|
|
102495
102495
|
const failed = results.length - succeeded;
|
|
102496
|
-
const absoluteSnapshotDir =
|
|
102496
|
+
const absoluteSnapshotDir = path22.resolve(opts.jsonOut);
|
|
102497
102497
|
const summary = {
|
|
102498
102498
|
kind: "siluzan-tso-google-analysis-snapshot-batch",
|
|
102499
102499
|
absoluteSnapshotDir,
|
|
@@ -102726,8 +102726,8 @@ var init_google_analysis3 = __esm({
|
|
|
102726
102726
|
// src/index.ts
|
|
102727
102727
|
init_dist();
|
|
102728
102728
|
init_dist();
|
|
102729
|
-
import * as
|
|
102730
|
-
import * as
|
|
102729
|
+
import * as fs16 from "fs";
|
|
102730
|
+
import * as path24 from "path";
|
|
102731
102731
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
102732
102732
|
import { Command } from "commander";
|
|
102733
102733
|
|
|
@@ -103212,8 +103212,8 @@ function deriveWebUrl(apiBaseUrl) {
|
|
|
103212
103212
|
}
|
|
103213
103213
|
var TSO_UMI_ROUTE_PREFIX = "/v3umijs/tso";
|
|
103214
103214
|
function buildTsoPageWebUrl(webUrl, tsoSubRoute, extraQuery) {
|
|
103215
|
-
const
|
|
103216
|
-
const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${
|
|
103215
|
+
const path25 = tsoSubRoute.replace(/^\/+/, "");
|
|
103216
|
+
const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${path25}`;
|
|
103217
103217
|
const q = new URLSearchParams({ tso: umiPath });
|
|
103218
103218
|
if (extraQuery) {
|
|
103219
103219
|
for (const [key, value] of Object.entries(extraQuery)) {
|
|
@@ -103221,7 +103221,7 @@ function buildTsoPageWebUrl(webUrl, tsoSubRoute, extraQuery) {
|
|
|
103221
103221
|
if (v) q.set(key, v);
|
|
103222
103222
|
}
|
|
103223
103223
|
}
|
|
103224
|
-
return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${
|
|
103224
|
+
return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${path25}?${q.toString()}`;
|
|
103225
103225
|
}
|
|
103226
103226
|
function cmdConfigShow() {
|
|
103227
103227
|
const shared = readSharedConfig();
|
|
@@ -104295,6 +104295,15 @@ async function fetchTikTokAccountByMediaCustomerId(config, mediaCustomerId, verb
|
|
|
104295
104295
|
const match = items.find((it) => String(it.ma?.mediaCustomerId ?? "") === id);
|
|
104296
104296
|
return match ?? null;
|
|
104297
104297
|
}
|
|
104298
|
+
function stripListAccountsEnrichmentFields(item) {
|
|
104299
|
+
delete item.ma.remainingAccountBudget;
|
|
104300
|
+
delete item.ma.platformStatus;
|
|
104301
|
+
delete item.ma.aritScore;
|
|
104302
|
+
const maExt = item.ma;
|
|
104303
|
+
for (const key of ["spend", "impressions", "clicks", "conversions", "costPerClick"]) {
|
|
104304
|
+
delete maExt[key];
|
|
104305
|
+
}
|
|
104306
|
+
}
|
|
104298
104307
|
async function fetchGoogleAccountByMediaCustomerId(config, mediaCustomerId, verbose) {
|
|
104299
104308
|
const cfg = PLATFORM_CONFIG.Google;
|
|
104300
104309
|
const params = new URLSearchParams();
|
|
@@ -104734,7 +104743,7 @@ async function runListAccounts(opts) {
|
|
|
104734
104743
|
process.exit(1);
|
|
104735
104744
|
}
|
|
104736
104745
|
}
|
|
104737
|
-
if (items.length > 0 &&
|
|
104746
|
+
if (items.length > 0 && opts.detail) {
|
|
104738
104747
|
const groups = /* @__PURE__ */ new Map();
|
|
104739
104748
|
for (const item of items) {
|
|
104740
104749
|
const id = item.ma.mediaCustomerId;
|
|
@@ -104808,6 +104817,10 @@ async function runListAccounts(opts) {
|
|
|
104808
104817
|
targetItem.ma.aritScore = report.summaryScore ?? "-";
|
|
104809
104818
|
}
|
|
104810
104819
|
}
|
|
104820
|
+
} else if (items.length > 0) {
|
|
104821
|
+
for (const item of items) {
|
|
104822
|
+
stripListAccountsEnrichmentFields(item);
|
|
104823
|
+
}
|
|
104811
104824
|
}
|
|
104812
104825
|
const oauthFailWarning = (() => {
|
|
104813
104826
|
if (items.length < 5) return null;
|
|
@@ -104835,8 +104848,8 @@ async function runListAccounts(opts) {
|
|
|
104835
104848
|
const totalInfo = total !== void 0 ? `\uFF0C\u5171 ${total} \u6761` : "";
|
|
104836
104849
|
let listHeader = `
|
|
104837
104850
|
\u5E7F\u544A\u8D26\u6237\u5217\u8868\uFF08\u7B2C ${page} \u9875\uFF0C\u672C\u9875 ${items.length} \u6761${totalInfo}\uFF09`;
|
|
104838
|
-
if (opts.
|
|
104839
|
-
listHeader += "\n \uFF08\
|
|
104851
|
+
if (!opts.detail) {
|
|
104852
|
+
listHeader += "\n \uFF08\u9ED8\u8BA4\u5217\u8868\uFF1A\u672A\u5408\u5E76\u4F59\u989D\u4E0E\u6D88\u8017\uFF0CJSON \u4E0D\u542B\u76F8\u5173\u5B57\u6BB5\uFF1B\u52A0 --detail \u53EF\u62C9\u53D6\u771F\u5B9E\u6570\u503C\uFF0C\u54CD\u5E94\u8F83\u6162\u3002TikTok/Meta \u4ECD\u5408\u5E76\u5217\u8868\u63A5\u53E3\u540C\u5305\u5185\u7684 adList\u3002\uFF09";
|
|
104840
104853
|
}
|
|
104841
104854
|
listHeader += "\n";
|
|
104842
104855
|
console.log(listHeader);
|
|
@@ -104899,9 +104912,9 @@ function register6(program2) {
|
|
|
104899
104912
|
"\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",
|
|
104900
104913
|
void 0
|
|
104901
104914
|
).option(
|
|
104902
|
-
"--
|
|
104903
|
-
"\
|
|
104904
|
-
|
|
104915
|
+
"--detail",
|
|
104916
|
+
"\u5408\u5E76\u4F59\u989D\u4E0E\u8FD1\u671F\u6D88\u8017\uFF08\u53CA\u5C55\u793A/\u70B9\u51FB/\u8F6C\u5316/CPC\u3001Arit \u5F97\u5206\uFF09\uFF1B\u672A\u4F20\u65F6 JSON \u4E0D\u542B\u8FD9\u4E9B\u5B57\u6BB5\uFF0C\u5217\u8868\u66F4\u5FEB",
|
|
104917
|
+
false
|
|
104905
104918
|
).option("--unicode", "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08cli-table3\uFF09\uFF1B\u9ED8\u8BA4 ASCII +-|", false).option("--plain", "\u5DF2\u9ED8\u8BA4 ASCII \u7EBF\u6846\uFF0C\u65E0\u9700\u518D\u4F20\uFF1B\u4FDD\u7559\u517C\u5BB9\u65E7\u811A\u672C", false).option(
|
|
104906
104919
|
"--refresh-dp",
|
|
104907
104920
|
"\u5F3A\u5236\u91CD\u62C9 Datapermission \u540E\u518D\u8BF7\u6C42\u5217\u8868\uFF08\u7528\u4E8E\u300C\u7B2C\u4E8C\u6B21\u62C9\u53D6\u5168\u90E8\u5931\u6548\u300D\u7C7B\u4F1A\u8BDD\u5F02\u5E38\u7684\u4E00\u952E\u6392\u67E5\uFF09",
|
|
@@ -104916,7 +104929,7 @@ function register6(program2) {
|
|
|
104916
104929
|
page: opts.page,
|
|
104917
104930
|
pageSize: opts.pageSize,
|
|
104918
104931
|
jsonOut: opts.jsonOut,
|
|
104919
|
-
|
|
104932
|
+
detail: opts.detail,
|
|
104920
104933
|
unicode: opts.unicode,
|
|
104921
104934
|
refreshDp: opts.refreshDp,
|
|
104922
104935
|
verbose: opts.verbose
|
|
@@ -110395,7 +110408,7 @@ function cloneKeywordBlockShell(block) {
|
|
|
110395
110408
|
delete shell["matchTypeV2"];
|
|
110396
110409
|
return shell;
|
|
110397
110410
|
}
|
|
110398
|
-
function splitKeywordsForBatchJobBlockIfMixed(block,
|
|
110411
|
+
function splitKeywordsForBatchJobBlockIfMixed(block, path25, warnings) {
|
|
110399
110412
|
const declaredUi = matchTypeV2ToUi(readBlockMatchTypeRaw(block));
|
|
110400
110413
|
const entries = collectKeywordEntriesFromBlock(block);
|
|
110401
110414
|
if (entries.length === 0) return [block];
|
|
@@ -110414,7 +110427,7 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path24, warnings) {
|
|
|
110414
110427
|
(ui) => matchTypeUiToV2(ui)
|
|
110415
110428
|
);
|
|
110416
110429
|
warnings.push(
|
|
110417
|
-
`${
|
|
110430
|
+
`${path25} \u542B\u591A\u79CD\u5339\u914D\u7C7B\u578B\uFF0C\u5DF2\u81EA\u52A8\u62C6\u5206\u4E3A ${groups.size} \u4E2A KeywordsForBatchJob \u5757\uFF08${labels.join("\u3001")}\uFF09`
|
|
110418
110431
|
);
|
|
110419
110432
|
const shell = cloneKeywordBlockShell(block);
|
|
110420
110433
|
const splitBlocks = [];
|
|
@@ -110434,16 +110447,16 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path24, warnings) {
|
|
|
110434
110447
|
}
|
|
110435
110448
|
return splitBlocks;
|
|
110436
110449
|
}
|
|
110437
|
-
function validateKeywordCore(core,
|
|
110450
|
+
function validateKeywordCore(core, path25, errors, lengthViolations) {
|
|
110438
110451
|
const trimmed = core.trim();
|
|
110439
110452
|
if (!trimmed) {
|
|
110440
|
-
errors.push(`${
|
|
110453
|
+
errors.push(`${path25} \u5173\u952E\u8BCD\u8BCD\u5E72\u4E0D\u80FD\u4E3A\u7A7A`);
|
|
110441
110454
|
return false;
|
|
110442
110455
|
}
|
|
110443
110456
|
if (trimmed.length > GOOGLE_KEYWORD_MAX_CORE_LENGTH) {
|
|
110444
110457
|
if (lengthViolations) {
|
|
110445
110458
|
pushLengthViolation(lengthViolations, {
|
|
110446
|
-
path:
|
|
110459
|
+
path: path25,
|
|
110447
110460
|
field: "KeywordText",
|
|
110448
110461
|
kind: "keyword_core",
|
|
110449
110462
|
limit: GOOGLE_KEYWORD_MAX_CORE_LENGTH,
|
|
@@ -110453,13 +110466,13 @@ function validateKeywordCore(core, path24, errors, lengthViolations) {
|
|
|
110453
110466
|
});
|
|
110454
110467
|
}
|
|
110455
110468
|
errors.push(
|
|
110456
|
-
`${
|
|
110469
|
+
`${path25} \u5173\u952E\u8BCD\u8BCD\u5E72\u8D85\u8FC7 ${GOOGLE_KEYWORD_MAX_CORE_LENGTH} \u5B57\u7B26\uFF08\u5F53\u524D ${trimmed.length}\uFF09\uFF1A"${trimmed}"`
|
|
110457
110470
|
);
|
|
110458
110471
|
return false;
|
|
110459
110472
|
}
|
|
110460
110473
|
if (!VALID_CORE_REGEX.test(trimmed)) {
|
|
110461
110474
|
errors.push(
|
|
110462
|
-
`${
|
|
110475
|
+
`${path25} \u542B Google \u4E0D\u5141\u8BB8\u7684\u5B57\u7B26\uFF08\u8BCD\u5E72\u4EC5\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u7A7A\u683C/\u8FDE\u5B57\u7B26/\u53E5\u70B9\uFF09\uFF1A"${trimmed.slice(0, 40)}${trimmed.length > 40 ? "\u2026" : ""}"`
|
|
110463
110476
|
);
|
|
110464
110477
|
return false;
|
|
110465
110478
|
}
|
|
@@ -110516,24 +110529,24 @@ function canonicalizeKeywordBatchBlock(block, resolvedMatchV2, normalizedTexts,
|
|
|
110516
110529
|
}
|
|
110517
110530
|
}
|
|
110518
110531
|
}
|
|
110519
|
-
function pushKeywordAutoFixWarning(warnings,
|
|
110532
|
+
function pushKeywordAutoFixWarning(warnings, path25, fieldLabel, trimmedRaw, formatted, declaredUi, beforeUi, matchType, inferredMatchType) {
|
|
110520
110533
|
if (inferredMatchType) {
|
|
110521
110534
|
warnings.push(
|
|
110522
|
-
`${
|
|
110535
|
+
`${path25}.${fieldLabel} \u672A\u6307\u5B9A MatchTypeV2\uFF0C\u5DF2\u6309\u8BCD\u9762\u63A8\u65AD\u4E3A ${matchTypeUiToV2(matchType)}`
|
|
110523
110536
|
);
|
|
110524
110537
|
return;
|
|
110525
110538
|
}
|
|
110526
110539
|
if (declaredUi && beforeUi !== declaredUi) {
|
|
110527
110540
|
warnings.push(
|
|
110528
|
-
`${
|
|
110541
|
+
`${path25}.${fieldLabel} MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u4E0E\u8BCD\u9762\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`
|
|
110529
110542
|
);
|
|
110530
110543
|
return;
|
|
110531
110544
|
}
|
|
110532
110545
|
if (formatted !== trimmedRaw) {
|
|
110533
|
-
warnings.push(`${
|
|
110546
|
+
warnings.push(`${path25}.${fieldLabel} \u5DF2\u81EA\u52A8\u4FEE\u590D\u8BCD\u9762\uFF1A${trimmedRaw} \u2192 ${formatted}`);
|
|
110534
110547
|
}
|
|
110535
110548
|
}
|
|
110536
|
-
function normalizeKeywordTextList(texts, matchTypeRaw,
|
|
110549
|
+
function normalizeKeywordTextList(texts, matchTypeRaw, path25, fieldLabel, errors, warnings, lengthViolations) {
|
|
110537
110550
|
const declaredUi = matchTypeV2ToUi(matchTypeRaw);
|
|
110538
110551
|
const normalized = [];
|
|
110539
110552
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -110541,7 +110554,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path24, fieldLabel, error
|
|
|
110541
110554
|
for (let k = 0; k < texts.length; k++) {
|
|
110542
110555
|
const raw = texts[k];
|
|
110543
110556
|
if (typeof raw !== "string" || !raw.trim()) {
|
|
110544
|
-
errors.push(`${
|
|
110557
|
+
errors.push(`${path25}.${fieldLabel}[${k}] \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
110545
110558
|
continue;
|
|
110546
110559
|
}
|
|
110547
110560
|
const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
|
|
@@ -110549,7 +110562,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path24, fieldLabel, error
|
|
|
110549
110562
|
const { formatted, matchType, inferredMatchType } = normalizeKeywordSurface(trimmedRaw, matchTypeRaw);
|
|
110550
110563
|
pushKeywordAutoFixWarning(
|
|
110551
110564
|
warnings,
|
|
110552
|
-
|
|
110565
|
+
path25,
|
|
110553
110566
|
fieldLabel,
|
|
110554
110567
|
trimmedRaw,
|
|
110555
110568
|
formatted,
|
|
@@ -110559,10 +110572,10 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path24, fieldLabel, error
|
|
|
110559
110572
|
inferredMatchType
|
|
110560
110573
|
);
|
|
110561
110574
|
const core = unwrapKeywordDisplayTextForEdit(formatted);
|
|
110562
|
-
if (!validateKeywordCore(core, `${
|
|
110575
|
+
if (!validateKeywordCore(core, `${path25}.${fieldLabel}[${k}]`, errors, lengthViolations)) continue;
|
|
110563
110576
|
const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
|
|
110564
110577
|
if (seen.has(dedupeKey)) {
|
|
110565
|
-
warnings.push(`${
|
|
110578
|
+
warnings.push(`${path25}.${fieldLabel}[${k}] \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
|
|
110566
110579
|
continue;
|
|
110567
110580
|
}
|
|
110568
110581
|
seen.add(dedupeKey);
|
|
@@ -110571,12 +110584,12 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path24, fieldLabel, error
|
|
|
110571
110584
|
}
|
|
110572
110585
|
return { normalized, resolvedUi };
|
|
110573
110586
|
}
|
|
110574
|
-
function normalizeKeywordsForBatchJobBlock(block,
|
|
110587
|
+
function normalizeKeywordsForBatchJobBlock(block, path25, errors, warnings, lengthViolations) {
|
|
110575
110588
|
const matchTypeRaw = readBlockMatchTypeRaw(block);
|
|
110576
110589
|
const declaredUi = matchTypeV2ToUi(matchTypeRaw);
|
|
110577
110590
|
if (matchTypeRaw !== void 0 && declaredUi === null) {
|
|
110578
110591
|
errors.push(
|
|
110579
|
-
`${
|
|
110592
|
+
`${path25}.MatchTypeV2 \u65E0\u6548\uFF08${String(matchTypeRaw)}\uFF09\uFF0C\u5408\u6CD5\u503C\uFF1ABROAD | PHRASE | EXACT`
|
|
110580
110593
|
);
|
|
110581
110594
|
return;
|
|
110582
110595
|
}
|
|
@@ -110591,7 +110604,7 @@ function normalizeKeywordsForBatchJobBlock(block, path24, errors, warnings, leng
|
|
|
110591
110604
|
const result = normalizeKeywordTextList(
|
|
110592
110605
|
texts,
|
|
110593
110606
|
matchTypeRaw,
|
|
110594
|
-
|
|
110607
|
+
path25,
|
|
110595
110608
|
"KeywordText",
|
|
110596
110609
|
errors,
|
|
110597
110610
|
warnings,
|
|
@@ -110600,7 +110613,7 @@ function normalizeKeywordsForBatchJobBlock(block, path24, errors, warnings, leng
|
|
|
110600
110613
|
normalizedTexts = result.normalized;
|
|
110601
110614
|
if (result.resolvedUi) resolvedUi = result.resolvedUi;
|
|
110602
110615
|
if (normalizedTexts.length === 0 && texts.length > 0) {
|
|
110603
|
-
errors.push(`${
|
|
110616
|
+
errors.push(`${path25}.KeywordText \u7ECF\u6821\u9A8C\u540E\u65E0\u6709\u6548\u5173\u952E\u8BCD\uFF0C\u8BF7\u4FEE\u6B63\u8BCD\u9762\u6216 MatchTypeV2`);
|
|
110604
110617
|
}
|
|
110605
110618
|
}
|
|
110606
110619
|
if (hasItems && items) {
|
|
@@ -110608,12 +110621,12 @@ function normalizeKeywordsForBatchJobBlock(block, path24, errors, warnings, leng
|
|
|
110608
110621
|
for (let k = 0; k < items.length; k++) {
|
|
110609
110622
|
const item = items[k];
|
|
110610
110623
|
if (!item || typeof item !== "object") {
|
|
110611
|
-
errors.push(`${
|
|
110624
|
+
errors.push(`${path25}.Items[${k}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
110612
110625
|
continue;
|
|
110613
110626
|
}
|
|
110614
110627
|
const raw = readItemText(item);
|
|
110615
110628
|
if (raw === null || !raw.trim()) {
|
|
110616
|
-
errors.push(`${
|
|
110629
|
+
errors.push(`${path25}.Items[${k}].Text \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
110617
110630
|
continue;
|
|
110618
110631
|
}
|
|
110619
110632
|
const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
|
|
@@ -110621,7 +110634,7 @@ function normalizeKeywordsForBatchJobBlock(block, path24, errors, warnings, leng
|
|
|
110621
110634
|
const { formatted, matchType, inferredMatchType } = normalizeKeywordSurface(trimmedRaw, matchTypeRaw);
|
|
110622
110635
|
pushKeywordAutoFixWarning(
|
|
110623
110636
|
warnings,
|
|
110624
|
-
|
|
110637
|
+
path25,
|
|
110625
110638
|
`Items[${k}].Text`,
|
|
110626
110639
|
trimmedRaw,
|
|
110627
110640
|
formatted,
|
|
@@ -110631,10 +110644,10 @@ function normalizeKeywordsForBatchJobBlock(block, path24, errors, warnings, leng
|
|
|
110631
110644
|
inferredMatchType
|
|
110632
110645
|
);
|
|
110633
110646
|
const core = unwrapKeywordDisplayTextForEdit(formatted);
|
|
110634
|
-
if (!validateKeywordCore(core, `${
|
|
110647
|
+
if (!validateKeywordCore(core, `${path25}.Items[${k}].Text`, errors, lengthViolations)) continue;
|
|
110635
110648
|
const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
|
|
110636
110649
|
if (seen.has(dedupeKey)) {
|
|
110637
|
-
warnings.push(`${
|
|
110650
|
+
warnings.push(`${path25}.Items[${k}].Text \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
|
|
110638
110651
|
continue;
|
|
110639
110652
|
}
|
|
110640
110653
|
seen.add(dedupeKey);
|
|
@@ -113242,12 +113255,12 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113242
113255
|
for (let ki = 0; ki < texts.length; ki++) {
|
|
113243
113256
|
const t = texts[ki];
|
|
113244
113257
|
if (typeof t !== "string" || !t.trim()) continue;
|
|
113245
|
-
const
|
|
113258
|
+
const path25 = `${groupPath}.KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
|
|
113246
113259
|
const key = keywordKey(t, matchTypeV2);
|
|
113247
113260
|
if (!liveKwKeys.has(key)) {
|
|
113248
113261
|
missing.push({
|
|
113249
113262
|
layer: "keyword",
|
|
113250
|
-
path:
|
|
113263
|
+
path: path25,
|
|
113251
113264
|
adGroupName: groupName,
|
|
113252
113265
|
plannedContent: `\u8BCD\u9762: ${t} | \u5339\u914D: ${matchTypeV2}`,
|
|
113253
113266
|
liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveKwInGroup.length} \u6761\u5173\u952E\u8BCD\uFF0C\u65E0\u952E ${key}`,
|
|
@@ -113265,12 +113278,12 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113265
113278
|
for (let ai = 0; ai < ads.length; ai++) {
|
|
113266
113279
|
const ad = asRecord2(ads[ai]);
|
|
113267
113280
|
if (!ad) continue;
|
|
113268
|
-
const
|
|
113281
|
+
const path25 = `${groupPath}.AdsForBatchJob[${ai}]`;
|
|
113269
113282
|
const primary = pickString(ad["headlinePart1"], ad["AdTitle"]);
|
|
113270
113283
|
if (!primary) {
|
|
113271
113284
|
missing.push({
|
|
113272
113285
|
layer: "ad",
|
|
113273
|
-
path:
|
|
113286
|
+
path: path25,
|
|
113274
113287
|
adGroupName: groupName,
|
|
113275
113288
|
plannedContent: `AdsForBatchJob[${ai}]\uFF08\u7F3A\u5C11 headlinePart1\uFF0C\u65E0\u6CD5\u6BD4\u5BF9\uFF09`,
|
|
113276
113289
|
liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761\u521B\u610F`,
|
|
@@ -113283,7 +113296,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113283
113296
|
if (!found) {
|
|
113284
113297
|
missing.push({
|
|
113285
113298
|
layer: "ad",
|
|
113286
|
-
path:
|
|
113299
|
+
path: path25,
|
|
113287
113300
|
adGroupName: groupName,
|
|
113288
113301
|
plannedContent: `RSA \u9996\u6807\u9898: ${primary}${finalUrl ? ` | \u843D\u5730\u9875: ${finalUrl}` : ""}`,
|
|
113289
113302
|
liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761 RSA\uFF0C\u65E0\u6B64\u9996\u6807\u9898`,
|
|
@@ -114481,13 +114494,13 @@ function pmaxChannelTypesUrl(googleApiUrl) {
|
|
|
114481
114494
|
}
|
|
114482
114495
|
function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
|
|
114483
114496
|
const base = googleApiUrl.replace(/\/$/, "");
|
|
114484
|
-
const
|
|
114485
|
-
return campaignId ? `${
|
|
114497
|
+
const path25 = `${base}/accounts/${accountId}/campaign/pmax`;
|
|
114498
|
+
return campaignId ? `${path25}/${campaignId}` : path25;
|
|
114486
114499
|
}
|
|
114487
114500
|
function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
|
|
114488
114501
|
const base = googleApiUrl.replace(/\/$/, "");
|
|
114489
|
-
const
|
|
114490
|
-
return suffix ? `${
|
|
114502
|
+
const path25 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
|
|
114503
|
+
return suffix ? `${path25}/${suffix.replace(/^\//, "")}` : path25;
|
|
114491
114504
|
}
|
|
114492
114505
|
function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
|
|
114493
114506
|
const base = googleApiUrl.replace(/\/$/, "");
|
|
@@ -121637,15 +121650,34 @@ async function runWebsiteDiagnosisCollect(opts) {
|
|
|
121637
121650
|
}
|
|
121638
121651
|
|
|
121639
121652
|
// src/commands/website-diagnosis/render-report.ts
|
|
121640
|
-
import fs12 from "fs
|
|
121653
|
+
import fs12 from "fs";
|
|
121654
|
+
import fsPromises from "fs/promises";
|
|
121641
121655
|
import path17 from "path";
|
|
121642
121656
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
121643
|
-
|
|
121657
|
+
var TEMPLATE_BASENAMES = {
|
|
121658
|
+
html: "website-diagnosis-report.html",
|
|
121659
|
+
runtime: "website-diagnosis-report.runtime.js"
|
|
121660
|
+
};
|
|
121661
|
+
function resolveSkillTemplatePath(basename11) {
|
|
121644
121662
|
const dir = path17.dirname(fileURLToPath4(import.meta.url));
|
|
121645
|
-
|
|
121663
|
+
const candidates = [
|
|
121664
|
+
path17.join(dir, "skill", "report-templates", basename11),
|
|
121665
|
+
path17.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
|
|
121666
|
+
path17.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
|
|
121667
|
+
];
|
|
121668
|
+
for (const p of candidates) {
|
|
121669
|
+
if (fs12.existsSync(p)) return p;
|
|
121670
|
+
}
|
|
121671
|
+
return candidates[0];
|
|
121672
|
+
}
|
|
121673
|
+
function websiteDiagnosisReportTemplatePath() {
|
|
121674
|
+
return resolveSkillTemplatePath(TEMPLATE_BASENAMES.html);
|
|
121675
|
+
}
|
|
121676
|
+
function websiteDiagnosisRuntimePath() {
|
|
121677
|
+
return resolveSkillTemplatePath(TEMPLATE_BASENAMES.runtime);
|
|
121646
121678
|
}
|
|
121647
121679
|
function readJsonFile(filePath) {
|
|
121648
|
-
const raw =
|
|
121680
|
+
const raw = fsPromises.readFile(filePath, "utf8");
|
|
121649
121681
|
return raw.then((text) => {
|
|
121650
121682
|
try {
|
|
121651
121683
|
return JSON.parse(text);
|
|
@@ -121688,7 +121720,7 @@ async function runWebsiteDiagnosisRender(opts) {
|
|
|
121688
121720
|
const templatePath = websiteDiagnosisReportTemplatePath();
|
|
121689
121721
|
let html;
|
|
121690
121722
|
try {
|
|
121691
|
-
html = await
|
|
121723
|
+
html = await fsPromises.readFile(templatePath, "utf8");
|
|
121692
121724
|
} catch {
|
|
121693
121725
|
console.error(`
|
|
121694
121726
|
\u274C \u672A\u627E\u5230\u62A5\u544A\u6A21\u677F\uFF1A${templatePath}
|
|
@@ -121699,12 +121731,26 @@ async function runWebsiteDiagnosisRender(opts) {
|
|
|
121699
121731
|
const outPath = path17.resolve(
|
|
121700
121732
|
opts.out ?? path17.join(path17.dirname(dataPath), "website-diagnosis-report.html")
|
|
121701
121733
|
);
|
|
121702
|
-
|
|
121703
|
-
await
|
|
121734
|
+
const outDir = path17.dirname(outPath);
|
|
121735
|
+
await fsPromises.mkdir(outDir, { recursive: true });
|
|
121736
|
+
await fsPromises.writeFile(outPath, injectReportData(html, data), "utf8");
|
|
121737
|
+
const runtimeSrc = websiteDiagnosisRuntimePath();
|
|
121738
|
+
const runtimeOut = path17.join(outDir, TEMPLATE_BASENAMES.runtime);
|
|
121739
|
+
try {
|
|
121740
|
+
await fsPromises.copyFile(runtimeSrc, runtimeOut);
|
|
121741
|
+
} catch {
|
|
121742
|
+
console.error(`
|
|
121743
|
+
\u274C \u672A\u627E\u5230\u62A5\u544A\u8FD0\u884C\u65F6\uFF1A${runtimeSrc}
|
|
121744
|
+
\u8BF7\u5148\u6267\u884C npm run build
|
|
121745
|
+
`);
|
|
121746
|
+
process.exit(1);
|
|
121747
|
+
}
|
|
121704
121748
|
console.log(`
|
|
121705
121749
|
\u2705 \u7F51\u7AD9\u8BCA\u65AD HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
121706
121750
|
`);
|
|
121707
|
-
console.log(
|
|
121751
|
+
console.log(` \u8FD0\u884C\u65F6\u811A\u672C\uFF1A${runtimeOut}
|
|
121752
|
+
`);
|
|
121753
|
+
console.log("\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 HTML \u6587\u4EF6\u5373\u53EF\u67E5\u770B\u56FE\u8868\u4E0E\u5B8C\u6574\u7AE0\u8282\uFF08\u9700\u8054\u7F51\u52A0\u8F7D ECharts CDN\uFF09\u3002\n");
|
|
121708
121754
|
}
|
|
121709
121755
|
|
|
121710
121756
|
// src/commands/website-diagnosis/register.ts
|
|
@@ -121753,6 +121799,578 @@ function registerWebsiteDiagnosisCommands(program2) {
|
|
|
121753
121799
|
});
|
|
121754
121800
|
}
|
|
121755
121801
|
|
|
121802
|
+
// src/commands/market-analysis/run.ts
|
|
121803
|
+
init_auth();
|
|
121804
|
+
init_cli_json_snapshot();
|
|
121805
|
+
|
|
121806
|
+
// src/commands/market-analysis/shared.ts
|
|
121807
|
+
var DEFAULT_TARGET_MARKET = "\u5168\u7403";
|
|
121808
|
+
var DEFAULT_TIME_RANGE = "\u8FD112\u4E2A\u6708";
|
|
121809
|
+
function validateMarketAnalysisInput(info) {
|
|
121810
|
+
const { customerName, website, industry, coreProducts } = info;
|
|
121811
|
+
if (!customerName?.trim() && !website?.trim() && !industry?.trim() && !coreProducts?.trim()) {
|
|
121812
|
+
return "\u5BA2\u6237\u540D\u79F0\u3001\u5BA2\u6237\u7F51\u7AD9\u3001\u6240\u5C5E\u884C\u4E1A\u3001\u6838\u5FC3\u4EA7\u54C1\u987B\u81F3\u5C11\u63D0\u4F9B\u4E00\u9879\u3002";
|
|
121813
|
+
}
|
|
121814
|
+
return null;
|
|
121815
|
+
}
|
|
121816
|
+
function normalizeCustomerInfo(input) {
|
|
121817
|
+
return {
|
|
121818
|
+
...input,
|
|
121819
|
+
targetMarket: input.targetMarket?.trim() || DEFAULT_TARGET_MARKET,
|
|
121820
|
+
timeRange: input.timeRange?.trim() || DEFAULT_TIME_RANGE
|
|
121821
|
+
};
|
|
121822
|
+
}
|
|
121823
|
+
function buildMarketAnalysisAgentHint() {
|
|
121824
|
+
return [
|
|
121825
|
+
"1) Read assets/market-analysis-rules.md\uFF08\u539F\u59CB\u4E1A\u52A1\u7EF4\u5EA6\u6E05\u5355\uFF09\u4E0E report-templates/market-analysis-report.md\uFF1B",
|
|
121826
|
+
"2) \u7528\u5BBF\u4E3B WebSearch/WebFetch \u6309\u7F3A\u5931\u7EF4\u5EA6\u8865\u5168\u516C\u5F00\u6570\u636E\uFF1B",
|
|
121827
|
+
"3) \u5C06\u5B8C\u6574 HTML \u5199\u5165 market-report.json\uFF1Brender \u4F1A\u6821\u9A8C\u5FC5\u542B\u7EF4\u5EA6\uFF0C\u7F3A\u9879\u5C06\u5931\u8D25\uFF1B",
|
|
121828
|
+
"4) siluzan-tso market-analysis render --data ./market-report.json --out ./market-analysis-report.html\u3002"
|
|
121829
|
+
].join(" ");
|
|
121830
|
+
}
|
|
121831
|
+
function injectChartSizeLimitStyles(htmlContent) {
|
|
121832
|
+
if (!htmlContent) return htmlContent;
|
|
121833
|
+
const chartSizeLimitStyle = `
|
|
121834
|
+
<style>
|
|
121835
|
+
canvas {
|
|
121836
|
+
max-height: 400px !important;
|
|
121837
|
+
width: 100% !important;
|
|
121838
|
+
height: auto !important;
|
|
121839
|
+
}
|
|
121840
|
+
</style>
|
|
121841
|
+
`;
|
|
121842
|
+
if (htmlContent.includes("<head>")) {
|
|
121843
|
+
return htmlContent.replace("</head>", `${chartSizeLimitStyle}</head>`);
|
|
121844
|
+
}
|
|
121845
|
+
if (htmlContent.includes("<html")) {
|
|
121846
|
+
return htmlContent.replace(/(<html[^>]*>)/i, `$1<head>${chartSizeLimitStyle}</head>`);
|
|
121847
|
+
}
|
|
121848
|
+
return `${chartSizeLimitStyle}${htmlContent}`;
|
|
121849
|
+
}
|
|
121850
|
+
function extractHtmlFromResponse(data) {
|
|
121851
|
+
if (!data) return "";
|
|
121852
|
+
let htmlContent = "";
|
|
121853
|
+
const htmlBlockRegex = /```html\s*([\s\S]*?)```/;
|
|
121854
|
+
const match = data.match(htmlBlockRegex);
|
|
121855
|
+
if (match?.[1]) {
|
|
121856
|
+
htmlContent = match[1].trim();
|
|
121857
|
+
} else if (data.includes("<!DOCTYPE html>") || data.includes("<html")) {
|
|
121858
|
+
htmlContent = data.trim();
|
|
121859
|
+
} else {
|
|
121860
|
+
htmlContent = data;
|
|
121861
|
+
}
|
|
121862
|
+
return injectChartSizeLimitStyles(htmlContent);
|
|
121863
|
+
}
|
|
121864
|
+
|
|
121865
|
+
// src/commands/market-analysis/run.ts
|
|
121866
|
+
var WEBSITE_PREVIEW_CHARS = 8e3;
|
|
121867
|
+
async function runMarketAnalysisCollect(opts) {
|
|
121868
|
+
const customerInfo = {
|
|
121869
|
+
customerName: opts.customerName,
|
|
121870
|
+
website: opts.website,
|
|
121871
|
+
industry: opts.industry,
|
|
121872
|
+
coreProducts: opts.coreProducts,
|
|
121873
|
+
businessPosition: opts.businessPosition,
|
|
121874
|
+
targetMarket: opts.targetMarket,
|
|
121875
|
+
timeRange: opts.timeRange
|
|
121876
|
+
};
|
|
121877
|
+
const validationError = validateMarketAnalysisInput(customerInfo);
|
|
121878
|
+
if (validationError) {
|
|
121879
|
+
console.error(`
|
|
121880
|
+
\u274C ${validationError}
|
|
121881
|
+
`);
|
|
121882
|
+
process.exit(1);
|
|
121883
|
+
}
|
|
121884
|
+
const normalized = normalizeCustomerInfo(customerInfo);
|
|
121885
|
+
let websiteMeta;
|
|
121886
|
+
let websitePreview;
|
|
121887
|
+
let websiteError;
|
|
121888
|
+
const websiteUrl = normalized.website?.trim() ? normalizeWebsiteUrl(normalized.website) : "";
|
|
121889
|
+
if (websiteUrl && !opts.skipWebsite) {
|
|
121890
|
+
const config = loadConfig(opts.token);
|
|
121891
|
+
try {
|
|
121892
|
+
const html = await fetchWebsiteHtml(config, websiteUrl, opts.verbose);
|
|
121893
|
+
websiteMeta = {
|
|
121894
|
+
originUrl: html.originUrl,
|
|
121895
|
+
finalUrl: html.finalUrl,
|
|
121896
|
+
mimeType: html.mimeType,
|
|
121897
|
+
contentLength: html.contentLength,
|
|
121898
|
+
downloadTime: html.downloadTime
|
|
121899
|
+
};
|
|
121900
|
+
websitePreview = String(html.content ?? "").slice(0, WEBSITE_PREVIEW_CHARS);
|
|
121901
|
+
} catch (err) {
|
|
121902
|
+
websiteError = err instanceof Error ? err.message : String(err);
|
|
121903
|
+
}
|
|
121904
|
+
}
|
|
121905
|
+
const label = normalized.customerName?.trim() || normalized.website?.trim() || normalized.industry?.trim() || normalized.coreProducts?.trim() || "market";
|
|
121906
|
+
const payload = {
|
|
121907
|
+
customerInfo: normalized,
|
|
121908
|
+
targetMarket: normalized.targetMarket,
|
|
121909
|
+
timeRange: normalized.timeRange,
|
|
121910
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
121911
|
+
...websiteMeta ? { website: websiteMeta } : {},
|
|
121912
|
+
...websitePreview ? { websitePreview } : {},
|
|
121913
|
+
...websiteError ? { websiteError } : {},
|
|
121914
|
+
agentHint: buildMarketAnalysisAgentHint()
|
|
121915
|
+
};
|
|
121916
|
+
if (await emitCliJsonOrSnapshot(
|
|
121917
|
+
{ jsonOut: opts.jsonOut },
|
|
121918
|
+
{
|
|
121919
|
+
section: "market-analysis-collect",
|
|
121920
|
+
commandLabel: "market-analysis collect",
|
|
121921
|
+
commandHint: label,
|
|
121922
|
+
payload
|
|
121923
|
+
}
|
|
121924
|
+
)) {
|
|
121925
|
+
return;
|
|
121926
|
+
}
|
|
121927
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
121928
|
+
}
|
|
121929
|
+
|
|
121930
|
+
// src/commands/market-analysis/render-report.ts
|
|
121931
|
+
import fs13 from "fs/promises";
|
|
121932
|
+
import path18 from "path";
|
|
121933
|
+
|
|
121934
|
+
// src/commands/market-analysis/report-content.ts
|
|
121935
|
+
var MARKET_REPORT_DIMENSIONS = [
|
|
121936
|
+
// ── 封面与元信息 ──
|
|
121937
|
+
{
|
|
121938
|
+
id: "cover",
|
|
121939
|
+
chapter: "\u5C01\u9762",
|
|
121940
|
+
dimension: "\u62A5\u544A\u6807\u9898\u542B\u5BA2\u6237\u4E0E\u76EE\u6807\u5E02\u573A",
|
|
121941
|
+
hint: "\u987B\u542B\u300C\u6218\u7565\u5E02\u573A\u5206\u6790\u300D\u6216\u300CKA\u5BA2\u6237\u300D\u53CA\u76EE\u6807\u5E02\u573A\u5173\u952E\u8BCD"
|
|
121942
|
+
},
|
|
121943
|
+
{
|
|
121944
|
+
id: "cover-kpi",
|
|
121945
|
+
chapter: "\u5C01\u9762",
|
|
121946
|
+
dimension: "\u6838\u5FC3\u7ED3\u8BBA\u6216 KPI \u6458\u8981",
|
|
121947
|
+
hint: "\u5C01\u9762\u987B\u6709\u53EF\u626B\u8BFB\u7684\u7ED3\u8BBA\u6BB5\u6216 metric \u5361\u7247\uFF0C\u975E\u4EC5\u6807\u9898"
|
|
121948
|
+
},
|
|
121949
|
+
// ── 一、市场与趋势诊断 ──
|
|
121950
|
+
{
|
|
121951
|
+
id: "ch1",
|
|
121952
|
+
chapter: "\u4E00\u3001\u5E02\u573A\u4E0E\u8D8B\u52BF\u8BCA\u65AD",
|
|
121953
|
+
dimension: "\u7B2C\u4E00\u7AE0\u6807\u9898",
|
|
121954
|
+
hint: "\u987B\u51FA\u73B0\u300C\u5E02\u573A\u4E0E\u8D8B\u52BF\u8BCA\u65AD\u300D"
|
|
121955
|
+
},
|
|
121956
|
+
{
|
|
121957
|
+
id: "ch1-tam",
|
|
121958
|
+
chapter: "\u4E00\u3001\u5E02\u573A\u4E0E\u8D8B\u52BF\u8BCA\u65AD",
|
|
121959
|
+
dimension: "\u5168\u7403\u5E02\u573A\u89C4\u6A21\u4E0E\u589E\u957F\uFF08TAM/SAM/SOM\u3001CAGR\uFF09",
|
|
121960
|
+
hint: "\u987B\u542B TAM \u6216 SAM \u6216 SOM \u6216\u300C\u5E02\u573A\u89C4\u6A21\u300D+ CAGR \u6216\u589E\u957F\u7387"
|
|
121961
|
+
},
|
|
121962
|
+
{
|
|
121963
|
+
id: "ch1-value-chain",
|
|
121964
|
+
chapter: "\u4E00\u3001\u5E02\u573A\u4E0E\u8D8B\u52BF\u8BCA\u65AD",
|
|
121965
|
+
dimension: "\u5E02\u573A\u8D5B\u9053\u4E0E\u4EF7\u503C\u94FE/\u751F\u6001\u5B9A\u4F4D",
|
|
121966
|
+
hint: "\u987B\u542B\u300C\u4EF7\u503C\u94FE\u300D\u6216\u300C\u751F\u6001\u5B9A\u4F4D\u300D\u6216\u4EA7\u4E1A\u94FE\u73AF\u8282\u8868"
|
|
121967
|
+
},
|
|
121968
|
+
{
|
|
121969
|
+
id: "ch1-target-geo",
|
|
121970
|
+
chapter: "\u4E00\u3001\u5E02\u573A\u4E0E\u8D8B\u52BF\u8BCA\u65AD",
|
|
121971
|
+
dimension: "\u76EE\u6807\u5E02\u573A\u56FD\u5BB6/\u533A\u57DF\u62C6\u89E3",
|
|
121972
|
+
hint: "\u987B\u6309\u56FD\u5BB6\u6216\u533A\u57DF\u5BF9\u6BD4\u89C4\u6A21\u3001\u589E\u957F\u3001\u4F18\u5148\u7EA7\uFF08\u8868\u683C\u6216\u5206\u6BB5\uFF09"
|
|
121973
|
+
},
|
|
121974
|
+
{
|
|
121975
|
+
id: "ch1-pestel",
|
|
121976
|
+
chapter: "\u4E00\u3001\u5E02\u573A\u4E0E\u8D8B\u52BF\u8BCA\u65AD",
|
|
121977
|
+
dimension: "\u6280\u672F\u8D8B\u52BF\u4E0E PESTEL",
|
|
121978
|
+
hint: "\u987B\u542B PESTEL \u6216\u653F\u7B56/\u7ECF\u6D4E/\u793E\u4F1A/\u6280\u672F/\u73AF\u5883/\u6CD5\u89C4\u7EF4\u5EA6\u5206\u6790"
|
|
121979
|
+
},
|
|
121980
|
+
{
|
|
121981
|
+
id: "ch1-forecast",
|
|
121982
|
+
chapter: "\u4E00\u3001\u5E02\u573A\u4E0E\u8D8B\u52BF\u8BCA\u65AD",
|
|
121983
|
+
dimension: "3\u20135 \u5E74\u8D8B\u52BF\u9884\u6D4B\u4E0E\u5047\u8BBE",
|
|
121984
|
+
hint: "\u987B\u542B\u300C\u9884\u6D4B\u300D\u6216\u300C3\u300D\u5E74/\u300C5\u300D\u5E74\u8D8B\u52BF\u8868\u8FF0"
|
|
121985
|
+
},
|
|
121986
|
+
// ── 二、目标行业深度洞察 ──
|
|
121987
|
+
{
|
|
121988
|
+
id: "ch2",
|
|
121989
|
+
chapter: "\u4E8C\u3001\u76EE\u6807\u884C\u4E1A\u6DF1\u5EA6\u6D1E\u5BDF",
|
|
121990
|
+
dimension: "\u7B2C\u4E8C\u7AE0\u6807\u9898",
|
|
121991
|
+
hint: "\u987B\u51FA\u73B0\u300C\u884C\u4E1A\u6DF1\u5EA6\u6D1E\u5BDF\u300D\u6216\u300C\u76EE\u6807\u884C\u4E1A\u300D"
|
|
121992
|
+
},
|
|
121993
|
+
{
|
|
121994
|
+
id: "ch2-pain",
|
|
121995
|
+
chapter: "\u4E8C\u3001\u76EE\u6807\u884C\u4E1A\u6DF1\u5EA6\u6D1E\u5BDF",
|
|
121996
|
+
dimension: "\u884C\u4E1A\u6838\u5FC3\u75DB\u70B9\uFF08\u22654 \u9879\uFF09",
|
|
121997
|
+
hint: "\u987B\u5217\u4E3E\u81F3\u5C11 4 \u6761\u75DB\u70B9\uFF08\u75DB\u70B91\u20134 \u6216\u5217\u8868\u9879\uFF09"
|
|
121998
|
+
},
|
|
121999
|
+
{
|
|
122000
|
+
id: "ch2-compliance",
|
|
122001
|
+
chapter: "\u4E8C\u3001\u76EE\u6807\u884C\u4E1A\u6DF1\u5EA6\u6D1E\u5BDF",
|
|
122002
|
+
dimension: "\u6CD5\u89C4\u73AF\u5883\u4E0E\u5408\u89C4\u8981\u6C42",
|
|
122003
|
+
hint: "\u987B\u542B\u6CD5\u89C4/\u5408\u89C4\u8868\uFF0C\u542B\u5E94\u5BF9\u7B56\u7565\uFF1B\u5B9C\u542B\u5408\u89C4\u6EA2\u4EF7\u6216\u6210\u672C\u4F30\u7B97"
|
|
122004
|
+
},
|
|
122005
|
+
{
|
|
122006
|
+
id: "ch2-bpmn",
|
|
122007
|
+
chapter: "\u4E8C\u3001\u76EE\u6807\u884C\u4E1A\u6DF1\u5EA6\u6D1E\u5BDF",
|
|
122008
|
+
dimension: "\u5178\u578B\u573A\u666F\u4E0E BPMN \u6D41\u7A0B\u5BF9\u6BD4",
|
|
122009
|
+
hint: "\u987B\u542B\u300C\u539F\u6D41\u7A0B\u300D\u4E0E\u300C\u65B0\u6D41\u7A0B\u300D\u6216 BPMN \u6587\u672C + \u6548\u7387/\u6210\u672C\u91CF\u5316"
|
|
122010
|
+
},
|
|
122011
|
+
// ── 三、目标受众与场景 ──
|
|
122012
|
+
{
|
|
122013
|
+
id: "ch3",
|
|
122014
|
+
chapter: "\u4E09\u3001\u76EE\u6807\u53D7\u4F17\u4E0E\u573A\u666F",
|
|
122015
|
+
dimension: "\u7B2C\u4E09\u7AE0\u6807\u9898",
|
|
122016
|
+
hint: "\u987B\u51FA\u73B0\u300C\u76EE\u6807\u53D7\u4F17\u300D\u6216\u300C\u53D7\u4F17\u4E0E\u573A\u666F\u300D"
|
|
122017
|
+
},
|
|
122018
|
+
{
|
|
122019
|
+
id: "ch3-audience",
|
|
122020
|
+
chapter: "\u4E09\u3001\u76EE\u6807\u53D7\u4F17\u4E0E\u573A\u666F",
|
|
122021
|
+
dimension: "\u53D7\u4F17\u5206\u5C42\u4E0E\u7528\u6237\u753B\u50CF",
|
|
122022
|
+
hint: "\u987B\u542B\u7528\u6237\u7FA4\u4F53\u5206\u7C7B\u8868\uFF1A\u89C4\u6A21/\u7279\u5F81/\u75DB\u70B9/\u4EF7\u503C"
|
|
122023
|
+
},
|
|
122024
|
+
{
|
|
122025
|
+
id: "ch3-scenario",
|
|
122026
|
+
chapter: "\u4E09\u3001\u76EE\u6807\u53D7\u4F17\u4E0E\u573A\u666F",
|
|
122027
|
+
dimension: "\u4EA7\u54C1\u5E94\u7528\u573A\u666F\u4E0E\u4EF7\u503C\u5206\u6790",
|
|
122028
|
+
hint: "\u987B\u542B\u5E94\u7528\u573A\u666F\u3001\u5DEE\u5F02\u5316\u3001\u7528\u6237\u4EF7\u503C\u4E0E\u4F01\u4E1A\u4EF7\u503C"
|
|
122029
|
+
},
|
|
122030
|
+
// ── 四、竞争策略与定位 ──
|
|
122031
|
+
{
|
|
122032
|
+
id: "ch4",
|
|
122033
|
+
chapter: "\u56DB\u3001\u7ADE\u4E89\u7B56\u7565\u4E0E\u5B9A\u4F4D",
|
|
122034
|
+
dimension: "\u7B2C\u56DB\u7AE0\u6807\u9898",
|
|
122035
|
+
hint: "\u987B\u51FA\u73B0\u300C\u7ADE\u4E89\u7B56\u7565\u300D\u6216\u300C\u7ADE\u4E89\u300D+\u300C\u5B9A\u4F4D\u300D"
|
|
122036
|
+
},
|
|
122037
|
+
{
|
|
122038
|
+
id: "ch4-competitors",
|
|
122039
|
+
chapter: "\u56DB\u3001\u7ADE\u4E89\u7B56\u7565\u4E0E\u5B9A\u4F4D",
|
|
122040
|
+
dimension: "\u4E3B\u8981\u7ADE\u54C1\u8BC6\u522B",
|
|
122041
|
+
hint: "\u987B\u70B9\u540D \u22653 \u4E2A\u7ADE\u54C1\u6216\u5BF9\u6807\u54C1\u724C"
|
|
122042
|
+
},
|
|
122043
|
+
{
|
|
122044
|
+
id: "ch4-compare-table",
|
|
122045
|
+
chapter: "\u56DB\u3001\u7ADE\u4E89\u7B56\u7565\u4E0E\u5B9A\u4F4D",
|
|
122046
|
+
dimension: "\u529F\u80FD\u4E0E\u6027\u80FD\u5BF9\u6BD4\u8868\uFF08\u226510 \u9879 \xD7 \u22653 \u7ADE\u54C1\uFF09",
|
|
122047
|
+
hint: "\u987B\u6709\u7ADE\u54C1\u5BF9\u6BD4\u8868\uFF0C\u884C\u6570\u5145\u8DB3\uFF08\u5EFA\u8BAE \u226510 \u884C\u7EF4\u5EA6\uFF09"
|
|
122048
|
+
},
|
|
122049
|
+
{
|
|
122050
|
+
id: "ch4-gaps",
|
|
122051
|
+
chapter: "\u56DB\u3001\u7ADE\u4E89\u7B56\u7565\u4E0E\u5B9A\u4F4D",
|
|
122052
|
+
dimension: "\u56DB\u7C7B\u7ADE\u4E89\u7A7A\u767D\uFF08\u529F\u80FD/\u573A\u666F/\u7528\u6237\u7FA4/\u5730\u57DF\uFF09",
|
|
122053
|
+
hint: "\u987B\u8BC6\u522B\u529F\u80FD\u3001\u573A\u666F\u3001\u7528\u6237\u7FA4\u3001\u5730\u57DF\u4E2D\u81F3\u5C11\u4E09\u7C7B\u7A7A\u767D"
|
|
122054
|
+
},
|
|
122055
|
+
{
|
|
122056
|
+
id: "ch4-positioning",
|
|
122057
|
+
chapter: "\u56DB\u3001\u7ADE\u4E89\u7B56\u7565\u4E0E\u5B9A\u4F4D",
|
|
122058
|
+
dimension: "\u6838\u5FC3\u5B9A\u4F4D\u9648\u8FF0\u4E0E\u5DEE\u5F02\u5316\u7EF4\u5EA6",
|
|
122059
|
+
hint: "\u987B\u6709\u4E00\u53E5\u5B9A\u4F4D\u9648\u8FF0 + \u591A\u4E2A\u5DEE\u5F02\u5316\u7EF4\u5EA6"
|
|
122060
|
+
},
|
|
122061
|
+
// ── 五、增长与品牌策略 ──
|
|
122062
|
+
{
|
|
122063
|
+
id: "ch5",
|
|
122064
|
+
chapter: "\u4E94\u3001\u589E\u957F\u4E0E\u54C1\u724C\u7B56\u7565",
|
|
122065
|
+
dimension: "\u7B2C\u4E94\u7AE0\u6807\u9898",
|
|
122066
|
+
hint: "\u987B\u51FA\u73B0\u300C\u589E\u957F\u4E0E\u54C1\u724C\u300D\u6216 GTM"
|
|
122067
|
+
},
|
|
122068
|
+
{
|
|
122069
|
+
id: "ch5-gtm",
|
|
122070
|
+
chapter: "\u4E94\u3001\u589E\u957F\u4E0E\u54C1\u724C\u7B56\u7565",
|
|
122071
|
+
dimension: "GTM \u4E09\u9636\u6BB5\uFF08\u8BD5\u70B9\u2192\u590D\u5236\u2192\u751F\u6001\uFF09",
|
|
122072
|
+
hint: "\u987B\u542B\u4E09\u9636\u6BB5\u8FDB\u5165\u8DEF\u5F84\u8868\uFF1A\u76EE\u6807\u3001\u52A8\u4F5C\u3001KPI/ROI"
|
|
122073
|
+
},
|
|
122074
|
+
{
|
|
122075
|
+
id: "ch5-brand",
|
|
122076
|
+
chapter: "\u4E94\u3001\u589E\u957F\u4E0E\u54C1\u724C\u7B56\u7565",
|
|
122077
|
+
dimension: "\u54C1\u724C\u5B9A\u4F4D\u4E0E\u4F20\u64AD",
|
|
122078
|
+
hint: "\u987B\u542B\u54C1\u724C\u4E3B\u5F20\u3001\u4F20\u64AD\u6E20\u9053\u6216\u5185\u5BB9\u7B56\u7565"
|
|
122079
|
+
},
|
|
122080
|
+
{
|
|
122081
|
+
id: "ch5-tactics",
|
|
122082
|
+
chapter: "\u4E94\u3001\u589E\u957F\u4E0E\u54C1\u724C\u7B56\u7565",
|
|
122083
|
+
dimension: "\u8425\u9500\u4E0E\u9500\u552E\u6218\u672F",
|
|
122084
|
+
hint: "\u987B\u542B\u5185\u5BB9\u8425\u9500/\u6E20\u9053/KOL/\u9500\u552E\u6A21\u5F0F\u7B49\u6218\u672F\u63CF\u8FF0"
|
|
122085
|
+
},
|
|
122086
|
+
// ── 六、实施、资源与风险 ──
|
|
122087
|
+
{
|
|
122088
|
+
id: "ch6",
|
|
122089
|
+
chapter: "\u516D\u3001\u5B9E\u65BD\u3001\u8D44\u6E90\u4E0E\u98CE\u9669",
|
|
122090
|
+
dimension: "\u7B2C\u516D\u7AE0\u6807\u9898",
|
|
122091
|
+
hint: "\u987B\u51FA\u73B0\u300C\u5B9E\u65BD\u300D+\u300C\u98CE\u9669\u300D\u6216\u300C\u8D44\u6E90\u4E0E\u98CE\u9669\u300D"
|
|
122092
|
+
},
|
|
122093
|
+
{
|
|
122094
|
+
id: "ch6-milestone",
|
|
122095
|
+
chapter: "\u516D\u3001\u5B9E\u65BD\u3001\u8D44\u6E90\u4E0E\u98CE\u9669",
|
|
122096
|
+
dimension: "\u5B9E\u65BD\u8DEF\u5F84\u4E0E\u91CC\u7A0B\u7891\u4EFB\u52A1\u8868",
|
|
122097
|
+
hint: "\u987B\u542B\u9636\u6BB5/\u4EFB\u52A1/\u8D1F\u8D23\u4EBA/\u6210\u679C/\u6307\u6807\u7C7B\u4EFB\u52A1\u8868"
|
|
122098
|
+
},
|
|
122099
|
+
{
|
|
122100
|
+
id: "ch6-budget",
|
|
122101
|
+
chapter: "\u516D\u3001\u5B9E\u65BD\u3001\u8D44\u6E90\u4E0E\u98CE\u9669",
|
|
122102
|
+
dimension: "\u8D44\u6E90\u9700\u6C42\u4E0E\u9884\u7B97\u89C4\u5212",
|
|
122103
|
+
hint: "\u987B\u542B\u4EBA\u529B/\u6280\u672F/\u5E02\u573A\u8D44\u6E90\u6216\u9884\u7B97\u6BD4\u4F8B\u5206\u914D"
|
|
122104
|
+
},
|
|
122105
|
+
{
|
|
122106
|
+
id: "ch6-risk",
|
|
122107
|
+
chapter: "\u516D\u3001\u5B9E\u65BD\u3001\u8D44\u6E90\u4E0E\u98CE\u9669",
|
|
122108
|
+
dimension: "\u98CE\u9669\u77E9\u9635\u4E0E\u5E94\u5BF9\u7B56\u7565",
|
|
122109
|
+
hint: "\u987B\u542B\u5E02\u573A/\u7ADE\u4E89/\u6280\u672F/\u8FD0\u8425\u7B49\u98CE\u9669 + \u6BCF\u7C7B\u5E94\u5BF9\u63AA\u65BD"
|
|
122110
|
+
},
|
|
122111
|
+
// ── 附录 ──
|
|
122112
|
+
{
|
|
122113
|
+
id: "appendix",
|
|
122114
|
+
chapter: "\u9644\u5F55",
|
|
122115
|
+
dimension: "\u9644\u5F55\u7AE0\u8282",
|
|
122116
|
+
hint: "\u987B\u542B\u300C\u9644\u5F55\u300D\u6216\u300C\u6570\u636E\u6765\u6E90\u300D\u6E05\u5355"
|
|
122117
|
+
},
|
|
122118
|
+
{
|
|
122119
|
+
id: "appendix-sources",
|
|
122120
|
+
chapter: "\u9644\u5F55",
|
|
122121
|
+
dimension: "\u6570\u636E\u6765\u6E90\u6E05\u5355",
|
|
122122
|
+
hint: "\u987B\u5217\u673A\u6784/\u62A5\u544A\u540D/\u65E5\u671F\u6216 URL"
|
|
122123
|
+
},
|
|
122124
|
+
{
|
|
122125
|
+
id: "appendix-methods",
|
|
122126
|
+
chapter: "\u9644\u5F55",
|
|
122127
|
+
dimension: "\u8C03\u7814\u65B9\u6CD5\u8BF4\u660E",
|
|
122128
|
+
hint: "\u987B\u8BF4\u660E\u684C\u9762\u7814\u7A76/\u8BBF\u8C08/\u95EE\u5377\u7B49\u65B9\u6CD5"
|
|
122129
|
+
},
|
|
122130
|
+
{
|
|
122131
|
+
id: "appendix-glossary",
|
|
122132
|
+
chapter: "\u9644\u5F55",
|
|
122133
|
+
dimension: "\u672F\u8BED\u8868\uFF08ROI/CAGR/GTM/BPMN \u7B49\uFF09",
|
|
122134
|
+
hint: "\u987B\u89E3\u91CA\u81F3\u5C11 3 \u4E2A\u7F29\u5199"
|
|
122135
|
+
},
|
|
122136
|
+
// ── 呈现规范(原始 prompt HTML 要求)──
|
|
122137
|
+
{
|
|
122138
|
+
id: "viz-chartjs",
|
|
122139
|
+
chapter: "\u5448\u73B0",
|
|
122140
|
+
dimension: "Chart.js \u6570\u636E\u53EF\u89C6\u5316",
|
|
122141
|
+
hint: "\u987B\u542B chart.js \u5F15\u7528\u4E0E new Chart \u56FE\u8868"
|
|
122142
|
+
},
|
|
122143
|
+
{
|
|
122144
|
+
id: "viz-source",
|
|
122145
|
+
chapter: "\u5448\u73B0",
|
|
122146
|
+
dimension: "\u56FE\u8868/\u5173\u952E\u6570\u636E\u6765\u6E90\u811A\u6CE8",
|
|
122147
|
+
hint: "\u987B\u542B Source: \u6216\u300C\u6765\u6E90\u300D\u811A\u6CE8"
|
|
122148
|
+
}
|
|
122149
|
+
];
|
|
122150
|
+
function countPainPoints(html) {
|
|
122151
|
+
const labeled = (html.match(/痛点\s*\d/g) ?? []).length;
|
|
122152
|
+
if (labeled >= 4) return labeled;
|
|
122153
|
+
const section = html.match(/痛点[\s\S]{0,3000}/)?.[0] ?? html;
|
|
122154
|
+
const listItems = (section.match(/<li[^>]*>/g) ?? []).length;
|
|
122155
|
+
return Math.max(labeled, listItems >= 4 ? 4 : listItems);
|
|
122156
|
+
}
|
|
122157
|
+
function competitorCompareTableRows(html) {
|
|
122158
|
+
const tables = html.match(/<table[\s\S]*?<\/table>/g) ?? [];
|
|
122159
|
+
let best = 0;
|
|
122160
|
+
for (const t of tables) {
|
|
122161
|
+
const hasSelf = /小米|我方|客户/.test(t);
|
|
122162
|
+
const rivalHits = ["Apple", "Samsung", "Google", "\u82F9\u679C", "\u4E09\u661F", "\u8C37\u6B4C", "\u534E\u4E3A"].filter(
|
|
122163
|
+
(b) => t.includes(b)
|
|
122164
|
+
).length;
|
|
122165
|
+
if (!hasSelf || rivalHits < 2) continue;
|
|
122166
|
+
const rows = (t.match(/<tr/g) ?? []).length;
|
|
122167
|
+
if (rows > best) best = rows;
|
|
122168
|
+
}
|
|
122169
|
+
return best;
|
|
122170
|
+
}
|
|
122171
|
+
function mentionsCompetitors(html) {
|
|
122172
|
+
const brands = ["Apple", "Samsung", "Google", "\u82F9\u679C", "\u4E09\u661F", "\u8C37\u6B4C", "\u534E\u4E3A", "Motorola"];
|
|
122173
|
+
let n = 0;
|
|
122174
|
+
for (const b of brands) {
|
|
122175
|
+
if (html.includes(b)) n++;
|
|
122176
|
+
}
|
|
122177
|
+
return n >= 3;
|
|
122178
|
+
}
|
|
122179
|
+
var CONTENT_RULES = [
|
|
122180
|
+
{
|
|
122181
|
+
id: "cover",
|
|
122182
|
+
test: (h) => /战略市场分析|KA客户/.test(h) && (/目标市场|北美|全球|欧洲|亚太/.test(h) || /targetMarket/i.test(h))
|
|
122183
|
+
},
|
|
122184
|
+
{ id: "cover-kpi", test: (h) => /核心结论|metric|kpi|结论/.test(h) },
|
|
122185
|
+
{ id: "ch1", test: (h) => /市场与趋势诊断/.test(h) },
|
|
122186
|
+
{
|
|
122187
|
+
id: "ch1-tam",
|
|
122188
|
+
test: (h) => (/TAM|SAM|SOM/.test(h) || /市场规模/.test(h)) && /CAGR|增长率|增长趋势/.test(h)
|
|
122189
|
+
},
|
|
122190
|
+
{ id: "ch1-value-chain", test: (h) => /价值链|生态定位|产业链/.test(h) },
|
|
122191
|
+
{
|
|
122192
|
+
id: "ch1-target-geo",
|
|
122193
|
+
test: (h) => (/美国|加拿大|墨西哥|中国|欧洲/.test(h) || /国家|区域/.test(h)) && /增长|规模|优先级|潜力/.test(h)
|
|
122194
|
+
},
|
|
122195
|
+
{ id: "ch1-pestel", test: (h) => /PESTEL|政策|法规环境|技术趋势/.test(h) },
|
|
122196
|
+
{ id: "ch1-forecast", test: (h) => /预测|3.?5年|未来\d/.test(h) },
|
|
122197
|
+
{ id: "ch2", test: (h) => /行业深度洞察|目标行业/.test(h) },
|
|
122198
|
+
{ id: "ch2-pain", test: (h, ctx) => ctx.painPointCount >= 4 },
|
|
122199
|
+
{ id: "ch2-compliance", test: (h) => /法规|合规|FCC|隐私/.test(h) && /<table/.test(h) },
|
|
122200
|
+
{ id: "ch2-bpmn", test: (h) => /原流程|BPMN|介入后|新流程/.test(h) && /效率|成本|提升|下降/.test(h) },
|
|
122201
|
+
{ id: "ch3", test: (h) => /目标受众|受众与场景/.test(h) },
|
|
122202
|
+
{ id: "ch3-audience", test: (h) => /用户|受众|画像|群体/.test(h) && /<table/.test(h) },
|
|
122203
|
+
{ id: "ch3-scenario", test: (h) => /应用场景|场景|差异化|用户价值|企业价值/.test(h) },
|
|
122204
|
+
{ id: "ch4", test: (h) => /竞争策略|竞争.{0,6}定位/.test(h) },
|
|
122205
|
+
{ id: "ch4-competitors", test: (h) => mentionsCompetitors(h) },
|
|
122206
|
+
// 表头 1 行 + ≥10 项维度 ≈ ≥11 个 <tr>
|
|
122207
|
+
{ id: "ch4-compare-table", test: (h) => competitorCompareTableRows(h) >= 11 },
|
|
122208
|
+
{
|
|
122209
|
+
id: "ch4-gaps",
|
|
122210
|
+
test: (h) => [/功能空白|功能.*空白/.test(h), /场景/.test(h), /用户群|用户/.test(h), /地域|地区|区域/.test(h)].filter(
|
|
122211
|
+
Boolean
|
|
122212
|
+
).length >= 3
|
|
122213
|
+
},
|
|
122214
|
+
{ id: "ch4-positioning", test: (h) => /定位陈述|定位:|核心定位|差异化/.test(h) },
|
|
122215
|
+
{ id: "ch5", test: (h) => /增长与品牌|GTM/.test(h) },
|
|
122216
|
+
{ id: "ch5-gtm", test: (h) => /试点|规模复制|生态扩展|三阶段/.test(h) && /KPI|ROI|指标/.test(h) },
|
|
122217
|
+
{ id: "ch5-brand", test: (h) => /品牌|传播|主张/.test(h) },
|
|
122218
|
+
{ id: "ch5-tactics", test: (h) => /内容营销|渠道|KOL|销售模式|营销/.test(h) },
|
|
122219
|
+
{ id: "ch6", test: (h) => /实施/.test(h) && /风险|资源/.test(h) },
|
|
122220
|
+
{ id: "ch6-milestone", test: (h) => /里程碑|阶段/.test(h) && /任务|负责人|成果|指标/.test(h) },
|
|
122221
|
+
{ id: "ch6-budget", test: (h) => /预算|资源需求|人力|分配/.test(h) },
|
|
122222
|
+
{ id: "ch6-risk", test: (h) => /风险/.test(h) && /应对|措施|矩阵/.test(h) },
|
|
122223
|
+
{ id: "appendix", test: (h) => /附录|数据来源/.test(h) },
|
|
122224
|
+
{ id: "appendix-sources", test: (h) => /数据来源|Source:|报告名称|发布日期|http/.test(h) },
|
|
122225
|
+
{ id: "appendix-methods", test: (h) => /调研方法|桌面研究|访谈|问卷/.test(h) },
|
|
122226
|
+
{ id: "appendix-glossary", test: (h) => [/ROI/.test(h), /CAGR/.test(h), /GTM/.test(h), /BPMN/.test(h)].filter(Boolean).length >= 3 },
|
|
122227
|
+
{ id: "viz-chartjs", test: (h, ctx) => /chart\.js/i.test(h) && ctx.chartCount >= 1 },
|
|
122228
|
+
{ id: "viz-source", test: (h) => /Source:|来源:|来源:/.test(h) }
|
|
122229
|
+
];
|
|
122230
|
+
function validateMarketReportContent(html) {
|
|
122231
|
+
const ctx = {
|
|
122232
|
+
tableCount: (html.match(/<table/g) ?? []).length,
|
|
122233
|
+
chartCount: (html.match(/new Chart\(/g) ?? []).length,
|
|
122234
|
+
painPointCount: countPainPoints(html)
|
|
122235
|
+
};
|
|
122236
|
+
const dimById = new Map(MARKET_REPORT_DIMENSIONS.map((d) => [d.id, d]));
|
|
122237
|
+
const missing = [];
|
|
122238
|
+
for (const rule of CONTENT_RULES) {
|
|
122239
|
+
const meta = dimById.get(rule.id);
|
|
122240
|
+
if (!meta) continue;
|
|
122241
|
+
if (!rule.test(html, ctx)) {
|
|
122242
|
+
missing.push({
|
|
122243
|
+
id: meta.id,
|
|
122244
|
+
dimension: meta.dimension,
|
|
122245
|
+
chapter: meta.chapter,
|
|
122246
|
+
hint: meta.hint
|
|
122247
|
+
});
|
|
122248
|
+
}
|
|
122249
|
+
}
|
|
122250
|
+
return {
|
|
122251
|
+
ok: missing.length === 0,
|
|
122252
|
+
missing,
|
|
122253
|
+
stats: {
|
|
122254
|
+
tables: ctx.tableCount,
|
|
122255
|
+
charts: ctx.chartCount,
|
|
122256
|
+
painPoints: ctx.painPointCount,
|
|
122257
|
+
competitorTableRows: competitorCompareTableRows(html)
|
|
122258
|
+
}
|
|
122259
|
+
};
|
|
122260
|
+
}
|
|
122261
|
+
function formatMarketReportContentErrors(result) {
|
|
122262
|
+
if (result.ok) return "";
|
|
122263
|
+
const lines = result.missing.map((m) => ` - [${m.chapter}] ${m.dimension}\uFF1A${m.hint}`);
|
|
122264
|
+
return [
|
|
122265
|
+
`\u62A5\u544A\u7F3A\u5C11 ${result.missing.length} \u9879\u5FC5\u542B\u5185\u5BB9\uFF08\u5BF9\u9F50 TSO getMarketReport \u539F\u59CB\u4E1A\u52A1\u7EF4\u5EA6\uFF09\uFF1A`,
|
|
122266
|
+
...lines,
|
|
122267
|
+
"",
|
|
122268
|
+
"\u8BF7 Read assets/market-analysis-rules.md\u300C\u539F\u59CB\u4E1A\u52A1\u7EF4\u5EA6\u6E05\u5355\u300D\uFF0CWebSearch \u8865\u5168\u7F3A\u5931\u7AE0\u8282\u540E\u91CD\u5199 market-report.json\u3002"
|
|
122269
|
+
].join("\n");
|
|
122270
|
+
}
|
|
122271
|
+
|
|
122272
|
+
// src/commands/market-analysis/render-report.ts
|
|
122273
|
+
function asRecord4(value) {
|
|
122274
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
122275
|
+
return value;
|
|
122276
|
+
}
|
|
122277
|
+
throw new Error("\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
|
|
122278
|
+
}
|
|
122279
|
+
function resolveMarketAnalysisHtml(data) {
|
|
122280
|
+
const html = data.htmlContent;
|
|
122281
|
+
if (typeof html === "string" && html.trim()) {
|
|
122282
|
+
return html;
|
|
122283
|
+
}
|
|
122284
|
+
const raw = data.rawResponse;
|
|
122285
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
122286
|
+
return extractHtmlFromResponse(raw);
|
|
122287
|
+
}
|
|
122288
|
+
throw new Error("JSON \u4E2D\u7F3A\u5C11 htmlContent \u6216 rawResponse \u5B57\u6BB5");
|
|
122289
|
+
}
|
|
122290
|
+
async function runMarketAnalysisRender(opts) {
|
|
122291
|
+
const dataPath = path18.resolve(opts.dataFile);
|
|
122292
|
+
let dataRaw;
|
|
122293
|
+
try {
|
|
122294
|
+
dataRaw = JSON.parse(await fs13.readFile(dataPath, "utf8"));
|
|
122295
|
+
} catch {
|
|
122296
|
+
console.error(`
|
|
122297
|
+
\u274C \u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${dataPath}
|
|
122298
|
+
`);
|
|
122299
|
+
process.exit(1);
|
|
122300
|
+
}
|
|
122301
|
+
let html;
|
|
122302
|
+
try {
|
|
122303
|
+
html = resolveMarketAnalysisHtml(asRecord4(dataRaw));
|
|
122304
|
+
} catch (err) {
|
|
122305
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
122306
|
+
console.error(`
|
|
122307
|
+
\u274C ${message}
|
|
122308
|
+
`);
|
|
122309
|
+
process.exit(1);
|
|
122310
|
+
}
|
|
122311
|
+
if (!html.trim()) {
|
|
122312
|
+
console.error("\n\u274C \u62A5\u544A HTML \u5185\u5BB9\u4E3A\u7A7A\n");
|
|
122313
|
+
process.exit(1);
|
|
122314
|
+
}
|
|
122315
|
+
const contentCheck = validateMarketReportContent(html);
|
|
122316
|
+
if (!contentCheck.ok) {
|
|
122317
|
+
console.error(`
|
|
122318
|
+
\u274C ${formatMarketReportContentErrors(contentCheck)}
|
|
122319
|
+
`);
|
|
122320
|
+
console.error(
|
|
122321
|
+
` \u7EDF\u8BA1\uFF1A${contentCheck.stats.tables} \u8868\uFF5C${contentCheck.stats.charts} \u56FE\uFF5C${contentCheck.stats.painPoints} \u75DB\u70B9\uFF5C\u7ADE\u54C1\u8868 ${contentCheck.stats.competitorTableRows} \u884C
|
|
122322
|
+
`
|
|
122323
|
+
);
|
|
122324
|
+
process.exit(1);
|
|
122325
|
+
}
|
|
122326
|
+
const outPath = path18.resolve(
|
|
122327
|
+
opts.out ?? path18.join(path18.dirname(dataPath), "market-analysis-report.html")
|
|
122328
|
+
);
|
|
122329
|
+
await fs13.mkdir(path18.dirname(outPath), { recursive: true });
|
|
122330
|
+
await fs13.writeFile(outPath, html, "utf8");
|
|
122331
|
+
const { stats } = contentCheck;
|
|
122332
|
+
console.log(`
|
|
122333
|
+
\u2705 \u5E02\u573A\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
122334
|
+
`);
|
|
122335
|
+
console.log(
|
|
122336
|
+
` \u5FC5\u542B\u7EF4\u5EA6\u5DF2\u5168\u90E8\u8986\u76D6\uFF5C${stats.tables} \u8868\uFF5C${stats.charts} \u56FE\uFF5C${stats.painPoints} \u75DB\u70B9\uFF5C\u7ADE\u54C1\u8868 ${stats.competitorTableRows} \u884C
|
|
122337
|
+
`
|
|
122338
|
+
);
|
|
122339
|
+
console.log("\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 HTML \u6587\u4EF6\u5373\u53EF\u67E5\u770B\u5B8C\u6574\u62A5\u544A\uFF08\u9700\u8054\u7F51\u52A0\u8F7D Bootstrap / Chart.js CDN\uFF09\u3002\n");
|
|
122340
|
+
}
|
|
122341
|
+
|
|
122342
|
+
// src/commands/market-analysis/register.ts
|
|
122343
|
+
function registerMarketAnalysisCommands(program2) {
|
|
122344
|
+
const root = program2.command("market-analysis").description("\u6218\u7565\u5E02\u573A\u5206\u6790\uFF1A\u91C7\u96C6\u5BA2\u6237\u4E0A\u4E0B\u6587\uFF0C\u7531 Agent \u8C03\u7814\u5E76\u751F\u6210 HTML \u62A5\u544A");
|
|
122345
|
+
root.command("collect").description(
|
|
122346
|
+
"\u91C7\u96C6\u5E02\u573A\u5206\u6790\u539F\u6599\uFF1A\u5BA2\u6237\u4FE1\u606F + \u53EF\u9009\u5B98\u7F51 HTML \u9884\u89C8\uFF1B\u62A5\u544A\u7531 Agent \u6309 market-analysis-rules.md \u751F\u6210"
|
|
122347
|
+
).option("--customer-name <name>", "\u5BA2\u6237\u540D\u79F0").option("--website <url>", "\u5BA2\u6237\u7F51\u7AD9\uFF08\u53EF\u9009\u62C9\u53D6\u9996\u9875\u9884\u89C8\u4F9B Agent \u53C2\u8003\uFF09").option("--industry <industry>", "\u6240\u5C5E\u884C\u4E1A").option("--core-products <products>", "\u6838\u5FC3\u4EA7\u54C1").option("--business-position <position>", "\u5546\u4E1A\u5B9A\u4F4D").option("--target-market <market>", "\u76EE\u6807\u5E02\u573A\uFF08\u9ED8\u8BA4\uFF1A\u5168\u7403\uFF09").option("--time-range <range>", "\u65F6\u95F4\u8303\u56F4\uFF08\u9ED8\u8BA4\uFF1A\u8FD112\u4E2A\u6708\uFF09").option("--skip-website", "\u4E0D\u8BF7\u6C42\u5B98\u7F51 HTML\uFF08\u4EC5\u843D\u76D8\u5BA2\u6237\u53C2\u6570\uFF09").option("--token <token>", "JWT\uFF08\u62C9\u53D6\u5B98\u7F51\u65F6\u9700\u8981\uFF1B\u9ED8\u8BA4\u8BFB config / \u73AF\u5883\u53D8\u91CF\uFF09").option("--verbose", "\u6253\u5370\u8BF7\u6C42\u8BE6\u60C5").option("--json-out <dir>", "\u843D\u76D8 cli-manifest + JSON\uFF08\u63A8\u8350\uFF09").action(
|
|
122348
|
+
async (opts) => {
|
|
122349
|
+
await runMarketAnalysisCollect({
|
|
122350
|
+
customerName: opts.customerName,
|
|
122351
|
+
website: opts.website,
|
|
122352
|
+
industry: opts.industry,
|
|
122353
|
+
coreProducts: opts.coreProducts,
|
|
122354
|
+
businessPosition: opts.businessPosition,
|
|
122355
|
+
targetMarket: opts.targetMarket,
|
|
122356
|
+
timeRange: opts.timeRange,
|
|
122357
|
+
skipWebsite: opts.skipWebsite,
|
|
122358
|
+
token: opts.token,
|
|
122359
|
+
verbose: opts.verbose,
|
|
122360
|
+
jsonOut: opts.jsonOut
|
|
122361
|
+
});
|
|
122362
|
+
}
|
|
122363
|
+
);
|
|
122364
|
+
root.command("render").description(
|
|
122365
|
+
"\u6839\u636E Agent \u751F\u6210\u7684 market-report.json\uFF08\u542B htmlContent\uFF09\u5199\u51FA HTML \u7EC8\u7A3F"
|
|
122366
|
+
).requiredOption("--data <file>", "Agent \u4EA7\u51FA\u7684 market-report.json").option("--out <file>", "\u8F93\u51FA HTML \u8DEF\u5F84\uFF08\u9ED8\u8BA4\u540C --data \u76EE\u5F55\uFF09").action(async (opts) => {
|
|
122367
|
+
await runMarketAnalysisRender({
|
|
122368
|
+
dataFile: opts.data,
|
|
122369
|
+
out: opts.out
|
|
122370
|
+
});
|
|
122371
|
+
});
|
|
122372
|
+
}
|
|
122373
|
+
|
|
121756
122374
|
// src/index.ts
|
|
121757
122375
|
init_google_analysis3();
|
|
121758
122376
|
init_google_analysis_batch();
|
|
@@ -121860,7 +122478,7 @@ function endpointHintForFacebookSection(def, apiId) {
|
|
|
121860
122478
|
|
|
121861
122479
|
// src/commands/facebook-analysis/run-batch.ts
|
|
121862
122480
|
init_auth();
|
|
121863
|
-
import * as
|
|
122481
|
+
import * as path23 from "path";
|
|
121864
122482
|
import { performance as performance5 } from "perf_hooks";
|
|
121865
122483
|
init_version();
|
|
121866
122484
|
|
|
@@ -121964,7 +122582,7 @@ async function runAllFacebookSections(opts) {
|
|
|
121964
122582
|
const results = await runWithConcurrency2(tasks, concurrency);
|
|
121965
122583
|
const succeeded = results.filter((r) => r.ok).length;
|
|
121966
122584
|
const failed = results.length - succeeded;
|
|
121967
|
-
const absoluteSnapshotDir =
|
|
122585
|
+
const absoluteSnapshotDir = path23.resolve(opts.jsonOut);
|
|
121968
122586
|
const summary = {
|
|
121969
122587
|
kind: "siluzan-tso-facebook-analysis-snapshot-batch",
|
|
121970
122588
|
absoluteSnapshotDir,
|
|
@@ -122013,9 +122631,9 @@ init_cli_json_snapshot();
|
|
|
122013
122631
|
installProcessHandlers();
|
|
122014
122632
|
function getVersion() {
|
|
122015
122633
|
try {
|
|
122016
|
-
const __dirname3 =
|
|
122017
|
-
const pkgPath =
|
|
122018
|
-
const pkg = JSON.parse(
|
|
122634
|
+
const __dirname3 = path24.dirname(fileURLToPath5(import.meta.url));
|
|
122635
|
+
const pkgPath = path24.join(__dirname3, "..", "package.json");
|
|
122636
|
+
const pkg = JSON.parse(fs16.readFileSync(pkgPath, "utf8"));
|
|
122019
122637
|
return pkg.version ?? "0.0.0";
|
|
122020
122638
|
} catch {
|
|
122021
122639
|
return "0.0.0";
|
|
@@ -122023,7 +122641,7 @@ function getVersion() {
|
|
|
122023
122641
|
}
|
|
122024
122642
|
var program = new Command();
|
|
122025
122643
|
program.name("siluzan-tso").description(
|
|
122026
|
-
"Siluzan \u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1A\u8D26\u6237\u67E5\u8BE2\u3001\u4F59\u989D\u3001\u6295\u653E\u6570\u636E\u3001\u5F00\u6237\u7533\u8BF7\uFF08Google/TikTok/Yandex/Bing/Kwai\uFF09\u3001\n\u8D26\u53F7\u5206\u4EAB/\u89E3\u7ED1\u3001\u4F18\u5316\u62A5\u544A\u3001Google \u8D26\u6237\u5206\u6790\u7F51\u5173\uFF08google-analysis\uFF09\u3001\u7F51\u7AD9\u8BCA\u65AD\uFF08website-diagnosis\uFF09\u3001\u5145\u503C\u8F6C\u8D26\u3001\u5F00\u7968\u3001\u667A\u80FD\u9884\u8B66\u3001Google \u5E7F\u544A\u7BA1\u7406\uFF08\u542B\u5F02\u6B65\u6279\u91CF\uFF09\u3002"
|
|
122644
|
+
"Siluzan \u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1A\u8D26\u6237\u67E5\u8BE2\u3001\u4F59\u989D\u3001\u6295\u653E\u6570\u636E\u3001\u5F00\u6237\u7533\u8BF7\uFF08Google/TikTok/Yandex/Bing/Kwai\uFF09\u3001\n\u8D26\u53F7\u5206\u4EAB/\u89E3\u7ED1\u3001\u4F18\u5316\u62A5\u544A\u3001Google \u8D26\u6237\u5206\u6790\u7F51\u5173\uFF08google-analysis\uFF09\u3001\u7F51\u7AD9\u8BCA\u65AD\uFF08website-diagnosis\uFF09\u3001\u6218\u7565\u5E02\u573A\u5206\u6790\uFF08market-analysis\uFF0CAgent \u751F\u6210\u62A5\u544A\uFF09\u3001\u5145\u503C\u8F6C\u8D26\u3001\u5F00\u7968\u3001\u667A\u80FD\u9884\u8B66\u3001Google \u5E7F\u544A\u7BA1\u7406\uFF08\u542B\u5F02\u6B65\u6279\u91CF\uFF09\u3002"
|
|
122027
122645
|
).version(getVersion());
|
|
122028
122646
|
program.option("--commit <text>", "\u5199\u64CD\u4F5C\u8BF4\u660E\uFF1B\u8BB0\u5165\u672C\u673A\u5199\u5BA1\u8BA1 JSONL");
|
|
122029
122647
|
program.hook("preAction", async () => {
|
|
@@ -122067,7 +122685,8 @@ var REGISTRARS = [
|
|
|
122067
122685
|
register23,
|
|
122068
122686
|
register24,
|
|
122069
122687
|
register25,
|
|
122070
|
-
registerWebsiteDiagnosisCommands
|
|
122688
|
+
registerWebsiteDiagnosisCommands,
|
|
122689
|
+
registerMarketAnalysisCommands
|
|
122071
122690
|
];
|
|
122072
122691
|
for (const reg of REGISTRARS) reg(program);
|
|
122073
122692
|
function argvHasDeprecatedJsonFlag(argv) {
|