@gobing-ai/knowledge-kit 0.0.32 → 0.0.33

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.
Files changed (65) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/index.js +32320 -15021
  3. package/package.json +4 -4
  4. package/plugins/generations/content-gen/dist/index.js +10 -9
  5. package/plugins/generations/core-facts-gen/dist/index.js +13 -12
  6. package/plugins/generations/daily-article-gen/dist/index.js +14 -13
  7. package/plugins/generations/daily-article-gen/src/index.ts +1 -1
  8. package/plugins/generations/dailynews-gen/dist/index.js +10 -9
  9. package/plugins/generations/episode-plan-gen/dist/index.js +58 -15
  10. package/plugins/generations/episode-plan-gen/src/index.ts +76 -6
  11. package/plugins/generations/gemini-tts/README.md +11 -0
  12. package/plugins/generations/gemini-tts/dist/index.js +10 -9
  13. package/plugins/generations/image-gen/dist/index.js +154 -151
  14. package/plugins/generations/image-gen/src/compose/chromium.ts +60 -42
  15. package/plugins/generations/news-report-gen/dist/index.js +13 -12
  16. package/plugins/generations/voice-gen/dist/index.js +10 -9
  17. package/plugins/ingestions/aihot-ingest/dist/index.js +13 -12
  18. package/plugins/ingestions/horizon-ingest/dist/index.js +13 -12
  19. package/plugins/ingestions/karakeep-local/dist/index.js +13 -13
  20. package/plugins/ingestions/last30days-ingest/dist/index.js +13 -12
  21. package/plugins/ingestions/web-search/dist/index.js +11 -10
  22. package/plugins/kk/README.md +38 -0
  23. package/plugins/kk/decisions/extract/ai-news.yaml +21 -0
  24. package/plugins/kk/decisions/kk-items.yaml +113 -0
  25. package/plugins/kk/plugin.json +1 -1
  26. package/plugins/kk/schedules/cron.tmpl +15 -0
  27. package/plugins/kk/schedules/launchd.plist.tmpl +77 -0
  28. package/plugins/kk/scripts/daily-stages.ts +1533 -0
  29. package/plugins/kk/scripts/kk-workflow-stages.ts +247 -1365
  30. package/plugins/kk/skills/content-safety/SKILL.md +38 -7
  31. package/plugins/kk/skills/web-automation/SKILL.md +100 -0
  32. package/plugins/kk/skills/web-automation/references/browser-launch.md +86 -0
  33. package/plugins/kk/workflows/kk-collect.yaml +258 -0
  34. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +9 -3
  35. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +655 -0
  36. package/plugins/kk/workflows/kk-daily-safety.yaml +170 -0
  37. package/plugins/publishings/emdash-pub/dist/index.js +10 -9
  38. package/plugins/publishings/infoq-pub/dist/index.js +152 -59
  39. package/plugins/publishings/infoq-pub/plugin.json +1 -0
  40. package/plugins/publishings/infoq-pub/src/index.ts +167 -55
  41. package/plugins/publishings/infoq-pub/src/profile-check.ts +4 -2
  42. package/plugins/publishings/juejin-pub/dist/index.js +111 -30
  43. package/plugins/publishings/juejin-pub/plugin.json +1 -0
  44. package/plugins/publishings/juejin-pub/src/index.ts +49 -9
  45. package/plugins/publishings/medium-pub/dist/index.js +10 -9
  46. package/plugins/publishings/podcast-pub/dist/index.js +10 -9
  47. package/plugins/publishings/qiita-pub/dist/index.js +10 -9
  48. package/plugins/publishings/substack-pub/dist/index.js +105 -28
  49. package/plugins/publishings/substack-pub/plugin.json +1 -0
  50. package/plugins/publishings/substack-pub/src/index.ts +39 -8
  51. package/plugins/publishings/substack-pub/src/profile-check.ts +2 -0
  52. package/plugins/publishings/surfdash-pub/dist/index.js +10 -9
  53. package/plugins/publishings/wechatmp-pub/dist/index.js +115 -31
  54. package/plugins/publishings/wechatmp-pub/plugin.json +1 -0
  55. package/plugins/publishings/wechatmp-pub/src/index.ts +42 -9
  56. package/plugins/publishings/wechatmp-pub/src/profile-check.ts +84 -40
  57. package/plugins/publishings/x-pub/dist/index.js +114 -35
  58. package/plugins/publishings/x-pub/plugin.json +1 -0
  59. package/plugins/publishings/x-pub/src/index.ts +68 -14
  60. package/plugins/publishings/xhs-pub/dist/index.js +100 -54
  61. package/plugins/publishings/xhs-pub/plugin.json +1 -0
  62. package/plugins/publishings/xhs-pub/src/index.ts +8 -4
  63. package/plugins/publishings/xhs-pub/src/page.ts +4 -17
  64. package/plugins/publishings/xhs-pub/src/profile-check.ts +28 -15
  65. 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.15/node_modules/@gobing-ai/ts-utils/dist/errors.js
