@gobing-ai/knowledge-kit 0.1.8 → 0.1.10

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 (78) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.js +725 -1101
  3. package/dist/board/daily-voice/index.js +722 -101
  4. package/dist/board/itc/index.js +13 -0
  5. package/dist/index.js +1796 -102
  6. package/package.json +1 -1
  7. package/plugins/generations/content-gen/dist/index.js +4 -0
  8. package/plugins/generations/core-facts-gen/dist/index.js +4 -0
  9. package/plugins/generations/daily-article-gen/dist/index.js +4 -0
  10. package/plugins/generations/dailynews-gen/dist/index.js +4 -0
  11. package/plugins/generations/episode-plan-gen/dist/index.js +102 -1
  12. package/plugins/generations/episode-plan-gen/src/index.ts +2 -4
  13. package/plugins/generations/gemini-tts/dist/index.js +4 -0
  14. package/plugins/generations/image-gen/dist/index.js +4 -0
  15. package/plugins/generations/news-report-gen/dist/index.js +4 -0
  16. package/plugins/generations/voice-gen/dist/index.js +4 -0
  17. package/plugins/generations/voxcpm-tts/dist/index.js +4 -0
  18. package/plugins/ingestions/aihot-ingest/dist/index.js +4 -0
  19. package/plugins/ingestions/horizon-ingest/dist/index.js +28 -1
  20. package/plugins/ingestions/horizon-ingest/src/index.ts +34 -0
  21. package/plugins/ingestions/last30days-ingest/dist/index.js +11 -3
  22. package/plugins/ingestions/last30days-ingest/src/index.ts +14 -3
  23. package/plugins/ingestions/web-search/dist/index.js +4 -0
  24. package/plugins/kk/config.example.yaml +8 -0
  25. package/plugins/kk/plugin.json +1 -1
  26. package/plugins/kk/publish-presets/daily-ai-voice.json +123 -0
  27. package/plugins/kk/schedules/kk-daily-publish3-cn-peak.cron.tmpl +20 -0
  28. package/plugins/kk/schedules/kk-daily-publish3-overseas.cron.tmpl +17 -0
  29. package/plugins/kk/scripts/daily-stages.ts +665 -78
  30. package/plugins/kk/scripts/kk-workflow-stages.ts +11 -1
  31. package/plugins/kk/scripts/sentences.ts +134 -0
  32. package/plugins/kk/scripts/series-names.ts +124 -0
  33. package/plugins/kk/scripts/topic-boundary.json +18 -83
  34. package/plugins/kk/workflows/kk-collect.yaml +2 -0
  35. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +112 -9
  36. package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +842 -0
  37. package/plugins/kk/workflows/kk-daily-publish3.yaml +282 -0
  38. package/plugins/kk/workflows/kk-daily-safety.yaml +50 -5
  39. package/plugins/publishings/bilibili-pub/dist/index.js +4 -0
  40. package/plugins/publishings/cnblogs-pub/dist/index.js +4 -0
  41. package/plugins/publishings/csdn-pub/dist/index.js +4 -0
  42. package/plugins/publishings/csdn-pub/dist/profile-check.js +4 -0
  43. package/plugins/publishings/emdash-pub/dist/index.js +4 -0
  44. package/plugins/publishings/infoq-pub/dist/index.js +4 -0
  45. package/plugins/publishings/infoq-pub/dist/profile-check.js +4 -0
  46. package/plugins/publishings/jike-pub/dist/index.js +4 -0
  47. package/plugins/publishings/jike-pub/dist/profile-check.js +4 -0
  48. package/plugins/publishings/juejin-pub/dist/index.js +4 -0
  49. package/plugins/publishings/juejin-pub/dist/profile-check.js +4 -0
  50. package/plugins/publishings/medium-pub/dist/index.js +4 -0
  51. package/plugins/publishings/podcast-pub/README.md +65 -0
  52. package/plugins/publishings/podcast-pub/dist/index.js +1906 -374
  53. package/plugins/publishings/podcast-pub/plugin.json +1 -1
  54. package/plugins/publishings/podcast-pub/src/backfill.ts +465 -0
  55. package/plugins/publishings/podcast-pub/src/index.ts +211 -34
  56. package/plugins/publishings/podcast-pub/src/microfeed-client.ts +32 -3
  57. package/plugins/publishings/podcast-pub/src/seo.ts +1079 -0
  58. package/plugins/publishings/podcast-pub/src/show-notes.ts +74 -14
  59. package/plugins/publishings/qiita-pub/dist/index.js +4 -0
  60. package/plugins/publishings/shipinhao-pub/dist/index.js +4 -0
  61. package/plugins/publishings/sspai-pub/dist/index.js +4 -0
  62. package/plugins/publishings/substack-pub/dist/index.js +4 -0
  63. package/plugins/publishings/substack-pub/dist/profile-check.js +4 -0
  64. package/plugins/publishings/surfdash-pub/dist/index.js +313 -32
  65. package/plugins/publishings/surfdash-pub/src/digest-check.ts +218 -0
  66. package/plugins/publishings/surfdash-pub/src/index.ts +170 -39
  67. package/plugins/publishings/surfdash-pub/src/patch.ts +44 -7
  68. package/plugins/publishings/threads-pub/dist/index.js +4 -0
  69. package/plugins/publishings/wechatmp-pub/dist/index.js +4 -0
  70. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +4 -0
  71. package/plugins/publishings/weibo-pub/dist/index.js +4 -0
  72. package/plugins/publishings/weibo-pub/dist/profile-check.js +4 -0
  73. package/plugins/publishings/x-pub/dist/index.js +4 -0
  74. package/plugins/publishings/x-pub/dist/profile-check.js +4 -0
  75. package/plugins/publishings/xhs-pub/dist/index.js +4 -0
  76. package/plugins/publishings/xhs-pub/dist/profile-check.js +4 -0
  77. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +4 -0
  78. package/plugins/publishings/zenn-pub/dist/index.js +4 -0
