@gobing-ai/knowledge-kit 0.0.37 → 0.1.0

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 (157) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.css +654 -16
  3. package/dist/board/collect/index.js +191 -64
  4. package/dist/board/daily-voice/index.css +679 -12
  5. package/dist/board/daily-voice/index.js +185 -66
  6. package/dist/board/itc/index.css +679 -13
  7. package/dist/board/itc/index.js +188 -66
  8. package/dist/index.js +2864 -139
  9. package/package.json +3 -2
  10. package/plugins/generations/content-gen/dist/index.js +158 -12
  11. package/plugins/generations/core-facts-gen/dist/index.js +158 -12
  12. package/plugins/generations/daily-article-gen/dist/index.js +158 -12
  13. package/plugins/generations/dailynews-gen/dist/index.js +158 -12
  14. package/plugins/generations/episode-plan-gen/dist/index.js +158 -12
  15. package/plugins/generations/gemini-tts/README.md +4 -3
  16. package/plugins/generations/gemini-tts/dist/index.js +158 -12
  17. package/plugins/generations/image-gen/dist/index.js +158 -12
  18. package/plugins/generations/news-report-gen/dist/index.js +158 -12
  19. package/plugins/generations/omni-voice-gen/README.md +15 -4
  20. package/plugins/generations/omni-voice-gen/profiles.json +1 -2
  21. package/plugins/generations/voice-gen/dist/index.js +158 -12
  22. package/plugins/generations/voxcpm-tts/README.md +21 -1
  23. package/plugins/generations/voxcpm-tts/dist/index.js +173 -13
  24. package/plugins/generations/voxcpm-tts/scripts/audit-speaker-consistency.py +109 -0
  25. package/plugins/generations/voxcpm-tts/src/voxcpm-client.ts +28 -1
  26. package/plugins/ingestions/aihot-ingest/dist/index.js +158 -12
  27. package/plugins/ingestions/horizon-ingest/dist/index.js +158 -12
  28. package/plugins/ingestions/last30days-ingest/dist/index.js +158 -12
  29. package/plugins/ingestions/web-search/dist/index.js +158 -12
  30. package/plugins/kk/commands/workflow-run.md +56 -18
  31. package/plugins/kk/decisions/kk-itc.yaml +46 -0
  32. package/plugins/kk/decisions/kk-seo-keyword.yaml +57 -0
  33. package/plugins/kk/decisions/kk-seo-page.yaml +230 -0
  34. package/plugins/kk/decisions/kk-seo-pair.yaml +21 -0
  35. package/plugins/kk/decisions/kk-seo-site.yaml +90 -0
  36. package/plugins/kk/plugin.json +1 -1
  37. package/plugins/kk/publish-presets/chinese-article.json +14 -0
  38. package/plugins/kk/publish-presets/technical-article.json +14 -0
  39. package/plugins/kk/references/smart-seo-attribution.md +85 -0
  40. package/plugins/kk/scripts/daily-stages.ts +42 -2
  41. package/plugins/kk/scripts/itc-stages.ts +25 -118
  42. package/plugins/kk/scripts/kk-workflow-stages.ts +38 -1
  43. package/plugins/kk/scripts/seo-decisions.ts +477 -0
  44. package/plugins/kk/scripts/seo-enrich.ts +728 -0
  45. package/plugins/kk/scripts/seo-facts.ts +610 -0
  46. package/plugins/kk/scripts/seo-fetch.ts +766 -0
  47. package/plugins/kk/scripts/seo-report.ts +460 -0
  48. package/plugins/kk/scripts/seo-rules.ts +1882 -0
  49. package/plugins/kk/scripts/seo-stages.ts +1044 -0
  50. package/plugins/kk/skills/article-adapt/SKILL.md +2 -2
  51. package/plugins/kk/skills/smart-seo/SKILL.md +183 -0
  52. package/plugins/kk/skills/smart-seo/references/method.md +126 -0
  53. package/plugins/kk/skills/smart-seo/references/narrative.md +125 -0
  54. package/plugins/kk/skills/smart-seo/references/operations.md +202 -0
  55. package/plugins/kk/skills/smart-seo/references/seo-geo-sources.md +90 -0
  56. package/plugins/kk/workflows/feature-verification.yaml +28 -24
  57. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +85 -17
  58. package/plugins/kk/workflows/kk-itc-author.yaml +406 -0
  59. package/plugins/kk/workflows/kk-itc.yaml +208 -682
  60. package/plugins/kk/workflows/kk-smart-seo.yaml +251 -0
  61. package/plugins/publishings/bilibili-pub/dist/index.js +21782 -0
  62. package/plugins/publishings/bilibili-pub/dist/profile-check.js +20410 -0
  63. package/plugins/publishings/bilibili-pub/package.json +20 -0
  64. package/plugins/publishings/bilibili-pub/plugin.json +12 -0
  65. package/plugins/publishings/bilibili-pub/src/flow.ts +1296 -0
  66. package/plugins/publishings/bilibili-pub/src/index.ts +139 -0
  67. package/plugins/publishings/bilibili-pub/src/profile-check.ts +19 -0
  68. package/plugins/publishings/bilibili-pub/src/selectors.ts +222 -0
  69. package/plugins/publishings/bilibili-pub/tsconfig.json +4 -0
  70. package/plugins/publishings/cnblogs-pub/dist/index.js +21383 -0
  71. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +20412 -0
  72. package/plugins/publishings/cnblogs-pub/package.json +19 -0
  73. package/plugins/publishings/cnblogs-pub/plugin.json +12 -0
  74. package/plugins/publishings/cnblogs-pub/src/flow.ts +708 -0
  75. package/plugins/publishings/cnblogs-pub/src/index.ts +172 -0
  76. package/plugins/publishings/cnblogs-pub/src/profile-check.ts +24 -0
  77. package/plugins/publishings/cnblogs-pub/src/selectors.ts +104 -0
  78. package/plugins/publishings/cnblogs-pub/tsconfig.json +4 -0
  79. package/plugins/publishings/csdn-pub/dist/index.js +21552 -0
  80. package/plugins/publishings/csdn-pub/dist/profile-check.js +20963 -0
  81. package/plugins/publishings/csdn-pub/package.json +19 -0
  82. package/plugins/publishings/csdn-pub/plugin.json +12 -0
  83. package/plugins/publishings/csdn-pub/src/index.ts +1178 -0
  84. package/plugins/publishings/csdn-pub/src/profile-check.ts +23 -0
  85. package/plugins/publishings/csdn-pub/tsconfig.json +4 -0
  86. package/plugins/publishings/emdash-pub/dist/index.js +158 -12
  87. package/plugins/publishings/emdash-pub/plugin.json +22 -1
  88. package/plugins/publishings/infoq-pub/dist/index.js +160 -12
  89. package/plugins/publishings/infoq-pub/dist/profile-check.js +160 -12
  90. package/plugins/publishings/infoq-pub/plugin.json +21 -0
  91. package/plugins/publishings/jike-pub/dist/index.js +21379 -0
  92. package/plugins/publishings/jike-pub/dist/profile-check.js +20951 -0
  93. package/plugins/publishings/jike-pub/package.json +19 -0
  94. package/plugins/publishings/jike-pub/plugin.json +12 -0
  95. package/plugins/publishings/jike-pub/src/index.ts +800 -0
  96. package/plugins/publishings/jike-pub/src/profile-check.ts +21 -0
  97. package/plugins/publishings/jike-pub/tsconfig.json +4 -0
  98. package/plugins/publishings/juejin-pub/dist/index.js +160 -12
  99. package/plugins/publishings/juejin-pub/dist/profile-check.js +160 -12
  100. package/plugins/publishings/juejin-pub/plugin.json +21 -0
  101. package/plugins/publishings/medium-pub/dist/index.js +158 -12
  102. package/plugins/publishings/medium-pub/plugin.json +22 -1
  103. package/plugins/publishings/podcast-pub/dist/index.js +158 -12
  104. package/plugins/publishings/qiita-pub/dist/index.js +158 -12
  105. package/plugins/publishings/qiita-pub/plugin.json +24 -1
  106. package/plugins/publishings/shipinhao-pub/dist/index.js +21715 -0
  107. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +20431 -0
  108. package/plugins/publishings/shipinhao-pub/package.json +19 -0
  109. package/plugins/publishings/shipinhao-pub/plugin.json +12 -0
  110. package/plugins/publishings/shipinhao-pub/src/flow.ts +1323 -0
  111. package/plugins/publishings/shipinhao-pub/src/index.ts +173 -0
  112. package/plugins/publishings/shipinhao-pub/src/profile-check.ts +25 -0
  113. package/plugins/publishings/shipinhao-pub/src/selectors.ts +241 -0
  114. package/plugins/publishings/shipinhao-pub/tsconfig.json +4 -0
  115. package/plugins/publishings/sspai-pub/dist/index.js +21371 -0
  116. package/plugins/publishings/sspai-pub/dist/profile-check.js +20414 -0
  117. package/plugins/publishings/sspai-pub/package.json +19 -0
  118. package/plugins/publishings/sspai-pub/plugin.json +12 -0
  119. package/plugins/publishings/sspai-pub/src/flow.ts +624 -0
  120. package/plugins/publishings/sspai-pub/src/index.ts +167 -0
  121. package/plugins/publishings/sspai-pub/src/profile-check.ts +25 -0
  122. package/plugins/publishings/sspai-pub/src/selectors.ts +122 -0
  123. package/plugins/publishings/sspai-pub/tsconfig.json +4 -0
  124. package/plugins/publishings/substack-pub/dist/index.js +160 -12
  125. package/plugins/publishings/substack-pub/dist/profile-check.js +160 -12
  126. package/plugins/publishings/substack-pub/plugin.json +21 -0
  127. package/plugins/publishings/surfdash-pub/dist/index.js +158 -12
  128. package/plugins/publishings/surfdash-pub/plugin.json +22 -1
  129. package/plugins/publishings/threads-pub/dist/index.js +158 -12
  130. package/plugins/publishings/wechatmp-pub/dist/index.js +1669 -502
  131. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +204 -21
  132. package/plugins/publishings/wechatmp-pub/plugin.json +32 -0
  133. package/plugins/publishings/wechatmp-pub/src/cgi-draft.ts +540 -0
  134. package/plugins/publishings/wechatmp-pub/src/cgi-media.ts +380 -0
  135. package/plugins/publishings/wechatmp-pub/src/cgi.ts +485 -0
  136. package/plugins/publishings/wechatmp-pub/src/flow.ts +104 -0
  137. package/plugins/publishings/weibo-pub/dist/index.js +160 -12
  138. package/plugins/publishings/weibo-pub/dist/profile-check.js +160 -12
  139. package/plugins/publishings/weibo-pub/plugin.json +35 -0
  140. package/plugins/publishings/x-pub/dist/index.js +160 -12
  141. package/plugins/publishings/x-pub/dist/profile-check.js +160 -12
  142. package/plugins/publishings/x-pub/plugin.json +40 -0
  143. package/plugins/publishings/xhs-pub/dist/index.js +160 -12
  144. package/plugins/publishings/xhs-pub/dist/profile-check.js +160 -12
  145. package/plugins/publishings/xhs-pub/plugin.json +37 -0
  146. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +21491 -0
  147. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +20417 -0
  148. package/plugins/publishings/xiaoyuzhou-pub/package.json +19 -0
  149. package/plugins/publishings/xiaoyuzhou-pub/plugin.json +12 -0
  150. package/plugins/publishings/xiaoyuzhou-pub/src/flow.ts +826 -0
  151. package/plugins/publishings/xiaoyuzhou-pub/src/index.ts +174 -0
  152. package/plugins/publishings/xiaoyuzhou-pub/src/profile-check.ts +26 -0
  153. package/plugins/publishings/xiaoyuzhou-pub/src/selectors.ts +125 -0
  154. package/plugins/publishings/xiaoyuzhou-pub/tsconfig.json +4 -0
  155. package/plugins/publishings/zenn-pub/dist/index.js +158 -12
  156. package/plugins/publishings/zenn-pub/plugin.json +22 -1
  157. package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * bilibili-pub — Bilibili video uploads and dynamic posts publish plugin surface (task 0297).
