@gobing-ai/knowledge-kit 0.0.32 → 0.0.34
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 +43047 -24724
- package/package.json +4 -4
- package/plugins/generations/content-gen/dist/index.js +10 -9
- package/plugins/generations/core-facts-gen/dist/index.js +13 -12
- package/plugins/generations/daily-article-gen/dist/index.js +16 -21
- package/plugins/generations/daily-article-gen/src/index.ts +8 -12
- package/plugins/generations/dailynews-gen/dist/index.js +23 -21
- package/plugins/generations/dailynews-gen/src/script-builder.ts +25 -26
- package/plugins/generations/episode-plan-gen/dist/index.js +58 -15
- package/plugins/generations/episode-plan-gen/src/index.ts +76 -6
- package/plugins/generations/gemini-tts/README.md +11 -0
- package/plugins/generations/gemini-tts/dist/index.js +10 -9
- package/plugins/generations/image-gen/dist/index.js +171 -153
- package/plugins/generations/image-gen/presets/formats/xhs-card.yaml +16 -14
- package/plugins/generations/image-gen/presets/styles/xhs-knowledge.yaml +2 -2
- package/plugins/generations/image-gen/src/compose/card.ts +17 -2
- package/plugins/generations/image-gen/src/compose/chromium.ts +60 -42
- package/plugins/generations/news-report-gen/dist/index.js +13 -12
- package/plugins/generations/voice-gen/dist/index.js +10 -9
- package/plugins/ingestions/aihot-ingest/dist/index.js +13 -12
- package/plugins/ingestions/horizon-ingest/dist/index.js +13 -12
- package/plugins/ingestions/karakeep-local/dist/index.js +13 -13
- package/plugins/ingestions/last30days-ingest/dist/index.js +13 -12
- package/plugins/ingestions/web-search/dist/index.js +11 -10
- package/plugins/kk/README.md +38 -0
- package/plugins/kk/decisions/extract/ai-news.yaml +27 -0
- package/plugins/kk/decisions/kk-items.yaml +179 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/schedules/cron.tmpl +15 -0
- package/plugins/kk/schedules/launchd.plist.tmpl +77 -0
- package/plugins/kk/scripts/daily-stages.ts +2023 -0
- package/plugins/kk/scripts/daily-voice-style.zh.md +15 -10
- package/plugins/kk/scripts/kk-workflow-stages.ts +253 -1368
- package/plugins/kk/skills/content-safety/SKILL.md +39 -8
- package/plugins/kk/skills/content-safety/references/platforms/apple-podcasts.md +2 -2
- package/plugins/kk/skills/web-automation/SKILL.md +100 -0
- package/plugins/kk/skills/web-automation/references/browser-launch.md +86 -0
- package/plugins/kk/workflows/kk-collect.yaml +256 -0
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +9 -3
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +774 -0
- package/plugins/kk/workflows/kk-daily-safety.yaml +167 -0
- package/plugins/publishings/emdash-pub/dist/index.js +10 -9
- package/plugins/publishings/infoq-pub/dist/index.js +152 -59
- package/plugins/publishings/infoq-pub/plugin.json +1 -0
- package/plugins/publishings/infoq-pub/src/index.ts +167 -55
- package/plugins/publishings/infoq-pub/src/profile-check.ts +4 -2
- package/plugins/publishings/juejin-pub/dist/index.js +111 -30
- package/plugins/publishings/juejin-pub/plugin.json +1 -0
- package/plugins/publishings/juejin-pub/src/index.ts +49 -9
- package/plugins/publishings/medium-pub/dist/index.js +10 -9
- package/plugins/publishings/podcast-pub/dist/index.js +10 -9
- package/plugins/publishings/qiita-pub/dist/index.js +10 -9
- package/plugins/publishings/substack-pub/dist/index.js +105 -28
- package/plugins/publishings/substack-pub/plugin.json +1 -0
- package/plugins/publishings/substack-pub/src/index.ts +39 -8
- package/plugins/publishings/substack-pub/src/profile-check.ts +2 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +10 -9
- package/plugins/publishings/wechatmp-pub/dist/index.js +115 -31
- package/plugins/publishings/wechatmp-pub/plugin.json +1 -0
- package/plugins/publishings/wechatmp-pub/src/index.ts +42 -9
- package/plugins/publishings/wechatmp-pub/src/profile-check.ts +84 -40
- package/plugins/publishings/x-pub/dist/index.js +114 -35
- package/plugins/publishings/x-pub/plugin.json +1 -0
- package/plugins/publishings/x-pub/src/index.ts +68 -14
- package/plugins/publishings/xhs-pub/dist/index.js +154 -59
- package/plugins/publishings/xhs-pub/plugin.json +1 -0
- package/plugins/publishings/xhs-pub/src/flow.ts +9 -3
- package/plugins/publishings/xhs-pub/src/index.ts +119 -9
- package/plugins/publishings/xhs-pub/src/page.ts +4 -17
- package/plugins/publishings/xhs-pub/src/profile-check.ts +28 -15
- package/plugins/publishings/zenn-pub/dist/index.js +10 -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.21/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.21/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.21/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.21/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.21/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.21/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.21/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.21/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.21+44edd5c4d6372504/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.21+44edd5c4d6372504/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.21+44edd5c4d6372504/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.21+44edd5c4d6372504/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.21+44edd5c4d6372504/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.21+44edd5c4d6372504/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
|
|
29598
29598
|
function getNodeFileSystem() {
|
|
29599
29599
|
if (!_nodeFileSystem)
|
|
29600
29600
|
_nodeFileSystem = createNodeFileSystem();
|
|
@@ -29741,15 +29741,16 @@ var PluginManifestSchema = exports_external.looseObject({
|
|
|
29741
29741
|
name: exports_external.string().min(1),
|
|
29742
29742
|
kind: PluginKindSchema,
|
|
29743
29743
|
entry: exports_external.string().min(1),
|
|
29744
|
+
check: exports_external.string().min(1).optional(),
|
|
29744
29745
|
version: exports_external.string().optional(),
|
|
29745
29746
|
description: exports_external.string().optional(),
|
|
29746
29747
|
operations: exports_external.record(exports_external.string(), PluginOperationSchema).optional()
|
|
29747
29748
|
});
|
|
29748
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29749
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.21+44edd5c4d6372504/node_modules/@gobing-ai/ts-runtime/dist/index.js
|
|
29749
29750
|
init_file_system_node();
|
|
29750
29751
|
init_config();
|
|
29751
29752
|
|
|
29752
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29753
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.21+44edd5c4d6372504/node_modules/@gobing-ai/ts-runtime/dist/path.js
|
|
29753
29754
|
function normalizeSeparators(path6) {
|
|
29754
29755
|
return path6.replaceAll("\\", "/");
|
|
29755
29756
|
}
|
|
@@ -29807,7 +29808,7 @@ function getProcessCwd() {
|
|
|
29807
29808
|
return globalThis.process?.cwd?.() ?? "/";
|
|
29808
29809
|
}
|
|
29809
29810
|
|
|
29810
|
-
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.
|
|
29811
|
+
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.21+44edd5c4d6372504/node_modules/@gobing-ai/ts-runtime/dist/index.js
|
|
29811
29812
|
init_runtime_node_bun();
|
|
29812
29813
|
|
|
29813
29814
|
// ../../packages/kk-core/src/render-md.ts
|
|
@@ -29920,7 +29921,10 @@ async function trySelectors(page, selectors, options = {}) {
|
|
|
29920
29921
|
return { found: false, selector: null };
|
|
29921
29922
|
}
|
|
29922
29923
|
|
|
29923
|
-
// ../../
|
|
29924
|
+
// ../../packages/publish-harness/src/browser.ts
|
|
29925
|
+
import { resolve as resolve2 } from "path";
|
|
29926
|
+
|
|
29927
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.21+b6949ba92ac553f0/node_modules/@gobing-ai/ts-browser-automation/dist/errors.js
|
|
29924
29928
|
class BrowserAutomationError extends Error {
|
|
29925
29929
|
constructor(message, options) {
|
|
29926
29930
|
super(message, options);
|
|
@@ -29969,10 +29973,10 @@ class OperationAbortedError extends BrowserAutomationError {
|
|
|
29969
29973
|
this.name = "OperationAbortedError";
|
|
29970
29974
|
}
|
|
29971
29975
|
}
|
|
29972
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.
|
|
29976
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.21+b6949ba92ac553f0/node_modules/@gobing-ai/ts-browser-automation/dist/launcher.js
|
|
29973
29977
|
import { mkdir } from "fs/promises";
|
|
29974
29978
|
|
|
29975
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.
|
|
29979
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.21+b6949ba92ac553f0/node_modules/@gobing-ai/ts-browser-automation/dist/profile-dir.js
|
|
29976
29980
|
function resolveProfileDir(profileDir) {
|
|
29977
29981
|
if (profileDir.trim() === "") {
|
|
29978
29982
|
throw new InvalidProfileDirError;
|
|
@@ -29984,7 +29988,7 @@ function resolveProfileDir(profileDir) {
|
|
|
29984
29988
|
return resolved;
|
|
29985
29989
|
}
|
|
29986
29990
|
|
|
29987
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.
|
|
29991
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.21+b6949ba92ac553f0/node_modules/@gobing-ai/ts-browser-automation/dist/launcher.js
|
|
29988
29992
|
var PROFILE_DIR_MODE = 448;
|
|
29989
29993
|
var BUSY_PROFILE_PATTERN = /singleton|already in use|in use by another/i;
|
|
29990
29994
|
var CHROMIUM_MISSING_PATTERN = /playwright install|executable doesn't exist/i;
|
|
@@ -30035,7 +30039,7 @@ function createPersistentLauncher(options = {}) {
|
|
|
30035
30039
|
};
|
|
30036
30040
|
}
|
|
30037
30041
|
|
|
30038
|
-
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.
|
|
30042
|
+
// ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.21+b6949ba92ac553f0/node_modules/@gobing-ai/ts-browser-automation/dist/operations.js
|
|
30039
30043
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
30040
30044
|
var POLL_INTERVAL_MS = 250;
|
|
30041
30045
|
var defaultLauncher;
|
|
@@ -30044,12 +30048,6 @@ function getDefaultLauncher() {
|
|
|
30044
30048
|
defaultLauncher = createPersistentLauncher();
|
|
30045
30049
|
return defaultLauncher;
|
|
30046
30050
|
}
|
|
30047
|
-
async function openPersistentBrowser(options) {
|
|
30048
|
-
return (options.launcher ?? getDefaultLauncher()).launch({
|
|
30049
|
-
profileDir: resolveProfileDir(options.profileDir),
|
|
30050
|
-
headless: options.headless
|
|
30051
|
-
});
|
|
30052
|
-
}
|
|
30053
30051
|
async function withProfilePage(options, callback) {
|
|
30054
30052
|
assertNotAborted(options.signal);
|
|
30055
30053
|
if (options.isAuthenticated !== undefined)
|
|
@@ -30120,11 +30118,67 @@ async function pollPredicate(page, predicate, timeoutMs, signal) {
|
|
|
30120
30118
|
function sleep(ms) {
|
|
30121
30119
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
30122
30120
|
}
|
|
30121
|
+
// ../../packages/publish-harness/src/profile-dir.ts
|
|
30122
|
+
import { mkdir as mkdir2, stat } from "fs/promises";
|
|
30123
|
+
async function ensureProfileDir2(profileDir, headless) {
|
|
30124
|
+
const existing = await stat(profileDir).catch(() => null);
|
|
30125
|
+
if (existing) {
|
|
30126
|
+
if (!existing.isDirectory())
|
|
30127
|
+
throw new InvalidProfileDirError(`profileDir is not a directory: "${profileDir}".`);
|
|
30128
|
+
return;
|
|
30129
|
+
}
|
|
30130
|
+
if (headless)
|
|
30131
|
+
throw new BrowserProfileMissingError(profileDir);
|
|
30132
|
+
await mkdir2(profileDir, { recursive: true, mode: 448 });
|
|
30133
|
+
}
|
|
30134
|
+
|
|
30123
30135
|
// ../../packages/publish-harness/src/browser.ts
|
|
30124
30136
|
var DEFAULT_CHECK_TIMEOUT_MS = 30000;
|
|
30125
30137
|
var DEFAULT_STEP_TIMEOUT_MS = 30000;
|
|
30126
30138
|
var DEFAULT_POLL_MS = 500;
|
|
30127
30139
|
var ATTACH_POLL_MS = 100;
|
|
30140
|
+
function createKkBrowserLauncher(loadPlaywright = () => import("playwright")) {
|
|
30141
|
+
return {
|
|
30142
|
+
async launch(request) {
|
|
30143
|
+
if (request.profileDir.trim() === "")
|
|
30144
|
+
throw new InvalidProfileDirError;
|
|
30145
|
+
const profileDir = resolve2(request.profileDir);
|
|
30146
|
+
await ensureProfileDir2(profileDir, request.headless);
|
|
30147
|
+
let playwright;
|
|
30148
|
+
try {
|
|
30149
|
+
playwright = await loadPlaywright();
|
|
30150
|
+
} catch (error51) {
|
|
30151
|
+
throw new PlaywrightUnavailableError('The "playwright" dependency is not installed. Run `bunx playwright install chromium`.', { cause: error51 });
|
|
30152
|
+
}
|
|
30153
|
+
const channel = process.env.KK_BROWSER_CHANNEL?.trim() || "chromium";
|
|
30154
|
+
let context2;
|
|
30155
|
+
try {
|
|
30156
|
+
context2 = await playwright.chromium.launchPersistentContext(profileDir, {
|
|
30157
|
+
headless: request.headless,
|
|
30158
|
+
channel,
|
|
30159
|
+
args: ["--disable-blink-features=AutomationControlled"],
|
|
30160
|
+
ignoreDefaultArgs: ["--enable-automation"]
|
|
30161
|
+
});
|
|
30162
|
+
} catch (error51) {
|
|
30163
|
+
const message = errorMessage2(error51);
|
|
30164
|
+
if (/singleton|already in use|in use by another/i.test(message)) {
|
|
30165
|
+
throw new BrowserProfileBusyError(profileDir, error51);
|
|
30166
|
+
}
|
|
30167
|
+
if (/playwright install|executable doesn't exist/i.test(message)) {
|
|
30168
|
+
throw new PlaywrightUnavailableError(`Browser channel '${channel}' is not installed. Run \`bunx playwright install chromium\` (or install Google Chrome for KK_BROWSER_CHANNEL=chrome).`, { cause: error51 });
|
|
30169
|
+
}
|
|
30170
|
+
throw error51;
|
|
30171
|
+
}
|
|
30172
|
+
await context2.addInitScript(() => {
|
|
30173
|
+
Object.defineProperty(navigator, "webdriver", { get: () => {
|
|
30174
|
+
return;
|
|
30175
|
+
} });
|
|
30176
|
+
});
|
|
30177
|
+
return context2;
|
|
30178
|
+
}
|
|
30179
|
+
};
|
|
30180
|
+
}
|
|
30181
|
+
var kkBrowserLauncher = createKkBrowserLauncher();
|
|
30128
30182
|
function resolveChannelProfileDir(channel, override) {
|
|
30129
30183
|
const tiers = [override, process.env[`${channel.toUpperCase()}_PROFILE_DIR`], process.env.KK_BROWSER_PROFILE_DIR];
|
|
30130
30184
|
const chosen = tiers.find((tier) => tier !== undefined && tier.trim().length > 0);
|
|
@@ -30132,7 +30186,7 @@ function resolveChannelProfileDir(channel, override) {
|
|
|
30132
30186
|
}
|
|
30133
30187
|
async function checkChannelSession(config3, opts) {
|
|
30134
30188
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS;
|
|
30135
|
-
const passes = opts.headedFallback === false ? [true] : [true, false];
|
|
30189
|
+
const passes = opts.headedOnly ? [false] : opts.headedFallback === false ? [true] : [true, false];
|
|
30136
30190
|
let lastDetail = `${config3.channel} readiness was never probed`;
|
|
30137
30191
|
for (const headless of passes) {
|
|
30138
30192
|
try {
|
|
@@ -30142,12 +30196,12 @@ async function checkChannelSession(config3, opts) {
|
|
|
30142
30196
|
url: config3.homeUrl,
|
|
30143
30197
|
isAuthenticated: config3.isAuthenticated,
|
|
30144
30198
|
timeoutMs,
|
|
30145
|
-
launcher: opts.launcher
|
|
30199
|
+
launcher: opts.launcher ?? kkBrowserLauncher
|
|
30146
30200
|
}, async () => true);
|
|
30147
30201
|
return {
|
|
30148
30202
|
status: "verified",
|
|
30149
30203
|
profileDir: opts.profileDir,
|
|
30150
|
-
detail: `authenticated ${config3.channel} session observed ${headless ? "headlessly" : "via headed
|
|
30204
|
+
detail: `authenticated ${config3.channel} session observed ${headless ? "headlessly" : "via a headed pass"} via profile ${opts.profileDir} (read-only)`
|
|
30151
30205
|
};
|
|
30152
30206
|
} catch (error51) {
|
|
30153
30207
|
if (error51 instanceof BrowserProfileMissingError) {
|
|
@@ -30168,7 +30222,7 @@ async function waitUntil(predicate, timeoutMs, pollMs = DEFAULT_POLL_MS) {
|
|
|
30168
30222
|
if (await predicate()) {
|
|
30169
30223
|
return true;
|
|
30170
30224
|
}
|
|
30171
|
-
await new Promise((
|
|
30225
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollMs));
|
|
30172
30226
|
}
|
|
30173
30227
|
return predicate();
|
|
30174
30228
|
}
|
|
@@ -30320,14 +30374,14 @@ init_dist();
|
|
|
30320
30374
|
|
|
30321
30375
|
// ../../plugins/publishings/xhs-pub/src/automation-guard.ts
|
|
30322
30376
|
import { homedir as homedir2 } from "os";
|
|
30323
|
-
import { join as join2, resolve as
|
|
30377
|
+
import { join as join2, resolve as resolve3 } from "path";
|
|
30324
30378
|
var fs = createNodeFileSystem();
|
|
30325
30379
|
var CAP_WINDOW_MS = 2 * 60 * 60 * 1000;
|
|
30326
30380
|
var AUTOMATION_STATE_ENV = "KK_XHS_AUTOMATION_STATE";
|
|
30327
30381
|
function resolveAutomationStatePath(override) {
|
|
30328
30382
|
const explicit = override ?? process.env[AUTOMATION_STATE_ENV];
|
|
30329
30383
|
if (explicit !== undefined && explicit !== "")
|
|
30330
|
-
return
|
|
30384
|
+
return resolve3(explicit);
|
|
30331
30385
|
return join2(process.env.HOME ?? homedir2(), ".config", "kk", "xhs-automation.json");
|
|
30332
30386
|
}
|
|
30333
30387
|
function resetHint(path7) {
|
|
@@ -30594,7 +30648,7 @@ async function clickCreatorTab(page, label, opts = {}) {
|
|
|
30594
30648
|
const timeoutMs = opts.timeoutMs ?? STEP_TIMEOUT_MS;
|
|
30595
30649
|
const pollMs = opts.pollMs ?? POLL_MS;
|
|
30596
30650
|
const onScreenCount = () => page.countOnScreen(CREATOR_TAB_BAR).catch(() => 0);
|
|
30597
|
-
const mounted = await
|
|
30651
|
+
const mounted = await waitUntil(() => onScreenCount().then((count2) => count2 >= CREATOR_TAB_COUNT), timeoutMs, pollMs);
|
|
30598
30652
|
if (!mounted) {
|
|
30599
30653
|
const observed = await onScreenCount();
|
|
30600
30654
|
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`);
|
|
@@ -30607,15 +30661,6 @@ async function clickCreatorTab(page, label, opts = {}) {
|
|
|
30607
30661
|
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
30662
|
}
|
|
30609
30663
|
}
|
|
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
|
-
}
|
|
30619
30664
|
function wrapPlaywrightPage(raw, shadow) {
|
|
30620
30665
|
const pw = raw;
|
|
30621
30666
|
const onScreen = async (selector) => {
|
|
@@ -30674,7 +30719,7 @@ async function findSelector(page, selectors, what, timeoutMs = STEP_TIMEOUT_MS)
|
|
|
30674
30719
|
const found = await trySelectors(page, selectors);
|
|
30675
30720
|
if (found.found && found.selector)
|
|
30676
30721
|
return found.selector;
|
|
30677
|
-
await new Promise((
|
|
30722
|
+
await new Promise((resolve4) => setTimeout(resolve4, POLL_MS));
|
|
30678
30723
|
}
|
|
30679
30724
|
throw new Error(`${what}: no matching element observed`);
|
|
30680
30725
|
}
|
|
@@ -30686,7 +30731,7 @@ async function findAttachedSelector(page, selectors, what, timeoutMs = STEP_TIME
|
|
|
30686
30731
|
if (attached > 0)
|
|
30687
30732
|
return selector;
|
|
30688
30733
|
}
|
|
30689
|
-
await new Promise((
|
|
30734
|
+
await new Promise((resolve4) => setTimeout(resolve4, POLL_MS));
|
|
30690
30735
|
}
|
|
30691
30736
|
throw new Error(`${what}: no matching element observed`);
|
|
30692
30737
|
}
|
|
@@ -30734,7 +30779,7 @@ function fail(error51) {
|
|
|
30734
30779
|
return ResultSchema.parse({ ok: false, target: TARGET, error: error51 });
|
|
30735
30780
|
}
|
|
30736
30781
|
function sleep2(ms) {
|
|
30737
|
-
return new Promise((
|
|
30782
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
30738
30783
|
}
|
|
30739
30784
|
function asPlaywrightPage(page) {
|
|
30740
30785
|
return page;
|
|
@@ -30856,7 +30901,7 @@ async function typeNoteBody(deps, selectors, what) {
|
|
|
30856
30901
|
}
|
|
30857
30902
|
async function confirmThumbnails(deps, expected) {
|
|
30858
30903
|
const selector = await findSelector(deps.page, NOTE_THUMBNAIL, "thumbnail wait", deps.stepTimeoutMs);
|
|
30859
|
-
const matches = await
|
|
30904
|
+
const matches = await waitUntil(() => deps.page.locator(selector).count().then((count2) => count2 >= expected), deps.stepTimeoutMs);
|
|
30860
30905
|
const observed = await deps.page.locator(selector).count();
|
|
30861
30906
|
if (!matches || observed !== expected) {
|
|
30862
30907
|
throw new Error(`thumbnail count: expected ${expected}, observed ${observed}`);
|
|
@@ -30896,16 +30941,16 @@ async function isPodcastModalOpen(page) {
|
|
|
30896
30941
|
}
|
|
30897
30942
|
async function leavePodcastModal(deps) {
|
|
30898
30943
|
const closed = () => isPodcastModalOpen(deps.page).then((open) => !open);
|
|
30899
|
-
if (await
|
|
30944
|
+
if (await waitUntil(closed, deps.stepTimeoutMs))
|
|
30900
30945
|
return;
|
|
30901
30946
|
if (deps.publishStatus === "public") {
|
|
30902
30947
|
const advance = await clickLightControl(deps.page, PODCAST_MODAL_ADVANCE, deps.stepTimeoutMs, "podcast modal advance");
|
|
30903
30948
|
if (!advance.ok)
|
|
30904
30949
|
throw new Error(advance.error);
|
|
30905
|
-
if (await
|
|
30950
|
+
if (await waitUntil(closed, deps.stepTimeoutMs))
|
|
30906
30951
|
return;
|
|
30907
30952
|
}
|
|
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]}'
|
|
30953
|
+
throw new Error(`podcast modal: the \u53D1\u64AD\u5BA2 modal was still open ${deps.stepTimeoutMs}ms after the audio and cover were attached; its post-attach save path has never been measured (task 0236 R3 \u2014 the attended walk is still pending) and the only recorded advance control is '${PODCAST_MODAL_ADVANCE[0]}', which a draft run does not click (it is publish-adjacent)`);
|
|
30909
30954
|
}
|
|
30910
30955
|
async function composePodcast(input, deps) {
|
|
30911
30956
|
await enterMode(deps, input.operation);
|
|
@@ -30989,7 +31034,7 @@ async function publishNote(input, deps) {
|
|
|
30989
31034
|
if (!submitted.ok)
|
|
30990
31035
|
return fail(submitted.error);
|
|
30991
31036
|
const urlBeforeSubmit = deps.page.url();
|
|
30992
|
-
const confirmed = await
|
|
31037
|
+
const confirmed = await waitUntil(async () => {
|
|
30993
31038
|
if (deps.page.url().includes(PUBLISHED_MARKER))
|
|
30994
31039
|
return true;
|
|
30995
31040
|
return await readScopedSignal(asPlaywrightPage(deps.page), PUBLISH_CONFIRM) !== null;
|
|
@@ -31027,6 +31072,38 @@ var AUTH_GATE = ["div.creator-tab", "text=\u4E0A\u4F20\u56FE\u6587"];
|
|
|
31027
31072
|
function fail2(error51) {
|
|
31028
31073
|
return ResultSchema.parse({ ok: false, target: TARGET, error: error51 });
|
|
31029
31074
|
}
|
|
31075
|
+
async function planVisit(primary, content) {
|
|
31076
|
+
const raw = content.metadata?.followUps;
|
|
31077
|
+
if (raw === undefined)
|
|
31078
|
+
return { plan: { primary, followUps: [] } };
|
|
31079
|
+
if (!Array.isArray(raw)) {
|
|
31080
|
+
return {
|
|
31081
|
+
error: `followUps validation: Content.metadata.followUps must be an array of Content objects, got ${typeof raw}`
|
|
31082
|
+
};
|
|
31083
|
+
}
|
|
31084
|
+
const followUps = [];
|
|
31085
|
+
for (const [index, entry] of raw.entries()) {
|
|
31086
|
+
const label = `followUps[${index}]`;
|
|
31087
|
+
const parsed = ContentSchema.safeParse(entry);
|
|
31088
|
+
if (!parsed.success) {
|
|
31089
|
+
followUps.push({ operation: label, error: `${label} is not a Content object` });
|
|
31090
|
+
continue;
|
|
31091
|
+
}
|
|
31092
|
+
const operation = typeof parsed.data.metadata?.operation === "string" ? parsed.data.metadata.operation : label;
|
|
31093
|
+
const requested = resolveXhsOperation(undefined, parsed.data);
|
|
31094
|
+
if (requested.error !== undefined) {
|
|
31095
|
+
followUps.push({ operation, error: requested.error });
|
|
31096
|
+
continue;
|
|
31097
|
+
}
|
|
31098
|
+
const validation = await validateModeInput(requested.operation, parsed.data);
|
|
31099
|
+
if (validation.error !== undefined) {
|
|
31100
|
+
followUps.push({ operation, error: validation.error });
|
|
31101
|
+
continue;
|
|
31102
|
+
}
|
|
31103
|
+
followUps.push({ content: parsed.data, operation: requested.operation, input: validation.input });
|
|
31104
|
+
}
|
|
31105
|
+
return { plan: { primary, followUps } };
|
|
31106
|
+
}
|
|
31030
31107
|
async function createShadowProbe(context2, page) {
|
|
31031
31108
|
const find = await createShadowFinder(context2, page);
|
|
31032
31109
|
return find === undefined ? undefined : { findControl: (labels) => find(labels) };
|
|
@@ -31034,7 +31111,7 @@ async function createShadowProbe(context2, page) {
|
|
|
31034
31111
|
async function isXhsConsoleReady(page) {
|
|
31035
31112
|
return trySelectors(page, AUTH_GATE).then((r) => r.found);
|
|
31036
31113
|
}
|
|
31037
|
-
var defaultLauncher2 =
|
|
31114
|
+
var defaultLauncher2 = kkBrowserLauncher.launch;
|
|
31038
31115
|
function makeDefaultOpenPage(launch = defaultLauncher2) {
|
|
31039
31116
|
return async (profileDir) => {
|
|
31040
31117
|
let context2;
|
|
@@ -31063,7 +31140,8 @@ async function checkXhsProfileSession(opts) {
|
|
|
31063
31140
|
}, {
|
|
31064
31141
|
profileDir: opts.profileDir,
|
|
31065
31142
|
launcher: opts.launcher,
|
|
31066
|
-
timeoutMs: opts.timeoutMs ?? PREFLIGHT_TIMEOUT_MS
|
|
31143
|
+
timeoutMs: opts.timeoutMs ?? PREFLIGHT_TIMEOUT_MS,
|
|
31144
|
+
headedOnly: opts.headedOnly === true
|
|
31067
31145
|
});
|
|
31068
31146
|
}
|
|
31069
31147
|
|
|
@@ -31099,9 +31177,12 @@ class XhsBrowserTransport {
|
|
|
31099
31177
|
if (validation.error !== undefined)
|
|
31100
31178
|
return fail2(validation.error);
|
|
31101
31179
|
const input = validation.input;
|
|
31180
|
+
const visited = await planVisit({ content: payload.content, input }, payload.content);
|
|
31181
|
+
if (visited.error !== undefined)
|
|
31182
|
+
return fail2(visited.error);
|
|
31183
|
+
const visit2 = visited.plan;
|
|
31102
31184
|
const override = typeof payload.options?.profileDir === "string" ? payload.options.profileDir : undefined;
|
|
31103
31185
|
const profileDir = resolveChannelProfileDir(CHANNEL, override);
|
|
31104
|
-
const publishStatus = payload.content.metadata?.publishStatus === "public" ? "public" : "draft";
|
|
31105
31186
|
const cap = this.automation.allow("publish");
|
|
31106
31187
|
if (!cap.allowed)
|
|
31107
31188
|
return fail2(`automation cap: ${cap.reason}`);
|
|
@@ -31117,6 +31198,21 @@ class XhsBrowserTransport {
|
|
|
31117
31198
|
return fail2(error51 instanceof Error ? error51.message : String(error51));
|
|
31118
31199
|
}
|
|
31119
31200
|
const { page, close } = opened;
|
|
31201
|
+
try {
|
|
31202
|
+
const primary = await this.runStep(visit2.primary.input, visit2.primary.content, page, profileDir);
|
|
31203
|
+
if (!primary.ok || visit2.followUps.length === 0)
|
|
31204
|
+
return primary;
|
|
31205
|
+
const followUps = [];
|
|
31206
|
+
for (const followUp of visit2.followUps) {
|
|
31207
|
+
const step = "error" in followUp ? fail2(`follow-up (${followUp.operation}): ${followUp.error}`) : await this.runStep(followUp.input, followUp.content, page, profileDir);
|
|
31208
|
+
followUps.push(ResultSchema.parse({ ...step, metadata: { operation: followUp.operation, ...step.metadata } }));
|
|
31209
|
+
}
|
|
31210
|
+
return ResultSchema.parse({ ...primary, metadata: { ...primary.metadata, followUps } });
|
|
31211
|
+
} finally {
|
|
31212
|
+
await close();
|
|
31213
|
+
}
|
|
31214
|
+
}
|
|
31215
|
+
async runStep(input, content, page, profileDir) {
|
|
31120
31216
|
const act = async (what, fn) => {
|
|
31121
31217
|
try {
|
|
31122
31218
|
return await fn();
|
|
@@ -31126,14 +31222,15 @@ class XhsBrowserTransport {
|
|
|
31126
31222
|
};
|
|
31127
31223
|
try {
|
|
31128
31224
|
await act("creator page goto", () => page.goto(PUBLISH_URL, { timeout: this.loginTimeoutMs }));
|
|
31129
|
-
const gated = await
|
|
31225
|
+
const gated = await waitUntil(() => isXhsConsoleReady(page), this.loginTimeoutMs);
|
|
31130
31226
|
if (!gated) {
|
|
31131
31227
|
return fail2(`login timeout after ${this.loginTimeoutMs}ms: no authenticated ${CHANNEL} creator console in ${profileDir}`);
|
|
31132
31228
|
}
|
|
31229
|
+
const publishStatus = content.metadata?.publishStatus === "public" ? "public" : "draft";
|
|
31133
31230
|
const draftBaseline = publishStatus === "draft" ? await readDraftBaseline(page) : { count: null, stamps: [] };
|
|
31134
31231
|
return await runMode(input, {
|
|
31135
31232
|
page,
|
|
31136
|
-
content
|
|
31233
|
+
content,
|
|
31137
31234
|
publishStatus,
|
|
31138
31235
|
profileDir,
|
|
31139
31236
|
driver: this.driver,
|
|
@@ -31144,8 +31241,6 @@ class XhsBrowserTransport {
|
|
|
31144
31241
|
});
|
|
31145
31242
|
} catch (error51) {
|
|
31146
31243
|
return fail2(error51 instanceof Error ? error51.message : String(error51));
|
|
31147
|
-
} finally {
|
|
31148
|
-
await close();
|
|
31149
31244
|
}
|
|
31150
31245
|
}
|
|
31151
31246
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"name": "xhs-pub",
|
|
3
3
|
"kind": "publish",
|
|
4
4
|
"entry": "./dist/index.js",
|
|
5
|
+
"check": "./src/profile-check.ts",
|
|
5
6
|
"version": "1.0.0",
|
|
6
7
|
"description": "Publish plugin for Xiaohongshu (xhs): one creator console, four validated operations (上传图文 ≤18 images | 上传视频 | 写长文 | 发播客 audio+cover) saved as a draft via 暂存离开 or published live via 发布, over the Playwright script transport"
|
|
7
8
|
}
|
|
@@ -403,8 +403,14 @@ async function isPodcastModalOpen(page: XhsPage): Promise<boolean> {
|
|
|
403
403
|
|
|
404
404
|
/**
|
|
405
405
|
* Leave the 发播客 modal. The recorded advance control is `去发布`, which is publish-adjacent, so a
|
|
406
|
-
|
|
407
|
-
|
|
406
|
+
draft run never clicks it: it waits for the platform to move on by itself and fails loud naming the
|
|
407
|
+
control it refused to click. Only an explicit `publishStatus: 'public'` may click it.
|
|
408
|
+
*
|
|
409
|
+
* 0236 R3 (deferred, operator decision 2026-10-09): whether the modal offers a save path OTHER than
|
|
410
|
+
* `去发布` once both files are attached was never measured (`.spur/run/discovery/` holds only the
|
|
411
|
+
* modal open), and an attended walk under ADR-025 is what would measure it. So no draft-save selector
|
|
412
|
+
* is invented here: the run stops before the publish-adjacent control and says so, which is the
|
|
413
|
+
* failure the 0236 result carries (a `ok:false` entry that never fails the visit).
|
|
408
414
|
*/
|
|
409
415
|
async function leavePodcastModal(deps: ModeRunDeps): Promise<void> {
|
|
410
416
|
const closed = () => isPodcastModalOpen(deps.page).then((open) => !open);
|
|
@@ -420,7 +426,7 @@ async function leavePodcastModal(deps: ModeRunDeps): Promise<void> {
|
|
|
420
426
|
if (await waitUntil(closed, deps.stepTimeoutMs)) return;
|
|
421
427
|
}
|
|
422
428
|
throw new Error(
|
|
423
|
-
`podcast modal: the 发播客 modal was still open ${deps.stepTimeoutMs}ms after the audio and cover were attached; the only recorded advance control is '${PODCAST_MODAL_ADVANCE[0]}'
|
|
429
|
+
`podcast modal: the 发播客 modal was still open ${deps.stepTimeoutMs}ms after the audio and cover were attached; its post-attach save path has never been measured (task 0236 R3 — the attended walk is still pending) and the only recorded advance control is '${PODCAST_MODAL_ADVANCE[0]}', which a draft run does not click (it is publish-adjacent)`,
|
|
424
430
|
);
|
|
425
431
|
}
|
|
426
432
|
|