@@ -11,7 +11,15 @@
11
11
  * input is escaped first, and only the tags this module emits reach the API.
12
12
  */
13
13
 
14
- import type { Doc } from '@gobing-ai/kk-core';
14
+ import { type Doc, splitSentences } from '@gobing-ai/kk-core';
15
+ import {
16
+ type PlanItem,
17
+ type PodcastSeoConfig,
18
+ SEO_DEFAULTS,
19
+ showNotesSummaryParagraph,
20
+ type TimelineStyle,
21
+ warn,
22
+ } from './seo';
15
23
 
16
24
  /** Escapes text content and attribute values (`&`, `<`, `>`, `"`). */
17
25
  function escapeHtml(value: string): string {
@@ -133,7 +141,7 @@ function isGeneratedTimeline(block: string): boolean {
133
141
  * input. `ponytail`: no JS — timestamp deep-linking depends on the theme's player
134
142
  * API; add when microfeed exposes one.
135
143
  */
136
- function renderTimelineHtml(docs: Doc[], offsets: number[], articleUrl: string): string {
144
+ function renderTimelineHtml(docs: Doc[], offsets: number[], articleUrl: string, style: TimelineStyle): string {
137
145
  const rows = docs
138
146
  .map((d, i) => {
139
147
  const title = escapeHtml(d.title ?? `条目 ${i + 1}`);
@@ -152,11 +160,15 @@ function renderTimelineHtml(docs: Doc[], offsets: number[], articleUrl: string):
152
160
  const articleTail = isHttpUrl(articleUrl)
153
161
  ? `<p>完整图文版:<a href="${escapeHtml(articleUrl)}" target="_blank" rel="noopener">${escapeHtml('本期完整文章')}</a></p>`
154
162
  : `<p>${escapeHtml('本期完整文章')}</p>`;
163
+ const styleBlock = style === 'omit' ? '' : `${TIMELINE_STYLE}\n`;
164
+ // `after` (default) keeps the style inside the markers but after the list, so content_text
165
+ // does not open on CSS. `before` is the pre-D9 position, for a byte-identical legacy render.
166
+ const before = style === 'before' ? styleBlock : '';
167
+ const after = style === 'after' ? styleBlock : '';
155
168
  return `<!--kk:timeline-->
156
- ${TIMELINE_STYLE}
157
- <div class="kk-tl">${rows}
169
+ ${before}<div class="kk-tl">${rows}
158
170
  </div>
159
- ${articleTail}
171
+ ${after}${articleTail}
160
172
  <!--/kk:timeline-->`;
161
173
  }
162
174
 
@@ -277,15 +289,29 @@ export interface ShowNotesRenderOptions {
277
289
  * matched monotonically against segment transcriptions; otherwise `--:--`.
278
290
  */
279
291
  audioSegments?: Array<{ duration?: number; transcription?: string }>;
292
+ /** R4. Omit to keep the default (summary on). `false` reproduces the pre-D9 body. */
293
+ summaryParagraph?: boolean;
294
+ /** R4. `after` moves `<style>` below the timeline; `before` is the legacy position; `omit` drops it. */
295
+ timelineStyle?: TimelineStyle;
296
+ seriesName?: string;
297
+ showName?: string;
298
+ descriptionMin?: number;
299
+ descriptionMax?: number;
300
+ descriptionSource?: PodcastSeoConfig['descriptionSource'];
301
+ descriptionPattern?: string;
302
+ /** Playback seconds for the `(约 N 分钟)` clause. Segment durations are the fallback. */
303
+ durationSeconds?: number;
304
+ /** Link text for the summary cross-link. Falls back to 本期完整文章. */
305
+ surfingTitle?: string;
306
+ refinedDescription?: string;
307
+ summaryClauses?: string[];
280
308
  }
281
309
 
282
310
  /** Per-item 1–2-sentence briefing: `metadata.brief` first, body sentences as fallback. */
283
311
  function itemBrief(d: Doc): string {
284
312
  const meta = d.metadata ?? {};
285
313
  if (typeof meta.brief === 'string' && meta.brief.trim().length > 0) return meta.brief.trim();
286
- return (d.body ?? '')
287
- .trim()
288
- .split(/(?<=[。!?])\s*/)
314
+ return splitSentences((d.body ?? '').trim(), { cjkOnly: true })
289
315
  .slice(0, 2)
290
316
  .join('');
291
317
  }
@@ -298,7 +324,7 @@ function itemBrief(d: Doc): string {
298
324
  * a `[来源:<label>](<sourceUri>)` link per item, and the full-article footer.
299
325
  * Downstream HTML rendering stays with {@link renderShowNotesHtml}.
300
326
  */
301
- export function renderShowNotesMarkdown(docs: Doc[], opts: ShowNotesRenderOptions): string {
327
+ export function renderShowNotesMarkdown(docs: Doc[], opts: ShowNotesRenderOptions, warnings?: string[]): string {
302
328
  const slugify = (t: string): string => {
303
329
  const s = t
304
330
  .toLowerCase()
@@ -323,6 +349,9 @@ export function renderShowNotesMarkdown(docs: Doc[], opts: ShowNotesRenderOption
323
349
  postSlug.length > 0
324
350
  ? `${baseUrl}${localePrefix}/posts/${postSlug}`
325
351
  : `${baseUrl}/zh/posts/${opts.runDate}-${slugify(opts.title)}`;
352
+ if (opts.briefMode === true && postSlug.length === 0) {
353
+ warn('brief mode requested but ARTICLE_POST_PATH is empty; using legacy layout', warnings);
354
+ }
326
355
  const briefMode = opts.briefMode === true && postSlug.length > 0;
327
356
  const sourceLabel = (d: Doc): string => {
328
357
  const m = d.metadata ?? {};
@@ -335,17 +364,48 @@ export function renderShowNotesMarkdown(docs: Doc[], opts: ShowNotesRenderOption
335
364
  };
336
365
  const offsets = computeAudioOffsets(docs, opts.audioSegments);
337
366
  const lines: string[] = [`# ${opts.title}`, ''];
367
+ const summaryOn = opts.summaryParagraph !== false;
368
+ if (summaryOn) {
369
+ const segmentSeconds = opts.audioSegments?.reduce((sum, segment) => sum + (segment.duration ?? 0), 0);
370
+ const durationSeconds =
371
+ opts.durationSeconds ?? (segmentSeconds && segmentSeconds > 0 ? segmentSeconds : undefined);
372
+ const plan: PlanItem[] = docs.map((doc) => {
373
+ const title = doc.title?.trim() ?? '';
374
+ const brief = itemBrief(doc);
375
+ return brief.length > 0 ? { title, brief } : { title };
376
+ });
377
+ const config: PodcastSeoConfig = {
378
+ ...SEO_DEFAULTS,
379
+ ...(opts.seriesName ? { seriesName: opts.seriesName } : {}),
380
+ ...(opts.showName ? { showName: opts.showName } : {}),
381
+ ...(opts.descriptionMin !== undefined ? { descriptionMin: opts.descriptionMin } : {}),
382
+ ...(opts.descriptionMax !== undefined ? { descriptionMax: opts.descriptionMax } : {}),
383
+ ...(opts.descriptionSource ? { descriptionSource: opts.descriptionSource } : {}),
384
+ ...(opts.descriptionPattern ? { descriptionPattern: opts.descriptionPattern } : {}),
385
+ summaryParagraph: true,
386
+ timelineStyle: opts.timelineStyle ?? 'after',
387
+ };
388
+ const summary = showNotesSummaryParagraph({
389
+ config,
390
+ runDate: opts.runDate,
391
+ ...(durationSeconds !== undefined ? { durationSeconds } : {}),
392
+ plan,
393
+ ...(opts.refinedDescription ? { refinedDescription: opts.refinedDescription } : {}),
394
+ ...(opts.summaryClauses ? { summaryClauses: opts.summaryClauses } : {}),
395
+ articleUrl,
396
+ linkText: opts.surfingTitle?.trim() || '本期完整文章',
397
+ });
398
+ for (const line of summary.warnings) warn(line.replace(/^WARN podcast-pub:\s*/, ''), warnings);
399
+ lines.push(summary.markdown, '');
400
+ }
338
401
  if (briefMode) {
339
402
  // Timeline layout (2026-09-16 feedback): real audio offsets left, title + briefing
340
403
  // right, article link last — the whole brief body is one pre-built HTML block.
341
- lines.push(renderTimelineHtml(docs, offsets, articleUrl), '');
404
+ lines.push(renderTimelineHtml(docs, offsets, articleUrl, opts.timelineStyle ?? 'after'), '');
342
405
  } else {
343
406
  docs.forEach((d, i) => {
344
407
  const body = (d.body ?? '').trim();
345
- const sentences = body
346
- .split(/(?<=[。!?])\s*/)
347
- .slice(0, 2)
348
- .join('');
408
+ const sentences = splitSentences(body, { cjkOnly: true }).slice(0, 2).join('');
349
409
  lines.push(
350
410
  `## ${i + 1}. ${d.title ?? `条目 ${i + 1}`}`,
351
411
  '',
@@ -12254,6 +12254,10 @@ init_config();
12254
12254
 
12255
12255
  // ../../packages/kk-core/src/render-md.ts
12256
12256
  if (false) {}
12257
+ // ../../packages/kk-core/src/sentences.ts
12258
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12259
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12260
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12257
12261
  // ../../packages/publish-harness/src/index.ts
12258
12262
  class FakeTransportCore {
12259
12263
  callLogs = [];
@@ -20163,6 +20163,10 @@ init_runtime_node_bun();
20163
20163
 
20164
20164
  // ../../packages/kk-core/src/render-md.ts
20165
20165
  if (false) {}
20166
+ // ../../packages/kk-core/src/sentences.ts
20167
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
20168
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
20169
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
20166
20170
  // ../../packages/publish-harness/src/index.ts
20167
20171
  import { homedir } from "os";
20168
20172
  import { join, resolve } from "path";
@@ -20163,6 +20163,10 @@ init_runtime_node_bun();
20163
20163
 
20164
20164
  // ../../packages/kk-core/src/render-md.ts
20165
20165
  if (false) {}
20166
+ // ../../packages/kk-core/src/sentences.ts
20167
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
20168
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
20169
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
20166
20170
  // ../../packages/publish-harness/src/index.ts
20167
20171
  import { homedir } from "os";
20168
20172
  import { join, resolve } from "path";
@@ -20163,6 +20163,10 @@ init_runtime_node_bun();
20163
20163
 
20164
20164
  // ../../packages/kk-core/src/render-md.ts
20165
20165
  if (false) {}
20166
+ // ../../packages/kk-core/src/sentences.ts
20167
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
20168
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
20169
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
20166
20170
  // ../../packages/publish-harness/src/index.ts
20167
20171
  import { homedir } from "os";
20168
20172
  import { join, resolve } from "path";
@@ -20972,6 +20972,10 @@ var PluginManifestSchema = PluginManifestSchemaBase.superRefine((manifest, ctx)
20972
20972
  });
20973
20973
  // ../../packages/kk-core/src/render-md.ts
20974
20974
  if (false) {}
20975
+ // ../../packages/kk-core/src/sentences.ts
20976
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
20977
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
20978
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
20975
20979
  // ../../plugins/publishings/substack-pub/src/index.ts
20976
20980
  var CHANNEL = "substack";
20977
20981
  var LOGIN_URL_RE = /(\/sign-in|substack\.com\/account\/login)/;