14674
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14685
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14708
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-utils/dist/env.js
14711
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14720
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-utils/dist/object.js
14723
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-utils/dist/output.js
14741
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-utils/dist/index.js
14759
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.18/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.15/node_modules/@gobing-ai/ts-runtime/dist/config.js
21757
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21835
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
21922
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/process-group.js
29133
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
29225
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
29597
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/index.js
29749
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/path.js
29753
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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.15/node_modules/@gobing-ai/ts-runtime/dist/index.js
29811
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.18+ea28d47622d670ff/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
- // ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/errors.js
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.18+1f5782d4d327a230/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.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/launcher.js
29976
+ // ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.18+1f5782d4d327a230/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.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/profile-dir.js
29979
+ // ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.18+1f5782d4d327a230/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.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/launcher.js
29991
+ // ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.18+1f5782d4d327a230/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.15+e28deaf1f2c5a2b1/node_modules/@gobing-ai/ts-browser-automation/dist/operations.js
30042
+ // ../../node_modules/.bun/@gobing-ai+ts-browser-automation@0.5.18+1f5782d4d327a230/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 fallback"} via profile ${opts.profileDir} (read-only)`
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((resolve2) => setTimeout(resolve2, pollMs));
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 resolve2 } from "path";
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 resolve2(explicit);
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 waitUntil2(() => onScreenCount().then((count2) => count2 >= CREATOR_TAB_COUNT), timeoutMs, pollMs);
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((resolve3) => setTimeout(resolve3, POLL_MS));
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((resolve3) => setTimeout(resolve3, POLL_MS));
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((resolve3) => setTimeout(resolve3, ms));
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 waitUntil2(() => deps.page.locator(selector).count().then((count2) => count2 >= expected), deps.stepTimeoutMs);
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,13 +30941,13 @@ 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 waitUntil2(closed, deps.stepTimeoutMs))
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 waitUntil2(closed, deps.stepTimeoutMs))
30950
+ if (await waitUntil(closed, deps.stepTimeoutMs))
30906
30951
  return;
30907
30952
  }