3
+ *
4
+ * Implements the manifest-facing plugin surface:
5
+ * - Declared operations list (`BILIBILI_OPERATIONS`: `['shortVideo', 'dynamic']`)
6
+ * - Operation and driver resolution BEFORE browser or profile initialization
7
+ * - `getPublishTransport`: returns FakeBrowserTransport under KNOWLEDGE_KIT_PUBLISH_TRANSPORT=fake
8
+ * or BilibiliBrowserTransport
9
+ * - `--in/--out` IO contract in `processPublishIO`
10
+ * - CLI `main` entrypoint
11
+ */
12
+
13
+ import { dirname } from 'node:path';
14
+ import { parseArgs } from 'node:util';
15
+ import { type Content, ContentSchema, type Result, ResultSchema } from '@gobing-ai/kk-core';
16
+ import { type BrowserDriver, FakeBrowserTransport, type PublishTransport } from '@gobing-ai/publish-harness';
17
+ import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
18
+ import { echoError } from '@gobing-ai/ts-utils';
19
+ import { BilibiliBrowserTransport, bounded, boundedDetail, errorMessage, fail } from './flow';
20
+ import { BILI_TARGET, BILIBILI_OPERATIONS, type BilibiliOperation } from './selectors';
21
+
22
+ export { BILIBILI_OPERATIONS, type BilibiliOperation };
23
+
24
+ /**
25
+ * Resolves the requested operation (`options.operation ?? content.metadata.operation ?? 'shortVideo'`)
26
+ * BEFORE any browser or profile is created. Undeclared values are refused by name.
27
+ */
28
+ export function resolveOperation(
29
+ options?: Record<string, unknown>,
30
+ content?: Content,
31
+ ): { operation: BilibiliOperation; error?: undefined } | { operation?: undefined; error: string } {
32
+ const raw = options?.operation ?? content?.metadata?.operation ?? 'shortVideo';
33
+ if (typeof raw !== 'string' || !(BILIBILI_OPERATIONS as readonly string[]).includes(raw)) {
34
+ return {
35
+ error: `unsupported operation '${bounded(raw)}' for ${BILI_TARGET}: expected one of ${BILIBILI_OPERATIONS.join(', ')}`,
36
+ };
37
+ }
38
+ return { operation: raw as BilibiliOperation };
39
+ }
40
+
41
+ /**
42
+ * Resolves the driver from `options.driver ?? 'script'`. Only 'script' is supported.
43
+ */
44
+ function resolveDriver(
45
+ options?: Record<string, unknown>,
46
+ ): { driver: BrowserDriver; error?: undefined } | { driver?: undefined; error: string } {
47
+ const raw = options?.driver ?? 'script';
48
+ if (raw !== 'script') {
49
+ return {
50
+ error: `unsupported driver '${bounded(raw)}' for ${BILI_TARGET}: expected one of script (the bilibili-pub browser flow has no agent path)`,
51
+ };
52
+ }
53
+ return { driver: 'script' };
54
+ }
55
+
56
+ /**
57
+ * Resolves the channel transport: FakeBrowserTransport when KNOWLEDGE_KIT_PUBLISH_TRANSPORT=fake,
58
+ * or BilibiliBrowserTransport.
59
+ */
60
+ export function getPublishTransport(
61
+ operation: BilibiliOperation = 'shortVideo',
62
+ driver: BrowserDriver = 'script',
63
+ ): PublishTransport {
64
+ if (process.env.KNOWLEDGE_KIT_PUBLISH_TRANSPORT === 'fake') return new FakeBrowserTransport('script');
65
+ return new BilibiliBrowserTransport(operation, driver);
66
+ }
67
+
68
+ /** Writes the Result to `--out`, creating parent directories if needed. */
69
+ async function writePublishResult(outputPath: string, result: Result): Promise<void> {
70
+ const outDir = dirname(outputPath);
71
+ if (outDir && outDir !== '.') await createNodeFileSystem().ensureDir(outDir);
72
+ await createNodeFileSystem().writeFile(outputPath, JSON.stringify(result, null, 2));
73
+ }
74
+
75
+ /**
76
+ * Process `--in/--out` publish IO contract.
77
+ */
78
+ export async function processPublishIO(
79
+ inputPath: string,
80
+ outputPath: string,
81
+ transport?: PublishTransport,
82
+ ): Promise<void> {
83
+ await createNodeFileSystem().deleteFile(outputPath);
84
+ let content: Content;
85
+ let options: Record<string, unknown> | undefined;
86
+ try {
87
+ const raw = JSON.parse(await createNodeFileSystem().readFile(inputPath)) as Record<string, unknown>;
88
+ content = ContentSchema.parse(raw);
89
+ if (raw.options !== undefined && typeof raw.options === 'object' && raw.options !== null) {
90
+ options = raw.options as Record<string, unknown>;
91
+ }
92
+ } catch (error: unknown) {
93
+ const message = boundedDetail(`Invalid Content input: ${errorMessage(error)}`);
94
+ await writePublishResult(outputPath, fail(BILI_TARGET, message));
95
+ throw new Error(message);
96
+ }
97
+ const mode = resolveOperation(options, content);
98
+ if (mode.error !== undefined) {
99
+ await writePublishResult(outputPath, fail(BILI_TARGET, mode.error));
100
+ throw new Error(mode.error);
101
+ }
102
+ const driverMode = resolveDriver(options);
103
+ if (driverMode.error !== undefined) {
104
+ await writePublishResult(outputPath, fail(BILI_TARGET, driverMode.error));
105
+ throw new Error(driverMode.error);
106
+ }
107
+ const resolved = transport ?? getPublishTransport(mode.operation, driverMode.driver);
108
+ const result = ResultSchema.parse(await resolved.publish({ content, options }));
109
+ await writePublishResult(outputPath, result);
110
+ if (!result.ok) throw new Error(result.error ?? 'Publish failed');
111
+ }
112
+
113
+ /**
114
+ * CLI entry point: `--in <content.json> --out <result.json>`.
115
+ */
116
+ export async function main(): Promise<number> {
117
+ let values: { in?: string; out?: string };
118
+ try {
119
+ ({ values } = parseArgs({ options: { in: { type: 'string' }, out: { type: 'string' } } }));
120
+ } catch (error: unknown) {
121
+ echoError(`Invalid arguments: ${errorMessage(error)}`);
122
+ return 1;
123
+ }
124
+ if (!values.in || !values.out) {
125
+ echoError('Missing required arguments: --in and --out');
126
+ return 1;
127
+ }
128
+ try {
129
+ await processPublishIO(values.in, values.out);
130
+ return 0;
131
+ } catch (error: unknown) {
132
+ echoError(`bilibili-pub failed: ${boundedDetail(errorMessage(error))}`);
133
+ return 1;
134
+ }
135
+ }
136
+
137
+ if (import.meta.main) {
138
+ process.exit(await main());
139
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * bilibili profile-session capability (task 0297, Chunk A).
3
+ *
4
+ * `login` opens the Bilibili member creator home (https://member.bilibili.com/,
5
+ * see `src/selectors.ts`) in a headed browser and resolves only once the site readiness predicate
6
+ * passes; `check` reopens the profile (headlessly, unless the operator passes `--headed` or the
7
+ * config sets `checkHeadedOnly`) and reports `verified` or `refused`; `open` just brings the
8
+ * window up. Arg parsing, profile precedence (`--profile` > `BILIBILI_PROFILE_DIR` >
9
+ * `KK_BROWSER_PROFILE_DIR` > `~/.config/kk/browser-profiles/bilibili`) and exit codes
10
+ * (0 verified, 1 refused, 2 usage error) all live in the shared `runProfileCli`, so the publish
11
+ * preflight and this CLI apply one gate.
12
+ */
13
+
14
+ import { runProfileCli } from '@gobing-ai/publish-harness/browser';
15
+ import { BILI_CONFIG } from './selectors';
16
+
17
+ if (import.meta.main) {
18
+ process.exit(await runProfileCli(BILI_CONFIG, Bun.argv.slice(2)));
19
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * bilibili-pub selectors — selector/URL/regex/timeout constants for Bilibili video uploads and
3
+ * dynamic posts (task 0297).
4
+ *
5
+ * Grounding: the recovered Bilibili platform adapters
6
+ * `.spur/run/yijianfa-source/platforms/bilibili.js` (810 lines) and
7
+ * `.spur/run/yijianfa-source/platforms/bilibili-main.js` (86 lines) pin the creator mechanics cited
8
+ * below:
9
+ * - Dynamic post composer at t.bilibili.com (bilibili.js:158-287, bilibili-main.js:33-86):
10
+ * the contenteditable editor (bilibili.js:171, 209), image cleanup (bilibili.js:174-184),
11
+ * image upload entry and preview readback (bilibili.js:231, 238, 257; the vendor injects the file
12
+ * through a MAIN-world message bridge, bilibili-main.js:54-81 — deliberately NOT reproduced),
13
+ * and the launch button (bilibili.js:273).
14
+ * - Video cover dialog (bilibili.js:290-360):
15
+ * div.cover-main entry (bilibili.js:292-293), file input in modal (bilibili.js:312), modal
16
+ * " 完成 " submit button (bilibili.js:342-346), and preview change detection (bilibili.js:296-301,
17
+ * 348-356).
18
+ * - Short video upload at member.bilibili.com (bilibili.js:365-556):
19
+ * video file input (bilibili.js:397, 401) and its file injection (bilibili.js:417), title input up
20
+ * to 80 chars (bilibili.js:431), description editor (bilibili.js:454), tags input and readback
21
+ * (bilibili.js:485, 488, 501-507), "自制" radio button (bilibili.js:520), and submit button
22
+ * (bilibili.js:548).
23
+ * The partition/category chooser has NO source control: the adapter only ever writes the API field
24
+ * `form.append("category", "daily")` (bilibili.js:571), and no partition/dropdown selector appears
25
+ * anywhere in the recovered file — the partition selectors below are therefore Provisional and fail
26
+ * closed.
27
+ * Anything the evidence does not pin is Provisional and fails closed: a missing control is a loud
28
+ * typed refusal, never a silent no-op or broad fallback.
29
+ */
30
+
31
+ import type { ChannelBrowserConfig } from '@gobing-ai/publish-harness/browser';
32
+ import { waitUntil } from '@gobing-ai/publish-harness/browser';
33
+ import type { IsAuthenticatedPredicate } from '@gobing-ai/ts-browser-automation';
34
+
35
+ /** Site-owned target slug; also the profile channel key (`BILIBILI_PROFILE_DIR`). */
36
+ export const BILI_TARGET = 'bilibili';
37
+
38
+ /**
39
+ * Fixed member home entry (task 0297 Design freeze): Bilibili Creator Studio.
40
+ * Grounded entry for shortVideo operation.
41
+ */
42
+ export const BILI_MEMBER_HOME_URL = 'https://member.bilibili.com/';
43
+
44
+ /**
45
+ * Fixed dynamic home entry (task 0297 Design freeze): Bilibili Dynamic Feed.
46
+ * Grounded entry for dynamic operation.
47
+ */
48
+ export const BILI_DYNAMIC_HOME_URL = 'https://t.bilibili.com/';
49
+
50
+ /**
51
+ * Login/passport redirect marker.
52
+ * Matches passport login or account authentication gateways.
53
+ */
54
+ export const BILI_LOGIN_URL_RE = /(passport\.bilibili\.com\/login|account\.bilibili\.com\/login|\/(signin|login))/;
55
+
56
+ /**
57
+ * Supported operations for bilibili-pub (task 0297 Design freeze).
58
+ * 'shortVideo' publishes to member.bilibili.com; 'dynamic' publishes to t.bilibili.com.
59
+ */
60
+ export const BILIBILI_OPERATIONS = ['shortVideo', 'dynamic'] as const;
61
+ export type BilibiliOperation = (typeof BILIBILI_OPERATIONS)[number];
62
+
63
+ /**
64
+ * Video upload form selectors (bilibili.js:290-360, 365-556).
65
+ */
66
+ export const BILI_VIDEO_SELECTORS = {
67
+ /** Video file input candidates (bilibili.js:397, 401). */
68
+ videoInput: [
69
+ 'input[type="file"][accept*="video"]',
70
+ 'input[type="file"][accept*=".mp4"]',
71
+ 'input[type="file"][accept*=".mov"]',
72
+ 'input[type="file"]',
73
+ ],
74
+ /** Video title input candidates: max 80 chars (bilibili.js:431). */
75
+ titleInput: ['input[maxlength="80"][type="text"]', 'input[placeholder*="标题"]'],
76
+ /** Video description editor (bilibili.js:454). */
77
+ descriptionEditor: ['div.ql-editor[contenteditable="true"]', '[contenteditable="true"].ql-editor'],
78
+ /**
79
+ * Partition / category selection candidates — **Provisional**: the recovered adapter has no
80
+ * partition UI control at all; it only writes the API field `form.append("category", "daily")`
81
+ * (bilibili.js:571). These candidates are probed read-only and a missing chooser is a typed
82
+ * refusal, never a silently skipped partition.
83
+ */
84
+ partitionCandidates: [
85
+ 'div.select-item',
86
+ 'div.category-item',
87
+ 'div.drop-cascader-item',
88
+ 'div.video-type-item',
89
+ 'div.select-controller',
90
+ 'div.radio-item',
91
+ ],
92
+ /** Tag input candidates (bilibili.js:485). */
93
+ tagInput: ['input[placeholder="按回车键Enter创建标签"]', 'input[placeholder*="创建标签"]'],
94
+ /** Tag elements container / labels for cleanup and readback (bilibili.js:479, 503). */
95
+ tagItems: 'div.tag-pre-wrp > div.label-item-v2-container',
96
+ /** "自制" (original) radio button (bilibili.js:520). */
97
+ originalRadio: 'span.check-radio-v2-name',
98
+ /** Cover main container (bilibili.js:292-293). */
99
+ coverMain: 'div.cover-main',
100
+ coverSlot: '.cover-slot',
101
+ coverImg: '.cover-img',
102
+ coverEntries: ['更换封面', '封面设置', '添加封面'],
103
+ /** Cover upload file input inside modal (bilibili.js:312). */
104
+ coverFileInput: [
105
+ "div.bcc-upload-wrapper > input[type='file'][accept='image/png, image/jpeg']",
106
+ "div.bcc-upload-wrapper input[type='file']",
107
+ "input[type='file'][accept*='image']",
108
+ ],
109
+ /** Cover modal completion submit button (bilibili.js:342-346). */
110
+ coverSubmit: 'div.submit',
111
+ /** Public video submission button (bilibili.js:548). */
112
+ submitAdd: [
113
+ 'span.submit-add',
114
+ 'button.submit-add',
115
+ 'span:text-is("立即投稿")',
116
+ 'button:text-is("立即投稿")',
117
+ 'span:text-is("投稿")',
118
+ 'button:text-is("投稿")',
119
+ ],
120
+ /** Save draft button for draft status (provisional — draft requires observed save draft control). */
121
+ saveDraft: [
122
+ 'span.submit-draft',
123
+ 'button.submit-draft',
124
+ 'button:text-is("存草稿")',
125
+ 'button:text-is("保存草稿")',
126
+ 'span:text-is("存草稿")',
127
+ 'span:text-is("保存草稿")',
128
+ ],
129
+ } as const;
130
+
131
+ /**
132
+ * Dynamic post composer selectors (bilibili.js:158-287, bilibili-main.js:33-86).
133
+ */
134
+ export const BILI_DYNAMIC_SELECTORS = {
135
+ /** Dynamic composer container (bilibili.js:270-271). */
136
+ composer: '.bili-dyn-publishing',
137
+ /** Dynamic text editor (bilibili.js:171, 209). */
138
+ editor: [
139
+ 'div[placeholder="有什么想和大家分享的?"][contenteditable="true"]',
140
+ 'div.bili-dyn-publishing div[contenteditable="true"]',
141
+ ],
142
+ /** Optional title input for dynamic posts (bilibili.js:187). */
143
+ titleInput: 'input[maxlength="20"][placeholder="好的标题更容易获得支持,选填20字"]',
144
+ /** Remove button for existing images (bilibili.js:178). */
145
+ removeImageBtn: 'div.bili-pics-uploader__item__remove',
146
+ /** Image upload entry container (bilibili.js:231). */
147
+ imageUploadEntry: 'div.bili-dyn-publishing__image-upload',
148
+ /** Native file input for dynamic images (bilibili-main.js:54-81). */
149
+ imageInput: [
150
+ 'div.bili-dyn-publishing__image-upload input[type="file"]',
151
+ '.bili-pics-uploader input[type="file"]',
152
+ 'input[type="file"][name="upload"]',
153
+ 'input[type="file"][accept*="image"]',
154
+ ],
155
+ /** Add image button (bilibili-main.js:75). */
156
+ imageAddBtn: '.bili-pics-uploader__add',
157
+ /** Preview items for dynamic images (bilibili.js:238, 257). */
158
+ imagePreviews: '.bili-pics-uploader__item, .bili-dyn-publishing__image-item',
159
+ /** Publish / Launch button for dynamic posts (bilibili.js:273). */
160
+ launchButton: ['button.bili-dyn-publishing__action__launch', 'button:text-is("发布")'],
161
+ } as const;
162
+
163
+ /**
164
+ * Receipt-shaped URLs for shortVideo and dynamic publishing.
165
+ * shortVideo pending-review receipt matches upload manager or video BV URL.
166
+ * dynamic receipt matches opus / t.bilibili.com post URL.
167
+ */
168
+ export const BILI_RECEIPT_URL_RES = [
169
+ /member\.bilibili\.com\/platform\/upload-manager\/article/,
170
+ /member\.bilibili\.com\/video\/BV[0-9a-zA-Z]+/,
171
+ /bilibili\.com\/video\/BV[0-9a-zA-Z]+/,
172
+ /member\.bilibili\.com\/platform\/upload\/video\/success/,
173
+ /t\.bilibili\.com\/[0-9]+/,
174
+ /t\.bilibili\.com\/opus\/[0-9]+/,
175
+ /bilibili\.com\/opus\/[0-9]+/,
176
+ ] as const;
177
+
178
+ /** Upload and media processing timeout ceiling (600 seconds = 10 minutes). */
179
+ export const BILI_UPLOAD_TIMEOUT_MS = 600_000;
180
+
181
+ /** Login timeout for profile CLI checks. */
182
+ export const BILI_LOGIN_TIMEOUT_MS = 180_000;
183
+
184
+ /**
185
+ * Checks whether the current page session is authenticated for Bilibili creator surfaces.
186
+ * Returns false if redirected to passport login; checks for creator navigation/avatar or
187
+ * composer presence within 15 seconds.
188
+ */
189
+ export const isBilibiliAuthenticated: IsAuthenticatedPredicate = async (page) => {
190
+ if (BILI_LOGIN_URL_RE.test(page.url())) return false;
191
+ return waitUntil(
192
+ async () => {
193
+ if (BILI_LOGIN_URL_RE.test(page.url())) return false;
194
+ const authenticated = await page.evaluate(`(() => {
195
+ const isLoginPage = Boolean(
196
+ document.querySelector('.login-box, .bili-mini-login, .login-container') ||
197
+ window.location.href.includes('passport.bilibili.com'),
198
+ );
199
+ if (isLoginPage) return false;
200
+ return Boolean(
201
+ document.querySelector(
202
+ '.header-entry-avatar, .nav-user-info, .bili-avatar, a[href*="space.bilibili.com"], .bili-dyn-publishing, div[placeholder="有什么想和大家分享的?"], input[type="file"]',
203
+ ),
204
+ );
205
+ })()`);
206
+ return Boolean(authenticated);
207
+ },
208
+ 15_000,
209
+ 500,
210
+ );
211
+ };
212
+
213
+ /**
214
+ * Site-owned config for shared session check and operator CLI (ADR-032).
215
+ */
216
+ export const BILI_CONFIG: ChannelBrowserConfig = {
217
+ channel: BILI_TARGET,
218
+ homeUrl: BILI_MEMBER_HOME_URL,
219
+ isAuthenticated: isBilibiliAuthenticated,
220
+ loginTimeoutMs: BILI_LOGIN_TIMEOUT_MS,
221
+ checkHeadedOnly: false,
222
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "../../../tooling/typescript/base.json",
3
+ "include": ["src"]
4
+ }