@gobing-ai/knowledge-kit 0.0.30 → 0.0.32
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/.claude-plugin/marketplace.json +1 -1
- package/dist/index.js +10 -10
- package/package.json +4 -4
- package/plugins/generations/content-gen/dist/index.js +9 -9
- package/plugins/generations/core-facts-gen/dist/index.js +12 -12
- package/plugins/generations/daily-article-gen/dist/index.js +12 -12
- package/plugins/generations/dailynews-gen/dist/index.js +10 -10
- package/plugins/generations/dailynews-gen/src/script-builder.ts +4 -2
- package/plugins/generations/episode-plan-gen/dist/index.js +23 -13
- package/plugins/generations/episode-plan-gen/src/index.ts +23 -2
- package/plugins/generations/gemini-tts/dist/index.js +9 -9
- package/plugins/generations/image-gen/dist/index.js +9 -9
- package/plugins/generations/news-report-gen/dist/index.js +12 -12
- package/plugins/generations/voice-gen/dist/index.js +9 -9
- package/plugins/ingestions/aihot-ingest/dist/index.js +12 -12
- package/plugins/ingestions/horizon-ingest/dist/index.js +12 -12
- package/plugins/ingestions/karakeep-local/dist/index.js +13 -13
- package/plugins/ingestions/last30days-ingest/dist/index.js +12 -12
- package/plugins/ingestions/web-search/dist/index.js +10 -10
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/kk-workflow-stages.ts +173 -15
- package/plugins/kk/skills/content-judge/SKILL.md +16 -4
- package/plugins/kk/skills/content-judge/references/rubrics.md +8 -2
- package/plugins/kk/skills/content-judge/references/workflow-integration.md +2 -1
- package/plugins/kk/skills/content-safety/SKILL.md +24 -13
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +65 -8
- package/plugins/publishings/emdash-pub/dist/index.js +9 -9
- package/plugins/publishings/infoq-pub/dist/index.js +8602 -211
- package/plugins/publishings/infoq-pub/package.json +3 -1
- package/plugins/publishings/infoq-pub/plugin.json +1 -1
- package/plugins/publishings/infoq-pub/src/index.ts +246 -29
- package/plugins/publishings/infoq-pub/src/profile-check.ts +19 -0
- package/plugins/publishings/juejin-pub/dist/index.js +8665 -211
- package/plugins/publishings/juejin-pub/package.json +3 -1
- package/plugins/publishings/juejin-pub/plugin.json +1 -1
- package/plugins/publishings/juejin-pub/src/index.ts +305 -29
- package/plugins/publishings/juejin-pub/src/profile-check.ts +19 -0
- package/plugins/publishings/medium-pub/dist/index.js +9 -9
- package/plugins/publishings/podcast-pub/dist/index.js +9 -9
- package/plugins/publishings/qiita-pub/dist/index.js +9 -9
- package/plugins/publishings/substack-pub/dist/index.js +8610 -211
- package/plugins/publishings/substack-pub/package.json +3 -1
- package/plugins/publishings/substack-pub/plugin.json +1 -1
- package/plugins/publishings/substack-pub/src/index.ts +271 -29
- package/plugins/publishings/substack-pub/src/profile-check.ts +74 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +9 -9
- package/plugins/publishings/wechatmp-pub/dist/index.js +1227 -348
- package/plugins/publishings/wechatmp-pub/src/enrich.ts +229 -0
- package/plugins/publishings/wechatmp-pub/src/flow.ts +896 -0
- package/plugins/publishings/wechatmp-pub/src/index.ts +186 -516
- package/plugins/publishings/wechatmp-pub/src/modes.ts +403 -0
- package/plugins/publishings/wechatmp-pub/src/page.ts +260 -0
- package/plugins/publishings/wechatmp-pub/src/profile-check.ts +7 -5
- package/plugins/publishings/x-pub/dist/index.js +8626 -221
- package/plugins/publishings/x-pub/package.json +3 -1
- package/plugins/publishings/x-pub/plugin.json +1 -1
- package/plugins/publishings/x-pub/src/index.ts +258 -20
- package/plugins/publishings/x-pub/src/profile-check.ts +19 -0
- package/plugins/publishings/xhs-pub/dist/index.js +843 -346
- package/plugins/publishings/xhs-pub/plugin.json +1 -1
- package/plugins/publishings/xhs-pub/src/flow.ts +572 -0
- package/plugins/publishings/xhs-pub/src/index.ts +101 -593
- package/plugins/publishings/xhs-pub/src/modes.ts +300 -0
- package/plugins/publishings/xhs-pub/src/page.ts +267 -0
- package/plugins/publishings/xhs-pub/src/profile-check.ts +2 -3
- package/plugins/publishings/zenn-pub/dist/index.js +9 -9
|
@@ -14671,7 +14671,7 @@ var init_zod = __esm(() => {
|
|
|
14671
14671
|
init_external();
|
|
14672
14672
|
});
|
|
14673
14673
|
|
|
14674
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14674
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/errors.js
|
|
14675
14675
|
var ErrorCode;
|
|
14676
14676
|
var init_errors3 = __esm(() => {
|
|
14677
14677
|
ErrorCode = {
|
|
@@ -14682,7 +14682,7 @@ var init_errors3 = __esm(() => {
|
|
|
14682
14682
|
};
|
|
14683
14683
|
});
|
|
14684
14684
|
|
|
14685
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14685
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/api-response.js
|
|
14686
14686
|
var API_ERROR_CODES, ERROR_CODE_TO_HTTP, CLIENT_SAFE_CODES;
|
|
14687
14687
|
var init_api_response = __esm(() => {
|
|
14688
14688
|
init_errors3();
|
|
@@ -14705,10 +14705,10 @@ var init_api_response = __esm(() => {
|
|
|
14705
14705
|
CLIENT_SAFE_CODES = new Set([ErrorCode.NotFound, ErrorCode.Validation, ErrorCode.Conflict]);
|
|
14706
14706
|
});
|
|
14707
14707
|
|
|
14708
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14708
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/cursor.js
|
|
14709
14709
|
var init_cursor = () => {};
|
|
14710
14710
|
|
|
14711
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14711
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/env.js
|
|
14712
14712
|
function getEnvVar(name, fallback) {
|
|
14713
14713
|
const raw = process.env[name];
|
|
14714
14714
|
return raw === undefined ? fallback : raw;
|
|
@@ -14717,10 +14717,10 @@ function getEnvVars() {
|
|
|
14717
14717
|
return process.env;
|
|
14718
14718
|
}
|
|
14719
14719
|
|
|
14720
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14720
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
|
|
14721
14721
|
var init_event_severity = () => {};
|
|
14722
14722
|
|
|
14723
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14723
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/object.js
|
|
14724
14724
|
function isPlainObject2(value) {
|
|
14725
14725
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14726
14726
|
}
|
|
@@ -14738,7 +14738,7 @@ function deepMerge(target, source) {
|
|
|
14738
14738
|
return result;
|
|
14739
14739
|
}
|
|
14740
14740
|
|
|
14741
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14741
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/output.js
|
|
14742
14742
|
function processStream(name) {
|
|
14743
14743
|
const proc = globalThis.process;
|
|
14744
14744
|
const stream = proc?.[name];
|
|
@@ -14756,7 +14756,7 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
|
|
|
14756
14756
|
}
|
|
14757
14757
|
var defaultStderrTarget;
|
|
14758
14758
|
|
|
14759
|
-
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.
|
|
14759
|
+
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/index.js
|
|
14760
14760
|
var init_dist = __esm(() => {
|
|
14761
14761
|
init_api_response();
|
|
14762
14762
|
init_cursor();
|
|
@@ -21754,7 +21754,7 @@ var init_dist2 = __esm(() => {
|
|
|
21754
21754
|
$visitAsync = visit.visitAsync;
|
|
21755
21755
|
});
|
|
21756
21756
|
|
|
21757
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
21757
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/config.js
|
|
21758
21758
|
function getProcessEnv() {
|
|
21759
21759
|
return getEnvVars();
|
|
21760
21760
|
}
|
|
@@ -21832,7 +21832,7 @@ var init_config = __esm(() => {
|
|
|
21832
21832
|
};
|
|
21833
21833
|
});
|
|
21834
21834
|
|
|
21835
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
21835
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
|
|
21836
21836
|
import { appendFileSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
|
|
21837
21837
|
import { dirname, resolve as resolvePath } from "path";
|
|
21838
21838
|
function createNodeFileSystem(root, paths) {
|
|
@@ -21919,7 +21919,7 @@ function findProjectRoot(startDir) {
|
|
|
21919
21919
|
}
|
|
21920
21920
|
var init_file_system_node = () => {};
|
|
21921
21921
|
|
|
21922
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
21922
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
|
|
21923
21923
|
var DbModuleNotInstalledError;
|
|
21924
21924
|
var init_db_errors = __esm(() => {
|
|
21925
21925
|
DbModuleNotInstalledError = class DbModuleNotInstalledError extends Error {
|
|
@@ -29130,7 +29130,7 @@ var init_execa = __esm(() => {
|
|
|
29130
29130
|
} = getIpcExport());
|
|
29131
29131
|
});
|
|
29132
29132
|
|
|
29133
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29133
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/process-group.js
|
|
29134
29134
|
function resolveDeadline(optionTimeout, defaultTimeout) {
|
|
29135
29135
|
if (optionTimeout === null)
|
|
29136
29136
|
return null;
|
|
@@ -29222,7 +29222,7 @@ function ownProcessGroupLifecycle(ownership) {
|
|
|
29222
29222
|
}
|
|
29223
29223
|
var MAX_TIMEOUT_MS = 2147483647, DEFAULT_KILL_GRACE_MS = 5000, GROUP_POLL_INTERVAL_MS = 25, GROUP_KILL_PATIENCE_MS = 2000;
|
|
29224
29224
|
|
|
29225
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29225
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
|
|
29226
29226
|
import { isatty } from "tty";
|
|
29227
29227
|
function processEventSeverity(reason, exitCode) {
|
|
29228
29228
|
if (reason === "error")
|
|
@@ -29594,7 +29594,7 @@ var init_process_executor = __esm(() => {
|
|
|
29594
29594
|
init_config();
|
|
29595
29595
|
});
|
|
29596
29596
|
|
|
29597
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29597
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
|
|
29598
29598
|
function getNodeFileSystem() {
|
|
29599
29599
|
if (!_nodeFileSystem)
|
|
29600
29600
|
_nodeFileSystem = createNodeFileSystem();
|
|
@@ -29655,7 +29655,7 @@ var init_runtime_node_bun = __esm(() => {
|
|
|
29655
29655
|
});
|
|
29656
29656
|
|
|
29657
29657
|
// ../../plugins/publishings/xhs-pub/src/index.ts
|
|
29658
|
-
import { dirname as dirname2
|
|
29658
|
+
import { dirname as dirname2 } from "path";
|
|
29659
29659
|
import { parseArgs } from "util";
|
|
29660
29660
|
|
|
29661
29661
|
// ../../packages/kk-core/src/kinds.ts
|
|
@@ -29745,11 +29745,11 @@ var PluginManifestSchema = exports_external.looseObject({
|
|
|
29745
29745
|
description: exports_external.string().optional(),
|
|
29746
29746
|
operations: exports_external.record(exports_external.string(), PluginOperationSchema).optional()
|
|
29747
29747
|
});
|
|
29748
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29748
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/index.js
|
|
29749
29749
|
init_file_system_node();
|
|
29750
29750
|
init_config();
|
|
29751
29751
|
|
|
29752
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29752
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/path.js
|
|
29753
29753
|
function normalizeSeparators(path6) {
|
|
29754
29754
|
return path6.replaceAll("\\", "/");
|
|
29755
29755
|
}
|
|
@@ -29807,7 +29807,7 @@ function getProcessCwd() {
|
|
|
29807
29807
|
return globalThis.process?.cwd?.() ?? "/";
|
|
29808
29808
|
}
|
|
29809
29809
|
|
|
29810
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29810
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/index.js
|
|
29811
29811
|
init_runtime_node_bun();
|
|
29812
29812
|
|
|
29813
29813
|
// ../../packages/kk-core/src/render-md.ts
|
|
@@ -29919,7 +29919,8 @@ async function trySelectors(page, selectors, options = {}) {
|
|
|
29919
29919
|
}
|
|
29920
29920
|
return { found: false, selector: null };
|
|
29921
29921
|
}
|
|
29922
|
-
|
|
29922
|
+
|
|
29923
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/errors.js
|
|
29923
29924
|
class BrowserAutomationError extends Error {
|
|
29924
29925
|
constructor(message, options) {
|
|
29925
29926
|
super(message, options);
|
|
@@ -29968,11 +29969,10 @@ class OperationAbortedError extends BrowserAutomationError {
|
|
|
29968
29969
|
this.name = "OperationAbortedError";
|
|
29969
29970
|
}
|
|
29970
29971
|
}
|
|
29971
|
-
|
|
29972
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.11+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/launcher.js
|
|
29972
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/launcher.js
|
|
29973
29973
|
import { mkdir } from "fs/promises";
|
|
29974
29974
|
|
|
29975
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.
|
|
29975
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/profile-dir.js
|
|
29976
29976
|
function resolveProfileDir(profileDir) {
|
|
29977
29977
|
if (profileDir.trim() === "") {
|
|
29978
29978
|
throw new InvalidProfileDirError;
|
|
@@ -29984,7 +29984,7 @@ function resolveProfileDir(profileDir) {
|
|
|
29984
29984
|
return resolved;
|
|
29985
29985
|
}
|
|
29986
29986
|
|
|
29987
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.
|
|
29987
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/launcher.js
|
|
29988
29988
|
var PROFILE_DIR_MODE = 448;
|
|
29989
29989
|
var BUSY_PROFILE_PATTERN = /singleton|already in use|in use by another/i;
|
|
29990
29990
|
var CHROMIUM_MISSING_PATTERN = /playwright install|executable doesn't exist/i;
|
|
@@ -30035,7 +30035,7 @@ function createPersistentLauncher(options = {}) {
|
|
|
30035
30035
|
};
|
|
30036
30036
|
}
|
|
30037
30037
|
|
|
30038
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.
|
|
30038
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/operations.js
|
|
30039
30039
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
30040
30040
|
var POLL_INTERVAL_MS = 250;
|
|
30041
30041
|
var defaultLauncher;
|
|
@@ -30120,6 +30120,201 @@ async function pollPredicate(page, predicate, timeoutMs, signal) {
|
|
|
30120
30120
|
function sleep(ms) {
|
|
30121
30121
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
30122
30122
|
}
|
|
30123
|
+
// ../../packages/publish-harness/src/browser.ts
|
|
30124
|
+
var DEFAULT_CHECK_TIMEOUT_MS = 30000;
|
|
30125
|
+
var DEFAULT_STEP_TIMEOUT_MS = 30000;
|
|
30126
|
+
var DEFAULT_POLL_MS = 500;
|
|
30127
|
+
var ATTACH_POLL_MS = 100;
|
|
30128
|
+
function resolveChannelProfileDir(channel, override) {
|
|
30129
|
+
const tiers = [override, process.env[`${channel.toUpperCase()}_PROFILE_DIR`], process.env.KK_BROWSER_PROFILE_DIR];
|
|
30130
|
+
const chosen = tiers.find((tier) => tier !== undefined && tier.trim().length > 0);
|
|
30131
|
+
return resolveBrowserProfileDir(channel, chosen);
|
|
30132
|
+
}
|
|
30133
|
+
async function checkChannelSession(config3, opts) {
|
|
30134
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS;
|
|
30135
|
+
const passes = opts.headedFallback === false ? [true] : [true, false];
|
|
30136
|
+
let lastDetail = `${config3.channel} readiness was never probed`;
|
|
30137
|
+
for (const headless of passes) {
|
|
30138
|
+
try {
|
|
30139
|
+
await withProfilePage({
|
|
30140
|
+
profileDir: opts.profileDir,
|
|
30141
|
+
headless,
|
|
30142
|
+
url: config3.homeUrl,
|
|
30143
|
+
isAuthenticated: config3.isAuthenticated,
|
|
30144
|
+
timeoutMs,
|
|
30145
|
+
launcher: opts.launcher
|
|
30146
|
+
}, async () => true);
|
|
30147
|
+
return {
|
|
30148
|
+
status: "verified",
|
|
30149
|
+
profileDir: opts.profileDir,
|
|
30150
|
+
detail: `authenticated ${config3.channel} session observed ${headless ? "headlessly" : "via headed fallback"} via profile ${opts.profileDir} (read-only)`
|
|
30151
|
+
};
|
|
30152
|
+
} catch (error51) {
|
|
30153
|
+
if (error51 instanceof BrowserProfileMissingError) {
|
|
30154
|
+
return {
|
|
30155
|
+
status: "refused",
|
|
30156
|
+
profileDir: opts.profileDir,
|
|
30157
|
+
detail: `no ${config3.channel} browser profile at ${opts.profileDir} \u2014 run the login command first`
|
|
30158
|
+
};
|
|
30159
|
+
}
|
|
30160
|
+
lastDetail = `${headless ? "headless" : "headed"} ${config3.channel} readiness refused: ${errorMessage2(error51)}`;
|
|
30161
|
+
}
|
|
30162
|
+
}
|
|
30163
|
+
return { status: "refused", profileDir: opts.profileDir, detail: lastDetail };
|
|
30164
|
+
}
|
|
30165
|
+
async function waitUntil(predicate, timeoutMs, pollMs = DEFAULT_POLL_MS) {
|
|
30166
|
+
const deadline = Date.now() + timeoutMs;
|
|
30167
|
+
while (Date.now() < deadline) {
|
|
30168
|
+
if (await predicate()) {
|
|
30169
|
+
return true;
|
|
30170
|
+
}
|
|
30171
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollMs));
|
|
30172
|
+
}
|
|
30173
|
+
return predicate();
|
|
30174
|
+
}
|
|
30175
|
+
async function pasteHtml(page, selector, html, opts = {}) {
|
|
30176
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
30177
|
+
await requireAttached(page, selector, "pasteHtml", timeoutMs);
|
|
30178
|
+
await page.evaluate(pasteScript(selector, html));
|
|
30179
|
+
const filled = await waitUntil(async () => String(await page.evaluate(editorTextScript(selector))).length > 0, timeoutMs, ATTACH_POLL_MS);
|
|
30180
|
+
if (!filled) {
|
|
30181
|
+
throw new Error(`pasteHtml: ${selector}: editor text is empty ${timeoutMs}ms after the paste`);
|
|
30182
|
+
}
|
|
30183
|
+
}
|
|
30184
|
+
async function uploadFile(page, selector, filePath, opts = {}) {
|
|
30185
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
30186
|
+
await requireAttached(page, selector, "uploadFile", timeoutMs);
|
|
30187
|
+
await page.setInputFiles(selector, filePath);
|
|
30188
|
+
}
|
|
30189
|
+
async function expectOutcome(probe, opts) {
|
|
30190
|
+
let evidence = "";
|
|
30191
|
+
let lastProbeError = null;
|
|
30192
|
+
const observed = await waitUntil(async () => {
|
|
30193
|
+
let result;
|
|
30194
|
+
try {
|
|
30195
|
+
result = await probe();
|
|
30196
|
+
} catch (error51) {
|
|
30197
|
+
lastProbeError = error51 instanceof Error ? error51.message : String(error51);
|
|
30198
|
+
return false;
|
|
30199
|
+
}
|
|
30200
|
+
if (result === null || result.length === 0) {
|
|
30201
|
+
return false;
|
|
30202
|
+
}
|
|
30203
|
+
evidence = result;
|
|
30204
|
+
return true;
|
|
30205
|
+
}, opts.timeoutMs, opts.pollMs ?? DEFAULT_POLL_MS);
|
|
30206
|
+
if (observed && evidence.length > 0) {
|
|
30207
|
+
return { ok: true, evidence };
|
|
30208
|
+
}
|
|
30209
|
+
const detail = lastProbeError === null ? "" : ` (last probe error: ${lastProbeError})`;
|
|
30210
|
+
return { ok: false, error: `${opts.label}: no outcome observed within ${opts.timeoutMs}ms${detail}` };
|
|
30211
|
+
}
|
|
30212
|
+
var DEFAULT_SIGNAL_CONTAINERS = [
|
|
30213
|
+
'[class*="toast" i]',
|
|
30214
|
+
'[class*="notification" i]',
|
|
30215
|
+
'[role="alert"]',
|
|
30216
|
+
'[class*="message" i]'
|
|
30217
|
+
];
|
|
30218
|
+
async function readScopedSignal(page, texts, containers = DEFAULT_SIGNAL_CONTAINERS) {
|
|
30219
|
+
for (const text of texts) {
|
|
30220
|
+
for (const container of containers) {
|
|
30221
|
+
const locator = page.locator(`${container} :text("${text}")`).first();
|
|
30222
|
+
if (await locator.count().catch(() => 0) === 0)
|
|
30223
|
+
continue;
|
|
30224
|
+
if (!await locator.isVisible().catch(() => false))
|
|
30225
|
+
continue;
|
|
30226
|
+
const observed = (await locator.textContent().catch(() => null))?.trim();
|
|
30227
|
+
return observed !== undefined && observed.length > 0 ? observed : text;
|
|
30228
|
+
}
|
|
30229
|
+
}
|
|
30230
|
+
return null;
|
|
30231
|
+
}
|
|
30232
|
+
var cdpNodeText = (node) => node.nodeName === "#text" ? node.nodeValue ?? "" : (node.children ?? []).map(cdpNodeText).join("");
|
|
30233
|
+
async function findShadowControl(session, labels) {
|
|
30234
|
+
const document = await session.send("DOM.getDocument", { depth: -1, pierce: true });
|
|
30235
|
+
if (!document.root)
|
|
30236
|
+
return null;
|
|
30237
|
+
const wanted = new Set(labels);
|
|
30238
|
+
const findNode = (node, insideShadow) => {
|
|
30239
|
+
if (insideShadow && node.nodeName === "BUTTON" && typeof node.backendNodeId === "number") {
|
|
30240
|
+
const label = cdpNodeText(node).replace(/\s+/g, "");
|
|
30241
|
+
if (wanted.has(label))
|
|
30242
|
+
return { label, backendNodeId: node.backendNodeId };
|
|
30243
|
+
}
|
|
30244
|
+
for (const shadowRoot of node.shadowRoots ?? []) {
|
|
30245
|
+
const hit2 = findNode(shadowRoot, true);
|
|
30246
|
+
if (hit2)
|
|
30247
|
+
return hit2;
|
|
30248
|
+
}
|
|
30249
|
+
for (const child of node.children ?? []) {
|
|
30250
|
+
const hit2 = findNode(child, insideShadow);
|
|
30251
|
+
if (hit2)
|
|
30252
|
+
return hit2;
|
|
30253
|
+
}
|
|
30254
|
+
return null;
|
|
30255
|
+
};
|
|
30256
|
+
const hit = findNode(document.root, false);
|
|
30257
|
+
if (hit === null)
|
|
30258
|
+
return null;
|
|
30259
|
+
const box = await session.send("DOM.getBoxModel", { backendNodeId: hit.backendNodeId });
|
|
30260
|
+
const quad = box.model?.content;
|
|
30261
|
+
if (quad === undefined || quad.length < 8)
|
|
30262
|
+
return null;
|
|
30263
|
+
const [x1 = 0, y1 = 0, x3 = 0, , , y4 = 0] = quad;
|
|
30264
|
+
return { label: hit.label, x: Math.round((x1 + x3) / 2), y: Math.round((y1 + y4) / 2) };
|
|
30265
|
+
}
|
|
30266
|
+
async function createShadowFinder(context2, page) {
|
|
30267
|
+
if (typeof context2.newCDPSession !== "function")
|
|
30268
|
+
return;
|
|
30269
|
+
try {
|
|
30270
|
+
const session = await context2.newCDPSession(page);
|
|
30271
|
+
await session.send("DOM.enable");
|
|
30272
|
+
return (labels) => findShadowControl(session, labels);
|
|
30273
|
+
} catch {
|
|
30274
|
+
return;
|
|
30275
|
+
}
|
|
30276
|
+
}
|
|
30277
|
+
var markdownToHtml = (markdown) => Bun.markdown.html(markdown);
|
|
30278
|
+
async function requireAttached(page, selector, action, timeoutMs) {
|
|
30279
|
+
await resolveSelector(page, [selector], { timeoutMs, label: action });
|
|
30280
|
+
}
|
|
30281
|
+
async function resolveSelector(page, selectors, opts = {}) {
|
|
30282
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
30283
|
+
const found = { selector: null };
|
|
30284
|
+
const attached = await waitUntil(async () => {
|
|
30285
|
+
const result = await trySelectors(page, selectors, { visible: false });
|
|
30286
|
+
found.selector = result.selector;
|
|
30287
|
+
return result.found && result.selector !== null;
|
|
30288
|
+
}, timeoutMs, ATTACH_POLL_MS);
|
|
30289
|
+
const selector = found.selector;
|
|
30290
|
+
if (!attached || selector === null) {
|
|
30291
|
+
throw new Error(`${opts.label ?? "selector"}: ${selectors.join(" | ")} not attached within ${timeoutMs}ms`);
|
|
30292
|
+
}
|
|
30293
|
+
return selector;
|
|
30294
|
+
}
|
|
30295
|
+
function errorMessage2(error51) {
|
|
30296
|
+
return error51 instanceof Error ? error51.message : String(error51);
|
|
30297
|
+
}
|
|
30298
|
+
function pasteScript(selector, html) {
|
|
30299
|
+
const text = html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
30300
|
+
return `(() => {
|
|
30301
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
30302
|
+
if (!el) return false;
|
|
30303
|
+
const data = new DataTransfer();
|
|
30304
|
+
data.setData('text/html', ${JSON.stringify(html)});
|
|
30305
|
+
data.setData('text/plain', ${JSON.stringify(text)});
|
|
30306
|
+
el.focus();
|
|
30307
|
+
el.dispatchEvent(new ClipboardEvent('paste', { clipboardData: data, bubbles: true, cancelable: true }));
|
|
30308
|
+
return true;
|
|
30309
|
+
})()`;
|
|
30310
|
+
}
|
|
30311
|
+
function editorTextScript(selector) {
|
|
30312
|
+
return `(() => {
|
|
30313
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
30314
|
+
return el ? (el.textContent ?? '').trim() : '';
|
|
30315
|
+
})()`;
|
|
30316
|
+
}
|
|
30317
|
+
|
|
30123
30318
|
// ../../plugins/publishings/xhs-pub/src/index.ts
|
|
30124
30319
|
init_dist();
|
|
30125
30320
|
|
|
@@ -30195,48 +30390,86 @@ function createAutomationGuard(options) {
|
|
|
30195
30390
|
};
|
|
30196
30391
|
}
|
|
30197
30392
|
|
|
30198
|
-
// ../../plugins/publishings/xhs-pub/src/
|
|
30199
|
-
|
|
30393
|
+
// ../../plugins/publishings/xhs-pub/src/modes.ts
|
|
30394
|
+
import { isAbsolute } from "path";
|
|
30200
30395
|
var TARGET = "xhs";
|
|
30201
30396
|
var PUBLISH_URL = "https://creator.xiaohongshu.com/publish/publish";
|
|
30202
30397
|
var PUBLISHED_MARKER = "published=true";
|
|
30203
|
-
var
|
|
30204
|
-
var
|
|
30205
|
-
|
|
30206
|
-
|
|
30207
|
-
|
|
30208
|
-
|
|
30209
|
-
|
|
30210
|
-
|
|
30211
|
-
|
|
30212
|
-
|
|
30213
|
-
|
|
30214
|
-
|
|
30215
|
-
|
|
30216
|
-
|
|
30217
|
-
|
|
30218
|
-
|
|
30219
|
-
|
|
30220
|
-
|
|
30221
|
-
|
|
30222
|
-
|
|
30223
|
-
var
|
|
30224
|
-
|
|
30225
|
-
|
|
30398
|
+
var XHS_OPERATIONS = ["\u4E0A\u4F20\u56FE\u6587", "\u4E0A\u4F20\u89C6\u9891", "\u5199\u957F\u6587", "\u53D1\u64AD\u5BA2"];
|
|
30399
|
+
var DEFAULT_XHS_OPERATION = "\u4E0A\u4F20\u56FE\u6587";
|
|
30400
|
+
function resolveXhsOperation(options, content) {
|
|
30401
|
+
const raw = options?.operation ?? content?.metadata?.operation;
|
|
30402
|
+
if (raw === undefined)
|
|
30403
|
+
return { operation: DEFAULT_XHS_OPERATION };
|
|
30404
|
+
if (typeof raw !== "string" || !XHS_OPERATIONS.includes(raw)) {
|
|
30405
|
+
return {
|
|
30406
|
+
error: `unsupported operation '${String(raw)}' for ${TARGET}: expected one of ${XHS_OPERATIONS.join(", ")}`
|
|
30407
|
+
};
|
|
30408
|
+
}
|
|
30409
|
+
return { operation: raw };
|
|
30410
|
+
}
|
|
30411
|
+
var IMAGE_EXTENSIONS = /\.(png|jpe?g|webp)$/i;
|
|
30412
|
+
var IMAGE_LIST = "png|jpg|jpeg|webp";
|
|
30413
|
+
var VIDEO_EXTENSIONS = /\.(mp4|mov|flv|f4v|mkv|rm|rmvb|m4v|mpg|mpeg|ts)$/i;
|
|
30414
|
+
var VIDEO_LIST = "mp4|mov|flv|f4v|mkv|rm|rmvb|m4v|mpg|mpeg|ts";
|
|
30415
|
+
var AUDIO_EXTENSIONS = /\.(mp3|wav|aac|flac|m4a)$/i;
|
|
30416
|
+
var AUDIO_LIST = "mp3|wav|aac|flac|m4a";
|
|
30417
|
+
var XHS_IMAGE_MAX = 18;
|
|
30418
|
+
var XHS_NOTE_TITLE_MAX = 20;
|
|
30419
|
+
var XHS_NOTE_BODY_MAX = 1000;
|
|
30420
|
+
var MODE_LIMITS = {
|
|
30421
|
+
\u{4e0a}\u{4f20}\u{56fe}\u{6587}: { imageMax: XHS_IMAGE_MAX, titleMax: XHS_NOTE_TITLE_MAX, bodyMax: XHS_NOTE_BODY_MAX },
|
|
30422
|
+
\u{4e0a}\u{4f20}\u{89c6}\u{9891}: { titleMax: XHS_NOTE_TITLE_MAX },
|
|
30423
|
+
\u{5199}\u{957f}\u{6587}: {},
|
|
30424
|
+
\u{53d1}\u{64ad}\u{5ba2}: {}
|
|
30226
30425
|
};
|
|
30227
|
-
function
|
|
30228
|
-
|
|
30426
|
+
function artifactPaths(input) {
|
|
30427
|
+
switch (input.operation) {
|
|
30428
|
+
case "\u4E0A\u4F20\u56FE\u6587":
|
|
30429
|
+
return input.images;
|
|
30430
|
+
case "\u4E0A\u4F20\u89C6\u9891":
|
|
30431
|
+
return [input.video];
|
|
30432
|
+
case "\u5199\u957F\u6587":
|
|
30433
|
+
return [];
|
|
30434
|
+
case "\u53D1\u64AD\u5BA2":
|
|
30435
|
+
return [input.audio, input.cover];
|
|
30436
|
+
}
|
|
30229
30437
|
}
|
|
30230
|
-
async function
|
|
30231
|
-
const
|
|
30232
|
-
|
|
30233
|
-
|
|
30438
|
+
async function resolveMediaPath(content, spec) {
|
|
30439
|
+
const raw = content.metadata?.[spec.key];
|
|
30440
|
+
const path7 = typeof raw === "string" ? raw : typeof raw?.path === "string" ? raw.path : undefined;
|
|
30441
|
+
if (path7 === undefined || path7.length === 0) {
|
|
30442
|
+
return {
|
|
30443
|
+
error: `${spec.label}: Content.metadata.${spec.key} is required (absolute path to an existing ${spec.list} file)`
|
|
30444
|
+
};
|
|
30445
|
+
}
|
|
30446
|
+
if (!isAbsolute(path7)) {
|
|
30447
|
+
return { error: `${spec.label}: Content.metadata.${spec.key} must be absolute, got '${path7}'` };
|
|
30448
|
+
}
|
|
30449
|
+
if (!spec.extensions.test(path7)) {
|
|
30450
|
+
return {
|
|
30451
|
+
error: `${spec.label}: Content.metadata.${spec.key} has unsupported extension (expected ${spec.list}): '${path7}'`
|
|
30452
|
+
};
|
|
30453
|
+
}
|
|
30454
|
+
if (!await createNodeFileSystem().exists(path7)) {
|
|
30455
|
+
return { error: `${spec.label}: Content.metadata.${spec.key} does not exist: '${path7}'` };
|
|
30234
30456
|
}
|
|
30457
|
+
return { path: path7 };
|
|
30458
|
+
}
|
|
30459
|
+
async function validatePublishInput(content) {
|
|
30460
|
+
const status = validatePublishStatus(content);
|
|
30461
|
+
if (status.error)
|
|
30462
|
+
return { error: status.error };
|
|
30235
30463
|
const images = content.metadata?.images;
|
|
30236
30464
|
if (!Array.isArray(images) || images.length === 0) {
|
|
30237
30465
|
return { error: "images validation: metadata.images must be a non-empty ordered array" };
|
|
30238
30466
|
}
|
|
30239
|
-
const
|
|
30467
|
+
const max = XHS_IMAGE_MAX;
|
|
30468
|
+
if (images.length > max) {
|
|
30469
|
+
return {
|
|
30470
|
+
error: `images validation: metadata.images holds ${images.length} entries but the \u4E0A\u4F20\u56FE\u6587 grid accepts \u2264${max}`
|
|
30471
|
+
};
|
|
30472
|
+
}
|
|
30240
30473
|
const paths = [];
|
|
30241
30474
|
for (const [i2, image] of images.entries()) {
|
|
30242
30475
|
const path7 = image?.path;
|
|
@@ -30246,20 +30479,163 @@ async function validatePublishInput(content) {
|
|
|
30246
30479
|
if (!isAbsolute(path7)) {
|
|
30247
30480
|
return { error: `images validation: images[${i2}].path must be absolute, got '${path7}'` };
|
|
30248
30481
|
}
|
|
30249
|
-
if (
|
|
30482
|
+
if (!IMAGE_EXTENSIONS.test(path7)) {
|
|
30250
30483
|
return {
|
|
30251
|
-
error: `images validation: images[${i2}].path has unsupported extension (expected
|
|
30484
|
+
error: `images validation: images[${i2}].path has unsupported extension (expected ${IMAGE_LIST}): '${path7}'`
|
|
30252
30485
|
};
|
|
30253
30486
|
}
|
|
30254
|
-
if (!await
|
|
30487
|
+
if (!await createNodeFileSystem().exists(path7)) {
|
|
30255
30488
|
return { error: `images validation: images[${i2}].path does not exist: '${path7}'` };
|
|
30256
30489
|
}
|
|
30257
30490
|
paths.push(path7);
|
|
30258
30491
|
}
|
|
30259
30492
|
return { paths };
|
|
30260
30493
|
}
|
|
30494
|
+
function validatePublishStatus(content) {
|
|
30495
|
+
const status = content.metadata?.publishStatus;
|
|
30496
|
+
if (status !== undefined && status !== "draft" && status !== "public") {
|
|
30497
|
+
return {
|
|
30498
|
+
error: `publishStatus validation: unknown publishStatus '${String(status)}' (expected 'draft' or 'public')`
|
|
30499
|
+
};
|
|
30500
|
+
}
|
|
30501
|
+
return {};
|
|
30502
|
+
}
|
|
30503
|
+
function validateTitle(content, operation) {
|
|
30504
|
+
const max = MODE_LIMITS[operation].titleMax;
|
|
30505
|
+
if (max === undefined)
|
|
30506
|
+
return {};
|
|
30507
|
+
const title = content.title ?? "";
|
|
30508
|
+
if (title.length > max) {
|
|
30509
|
+
return {
|
|
30510
|
+
error: `title validation: Content.title is ${title.length} chars but ${operation} accepts \u2264${max}`
|
|
30511
|
+
};
|
|
30512
|
+
}
|
|
30513
|
+
return {};
|
|
30514
|
+
}
|
|
30515
|
+
function validateBody(content, operation) {
|
|
30516
|
+
const max = MODE_LIMITS[operation].bodyMax;
|
|
30517
|
+
if (max === undefined)
|
|
30518
|
+
return {};
|
|
30519
|
+
if (content.body.length > max) {
|
|
30520
|
+
return {
|
|
30521
|
+
error: `body validation: Content.body is ${content.body.length} chars but ${operation} accepts \u2264${max}`
|
|
30522
|
+
};
|
|
30523
|
+
}
|
|
30524
|
+
return {};
|
|
30525
|
+
}
|
|
30526
|
+
async function validateModeInput(operation, content) {
|
|
30527
|
+
const status = validatePublishStatus(content);
|
|
30528
|
+
if (status.error)
|
|
30529
|
+
return { error: status.error };
|
|
30530
|
+
switch (operation) {
|
|
30531
|
+
case "\u4E0A\u4F20\u56FE\u6587": {
|
|
30532
|
+
const title = validateTitle(content, operation);
|
|
30533
|
+
if (title.error)
|
|
30534
|
+
return { error: title.error };
|
|
30535
|
+
const body = validateBody(content, operation);
|
|
30536
|
+
if (body.error)
|
|
30537
|
+
return { error: body.error };
|
|
30538
|
+
const images = await validatePublishInput(content);
|
|
30539
|
+
if (images.error)
|
|
30540
|
+
return { error: images.error };
|
|
30541
|
+
return { input: { operation, images: images.paths ?? [] } };
|
|
30542
|
+
}
|
|
30543
|
+
case "\u4E0A\u4F20\u89C6\u9891": {
|
|
30544
|
+
const title = validateTitle(content, operation);
|
|
30545
|
+
if (title.error)
|
|
30546
|
+
return { error: title.error };
|
|
30547
|
+
const video = await resolveMediaPath(content, {
|
|
30548
|
+
key: "video",
|
|
30549
|
+
label: "video validation",
|
|
30550
|
+
extensions: VIDEO_EXTENSIONS,
|
|
30551
|
+
list: VIDEO_LIST
|
|
30552
|
+
});
|
|
30553
|
+
if (video.error)
|
|
30554
|
+
return { error: video.error };
|
|
30555
|
+
return { input: { operation, video: video.path ?? "" } };
|
|
30556
|
+
}
|
|
30557
|
+
case "\u5199\u957F\u6587": {
|
|
30558
|
+
if (content.body.trim().length === 0) {
|
|
30559
|
+
return { error: "body validation: Content.body must be non-empty for \u5199\u957F\u6587 (authored in place)" };
|
|
30560
|
+
}
|
|
30561
|
+
return { input: { operation } };
|
|
30562
|
+
}
|
|
30563
|
+
case "\u53D1\u64AD\u5BA2": {
|
|
30564
|
+
const audio = await resolveMediaPath(content, {
|
|
30565
|
+
key: "audio",
|
|
30566
|
+
label: "audio validation",
|
|
30567
|
+
extensions: AUDIO_EXTENSIONS,
|
|
30568
|
+
list: AUDIO_LIST
|
|
30569
|
+
});
|
|
30570
|
+
if (audio.error)
|
|
30571
|
+
return { error: audio.error };
|
|
30572
|
+
const cover = await resolveMediaPath(content, {
|
|
30573
|
+
key: "coverImage",
|
|
30574
|
+
label: "cover validation",
|
|
30575
|
+
extensions: IMAGE_EXTENSIONS,
|
|
30576
|
+
list: IMAGE_LIST
|
|
30577
|
+
});
|
|
30578
|
+
if (cover.error)
|
|
30579
|
+
return { error: cover.error };
|
|
30580
|
+
return { input: { operation, audio: audio.path ?? "", cover: cover.path ?? "" } };
|
|
30581
|
+
}
|
|
30582
|
+
}
|
|
30583
|
+
}
|
|
30584
|
+
|
|
30585
|
+
// ../../plugins/publishings/xhs-pub/src/page.ts
|
|
30586
|
+
var POLL_MS = 500;
|
|
30587
|
+
var STEP_TIMEOUT_MS = 30000;
|
|
30588
|
+
var CREATOR_TAB_BAR = "div.creator-tab";
|
|
30589
|
+
var CREATOR_TAB_COUNT = 4;
|
|
30590
|
+
function creatorTabSelector(label) {
|
|
30591
|
+
return `${CREATOR_TAB_BAR}:has-text("${label}")`;
|
|
30592
|
+
}
|
|
30593
|
+
async function clickCreatorTab(page, label, opts = {}) {
|
|
30594
|
+
const timeoutMs = opts.timeoutMs ?? STEP_TIMEOUT_MS;
|
|
30595
|
+
const pollMs = opts.pollMs ?? POLL_MS;
|
|
30596
|
+
const onScreenCount = () => page.countOnScreen(CREATOR_TAB_BAR).catch(() => 0);
|
|
30597
|
+
const mounted = await waitUntil2(() => onScreenCount().then((count2) => count2 >= CREATOR_TAB_COUNT), timeoutMs, pollMs);
|
|
30598
|
+
if (!mounted) {
|
|
30599
|
+
const observed = await onScreenCount();
|
|
30600
|
+
throw new Error(`mode entry: the console tab bar did not mount within ${timeoutMs}ms (${observed} of ${CREATOR_TAB_COUNT} on-screen '${CREATOR_TAB_BAR}' nodes) \u2014 every tab renders aria-hidden and off-viewport clones`);
|
|
30601
|
+
}
|
|
30602
|
+
if (!page.clickInViewport) {
|
|
30603
|
+
throw new Error(`mode entry: this driver cannot read viewport geometry, so the on-screen '${label}' tab cannot be told apart from its off-screen clones`);
|
|
30604
|
+
}
|
|
30605
|
+
const clicked = await page.clickInViewport(creatorTabSelector(label));
|
|
30606
|
+
if (!clicked) {
|
|
30607
|
+
throw new Error(`mode entry: no in-viewport '${label}' tab found (only aria-hidden or off-screen clones matched) \u2014 the mode was not entered`);
|
|
30608
|
+
}
|
|
30609
|
+
}
|
|
30610
|
+
async function waitUntil2(predicate, timeoutMs, pollMs = POLL_MS) {
|
|
30611
|
+
const deadline = Date.now() + timeoutMs;
|
|
30612
|
+
while (Date.now() < deadline) {
|
|
30613
|
+
if (await predicate())
|
|
30614
|
+
return true;
|
|
30615
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollMs));
|
|
30616
|
+
}
|
|
30617
|
+
return predicate();
|
|
30618
|
+
}
|
|
30261
30619
|
function wrapPlaywrightPage(raw, shadow) {
|
|
30262
30620
|
const pw = raw;
|
|
30621
|
+
const onScreen = async (selector) => {
|
|
30622
|
+
const viewport = pw.viewportSize?.() ?? null;
|
|
30623
|
+
if (viewport === null)
|
|
30624
|
+
return [];
|
|
30625
|
+
const total = await pw.locator(selector).count();
|
|
30626
|
+
const found = [];
|
|
30627
|
+
for (let index = 0;index < total; index += 1) {
|
|
30628
|
+
const candidate = pw.locator(selector).nth(index);
|
|
30629
|
+
const box = await candidate.boundingBox().catch(() => null);
|
|
30630
|
+
if (!box || box.width === 0 || box.height === 0)
|
|
30631
|
+
continue;
|
|
30632
|
+
const outside = box.x < 0 || box.y < 0 || box.x >= viewport.width || box.y >= viewport.height;
|
|
30633
|
+
if (outside)
|
|
30634
|
+
continue;
|
|
30635
|
+
found.push({ index, box });
|
|
30636
|
+
}
|
|
30637
|
+
return found;
|
|
30638
|
+
};
|
|
30263
30639
|
return {
|
|
30264
30640
|
goto: (url2, options) => pw.goto(url2, options),
|
|
30265
30641
|
locator: (selector) => ({
|
|
@@ -30279,84 +30655,384 @@ function wrapPlaywrightPage(raw, shadow) {
|
|
|
30279
30655
|
setInputFiles: (selector, paths) => pw.setInputFiles(selector, paths),
|
|
30280
30656
|
waitForSelector: (selector, options) => pw.waitForSelector(selector, options),
|
|
30281
30657
|
url: () => pw.url(),
|
|
30658
|
+
evaluate: (expression) => pw.evaluate(expression),
|
|
30659
|
+
countOnScreen: async (selector) => (await onScreen(selector)).length,
|
|
30282
30660
|
clickInViewport: async (selector) => {
|
|
30283
|
-
const
|
|
30284
|
-
if (
|
|
30661
|
+
const [candidate] = await onScreen(selector);
|
|
30662
|
+
if (candidate === undefined)
|
|
30285
30663
|
return false;
|
|
30286
|
-
|
|
30287
|
-
|
|
30288
|
-
const candidate = pw.locator(selector).nth(index);
|
|
30289
|
-
const box = await candidate.boundingBox().catch(() => null);
|
|
30290
|
-
if (!box || box.width === 0)
|
|
30291
|
-
continue;
|
|
30292
|
-
const outside = box.x < 0 || box.y < 0 || box.x >= viewport.width || box.y >= viewport.height;
|
|
30293
|
-
if (outside)
|
|
30294
|
-
continue;
|
|
30295
|
-
await candidate.click({ force: true });
|
|
30296
|
-
return true;
|
|
30297
|
-
}
|
|
30298
|
-
return false;
|
|
30664
|
+
await pw.locator(selector).nth(candidate.index).click({ force: true });
|
|
30665
|
+
return true;
|
|
30299
30666
|
},
|
|
30300
30667
|
findShadowControl: async (labels) => shadow ? shadow.findControl(labels) : null,
|
|
30301
30668
|
clickAt: (x, y) => pw.mouse.click(x, y)
|
|
30302
30669
|
};
|
|
30303
30670
|
}
|
|
30304
|
-
async function
|
|
30305
|
-
const
|
|
30306
|
-
|
|
30307
|
-
|
|
30308
|
-
|
|
30309
|
-
|
|
30310
|
-
|
|
30311
|
-
|
|
30312
|
-
|
|
30313
|
-
|
|
30314
|
-
|
|
30315
|
-
|
|
30316
|
-
|
|
30317
|
-
|
|
30318
|
-
|
|
30319
|
-
|
|
30320
|
-
|
|
30321
|
-
if (wanted.has(label) && typeof node.backendNodeId === "number") {
|
|
30322
|
-
hits.push({ label, backendNodeId: node.backendNodeId });
|
|
30323
|
-
return;
|
|
30324
|
-
}
|
|
30671
|
+
async function findSelector(page, selectors, what, timeoutMs = STEP_TIMEOUT_MS) {
|
|
30672
|
+
const deadline = Date.now() + timeoutMs;
|
|
30673
|
+
while (Date.now() < deadline) {
|
|
30674
|
+
const found = await trySelectors(page, selectors);
|
|
30675
|
+
if (found.found && found.selector)
|
|
30676
|
+
return found.selector;
|
|
30677
|
+
await new Promise((resolve3) => setTimeout(resolve3, POLL_MS));
|
|
30678
|
+
}
|
|
30679
|
+
throw new Error(`${what}: no matching element observed`);
|
|
30680
|
+
}
|
|
30681
|
+
async function findAttachedSelector(page, selectors, what, timeoutMs = STEP_TIMEOUT_MS) {
|
|
30682
|
+
const deadline = Date.now() + timeoutMs;
|
|
30683
|
+
while (Date.now() < deadline) {
|
|
30684
|
+
for (const selector of selectors) {
|
|
30685
|
+
const attached = await page.locator(selector).count().catch(() => 0);
|
|
30686
|
+
if (attached > 0)
|
|
30687
|
+
return selector;
|
|
30325
30688
|
}
|
|
30326
|
-
|
|
30327
|
-
|
|
30328
|
-
|
|
30329
|
-
|
|
30689
|
+
await new Promise((resolve3) => setTimeout(resolve3, POLL_MS));
|
|
30690
|
+
}
|
|
30691
|
+
throw new Error(`${what}: no matching element observed`);
|
|
30692
|
+
}
|
|
30693
|
+
|
|
30694
|
+
// ../../plugins/publishings/xhs-pub/src/flow.ts
|
|
30695
|
+
var NOTE_TITLE = [
|
|
30696
|
+
'input.d-text[placeholder="\u586B\u5199\u6807\u9898\u4F1A\u6709\u66F4\u591A\u8D5E\u54E6"]',
|
|
30697
|
+
'input[placeholder*="\u6807\u9898"]',
|
|
30698
|
+
"#title-textarea input"
|
|
30699
|
+
];
|
|
30700
|
+
var NOTE_BODY = [
|
|
30701
|
+
'div.tiptap[contenteditable="true"]',
|
|
30702
|
+
'div[contenteditable="true"].ql-editor',
|
|
30703
|
+
"#post-textarea",
|
|
30704
|
+
".ql-editor"
|
|
30705
|
+
];
|
|
30706
|
+
var NOTE_THUMBNAIL = ["img.preivew-image", ".img-preview img"];
|
|
30707
|
+
var IMAGE_FILE_INPUT = [
|
|
30708
|
+
'input[type="file"][accept*=".jpg"]',
|
|
30709
|
+
'input[type="file"].upload-input',
|
|
30710
|
+
'input[type="file"]'
|
|
30711
|
+
];
|
|
30712
|
+
var VIDEO_FILE_INPUT = ['input[type="file"][accept*=".mp4"]', 'input[type="file"].upload-input'];
|
|
30713
|
+
var VIDEO_EDITOR = ["div.tiptap.ProseMirror", 'div.tiptap[contenteditable="true"]', ".ProseMirror"];
|
|
30714
|
+
var ARTICLE_NEW = ["text=\u65B0\u7684\u521B\u4F5C", 'button:has-text("\u65B0\u7684\u521B\u4F5C")'];
|
|
30715
|
+
var ARTICLE_EDITOR = ["div.tiptap.ProseMirror.ProseMirror-focused", "div.tiptap.ProseMirror", ".ProseMirror"];
|
|
30716
|
+
var ARTICLE_LAYOUT = ['button:has-text("\u4E00\u952E\u6392\u7248")', "div.ailayout-btn_wrp", "div.ailayout-btn_label"];
|
|
30717
|
+
var PODCAST_OPEN = ["text=\u4E0A\u4F20\u97F3\u9891", 'button:has-text("\u4E0A\u4F20\u97F3\u9891")'];
|
|
30718
|
+
var PODCAST_MODAL = ["div.creator-modal-sty", ".d-modal.d-modal-centered", ".d-modal"];
|
|
30719
|
+
var PODCAST_AUDIO_INPUT = ['input[type="file"][accept*=".mp3"]', 'input[type="file"][accept*=".wav"]'];
|
|
30720
|
+
var PODCAST_COVER_INPUT = ['input[type="file"][accept*=".jpg"]', 'input[type="file"][accept*=".png"]'];
|
|
30721
|
+
var PODCAST_MODAL_ADVANCE = ["\u53BB\u53D1\u5E03"];
|
|
30722
|
+
var DRAFT_LABELS = ["\u6682\u5B58\u79BB\u5F00", "\u5B58\u8349\u7A3F", "\u4FDD\u5B58\u8349\u7A3F"];
|
|
30723
|
+
var PUBLISH_LABELS = ["\u53D1\u5E03"];
|
|
30724
|
+
var PUBLISH_CONFIRM = ["\u53D1\u5E03\u6210\u529F", "\u5DF2\u53D1\u5E03"];
|
|
30725
|
+
var DRAFT_TOASTS = ["\u4FDD\u5B58\u6210\u529F"];
|
|
30726
|
+
var SHADOW_BAR = "the xhs publish bar (closed shadow root)";
|
|
30727
|
+
var MODE_ACTION_PATHS = {
|
|
30728
|
+
\u{4e0a}\u{4f20}\u{56fe}\u{6587}: ["shadow"],
|
|
30729
|
+
\u{4e0a}\u{4f20}\u{89c6}\u{9891}: ["shadow"],
|
|
30730
|
+
\u{5199}\u{957f}\u{6587}: ["light", "shadow"],
|
|
30731
|
+
\u{53d1}\u{64ad}\u{5ba2}: ["shadow"]
|
|
30732
|
+
};
|
|
30733
|
+
function fail(error51) {
|
|
30734
|
+
return ResultSchema.parse({ ok: false, target: TARGET, error: error51 });
|
|
30735
|
+
}
|
|
30736
|
+
function sleep2(ms) {
|
|
30737
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
30738
|
+
}
|
|
30739
|
+
function asPlaywrightPage(page) {
|
|
30740
|
+
return page;
|
|
30741
|
+
}
|
|
30742
|
+
async function enterMode(deps, label) {
|
|
30743
|
+
await clickCreatorTab(deps.page, label, { timeoutMs: deps.stepTimeoutMs });
|
|
30744
|
+
}
|
|
30745
|
+
async function clickShadowControl(page, labels) {
|
|
30746
|
+
if (!page.findShadowControl || !page.clickAt) {
|
|
30747
|
+
return {
|
|
30748
|
+
ok: false,
|
|
30749
|
+
error: `submit control: ${SHADOW_BAR} and this driver has no CDP access to resolve it`
|
|
30750
|
+
};
|
|
30751
|
+
}
|
|
30752
|
+
const control = await page.findShadowControl(labels);
|
|
30753
|
+
if (!control) {
|
|
30754
|
+
return {
|
|
30755
|
+
ok: false,
|
|
30756
|
+
error: `submit control: none of [${labels.join(", ")}] found in ${SHADOW_BAR}`
|
|
30757
|
+
};
|
|
30758
|
+
}
|
|
30759
|
+
await page.clickAt(control.x, control.y);
|
|
30760
|
+
return { ok: true, label: control.label, x: control.x, y: control.y };
|
|
30761
|
+
}
|
|
30762
|
+
async function clickLightControl(page, labels, timeoutMs, what) {
|
|
30763
|
+
const candidates = labels.flatMap((label2) => [`button:has-text("${label2}")`, `text=${label2}`]);
|
|
30764
|
+
let selector;
|
|
30765
|
+
try {
|
|
30766
|
+
selector = await findSelector(page, candidates, `${what} (light DOM) none of [${labels.join(", ")}]`, timeoutMs);
|
|
30767
|
+
} catch (error51) {
|
|
30768
|
+
return { ok: false, error: error51 instanceof Error ? error51.message : String(error51) };
|
|
30769
|
+
}
|
|
30770
|
+
const label = labels.find((candidate) => selector.includes(candidate)) ?? labels[0];
|
|
30771
|
+
await page.locator(selector).first().click({ timeout: timeoutMs });
|
|
30772
|
+
return { ok: true, label: label ?? "", selector };
|
|
30773
|
+
}
|
|
30774
|
+
async function clickActionControl(operation, deps, labels, what) {
|
|
30775
|
+
let lastError = "";
|
|
30776
|
+
for (const path7 of MODE_ACTION_PATHS[operation]) {
|
|
30777
|
+
if (path7 === "shadow") {
|
|
30778
|
+
const clicked2 = await clickShadowControl(deps.page, labels);
|
|
30779
|
+
if (clicked2.ok)
|
|
30780
|
+
return { ok: true, path: path7, label: clicked2.label, click: `${clicked2.x},${clicked2.y}` };
|
|
30781
|
+
lastError = clicked2.error;
|
|
30782
|
+
continue;
|
|
30330
30783
|
}
|
|
30331
|
-
|
|
30332
|
-
|
|
30333
|
-
|
|
30334
|
-
|
|
30335
|
-
|
|
30336
|
-
|
|
30337
|
-
const quad = box.model?.content;
|
|
30338
|
-
if (!quad || quad.length < 8)
|
|
30339
|
-
return null;
|
|
30340
|
-
const [x1 = 0, y1 = 0, x3 = 0, , , y4 = 0] = quad;
|
|
30341
|
-
return {
|
|
30342
|
-
label: hit.label,
|
|
30343
|
-
x: Math.round((x1 + x3) / 2),
|
|
30344
|
-
y: Math.round((y1 + y4) / 2)
|
|
30345
|
-
};
|
|
30784
|
+
const clicked = await clickLightControl(deps.page, labels, deps.stepTimeoutMs, what);
|
|
30785
|
+
if (clicked.ok)
|
|
30786
|
+
return { ok: true, path: path7, label: clicked.label, click: `selector:${clicked.selector}` };
|
|
30787
|
+
lastError = clicked.error;
|
|
30788
|
+
}
|
|
30789
|
+
return { ok: false, error: lastError };
|
|
30346
30790
|
}
|
|
30347
|
-
async function
|
|
30348
|
-
|
|
30791
|
+
async function readDraftCount(page) {
|
|
30792
|
+
for (const selector of ["text=/\u8349\u7A3F\u7BB1\\(\\d+\\)/", "text=\u8349\u7A3F\u7BB1"]) {
|
|
30793
|
+
const attached = await page.locator(selector).count().catch(() => 0);
|
|
30794
|
+
if (attached === 0)
|
|
30795
|
+
continue;
|
|
30796
|
+
const locator = page.locator(selector).first();
|
|
30797
|
+
if (!locator.textContent)
|
|
30798
|
+
continue;
|
|
30799
|
+
const text = await locator.textContent().catch(() => "");
|
|
30800
|
+
const match = /\u8349\u7A3F\u7BB1\s*[\uFF08(]\s*(\d+)\s*[\uFF09)]/.exec(text);
|
|
30801
|
+
if (match?.[1] !== undefined)
|
|
30802
|
+
return Number(match[1]);
|
|
30803
|
+
}
|
|
30804
|
+
return null;
|
|
30805
|
+
}
|
|
30806
|
+
function draftSavedStamps(now = new Date) {
|
|
30807
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
30808
|
+
return [0, 1, 2].map((minutesAgo) => {
|
|
30809
|
+
const stamp = new Date(now.getTime() - minutesAgo * 60000);
|
|
30810
|
+
return `\u4FDD\u5B58\u4E8E${stamp.getFullYear()}-${pad(stamp.getMonth() + 1)}-${pad(stamp.getDate())} ${pad(stamp.getHours())}:${pad(stamp.getMinutes())}`;
|
|
30811
|
+
});
|
|
30812
|
+
}
|
|
30813
|
+
async function readDraftStamps(page) {
|
|
30814
|
+
const present = [];
|
|
30815
|
+
for (const stamp of draftSavedStamps()) {
|
|
30816
|
+
const attached = await page.locator(`text=${stamp}`).count().catch(() => 0);
|
|
30817
|
+
if (attached > 0)
|
|
30818
|
+
present.push(stamp);
|
|
30819
|
+
}
|
|
30820
|
+
return present;
|
|
30821
|
+
}
|
|
30822
|
+
async function readDraftBaseline(page) {
|
|
30823
|
+
return { count: await readDraftCount(page), stamps: await readDraftStamps(page) };
|
|
30824
|
+
}
|
|
30825
|
+
async function isDraftRowSaved(page, baseline) {
|
|
30826
|
+
const stamps = await readDraftStamps(page);
|
|
30827
|
+
if (stamps.some((stamp) => !baseline.stamps.includes(stamp)))
|
|
30828
|
+
return true;
|
|
30829
|
+
const after = await readDraftCount(page);
|
|
30830
|
+
if (baseline.count === null || after === null)
|
|
30831
|
+
return false;
|
|
30832
|
+
return after > baseline.count;
|
|
30833
|
+
}
|
|
30834
|
+
async function draftSignalEvidence(page, baseline) {
|
|
30835
|
+
const toast = await readScopedSignal(asPlaywrightPage(page), DRAFT_TOASTS);
|
|
30836
|
+
if (toast !== null)
|
|
30837
|
+
return `toast:${toast}`;
|
|
30838
|
+
if (await isDraftRowSaved(page, baseline))
|
|
30839
|
+
return "draft-row";
|
|
30840
|
+
return null;
|
|
30841
|
+
}
|
|
30842
|
+
async function applyNoteTitle(deps) {
|
|
30843
|
+
const selector = await findSelector(deps.page, NOTE_TITLE, "title input", deps.stepTimeoutMs);
|
|
30844
|
+
await deps.page.locator(selector).first().fill(deps.content.title ?? "");
|
|
30845
|
+
}
|
|
30846
|
+
async function typeNoteBody(deps, selectors, what) {
|
|
30847
|
+
const selector = await findSelector(deps.page, selectors, what, deps.stepTimeoutMs);
|
|
30848
|
+
const body = deps.page.locator(selector).first();
|
|
30849
|
+
const text = deps.content.body ?? "";
|
|
30850
|
+
if (typeof body.type === "function") {
|
|
30851
|
+
await body.click();
|
|
30852
|
+
await body.type(text, { delay: 5 });
|
|
30349
30853
|
return;
|
|
30854
|
+
}
|
|
30855
|
+
await body.fill(text);
|
|
30856
|
+
}
|
|
30857
|
+
async function confirmThumbnails(deps, expected) {
|
|
30858
|
+
const selector = await findSelector(deps.page, NOTE_THUMBNAIL, "thumbnail wait", deps.stepTimeoutMs);
|
|
30859
|
+
const matches = await waitUntil2(() => deps.page.locator(selector).count().then((count2) => count2 >= expected), deps.stepTimeoutMs);
|
|
30860
|
+
const observed = await deps.page.locator(selector).count();
|
|
30861
|
+
if (!matches || observed !== expected) {
|
|
30862
|
+
throw new Error(`thumbnail count: expected ${expected}, observed ${observed}`);
|
|
30863
|
+
}
|
|
30864
|
+
}
|
|
30865
|
+
async function composeImageText(input, deps) {
|
|
30866
|
+
await enterMode(deps, input.operation);
|
|
30867
|
+
const fileSelector = await findAttachedSelector(deps.page, IMAGE_FILE_INPUT, "image file input", deps.stepTimeoutMs);
|
|
30868
|
+
await deps.page.setInputFiles(fileSelector, input.images);
|
|
30869
|
+
await confirmThumbnails(deps, input.images.length);
|
|
30870
|
+
await applyNoteTitle(deps);
|
|
30871
|
+
await typeNoteBody(deps, NOTE_BODY, "note body");
|
|
30872
|
+
}
|
|
30873
|
+
async function composeVideo(input, deps) {
|
|
30874
|
+
await enterMode(deps, input.operation);
|
|
30875
|
+
const fileSelector = await findAttachedSelector(deps.page, VIDEO_FILE_INPUT, "video file input", deps.stepTimeoutMs);
|
|
30876
|
+
await uploadFile(asPlaywrightPage(deps.page), fileSelector, input.video, { timeoutMs: deps.stepTimeoutMs });
|
|
30877
|
+
await findSelector(deps.page, VIDEO_EDITOR, "video note editor", deps.stepTimeoutMs);
|
|
30878
|
+
await applyNoteTitle(deps);
|
|
30879
|
+
await typeNoteBody(deps, NOTE_BODY, "note body");
|
|
30880
|
+
}
|
|
30881
|
+
async function composeArticle(deps) {
|
|
30882
|
+
await enterMode(deps, "\u5199\u957F\u6587");
|
|
30883
|
+
const newArticle = await findAttachedSelector(deps.page, ARTICLE_NEW, "\u65B0\u7684\u521B\u4F5C control", deps.stepTimeoutMs);
|
|
30884
|
+
await deps.page.locator(newArticle).first().click();
|
|
30885
|
+
const editor = await findSelector(deps.page, ARTICLE_EDITOR, "long-article editor", deps.stepTimeoutMs);
|
|
30886
|
+
await pasteHtml(asPlaywrightPage(deps.page), editor, markdownToHtml(deps.content.body), {
|
|
30887
|
+
timeoutMs: deps.stepTimeoutMs
|
|
30888
|
+
});
|
|
30350
30889
|
try {
|
|
30351
|
-
const
|
|
30352
|
-
await
|
|
30353
|
-
|
|
30354
|
-
|
|
30890
|
+
const layout = await findSelector(deps.page, ARTICLE_LAYOUT, "\u4E00\u952E\u6392\u7248 control", deps.stepTimeoutMs);
|
|
30891
|
+
await deps.page.locator(layout).first().click();
|
|
30892
|
+
} catch {}
|
|
30893
|
+
}
|
|
30894
|
+
async function isPodcastModalOpen(page) {
|
|
30895
|
+
return trySelectors(page, PODCAST_MODAL).then((found) => found.found).catch(() => false);
|
|
30896
|
+
}
|
|
30897
|
+
async function leavePodcastModal(deps) {
|
|
30898
|
+
const closed = () => isPodcastModalOpen(deps.page).then((open) => !open);
|
|
30899
|
+
if (await waitUntil2(closed, deps.stepTimeoutMs))
|
|
30355
30900
|
return;
|
|
30901
|
+
if (deps.publishStatus === "public") {
|
|
30902
|
+
const advance = await clickLightControl(deps.page, PODCAST_MODAL_ADVANCE, deps.stepTimeoutMs, "podcast modal advance");
|
|
30903
|
+
if (!advance.ok)
|
|
30904
|
+
throw new Error(advance.error);
|
|
30905
|
+
if (await waitUntil2(closed, deps.stepTimeoutMs))
|
|
30906
|
+
return;
|
|
30356
30907
|
}
|
|
30908
|
+
throw new Error(`podcast modal: the \u53D1\u64AD\u5BA2 modal was still open ${deps.stepTimeoutMs}ms after the audio and cover were attached; the only recorded advance control is '${PODCAST_MODAL_ADVANCE[0]}' and a draft run does not click it (it is publish-adjacent)`);
|
|
30909
|
+
}
|
|
30910
|
+
async function composePodcast(input, deps) {
|
|
30911
|
+
await enterMode(deps, input.operation);
|
|
30912
|
+
const open = await findAttachedSelector(deps.page, PODCAST_OPEN, "\u4E0A\u4F20\u97F3\u9891 control", deps.stepTimeoutMs);
|
|
30913
|
+
await deps.page.locator(open).first().click();
|
|
30914
|
+
await findSelector(deps.page, PODCAST_MODAL, "podcast modal", deps.stepTimeoutMs);
|
|
30915
|
+
const audioInput = await findAttachedSelector(deps.page, PODCAST_AUDIO_INPUT, "podcast audio input", deps.stepTimeoutMs);
|
|
30916
|
+
await uploadFile(asPlaywrightPage(deps.page), audioInput, input.audio, { timeoutMs: deps.stepTimeoutMs });
|
|
30917
|
+
const coverInput = await findAttachedSelector(deps.page, PODCAST_COVER_INPUT, "podcast cover input", deps.stepTimeoutMs);
|
|
30918
|
+
await uploadFile(asPlaywrightPage(deps.page), coverInput, input.cover, { timeoutMs: deps.stepTimeoutMs });
|
|
30919
|
+
await leavePodcastModal(deps);
|
|
30920
|
+
}
|
|
30921
|
+
async function compose(input, deps) {
|
|
30922
|
+
switch (input.operation) {
|
|
30923
|
+
case "\u4E0A\u4F20\u56FE\u6587":
|
|
30924
|
+
await composeImageText(input, deps);
|
|
30925
|
+
return;
|
|
30926
|
+
case "\u4E0A\u4F20\u89C6\u9891":
|
|
30927
|
+
await composeVideo(input, deps);
|
|
30928
|
+
return;
|
|
30929
|
+
case "\u5199\u957F\u6587":
|
|
30930
|
+
await composeArticle(deps);
|
|
30931
|
+
return;
|
|
30932
|
+
case "\u53D1\u64AD\u5BA2":
|
|
30933
|
+
await composePodcast(input, deps);
|
|
30934
|
+
return;
|
|
30935
|
+
}
|
|
30936
|
+
}
|
|
30937
|
+
function baseMetadata(input, deps) {
|
|
30938
|
+
return {
|
|
30939
|
+
operation: input.operation,
|
|
30940
|
+
driver: deps.driver,
|
|
30941
|
+
publishStatus: deps.publishStatus,
|
|
30942
|
+
profileDir: deps.profileDir,
|
|
30943
|
+
artifactPaths: artifactPaths(input),
|
|
30944
|
+
...input.operation === "\u4E0A\u4F20\u56FE\u6587" ? { imageCount: input.images.length } : {}
|
|
30945
|
+
};
|
|
30946
|
+
}
|
|
30947
|
+
async function saveDraft(input, deps) {
|
|
30948
|
+
let clicked = null;
|
|
30949
|
+
let signal = null;
|
|
30950
|
+
let attempts = 0;
|
|
30951
|
+
while (signal === null && attempts < deps.submitAttempts) {
|
|
30952
|
+
attempts += 1;
|
|
30953
|
+
await sleep2(deps.submitSettleMs);
|
|
30954
|
+
const control = await clickActionControl(input.operation, deps, DRAFT_LABELS, "draft control");
|
|
30955
|
+
if (!control.ok)
|
|
30956
|
+
return fail(control.error);
|
|
30957
|
+
clicked = control;
|
|
30958
|
+
const outcome = await expectOutcome(() => draftSignalEvidence(deps.page, deps.draftBaseline), {
|
|
30959
|
+
timeoutMs: deps.submitSettleMs,
|
|
30960
|
+
label: `draft confirmation '${control.label}'`,
|
|
30961
|
+
pollMs: POLL_MS
|
|
30962
|
+
});
|
|
30963
|
+
if (outcome.ok)
|
|
30964
|
+
signal = outcome.evidence;
|
|
30965
|
+
}
|
|
30966
|
+
const draftCountAfter = await readDraftCount(deps.page);
|
|
30967
|
+
if (signal === null || clicked === null) {
|
|
30968
|
+
return fail(`draft confirmation: '${clicked?.label ?? DRAFT_LABELS[0]}' was clicked ${attempts}\xD7 but neither a fresh ${TARGET} save timestamp, a moved draft counter, nor a scoped '${DRAFT_TOASTS[0]}' toast was observed within ${deps.submitSettleMs}ms (before=${String(deps.draftBaseline.count)}, after=${String(draftCountAfter)})`);
|
|
30969
|
+
}
|
|
30970
|
+
return ResultSchema.parse({
|
|
30971
|
+
ok: true,
|
|
30972
|
+
target: TARGET,
|
|
30973
|
+
message: `${TARGET} draft saved (unpublished) in profile ${deps.profileDir} via '${clicked.label}' (${input.operation})`,
|
|
30974
|
+
metadata: {
|
|
30975
|
+
...baseMetadata(input, deps),
|
|
30976
|
+
draftControl: clicked.label,
|
|
30977
|
+
draftClick: clicked.click,
|
|
30978
|
+
draftPath: clicked.path,
|
|
30979
|
+
draftAttempts: attempts,
|
|
30980
|
+
draftSignal: signal,
|
|
30981
|
+
draftCountBefore: deps.draftBaseline.count,
|
|
30982
|
+
draftCountAfter
|
|
30983
|
+
}
|
|
30984
|
+
});
|
|
30985
|
+
}
|
|
30986
|
+
async function publishNote(input, deps) {
|
|
30987
|
+
await sleep2(deps.submitSettleMs);
|
|
30988
|
+
const submitted = await clickActionControl(input.operation, deps, PUBLISH_LABELS, "publish control");
|
|
30989
|
+
if (!submitted.ok)
|
|
30990
|
+
return fail(submitted.error);
|
|
30991
|
+
const urlBeforeSubmit = deps.page.url();
|
|
30992
|
+
const confirmed = await waitUntil2(async () => {
|
|
30993
|
+
if (deps.page.url().includes(PUBLISHED_MARKER))
|
|
30994
|
+
return true;
|
|
30995
|
+
return await readScopedSignal(asPlaywrightPage(deps.page), PUBLISH_CONFIRM) !== null;
|
|
30996
|
+
}, deps.stepTimeoutMs);
|
|
30997
|
+
if (!confirmed) {
|
|
30998
|
+
return fail(`publish submission: state unknown \u2014 the '${submitted.label}' click (${submitted.click}) was sent from ${urlBeforeSubmit} but no success state was observed (now ${deps.page.url()}). Check the ${TARGET} creator note manager before retrying: a retry could duplicate the post.`);
|
|
30999
|
+
}
|
|
31000
|
+
const url2 = deps.page.url();
|
|
31001
|
+
return ResultSchema.parse({
|
|
31002
|
+
ok: true,
|
|
31003
|
+
target: TARGET,
|
|
31004
|
+
...url2 !== PUBLISH_URL ? { url: url2 } : {},
|
|
31005
|
+
message: `${TARGET} ${input.operation} post published`,
|
|
31006
|
+
metadata: {
|
|
31007
|
+
...baseMetadata(input, deps),
|
|
31008
|
+
publishControl: submitted.label,
|
|
31009
|
+
publishPath: submitted.path,
|
|
31010
|
+
publishClick: submitted.click,
|
|
31011
|
+
publishSignal: url2.includes(PUBLISHED_MARKER) ? `url:${PUBLISHED_MARKER}` : "toast"
|
|
31012
|
+
}
|
|
31013
|
+
});
|
|
31014
|
+
}
|
|
31015
|
+
async function runMode(input, deps) {
|
|
31016
|
+
await compose(input, deps);
|
|
31017
|
+
return deps.publishStatus === "public" ? publishNote(input, deps) : saveDraft(input, deps);
|
|
31018
|
+
}
|
|
31019
|
+
|
|
31020
|
+
// ../../plugins/publishings/xhs-pub/src/index.ts
|
|
31021
|
+
var CHANNEL = "xhs";
|
|
31022
|
+
var LOGIN_TIMEOUT_MS = 120000;
|
|
31023
|
+
var PREFLIGHT_TIMEOUT_MS = 30000;
|
|
31024
|
+
var SUBMIT_SETTLE_MS = 2500;
|
|
31025
|
+
var SUBMIT_ATTEMPTS = 3;
|
|
31026
|
+
var AUTH_GATE = ["div.creator-tab", "text=\u4E0A\u4F20\u56FE\u6587"];
|
|
31027
|
+
function fail2(error51) {
|
|
31028
|
+
return ResultSchema.parse({ ok: false, target: TARGET, error: error51 });
|
|
31029
|
+
}
|
|
31030
|
+
async function createShadowProbe(context2, page) {
|
|
31031
|
+
const find = await createShadowFinder(context2, page);
|
|
31032
|
+
return find === undefined ? undefined : { findControl: (labels) => find(labels) };
|
|
30357
31033
|
}
|
|
30358
31034
|
async function isXhsConsoleReady(page) {
|
|
30359
|
-
return trySelectors(page,
|
|
31035
|
+
return trySelectors(page, AUTH_GATE).then((r) => r.found);
|
|
30360
31036
|
}
|
|
30361
31037
|
var defaultLauncher2 = openPersistentBrowser;
|
|
30362
31038
|
function makeDefaultOpenPage(launch = defaultLauncher2) {
|
|
@@ -30379,67 +31055,16 @@ function makeDefaultOpenPage(launch = defaultLauncher2) {
|
|
|
30379
31055
|
};
|
|
30380
31056
|
}
|
|
30381
31057
|
var defaultOpenPage = makeDefaultOpenPage();
|
|
30382
|
-
async function waitUntil(predicate, timeoutMs) {
|
|
30383
|
-
const deadline = Date.now() + timeoutMs;
|
|
30384
|
-
while (Date.now() < deadline) {
|
|
30385
|
-
if (await predicate())
|
|
30386
|
-
return true;
|
|
30387
|
-
await new Promise((resolve3) => setTimeout(resolve3, POLL_MS));
|
|
30388
|
-
}
|
|
30389
|
-
return predicate();
|
|
30390
|
-
}
|
|
30391
|
-
async function findSelector(page, selectors, what, timeoutMs = STEP_TIMEOUT_MS) {
|
|
30392
|
-
const deadline = Date.now() + timeoutMs;
|
|
30393
|
-
while (Date.now() < deadline) {
|
|
30394
|
-
const found = await trySelectors(page, selectors);
|
|
30395
|
-
if (found.found && found.selector)
|
|
30396
|
-
return found.selector;
|
|
30397
|
-
await new Promise((resolve3) => setTimeout(resolve3, POLL_MS));
|
|
30398
|
-
}
|
|
30399
|
-
throw new Error(`${what}: no matching element observed`);
|
|
30400
|
-
}
|
|
30401
|
-
async function findAttachedSelector(page, selectors, what, timeoutMs = STEP_TIMEOUT_MS) {
|
|
30402
|
-
const deadline = Date.now() + timeoutMs;
|
|
30403
|
-
while (Date.now() < deadline) {
|
|
30404
|
-
for (const selector of selectors) {
|
|
30405
|
-
const attached = await page.locator(selector).count().catch(() => 0);
|
|
30406
|
-
if (attached > 0)
|
|
30407
|
-
return selector;
|
|
30408
|
-
}
|
|
30409
|
-
await new Promise((resolve3) => setTimeout(resolve3, POLL_MS));
|
|
30410
|
-
}
|
|
30411
|
-
throw new Error(`${what}: no matching element observed`);
|
|
30412
|
-
}
|
|
30413
31058
|
async function checkXhsProfileSession(opts) {
|
|
30414
|
-
|
|
30415
|
-
|
|
30416
|
-
|
|
30417
|
-
|
|
30418
|
-
|
|
30419
|
-
|
|
30420
|
-
|
|
30421
|
-
|
|
30422
|
-
|
|
30423
|
-
launcher: opts.launcher
|
|
30424
|
-
}, async () => {
|
|
30425
|
-
return;
|
|
30426
|
-
});
|
|
30427
|
-
return {
|
|
30428
|
-
status: "verified",
|
|
30429
|
-
profileDir: opts.profileDir,
|
|
30430
|
-
detail: `authenticated ${CHANNEL} creator console observed ${headless ? "headlessly" : "via headed fallback"} via profile ${opts.profileDir} (read-only)`
|
|
30431
|
-
};
|
|
30432
|
-
} catch (error51) {
|
|
30433
|
-
if (headless)
|
|
30434
|
-
continue;
|
|
30435
|
-
return {
|
|
30436
|
-
status: "refused",
|
|
30437
|
-
profileDir: opts.profileDir,
|
|
30438
|
-
detail: `headless and headed readiness refused: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
30439
|
-
};
|
|
30440
|
-
}
|
|
30441
|
-
}
|
|
30442
|
-
throw new Error("unreachable: session check loop must return or throw");
|
|
31059
|
+
return checkChannelSession({
|
|
31060
|
+
channel: CHANNEL,
|
|
31061
|
+
homeUrl: PUBLISH_URL,
|
|
31062
|
+
isAuthenticated: (page) => isXhsConsoleReady(wrapPlaywrightPage(page))
|
|
31063
|
+
}, {
|
|
31064
|
+
profileDir: opts.profileDir,
|
|
31065
|
+
launcher: opts.launcher,
|
|
31066
|
+
timeoutMs: opts.timeoutMs ?? PREFLIGHT_TIMEOUT_MS
|
|
31067
|
+
});
|
|
30443
31068
|
}
|
|
30444
31069
|
|
|
30445
31070
|
class XhsBrowserTransport {
|
|
@@ -30462,91 +31087,34 @@ class XhsBrowserTransport {
|
|
|
30462
31087
|
this.automation = opts?.automation ?? createAutomationGuard();
|
|
30463
31088
|
this.preflight = opts?.preflight ?? ((profileDir) => checkXhsProfileSession({ profileDir }));
|
|
30464
31089
|
}
|
|
30465
|
-
async readDraftCount(page) {
|
|
30466
|
-
for (const selector of SELECTORS.draftBox) {
|
|
30467
|
-
const attached = await page.locator(selector).count().catch(() => 0);
|
|
30468
|
-
if (attached === 0)
|
|
30469
|
-
continue;
|
|
30470
|
-
const locator = page.locator(selector).first();
|
|
30471
|
-
if (!locator.textContent)
|
|
30472
|
-
continue;
|
|
30473
|
-
const text = await locator.textContent().catch(() => "");
|
|
30474
|
-
const match = /\u8349\u7A3F\u7BB1\s*[\uFF08(]\s*(\d+)\s*[\uFF09)]/.exec(text);
|
|
30475
|
-
if (match?.[1] !== undefined)
|
|
30476
|
-
return Number(match[1]);
|
|
30477
|
-
}
|
|
30478
|
-
return null;
|
|
30479
|
-
}
|
|
30480
|
-
static draftSavedStamps(now = new Date) {
|
|
30481
|
-
const pad = (value) => String(value).padStart(2, "0");
|
|
30482
|
-
return [0, 1, 2].map((minutesAgo) => {
|
|
30483
|
-
const stamp = new Date(now.getTime() - minutesAgo * 60000);
|
|
30484
|
-
return `\u4FDD\u5B58\u4E8E${stamp.getFullYear()}-${pad(stamp.getMonth() + 1)}-${pad(stamp.getDate())} ${pad(stamp.getHours())}:${pad(stamp.getMinutes())}`;
|
|
30485
|
-
});
|
|
30486
|
-
}
|
|
30487
|
-
async readDraftStamps(page) {
|
|
30488
|
-
const present = [];
|
|
30489
|
-
for (const stamp of XhsBrowserTransport.draftSavedStamps()) {
|
|
30490
|
-
const attached = await page.locator(`text=${stamp}`).count().catch(() => 0);
|
|
30491
|
-
if (attached > 0)
|
|
30492
|
-
present.push(stamp);
|
|
30493
|
-
}
|
|
30494
|
-
return present;
|
|
30495
|
-
}
|
|
30496
|
-
async readDraftBaseline(page) {
|
|
30497
|
-
return { count: await this.readDraftCount(page), stamps: await this.readDraftStamps(page) };
|
|
30498
|
-
}
|
|
30499
|
-
async isDraftSaved(page, baseline) {
|
|
30500
|
-
const stamps = await this.readDraftStamps(page);
|
|
30501
|
-
if (stamps.some((stamp) => !baseline.stamps.includes(stamp)))
|
|
30502
|
-
return true;
|
|
30503
|
-
const after = await this.readDraftCount(page);
|
|
30504
|
-
if (baseline.count === null || after === null)
|
|
30505
|
-
return false;
|
|
30506
|
-
return after > baseline.count;
|
|
30507
|
-
}
|
|
30508
|
-
async clickShadowControl(page, labels) {
|
|
30509
|
-
if (!page.findShadowControl || !page.clickAt) {
|
|
30510
|
-
return {
|
|
30511
|
-
ok: false,
|
|
30512
|
-
error: `submit control: the ${CHANNEL} publish bar lives in a closed shadow root and this driver has no CDP access to resolve it`
|
|
30513
|
-
};
|
|
30514
|
-
}
|
|
30515
|
-
const control = await page.findShadowControl(labels);
|
|
30516
|
-
if (!control) {
|
|
30517
|
-
return {
|
|
30518
|
-
ok: false,
|
|
30519
|
-
error: `submit control: none of [${labels.join(", ")}] found in the ${CHANNEL} publish bar (closed shadow root)`
|
|
30520
|
-
};
|
|
30521
|
-
}
|
|
30522
|
-
await page.clickAt(control.x, control.y);
|
|
30523
|
-
return { ok: true, label: control.label, x: control.x, y: control.y };
|
|
30524
|
-
}
|
|
30525
31090
|
async publish(payload) {
|
|
30526
31091
|
if (this.driver !== "script") {
|
|
30527
|
-
return
|
|
30528
|
-
}
|
|
30529
|
-
const
|
|
30530
|
-
|
|
30531
|
-
|
|
30532
|
-
|
|
30533
|
-
|
|
30534
|
-
|
|
31092
|
+
return fail2(`unsupported driver '${this.driver}' for ${TARGET}: the console modes (${XHS_OPERATIONS.join(", ")}) require the script driver (Playwright setInputFiles)`);
|
|
31093
|
+
}
|
|
31094
|
+
const requested = resolveXhsOperation(payload.options, payload.content);
|
|
31095
|
+
if (requested.error !== undefined)
|
|
31096
|
+
return fail2(requested.error);
|
|
31097
|
+
const operation = requested.operation;
|
|
31098
|
+
const validation = await validateModeInput(operation, payload.content);
|
|
31099
|
+
if (validation.error !== undefined)
|
|
31100
|
+
return fail2(validation.error);
|
|
31101
|
+
const input = validation.input;
|
|
31102
|
+
const override = typeof payload.options?.profileDir === "string" ? payload.options.profileDir : undefined;
|
|
31103
|
+
const profileDir = resolveChannelProfileDir(CHANNEL, override);
|
|
30535
31104
|
const publishStatus = payload.content.metadata?.publishStatus === "public" ? "public" : "draft";
|
|
30536
|
-
const base2 = { driver: this.driver, publishStatus, profileDir, imageCount: paths.length };
|
|
30537
31105
|
const cap = this.automation.allow("publish");
|
|
30538
31106
|
if (!cap.allowed)
|
|
30539
|
-
return
|
|
31107
|
+
return fail2(`automation cap: ${cap.reason}`);
|
|
30540
31108
|
const session = await this.preflight(profileDir);
|
|
30541
31109
|
if (session.status !== "verified") {
|
|
30542
|
-
return
|
|
31110
|
+
return fail2(`session preflight refused (no headed visit attempted): ${session.detail}`);
|
|
30543
31111
|
}
|
|
30544
31112
|
this.automation.record("publish");
|
|
30545
31113
|
let opened;
|
|
30546
31114
|
try {
|
|
30547
31115
|
opened = await this.openPage(profileDir);
|
|
30548
31116
|
} catch (error51) {
|
|
30549
|
-
return
|
|
31117
|
+
return fail2(error51 instanceof Error ? error51.message : String(error51));
|
|
30550
31118
|
}
|
|
30551
31119
|
const { page, close } = opened;
|
|
30552
31120
|
const act = async (what, fn) => {
|
|
@@ -30558,101 +31126,24 @@ class XhsBrowserTransport {
|
|
|
30558
31126
|
};
|
|
30559
31127
|
try {
|
|
30560
31128
|
await act("creator page goto", () => page.goto(PUBLISH_URL, { timeout: this.loginTimeoutMs }));
|
|
30561
|
-
const gated = await
|
|
31129
|
+
const gated = await waitUntil2(() => isXhsConsoleReady(page), this.loginTimeoutMs);
|
|
30562
31130
|
if (!gated) {
|
|
30563
|
-
return
|
|
30564
|
-
}
|
|
30565
|
-
const
|
|
30566
|
-
|
|
30567
|
-
|
|
30568
|
-
|
|
30569
|
-
|
|
30570
|
-
|
|
30571
|
-
|
|
30572
|
-
|
|
30573
|
-
|
|
30574
|
-
|
|
30575
|
-
|
|
30576
|
-
const fileSelector = await findAttachedSelector(page, SELECTORS.fileInput, "image file input", this.stepTimeoutMs);
|
|
30577
|
-
await act("image upload", () => page.setInputFiles(fileSelector, paths));
|
|
30578
|
-
const thumbSelector = await step(SELECTORS.thumbnail, "thumbnail wait");
|
|
30579
|
-
const countMatches = await waitUntil(() => act("thumbnail count read", () => page.locator(thumbSelector).count()).then((n2) => n2 >= paths.length), this.stepTimeoutMs);
|
|
30580
|
-
const thumbCount = await act("thumbnail count read", () => page.locator(thumbSelector).count());
|
|
30581
|
-
if (!countMatches || thumbCount !== paths.length) {
|
|
30582
|
-
return fail(`thumbnail count: expected ${paths.length}, observed ${thumbCount}`);
|
|
30583
|
-
}
|
|
30584
|
-
const titleSelector = await step(SELECTORS.title, "title input");
|
|
30585
|
-
await act("title fill", () => page.locator(titleSelector).first().fill(payload.content.title ?? ""));
|
|
30586
|
-
const bodySelector = await step(SELECTORS.body, "note body");
|
|
30587
|
-
await act("body fill", async () => {
|
|
30588
|
-
const body = page.locator(bodySelector).first();
|
|
30589
|
-
const text = payload.content.body ?? "";
|
|
30590
|
-
if (typeof body.type === "function") {
|
|
30591
|
-
await body.click();
|
|
30592
|
-
await body.type(text, { delay: 5 });
|
|
30593
|
-
return;
|
|
30594
|
-
}
|
|
30595
|
-
await body.fill(text);
|
|
30596
|
-
});
|
|
30597
|
-
if (publishStatus === "draft") {
|
|
30598
|
-
let submitted2 = null;
|
|
30599
|
-
let confirmed2 = false;
|
|
30600
|
-
let attempts = 0;
|
|
30601
|
-
while (!confirmed2 && attempts < this.submitAttempts) {
|
|
30602
|
-
attempts += 1;
|
|
30603
|
-
await new Promise((resolve3) => setTimeout(resolve3, this.submitSettleMs));
|
|
30604
|
-
const clicked = await this.clickShadowControl(page, SHADOW_CONTROLS.draft);
|
|
30605
|
-
if (!clicked.ok)
|
|
30606
|
-
return fail(clicked.error);
|
|
30607
|
-
submitted2 = clicked;
|
|
30608
|
-
confirmed2 = await waitUntil(() => this.isDraftSaved(page, draftBaseline), this.submitSettleMs);
|
|
30609
|
-
}
|
|
30610
|
-
const draftCountAfter = await this.readDraftCount(page);
|
|
30611
|
-
if (!confirmed2 || submitted2 === null) {
|
|
30612
|
-
return fail(`draft confirmation: '${submitted2?.label ?? SHADOW_CONTROLS.draft[0]}' was clicked ${attempts}\xD7 but neither a fresh ${CHANNEL} save timestamp nor a moved draft counter was observed (before=${String(draftBaseline.count)}, after=${String(draftCountAfter)})`);
|
|
30613
|
-
}
|
|
30614
|
-
return ResultSchema.parse({
|
|
30615
|
-
ok: true,
|
|
30616
|
-
target: TARGET,
|
|
30617
|
-
message: `${TARGET} draft saved (unpublished) in profile ${profileDir} via '${submitted2.label}'`,
|
|
30618
|
-
metadata: {
|
|
30619
|
-
...base2,
|
|
30620
|
-
draftControl: submitted2.label,
|
|
30621
|
-
draftClick: `${submitted2.x},${submitted2.y}`,
|
|
30622
|
-
draftAttempts: attempts,
|
|
30623
|
-
draftCountBefore: draftBaseline.count,
|
|
30624
|
-
draftCountAfter
|
|
30625
|
-
}
|
|
30626
|
-
});
|
|
30627
|
-
}
|
|
30628
|
-
await new Promise((resolve3) => setTimeout(resolve3, this.submitSettleMs));
|
|
30629
|
-
const submitted = await this.clickShadowControl(page, SHADOW_CONTROLS.publish);
|
|
30630
|
-
if (!submitted.ok)
|
|
30631
|
-
return fail(submitted.error);
|
|
30632
|
-
const urlBeforeSubmit = page.url();
|
|
30633
|
-
const confirmed = await waitUntil(async () => {
|
|
30634
|
-
if (page.url().includes(PUBLISHED_MARKER))
|
|
30635
|
-
return true;
|
|
30636
|
-
return trySelectors(page, SELECTORS.publishConfirm).then((r) => r.found).catch(() => false);
|
|
30637
|
-
}, this.stepTimeoutMs);
|
|
30638
|
-
if (!confirmed) {
|
|
30639
|
-
return fail(`publish submission: state unknown \u2014 the '${submitted.label}' click at ${submitted.x},${submitted.y} was sent from ${urlBeforeSubmit} but no success state was observed (now ${page.url()}). Check the ${CHANNEL} creator note manager before retrying: a retry could duplicate the post.`);
|
|
30640
|
-
}
|
|
30641
|
-
const url2 = page.url();
|
|
30642
|
-
return ResultSchema.parse({
|
|
30643
|
-
ok: true,
|
|
30644
|
-
target: TARGET,
|
|
30645
|
-
...url2 !== PUBLISH_URL ? { url: url2 } : {},
|
|
30646
|
-
message: `${TARGET} post published`,
|
|
30647
|
-
metadata: {
|
|
30648
|
-
...base2,
|
|
30649
|
-
publishControl: submitted.label,
|
|
30650
|
-
publishClick: `${submitted.x},${submitted.y}`,
|
|
30651
|
-
publishSignal: url2.includes(PUBLISHED_MARKER) ? `url:${PUBLISHED_MARKER}` : "text"
|
|
30652
|
-
}
|
|
31131
|
+
return fail2(`login timeout after ${this.loginTimeoutMs}ms: no authenticated ${CHANNEL} creator console in ${profileDir}`);
|
|
31132
|
+
}
|
|
31133
|
+
const draftBaseline = publishStatus === "draft" ? await readDraftBaseline(page) : { count: null, stamps: [] };
|
|
31134
|
+
return await runMode(input, {
|
|
31135
|
+
page,
|
|
31136
|
+
content: payload.content,
|
|
31137
|
+
publishStatus,
|
|
31138
|
+
profileDir,
|
|
31139
|
+
driver: this.driver,
|
|
31140
|
+
stepTimeoutMs: this.stepTimeoutMs,
|
|
31141
|
+
submitSettleMs: this.submitSettleMs,
|
|
31142
|
+
submitAttempts: this.submitAttempts,
|
|
31143
|
+
draftBaseline
|
|
30653
31144
|
});
|
|
30654
31145
|
} catch (error51) {
|
|
30655
|
-
return
|
|
31146
|
+
return fail2(error51 instanceof Error ? error51.message : String(error51));
|
|
30656
31147
|
} finally {
|
|
30657
31148
|
await close();
|
|
30658
31149
|
}
|
|
@@ -30682,7 +31173,7 @@ async function processPublishIO(inputPath, outputPath, transport = getPublishTra
|
|
|
30682
31173
|
}
|
|
30683
31174
|
} catch (error51) {
|
|
30684
31175
|
const message = `Invalid Content input: ${error51 instanceof Error ? error51.message : String(error51)}`;
|
|
30685
|
-
await writePublishResult(outputPath,
|
|
31176
|
+
await writePublishResult(outputPath, fail2(message));
|
|
30686
31177
|
throw new Error(message);
|
|
30687
31178
|
}
|
|
30688
31179
|
const result = ResultSchema.parse(await transport.publish({ content, options }));
|
|
@@ -30723,10 +31214,16 @@ export {
|
|
|
30723
31214
|
getPublishTransport,
|
|
30724
31215
|
defaultOpenPage,
|
|
30725
31216
|
defaultLauncher2 as defaultLauncher,
|
|
31217
|
+
creatorTabSelector,
|
|
30726
31218
|
createShadowProbe,
|
|
31219
|
+
clickCreatorTab,
|
|
30727
31220
|
checkXhsProfileSession,
|
|
30728
31221
|
XhsBrowserTransport,
|
|
31222
|
+
XHS_OPERATIONS,
|
|
30729
31223
|
PUBLISH_URL,
|
|
31224
|
+
MODE_LIMITS,
|
|
30730
31225
|
LOGIN_TIMEOUT_MS,
|
|
31226
|
+
CREATOR_TAB_COUNT,
|
|
31227
|
+
CREATOR_TAB_BAR,
|
|
30731
31228
|
CHANNEL
|
|
30732
31229
|
};
|