30908
30953
  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)`);
@@ -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 waitUntil2(async () => {
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;
@@ -31034,7 +31079,7 @@ async function createShadowProbe(context2, page) {
31034
31079
  async function isXhsConsoleReady(page) {
31035
31080
  return trySelectors(page, AUTH_GATE).then((r) => r.found);
31036
31081
  }
31037
- var defaultLauncher2 = openPersistentBrowser;
31082
+ var defaultLauncher2 = kkBrowserLauncher.launch;
31038
31083
  function makeDefaultOpenPage(launch = defaultLauncher2) {
31039
31084
  return async (profileDir) => {
31040
31085
  let context2;
@@ -31063,7 +31108,8 @@ async function checkXhsProfileSession(opts) {
31063
31108
  }, {
31064
31109
  profileDir: opts.profileDir,
31065
31110
  launcher: opts.launcher,
31066
- timeoutMs: opts.timeoutMs ?? PREFLIGHT_TIMEOUT_MS
31111
+ timeoutMs: opts.timeoutMs ?? PREFLIGHT_TIMEOUT_MS,
31112
+ headedOnly: opts.headedOnly === true
31067
31113
  });
31068
31114
  }
31069
31115
 
@@ -31126,7 +31172,7 @@ class XhsBrowserTransport {
31126
31172
  };
31127
31173
  try {
31128
31174
  await act("creator page goto", () => page.goto(PUBLISH_URL, { timeout: this.loginTimeoutMs }));
31129
- const gated = await waitUntil2(() => isXhsConsoleReady(page), this.loginTimeoutMs);
31175
+ const gated = await waitUntil(() => isXhsConsoleReady(page), this.loginTimeoutMs);
31130
31176
  if (!gated) {
31131
31177
  return fail2(`login timeout after ${this.loginTimeoutMs}ms: no authenticated ${CHANNEL} creator console in ${profileDir}`);
31132
31178
  }
@@ -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
  }
@@ -10,10 +10,11 @@ import {
10
10
  import {
11
11
  checkChannelSession,
12
12
  createShadowFinder,
13
+ kkBrowserLauncher,
13
14
  resolveChannelProfileDir,
14
15
  type ShadowCdpSession,
15
16
  } from '@gobing-ai/publish-harness/browser';
16
- import { type BrowserLauncher, openPersistentBrowser } from '@gobing-ai/ts-browser-automation';
17
+ import type { BrowserLauncher } from '@gobing-ai/ts-browser-automation';
17
18
  import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
18
19
  import { echoError } from '@gobing-ai/ts-utils';
19
20
  import { type AutomationGuard, createAutomationGuard } from './automation-guard';
@@ -93,15 +94,15 @@ export async function isXhsConsoleReady(page: XhsPage): Promise<boolean> {
93
94
  }
94
95
 
95
96
  /**
96
- * Production launcher: the upstream typed persistent-context opener (0177). Named so the
97
+ * Production launcher: the harness kk launcher (0214; full-Chromium channel + stealth). Named so the
97
98
  * migration is provable offline — tests assert this binding, never launch a real browser.
98
99
  */
99
- export const defaultLauncher = openPersistentBrowser;
100
+ export const defaultLauncher = kkBrowserLauncher.launch;
100
101
 
101
102
  /**
102
103
  * Build the default page opener. The launcher is injected so the seam (page wrapping, shadow
103
104
  * probe wiring, context close, launch-failure message) is testable without launching a browser;
104
- * production uses the upstream typed `openPersistentBrowser` (@gobing-ai/ts-browser-automation).
105
+ * production uses the harness `kkBrowserLauncher` (@gobing-ai/publish-harness/browser, 0214).
105
106
  */
106
107
  export function makeDefaultOpenPage(
107
108
  launch: (options: { profileDir: string; headless: boolean }) => Promise<unknown> = defaultLauncher,
@@ -147,6 +148,8 @@ export interface SessionCheckOptions {
147
148
  profileDir: string;
148
149
  timeoutMs?: number;
149
150
  launcher?: BrowserLauncher;
151
+ /** 0216: skip the headless pass and probe headed only (manual `check --headed`). */
152
+ headedOnly?: boolean;
150
153
  }
151
154
 
152
155
  /**
@@ -173,6 +176,7 @@ export async function checkXhsProfileSession(opts: SessionCheckOptions): Promise
173
176
  profileDir: opts.profileDir,
174
177
  launcher: opts.launcher,
175
178
  timeoutMs: opts.timeoutMs ?? PREFLIGHT_TIMEOUT_MS,
179
+ headedOnly: opts.headedOnly === true,
176
180
  },
177
181
  );
178
182
  }
@@ -1,4 +1,5 @@
1
1
  import { trySelectors } from '@gobing-ai/publish-harness';
2
+ import { waitUntil } from '@gobing-ai/publish-harness/browser';
2
3
 
3
4
  /**
4
5
  * The console's page seam (ADR-029) and the helpers every xhs operation shares.
@@ -10,6 +11,9 @@ import { trySelectors } from '@gobing-ai/publish-harness';
10
11
  */
11
12
 
12
13
  export const POLL_MS = 500;
14
+
15
+ // The shared poll (0184 R2); re-exported so flow/index keep importing it from here.
16
+ export { waitUntil };
13
17
  export const STEP_TIMEOUT_MS = 30_000;
14
18
 
15
19
  /** The console's mode tab bar; it mounts late and renders every tab several times over. */
@@ -120,23 +124,6 @@ export async function clickCreatorTab(
120
124
  }
121
125
  }
122
126
 
123
- /**
124
- * Poll `predicate` every `pollMs` until the deadline, then evaluate it one final time. The trailing
125
- * call matters: the condition often becomes true exactly at the boundary.
126
- */
127
- export async function waitUntil(
128
- predicate: () => Promise<boolean>,
129
- timeoutMs: number,
130
- pollMs = POLL_MS,
131
- ): Promise<boolean> {
132
- const deadline = Date.now() + timeoutMs;
133
- while (Date.now() < deadline) {
134
- if (await predicate()) return true;
135
- await new Promise((resolve) => setTimeout(resolve, pollMs));
136
- }
137
- return predicate();
138
- }
139
-
140
127
  export function wrapPlaywrightPage(raw: unknown, shadow?: ShadowProbe): XhsPage {
141
128
  const pw = raw as {
142
129
  goto(url: string, options?: Record<string, unknown>): Promise<unknown>;
@@ -6,7 +6,8 @@
6
6
  * - `login` recovers an absent/expired session headed via the upstream
7
7
  * `loginWithProfile`, polled with the same XHS-owned readiness gate the publish
8
8
  * flow uses;
9
- * - `check` reopens the existing profile headless and reports the session as
9
+ * - `check` reopens the existing profile (headless by default; `--headed` skips the headless
10
+ * pass and probes headed only, 0216) and reports the session as
10
11
  * `verified` or `refused` — a site refusal, expired session, or missing profile
11
12
  * is an explicit failed readiness, never a success. The upstream launcher never
12
13
  * creates a profile headless, so a check cannot fabricate a session from a
@@ -21,7 +22,7 @@
21
22
  * the full recipe lives in docs/design/xhs-image-cards.md.
22
23
  */
23
24
  import { parseArgs } from 'node:util';
24
- import { resolveChannelProfileDir } from '@gobing-ai/publish-harness/browser';
25
+ import { kkBrowserLauncher, resolveChannelProfileDir } from '@gobing-ai/publish-harness/browser';
25
26
  import { type BrowserLauncher, loginWithProfile } from '@gobing-ai/ts-browser-automation';
26
27
  import { echo, echoError } from '@gobing-ai/ts-utils';
27
28
  import { type AutomationGuard, createAutomationGuard } from './automation-guard';
@@ -60,11 +61,20 @@ export async function loginProfile(opts: ProfileLoginOptions): Promise<void> {
60
61
  url: PUBLISH_URL,
61
62
  isAuthenticated: (page) => isXhsConsoleReady(wrapPlaywrightPage(page)),
62
63
  timeoutMs: opts.timeoutMs ?? LOGIN_TIMEOUT_MS,
63
- launcher: opts.launcher,
64
+ launcher: opts.launcher ?? kkBrowserLauncher,
64
65
  });
65
66
  }
66
67
 
67
- /** `login|check [--profile <dir>] [--timeout <ms>]`; profile precedence matches the publish flow. */
68
+ /** Exit 2 with the human usage on stderr and the ADR-032 `error` verdict as the last stdout line. */
69
+ function usageError(message: string): number {
70
+ echoError(
71
+ `profile-check: ${message} — usage: profile-check.ts [login|check] [--profile <dir>] [--timeout <ms>] [--headed]`,
72
+ );
73
+ echo(JSON.stringify({ status: 'error', detail: message }));
74
+ return 2;
75
+ }
76
+
77
+ /** `login|check [--profile <dir>] [--timeout <ms>] [--headed]`; profile precedence matches the publish flow. */
68
78
  export async function main(
69
79
  argv: string[] = process.argv.slice(2),
70
80
  launcher?: BrowserLauncher,
@@ -73,13 +83,19 @@ export async function main(
73
83
  let command: string;
74
84
  let profileDir: string;
75
85
  let timeoutMs: number | undefined;
86
+ let headed = false;
76
87
  try {
77
88
  const { positionals, values } = parseArgs({
78
89
  args: argv,
79
90
  allowPositionals: true,
80
- options: { profile: { type: 'string' }, timeout: { type: 'string' } },
91
+ options: {
92
+ profile: { type: 'string' },
93
+ timeout: { type: 'string' },
94
+ headed: { type: 'boolean' },
95
+ },
81
96
  });
82
97
  command = positionals[0] ?? 'check';
98
+ headed = values.headed === true;
83
99
  if (values.timeout !== undefined) {
84
100
  timeoutMs = Number(values.timeout);
85
101
  if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
@@ -88,10 +104,7 @@ export async function main(
88
104
  }
89
105
  profileDir = resolveChannelProfileDir(CHANNEL, values.profile);
90
106
  } catch (error) {
91
- echoError(
92
- `profile-check: ${error instanceof Error ? error.message : String(error)} — usage: profile-check.ts [login|check] [--profile <dir>] [--timeout <ms>]`,
93
- );
94
- return 1;
107
+ return usageError(error instanceof Error ? error.message : String(error));
95
108
  }
96
109
  if (command === 'login') {
97
110
  try {
@@ -104,21 +117,21 @@ export async function main(
104
117
  }
105
118
  }
106
119
  if (command !== 'check') {
107
- echoError(
108
- `profile-check: unknown command '${command}' — usage: profile-check.ts [login|check] [--profile <dir>] [--timeout <ms>]`,
109
- );
110
- return 1;
120
+ return usageError(`unknown command '${command}'`);
111
121
  }
112
122
  // ADR-025: a manual session check is a real console visit, so it draws on the shared 2h
113
123
  // automation budget rather than a separate one. `login` is deliberately uncapped above: it is
114
124
  // operator-interactive, so capping it would only block recovery from an expired session.
115
125
  const cap = guard.allow('check');
116
126
  if (!cap.allowed) {
117
- echoError(`profile-check: ${cap.reason}`);
127
+ // The budget refusal is still a readiness verdict (0216 contract): emit the JSON line so
128
+ // `kk plugin check` reports a refusal with the operator hint instead of a malformed probe.
129
+ echo(JSON.stringify({ status: 'refused', profileDir, detail: `xhs readiness refused: ${cap.reason}` }));
130
+ echoError(cap.reason);
118
131
  return 1;
119
132
  }
120
133
  guard.record('check');
121
- const outcome = await checkXhsProfileSession({ profileDir, timeoutMs, launcher });
134
+ const outcome = await checkXhsProfileSession({ profileDir, timeoutMs, launcher, headedOnly: headed });
122
135
  echo(JSON.stringify(outcome));
123
136
  if (outcome.status !== 'verified') {
124
137
  echoError(outcome.detail);