@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/board/collect/index.js +725 -1101
- package/dist/board/daily-voice/index.js +722 -101
- package/dist/board/itc/index.js +13 -0
- package/dist/index.js +1796 -102
- package/package.json +1 -1
- package/plugins/generations/content-gen/dist/index.js +4 -0
- package/plugins/generations/core-facts-gen/dist/index.js +4 -0
- package/plugins/generations/daily-article-gen/dist/index.js +4 -0
- package/plugins/generations/dailynews-gen/dist/index.js +4 -0
- package/plugins/generations/episode-plan-gen/dist/index.js +102 -1
- package/plugins/generations/episode-plan-gen/src/index.ts +2 -4
- package/plugins/generations/gemini-tts/dist/index.js +4 -0
- package/plugins/generations/image-gen/dist/index.js +4 -0
- package/plugins/generations/news-report-gen/dist/index.js +4 -0
- package/plugins/generations/voice-gen/dist/index.js +4 -0
- package/plugins/generations/voxcpm-tts/dist/index.js +4 -0
- package/plugins/ingestions/aihot-ingest/dist/index.js +4 -0
- package/plugins/ingestions/horizon-ingest/dist/index.js +28 -1
- package/plugins/ingestions/horizon-ingest/src/index.ts +34 -0
- package/plugins/ingestions/last30days-ingest/dist/index.js +11 -3
- package/plugins/ingestions/last30days-ingest/src/index.ts +14 -3
- package/plugins/ingestions/web-search/dist/index.js +4 -0
- package/plugins/kk/config.example.yaml +8 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/publish-presets/daily-ai-voice.json +123 -0
- package/plugins/kk/schedules/kk-daily-publish3-cn-peak.cron.tmpl +20 -0
- package/plugins/kk/schedules/kk-daily-publish3-overseas.cron.tmpl +17 -0
- package/plugins/kk/scripts/daily-stages.ts +665 -78
- package/plugins/kk/scripts/kk-workflow-stages.ts +11 -1
- package/plugins/kk/scripts/sentences.ts +134 -0
- package/plugins/kk/scripts/series-names.ts +124 -0
- package/plugins/kk/scripts/topic-boundary.json +18 -83
- package/plugins/kk/workflows/kk-collect.yaml +2 -0
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +112 -9
- package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +842 -0
- package/plugins/kk/workflows/kk-daily-publish3.yaml +282 -0
- package/plugins/kk/workflows/kk-daily-safety.yaml +50 -5
- package/plugins/publishings/bilibili-pub/dist/index.js +4 -0
- package/plugins/publishings/cnblogs-pub/dist/index.js +4 -0
- package/plugins/publishings/csdn-pub/dist/index.js +4 -0
- package/plugins/publishings/csdn-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/emdash-pub/dist/index.js +4 -0
- package/plugins/publishings/infoq-pub/dist/index.js +4 -0
- package/plugins/publishings/infoq-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/jike-pub/dist/index.js +4 -0
- package/plugins/publishings/jike-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/juejin-pub/dist/index.js +4 -0
- package/plugins/publishings/juejin-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/medium-pub/dist/index.js +4 -0
- package/plugins/publishings/podcast-pub/README.md +65 -0
- package/plugins/publishings/podcast-pub/dist/index.js +1906 -374
- package/plugins/publishings/podcast-pub/plugin.json +1 -1
- package/plugins/publishings/podcast-pub/src/backfill.ts +465 -0
- package/plugins/publishings/podcast-pub/src/index.ts +211 -34
- package/plugins/publishings/podcast-pub/src/microfeed-client.ts +32 -3
- package/plugins/publishings/podcast-pub/src/seo.ts +1079 -0
- package/plugins/publishings/podcast-pub/src/show-notes.ts +74 -14
- package/plugins/publishings/qiita-pub/dist/index.js +4 -0
- package/plugins/publishings/shipinhao-pub/dist/index.js +4 -0
- package/plugins/publishings/sspai-pub/dist/index.js +4 -0
- package/plugins/publishings/substack-pub/dist/index.js +4 -0
- package/plugins/publishings/substack-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +313 -32
- package/plugins/publishings/surfdash-pub/src/digest-check.ts +218 -0
- package/plugins/publishings/surfdash-pub/src/index.ts +170 -39
- package/plugins/publishings/surfdash-pub/src/patch.ts +44 -7
- package/plugins/publishings/threads-pub/dist/index.js +4 -0
- package/plugins/publishings/wechatmp-pub/dist/index.js +4 -0
- package/plugins/publishings/wechatmp-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/weibo-pub/dist/index.js +4 -0
- package/plugins/publishings/weibo-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/x-pub/dist/index.js +4 -0
- package/plugins/publishings/x-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/xhs-pub/dist/index.js +4 -0
- package/plugins/publishings/xhs-pub/dist/profile-check.js +4 -0
- package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +4 -0
- package/plugins/publishings/zenn-pub/dist/index.js +4 -0
package/package.json
CHANGED
|
@@ -12253,6 +12253,10 @@ init_config();
|
|
|
12253
12253
|
|
|
12254
12254
|
// ../../packages/kk-core/src/render-md.ts
|
|
12255
12255
|
if (false) {}
|
|
12256
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12257
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12258
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12259
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12256
12260
|
// ../../plugins/generations/content-gen/src/index.ts
|
|
12257
12261
|
init_dist();
|
|
12258
12262
|
|
|
@@ -12319,6 +12319,10 @@ function uniqueToken() {
|
|
|
12319
12319
|
}
|
|
12320
12320
|
// ../../packages/kk-core/src/render-md.ts
|
|
12321
12321
|
if (false) {}
|
|
12322
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12323
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12324
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12325
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12322
12326
|
// ../../plugins/generations/core-facts-gen/src/index.ts
|
|
12323
12327
|
init_dist();
|
|
12324
12328
|
function sourceFileOf(entry) {
|
|
@@ -12319,6 +12319,10 @@ function uniqueToken() {
|
|
|
12319
12319
|
}
|
|
12320
12320
|
// ../../packages/kk-core/src/render-md.ts
|
|
12321
12321
|
if (false) {}
|
|
12322
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12323
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12324
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12325
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12322
12326
|
// ../../plugins/generations/daily-article-gen/src/index.ts
|
|
12323
12327
|
init_dist();
|
|
12324
12328
|
function sourceLabelOf(doc) {
|
|
@@ -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
|
// ../../plugins/generations/dailynews-gen/src/index.ts
|
|
12258
12262
|
init_dist();
|
|
12259
12263
|
|
|
@@ -12319,6 +12319,107 @@ function uniqueToken() {
|
|
|
12319
12319
|
}
|
|
12320
12320
|
// ../../packages/kk-core/src/render-md.ts
|
|
12321
12321
|
if (false) {}
|
|
12322
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12323
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12324
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12325
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12326
|
+
var OPEN_PAIRS = {
|
|
12327
|
+
"\u300C": "\u300D",
|
|
12328
|
+
"\u300E": "\u300F",
|
|
12329
|
+
"\u201C": "\u201D",
|
|
12330
|
+
"\uFF08": "\uFF09",
|
|
12331
|
+
"(": ")"
|
|
12332
|
+
};
|
|
12333
|
+
var CLOSE_PAIRS = {
|
|
12334
|
+
"\u300D": "\u300C",
|
|
12335
|
+
"\u300F": "\u300E",
|
|
12336
|
+
"\u201D": "\u201C",
|
|
12337
|
+
"\uFF09": "\uFF08",
|
|
12338
|
+
")": "("
|
|
12339
|
+
};
|
|
12340
|
+
function splitSentences(text, options = {}) {
|
|
12341
|
+
if (!text || typeof text !== "string")
|
|
12342
|
+
return [];
|
|
12343
|
+
const results = [];
|
|
12344
|
+
let sentenceStart = 0;
|
|
12345
|
+
let stack = [];
|
|
12346
|
+
let i = 0;
|
|
12347
|
+
const isTerminator = (c) => CJK_TERMINATORS.has(c) || options.cjkOnly !== true && LATIN_TERMINATORS.has(c);
|
|
12348
|
+
while (i < text.length) {
|
|
12349
|
+
const ch = text[i];
|
|
12350
|
+
if (isTerminator(ch)) {
|
|
12351
|
+
let termEnd = i;
|
|
12352
|
+
let hasCjkTerm = false;
|
|
12353
|
+
let hasLatinTerm = false;
|
|
12354
|
+
while (termEnd < text.length) {
|
|
12355
|
+
const termChar = text[termEnd];
|
|
12356
|
+
if (!termChar || !isTerminator(termChar)) {
|
|
12357
|
+
break;
|
|
12358
|
+
}
|
|
12359
|
+
if (CJK_TERMINATORS.has(termChar))
|
|
12360
|
+
hasCjkTerm = true;
|
|
12361
|
+
if (LATIN_TERMINATORS.has(termChar))
|
|
12362
|
+
hasLatinTerm = true;
|
|
12363
|
+
termEnd++;
|
|
12364
|
+
}
|
|
12365
|
+
let closerEnd = termEnd;
|
|
12366
|
+
const tempStack = [...stack];
|
|
12367
|
+
while (closerEnd < text.length) {
|
|
12368
|
+
const closer = text[closerEnd];
|
|
12369
|
+
if (!closer || !ATTACHED_CLOSERS.has(closer)) {
|
|
12370
|
+
break;
|
|
12371
|
+
}
|
|
12372
|
+
const top = tempStack[tempStack.length - 1];
|
|
12373
|
+
if (top !== undefined && top === closer) {
|
|
12374
|
+
tempStack.pop();
|
|
12375
|
+
} else if (CLOSE_PAIRS[closer]) {
|
|
12376
|
+
const idx = tempStack.lastIndexOf(closer);
|
|
12377
|
+
if (idx >= 0)
|
|
12378
|
+
tempStack.splice(idx, 1);
|
|
12379
|
+
}
|
|
12380
|
+
closerEnd++;
|
|
12381
|
+
}
|
|
12382
|
+
let isBoundary = false;
|
|
12383
|
+
if (tempStack.length === 0) {
|
|
12384
|
+
if (hasCjkTerm) {
|
|
12385
|
+
isBoundary = true;
|
|
12386
|
+
} else if (hasLatinTerm) {
|
|
12387
|
+
const nextChar = text[closerEnd];
|
|
12388
|
+
if (closerEnd === text.length || nextChar !== undefined && /\s/.test(nextChar)) {
|
|
12389
|
+
isBoundary = true;
|
|
12390
|
+
}
|
|
12391
|
+
}
|
|
12392
|
+
}
|
|
12393
|
+
if (isBoundary) {
|
|
12394
|
+
const sentence = text.slice(sentenceStart, closerEnd).trim();
|
|
12395
|
+
if (sentence.length > 0) {
|
|
12396
|
+
results.push(sentence);
|
|
12397
|
+
}
|
|
12398
|
+
sentenceStart = closerEnd;
|
|
12399
|
+
stack = tempStack;
|
|
12400
|
+
i = closerEnd;
|
|
12401
|
+
continue;
|
|
12402
|
+
}
|
|
12403
|
+
}
|
|
12404
|
+
const openMatch = OPEN_PAIRS[ch];
|
|
12405
|
+
const closeMatch = CLOSE_PAIRS[ch];
|
|
12406
|
+
if (openMatch) {
|
|
12407
|
+
stack.push(openMatch);
|
|
12408
|
+
} else if (closeMatch) {
|
|
12409
|
+
const idx = stack.lastIndexOf(ch);
|
|
12410
|
+
if (idx >= 0)
|
|
12411
|
+
stack.splice(idx, 1);
|
|
12412
|
+
}
|
|
12413
|
+
i++;
|
|
12414
|
+
}
|
|
12415
|
+
if (sentenceStart < text.length) {
|
|
12416
|
+
const tail = text.slice(sentenceStart).trim();
|
|
12417
|
+
if (tail.length > 0) {
|
|
12418
|
+
results.push(tail);
|
|
12419
|
+
}
|
|
12420
|
+
}
|
|
12421
|
+
return results;
|
|
12422
|
+
}
|
|
12322
12423
|
// ../../plugins/generations/episode-plan-gen/src/index.ts
|
|
12323
12424
|
init_dist();
|
|
12324
12425
|
var SOURCE_BASE = {
|
|
@@ -12731,7 +12832,7 @@ function planEpisode(docs, options) {
|
|
|
12731
12832
|
return selected.map((r) => {
|
|
12732
12833
|
const meta = asRecord(r.doc.metadata);
|
|
12733
12834
|
const ascii = asciiRatio(r.doc.title ?? "");
|
|
12734
|
-
const brief = (r.doc.body ?? "").trim()
|
|
12835
|
+
const brief = splitSentences((r.doc.body ?? "").trim(), { cjkOnly: true }).slice(0, 2).join("");
|
|
12735
12836
|
const published = docTimestamp(r.doc);
|
|
12736
12837
|
return {
|
|
12737
12838
|
...r.doc,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
2
|
import { parseArgs } from 'node:util';
|
|
3
|
-
import { type Content, ContentSchema, type Doc, DocListSchema } from '@gobing-ai/kk-core';
|
|
3
|
+
import { type Content, ContentSchema, type Doc, DocListSchema, splitSentences } from '@gobing-ai/kk-core';
|
|
4
4
|
import { atomicWriteJson, createNodeFileSystem, readJsonFile } from '@gobing-ai/ts-runtime';
|
|
5
5
|
import { echoError } from '@gobing-ai/ts-utils';
|
|
6
6
|
|
|
@@ -597,9 +597,7 @@ export function planEpisode(docs: Doc[], options: PlanOptions): Doc[] {
|
|
|
597
597
|
// Task 0118: 1–2-sentence briefing (first two 。!?-terminated sentences of the
|
|
598
598
|
// cleaned body) + best-effort publish timestamp ride the plan metadata so the
|
|
599
599
|
// podcast show-notes brief/timeline render stays deterministic, no LLM.
|
|
600
|
-
const brief = (r.doc.body ?? '')
|
|
601
|
-
.trim()
|
|
602
|
-
.split(/(?<=[。!?])\s*/)
|
|
600
|
+
const brief = splitSentences((r.doc.body ?? '').trim(), { cjkOnly: true })
|
|
603
601
|
.slice(0, 2)
|
|
604
602
|
.join('');
|
|
605
603
|
const published = docTimestamp(r.doc);
|
|
@@ -12257,6 +12257,10 @@ init_config();
|
|
|
12257
12257
|
|
|
12258
12258
|
// ../../packages/kk-core/src/render-md.ts
|
|
12259
12259
|
if (false) {}
|
|
12260
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12261
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12262
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12263
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12260
12264
|
// ../../plugins/generations/voice-gen/src/index.ts
|
|
12261
12265
|
init_dist();
|
|
12262
12266
|
|
|
@@ -12595,6 +12595,10 @@ init_config();
|
|
|
12595
12595
|
|
|
12596
12596
|
// ../../packages/kk-core/src/render-md.ts
|
|
12597
12597
|
if (false) {}
|
|
12598
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12599
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12600
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12601
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12598
12602
|
// ../../plugins/generations/image-gen/src/index.ts
|
|
12599
12603
|
init_dist();
|
|
12600
12604
|
|
|
@@ -12319,6 +12319,10 @@ function uniqueToken() {
|
|
|
12319
12319
|
}
|
|
12320
12320
|
// ../../packages/kk-core/src/render-md.ts
|
|
12321
12321
|
if (false) {}
|
|
12322
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12323
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12324
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12325
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12322
12326
|
// ../../plugins/generations/news-report-gen/src/index.ts
|
|
12323
12327
|
init_dist();
|
|
12324
12328
|
var QC_DIMENSIONS = ["quality", "importance", "urgency", "impact"];
|
|
@@ -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
|
// ../../plugins/generations/voice-gen/src/index.ts
|
|
12258
12262
|
init_dist();
|
|
12259
12263
|
|
|
@@ -12257,6 +12257,10 @@ init_config();
|
|
|
12257
12257
|
|
|
12258
12258
|
// ../../packages/kk-core/src/render-md.ts
|
|
12259
12259
|
if (false) {}
|
|
12260
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12261
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12262
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12263
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12260
12264
|
// ../../plugins/generations/voice-gen/src/index.ts
|
|
12261
12265
|
init_dist();
|
|
12262
12266
|
|
|
@@ -12571,6 +12571,10 @@ function uniqueToken() {
|
|
|
12571
12571
|
}
|
|
12572
12572
|
// ../../packages/kk-core/src/render-md.ts
|
|
12573
12573
|
if (false) {}
|
|
12574
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12575
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12576
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12577
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12574
12578
|
// ../../plugins/ingestions/aihot-ingest/src/index.ts
|
|
12575
12579
|
init_dist();
|
|
12576
12580
|
init_zod();
|
|
@@ -12320,6 +12320,10 @@ function uniqueToken() {
|
|
|
12320
12320
|
}
|
|
12321
12321
|
// ../../packages/kk-core/src/render-md.ts
|
|
12322
12322
|
if (false) {}
|
|
12323
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12324
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12325
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12326
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12323
12327
|
// ../../plugins/ingestions/horizon-ingest/src/index.ts
|
|
12324
12328
|
init_dist();
|
|
12325
12329
|
init_zod();
|
|
@@ -12328,8 +12332,10 @@ var InputConfigSchema = object({
|
|
|
12328
12332
|
config: string2().trim().min(1).optional(),
|
|
12329
12333
|
hours: number2().int().min(1).optional(),
|
|
12330
12334
|
languages: array(string2().trim().min(1)).optional(),
|
|
12331
|
-
horizon_dir: string2().trim().min(1).optional()
|
|
12335
|
+
horizon_dir: string2().trim().min(1).optional(),
|
|
12336
|
+
stale_fallback_ms: number2().int().min(0).optional()
|
|
12332
12337
|
});
|
|
12338
|
+
var DEFAULT_STALE_FALLBACK_MS = 26 * 3600000;
|
|
12333
12339
|
var defaultRunner = async (config, horizonDir) => {
|
|
12334
12340
|
const argv = [process.env.HORIZON_UV ?? "uv", "run", "horizon"];
|
|
12335
12341
|
if (config.hours !== undefined)
|
|
@@ -12447,6 +12453,27 @@ async function processIngestionIO(options, config, runner = defaultRunner, now =
|
|
|
12447
12453
|
continue;
|
|
12448
12454
|
docs.push(...digestToDocs(await fs.readFile(file), match[1], match[2]));
|
|
12449
12455
|
}
|
|
12456
|
+
if (docs.length === 0) {
|
|
12457
|
+
const fallbackMs = config.stale_fallback_ms ?? DEFAULT_STALE_FALLBACK_MS;
|
|
12458
|
+
let newest = null;
|
|
12459
|
+
for (const entry of entries) {
|
|
12460
|
+
const match = entry.match(digestRe);
|
|
12461
|
+
if (!match?.[1] || !match?.[2] || !langs.has(match[2]))
|
|
12462
|
+
continue;
|
|
12463
|
+
const stat = await fs.stat(join(summariesDir, entry));
|
|
12464
|
+
if (!stat)
|
|
12465
|
+
continue;
|
|
12466
|
+
if (!newest || stat.mtimeMs > newest.mtimeMs)
|
|
12467
|
+
newest = { entry, mtimeMs: stat.mtimeMs };
|
|
12468
|
+
}
|
|
12469
|
+
if (newest && runStartedAt - newest.mtimeMs <= fallbackMs) {
|
|
12470
|
+
const match = newest.entry.match(digestRe);
|
|
12471
|
+
if (match?.[1] && match?.[2]) {
|
|
12472
|
+
echoError(`horizon-ingest: no fresh digest this run \u2014 falling back to ${newest.entry} (${Math.round((runStartedAt - newest.mtimeMs) / 3600000)}h old)`);
|
|
12473
|
+
docs.push(...digestToDocs(await fs.readFile(join(summariesDir, newest.entry)), match[1], match[2]));
|
|
12474
|
+
}
|
|
12475
|
+
}
|
|
12476
|
+
}
|
|
12450
12477
|
if (docs.length === 0) {
|
|
12451
12478
|
echoError(`horizon-ingest: no fresh digest in ${summariesDir} (languages: ${[...langs].join(", ")}) \u2014 emitting []`);
|
|
12452
12479
|
}
|
|
@@ -19,8 +19,18 @@ const InputConfigSchema = z.object({
|
|
|
19
19
|
hours: z.number().int().min(1).optional(),
|
|
20
20
|
languages: z.array(z.string().trim().min(1)).optional(),
|
|
21
21
|
horizon_dir: z.string().trim().min(1).optional(),
|
|
22
|
+
/**
|
|
23
|
+
* When this tick's run writes no digest (Horizon exits 0 on an empty 6h
|
|
24
|
+
* window), fall back to the newest digest at most this many ms old instead
|
|
25
|
+
* of emitting []. Doc ids are date-keyed and stable, so repeats dedupe
|
|
26
|
+
* downstream (2026-10-23: Oct 21 contributed zero docs — every tick hit an
|
|
27
|
+
* empty window).
|
|
28
|
+
*/
|
|
29
|
+
stale_fallback_ms: z.number().int().min(0).optional(),
|
|
22
30
|
});
|
|
23
31
|
|
|
32
|
+
const DEFAULT_STALE_FALLBACK_MS = 26 * 3_600_000;
|
|
33
|
+
|
|
24
34
|
export interface HorizonRunResult {
|
|
25
35
|
exitCode: number;
|
|
26
36
|
stderr: string;
|
|
@@ -166,6 +176,30 @@ export async function processIngestionIO(
|
|
|
166
176
|
if (!stat || stat.mtimeMs < runStartedAt - 1000) continue; // stale file from an earlier run
|
|
167
177
|
docs.push(...digestToDocs(await fs.readFile(file), match[1], match[2]));
|
|
168
178
|
}
|
|
179
|
+
if (docs.length === 0) {
|
|
180
|
+
// This tick wrote no digest (empty fetch window exits 0). Reuse the newest
|
|
181
|
+
// digest while it is young enough to still matter for a daily podcast.
|
|
182
|
+
const fallbackMs = config.stale_fallback_ms ?? DEFAULT_STALE_FALLBACK_MS;
|
|
183
|
+
let newest: { entry: string; mtimeMs: number } | null = null;
|
|
184
|
+
for (const entry of entries) {
|
|
185
|
+
const match = entry.match(digestRe);
|
|
186
|
+
if (!match?.[1] || !match?.[2] || !langs.has(match[2])) continue;
|
|
187
|
+
const stat = await fs.stat(join(summariesDir, entry));
|
|
188
|
+
if (!stat) continue;
|
|
189
|
+
if (!newest || stat.mtimeMs > newest.mtimeMs) newest = { entry, mtimeMs: stat.mtimeMs };
|
|
190
|
+
}
|
|
191
|
+
if (newest && runStartedAt - newest.mtimeMs <= fallbackMs) {
|
|
192
|
+
const match = newest.entry.match(digestRe);
|
|
193
|
+
if (match?.[1] && match?.[2]) {
|
|
194
|
+
echoError(
|
|
195
|
+
`horizon-ingest: no fresh digest this run — falling back to ${newest.entry} (${Math.round(
|
|
196
|
+
(runStartedAt - newest.mtimeMs) / 3_600_000,
|
|
197
|
+
)}h old)`,
|
|
198
|
+
);
|
|
199
|
+
docs.push(...digestToDocs(await fs.readFile(join(summariesDir, newest.entry)), match[1], match[2]));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
169
203
|
if (docs.length === 0) {
|
|
170
204
|
echoError(
|
|
171
205
|
`horizon-ingest: no fresh digest in ${summariesDir} (languages: ${[...langs].join(', ')}) — emitting []`,
|
|
@@ -11983,7 +11983,7 @@ var init_file_system_node = () => {};
|
|
|
11983
11983
|
// ../../plugins/ingestions/last30days-ingest/src/index.ts
|
|
11984
11984
|
import { createHash } from "crypto";
|
|
11985
11985
|
import { homedir } from "os";
|
|
11986
|
-
import { dirname as dirname2, join } from "path";
|
|
11986
|
+
import { dirname as dirname2, join, resolve } from "path";
|
|
11987
11987
|
import { parseArgs } from "util";
|
|
11988
11988
|
|
|
11989
11989
|
// ../../packages/kk-core/src/content-limits.ts
|
|
@@ -12321,6 +12321,10 @@ function uniqueToken() {
|
|
|
12321
12321
|
}
|
|
12322
12322
|
// ../../packages/kk-core/src/render-md.ts
|
|
12323
12323
|
if (false) {}
|
|
12324
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12325
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12326
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12327
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12324
12328
|
// ../../plugins/ingestions/last30days-ingest/src/index.ts
|
|
12325
12329
|
init_dist();
|
|
12326
12330
|
init_zod();
|
|
@@ -12332,6 +12336,9 @@ var InputConfigSchema = object({
|
|
|
12332
12336
|
});
|
|
12333
12337
|
var defaultRunner = async (config, outputPath) => {
|
|
12334
12338
|
const skillDir = process.env.LAST30DAYS_SKILL_DIR ?? join(homedir(), ".agents", "skills", "last30days");
|
|
12339
|
+
if (!createNodeFileSystem().exists(skillDir)) {
|
|
12340
|
+
throw new Error(`skill dir missing at ${skillDir} \u2014 restore the last30days skill checkout/symlink or set LAST30DAYS_SKILL_DIR`);
|
|
12341
|
+
}
|
|
12335
12342
|
const argv = [
|
|
12336
12343
|
process.env.LAST30DAYS_PYTHON ?? "python3",
|
|
12337
12344
|
join(skillDir, "scripts", "last30days.py"),
|
|
@@ -12391,7 +12398,8 @@ function mapResultsToDocs(agentJson, topic) {
|
|
|
12391
12398
|
}
|
|
12392
12399
|
async function processIngestionIO(options, config, runner = defaultRunner) {
|
|
12393
12400
|
const fs = createNodeFileSystem();
|
|
12394
|
-
const
|
|
12401
|
+
const out = resolve(options.out);
|
|
12402
|
+
const outDir = dirname2(out);
|
|
12395
12403
|
if (outDir && outDir !== ".")
|
|
12396
12404
|
await fs.ensureDir(outDir);
|
|
12397
12405
|
const capturePath = join(outDir || ".", ".last30days-agent.json");
|
|
@@ -12400,7 +12408,7 @@ async function processIngestionIO(options, config, runner = defaultRunner) {
|
|
|
12400
12408
|
throw new Error(`last30days exited ${run.exitCode}: ${run.stderr.trim() || "no stderr"}`);
|
|
12401
12409
|
}
|
|
12402
12410
|
const docs = mapResultsToDocs(await readJsonFile(capturePath), config.topic);
|
|
12403
|
-
await atomicWriteJson(
|
|
12411
|
+
await atomicWriteJson(out, docs, fs);
|
|
12404
12412
|
return { docs };
|
|
12405
12413
|
}
|
|
12406
12414
|
async function main2(runner) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { dirname, join } from 'node:path';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
4
|
import { parseArgs } from 'node:util';
|
|
5
5
|
import { type Doc, DocListSchema } from '@gobing-ai/kk-core';
|
|
6
6
|
import { atomicWriteJson, createNodeFileSystem, readJsonFile } from '@gobing-ai/ts-runtime';
|
|
@@ -36,6 +36,14 @@ export type RunLast30Days = (
|
|
|
36
36
|
|
|
37
37
|
export const defaultRunner: RunLast30Days = async (config, outputPath) => {
|
|
38
38
|
const skillDir = process.env.LAST30DAYS_SKILL_DIR ?? join(homedir(), '.agents', 'skills', 'last30days');
|
|
39
|
+
// The skill dir is the spawn cwd: when the ~/.agents/skills/last30days symlink dangles,
|
|
40
|
+
// Bun reports it as `ENOENT ... posix_spawn 'python3'` — pointing at the interpreter, not
|
|
41
|
+
// the dir (2026-10-20 incident lost 9h of diagnosis to that). Fail with the real problem.
|
|
42
|
+
if (!createNodeFileSystem().exists(skillDir)) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`skill dir missing at ${skillDir} — restore the last30days skill checkout/symlink or set LAST30DAYS_SKILL_DIR`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
39
47
|
const argv = [
|
|
40
48
|
process.env.LAST30DAYS_PYTHON ?? 'python3',
|
|
41
49
|
join(skillDir, 'scripts', 'last30days.py'),
|
|
@@ -109,7 +117,10 @@ export async function processIngestionIO(
|
|
|
109
117
|
runner: RunLast30Days = defaultRunner,
|
|
110
118
|
): Promise<{ docs: Doc[] }> {
|
|
111
119
|
const fs = createNodeFileSystem();
|
|
112
|
-
|
|
120
|
+
// Relative --out would split cwd semantics: the engine writes its capture relative to the
|
|
121
|
+
// skill dir (its cwd) while we read it relative to ours. Pin everything to an absolute path.
|
|
122
|
+
const out = resolve(options.out);
|
|
123
|
+
const outDir = dirname(out);
|
|
113
124
|
if (outDir && outDir !== '.') await fs.ensureDir(outDir);
|
|
114
125
|
const capturePath = join(outDir || '.', '.last30days-agent.json');
|
|
115
126
|
const run = await runner(config, capturePath);
|
|
@@ -117,7 +128,7 @@ export async function processIngestionIO(
|
|
|
117
128
|
throw new Error(`last30days exited ${run.exitCode}: ${run.stderr.trim() || 'no stderr'}`);
|
|
118
129
|
}
|
|
119
130
|
const docs = mapResultsToDocs(await readJsonFile(capturePath), config.topic);
|
|
120
|
-
await atomicWriteJson(
|
|
131
|
+
await atomicWriteJson(out, docs, fs);
|
|
121
132
|
return { docs };
|
|
122
133
|
}
|
|
123
134
|
|
|
@@ -12275,6 +12275,10 @@ init_config();
|
|
|
12275
12275
|
|
|
12276
12276
|
// ../../packages/kk-core/src/render-md.ts
|
|
12277
12277
|
if (false) {}
|
|
12278
|
+
// ../../packages/kk-core/src/sentences.ts
|
|
12279
|
+
var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
|
|
12280
|
+
var LATIN_TERMINATORS = new Set([".", "!", "?"]);
|
|
12281
|
+
var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
|
|
12278
12282
|
// ../../node_modules/.bun/@logtape+logtape@2.3.1/node_modules/@logtape/logtape/dist/context.js
|
|
12279
12283
|
var categoryPrefixSymbol = Symbol.for("logtape.categoryPrefix");
|
|
12280
12284
|
function getCategoryPrefix() {
|
|
@@ -9,6 +9,14 @@ defaults:
|
|
|
9
9
|
maxResults: 8
|
|
10
10
|
fixture: false
|
|
11
11
|
|
|
12
|
+
# Per-workflow settings. `publish_preset` names the publish preset that owns the daily channel
|
|
13
|
+
# matrix (files live in `plugins/kk/publish-presets/`, overridable per project under
|
|
14
|
+
# `.kk/publish-presets/`). The preset decides WHICH channels publish; `live` is never in a preset
|
|
15
|
+
# file — it is a launch var (`live_channels`) or an installed schedule entry (ADR-022).
|
|
16
|
+
workflows:
|
|
17
|
+
kk-daily-ai-voice3:
|
|
18
|
+
publish_preset: "daily-ai-voice"
|
|
19
|
+
|
|
12
20
|
# Plugin credentials — paste values between the quotes.
|
|
13
21
|
# Path: plugins/<collection>/<name>/env — each entry is injected into that plugin's
|
|
14
22
|
# process environment at exec time. A real environment variable always wins
|
package/plugins/kk/plugin.json
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"name": "daily-ai-voice",
|
|
4
|
+
"targets": [
|
|
5
|
+
{
|
|
6
|
+
"plugin": "surfdash-pub",
|
|
7
|
+
"operation": "post",
|
|
8
|
+
"variant": "article",
|
|
9
|
+
"tier": 0,
|
|
10
|
+
"window": "after-generate",
|
|
11
|
+
"mode": "draft",
|
|
12
|
+
"required": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"plugin": "podcast-pub",
|
|
16
|
+
"operation": "episode",
|
|
17
|
+
"variant": "episode",
|
|
18
|
+
"tier": 0,
|
|
19
|
+
"window": "after-generate",
|
|
20
|
+
"mode": "draft",
|
|
21
|
+
"required": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"plugin": "wechatmp-pub",
|
|
25
|
+
"operation": "播客",
|
|
26
|
+
"variant": "episode",
|
|
27
|
+
"tier": 1,
|
|
28
|
+
"window": "after-generate",
|
|
29
|
+
"mode": "draft"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"plugin": "xhs-pub",
|
|
33
|
+
"operation": "发播客",
|
|
34
|
+
"variant": "episode",
|
|
35
|
+
"tier": 1,
|
|
36
|
+
"window": "after-generate",
|
|
37
|
+
"mode": "draft"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"plugin": "wechatmp-pub",
|
|
41
|
+
"operation": "文章",
|
|
42
|
+
"variant": "article",
|
|
43
|
+
"tier": 2,
|
|
44
|
+
"window": "after-generate",
|
|
45
|
+
"mode": "draft",
|
|
46
|
+
"options": {
|
|
47
|
+
"collection": "每日AI资讯"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"plugin": "xhs-pub",
|
|
52
|
+
"operation": "写长文",
|
|
53
|
+
"variant": "article",
|
|
54
|
+
"tier": 2,
|
|
55
|
+
"window": "after-generate",
|
|
56
|
+
"mode": "dummy"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"plugin": "xhs-pub",
|
|
60
|
+
"operation": "上传图文",
|
|
61
|
+
"variant": "cards",
|
|
62
|
+
"tier": 3,
|
|
63
|
+
"window": "after-generate",
|
|
64
|
+
"mode": "off"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"plugin": "weibo-pub",
|
|
68
|
+
"operation": "article",
|
|
69
|
+
"variant": "article",
|
|
70
|
+
"tier": 2,
|
|
71
|
+
"window": "cn-peak",
|
|
72
|
+
"mode": "draft"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"plugin": "x-pub",
|
|
76
|
+
"operation": "article",
|
|
77
|
+
"variant": "article",
|
|
78
|
+
"tier": 2,
|
|
79
|
+
"window": "cn-peak",
|
|
80
|
+
"mode": "draft"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"plugin": "x-pub",
|
|
84
|
+
"operation": "post",
|
|
85
|
+
"variant": "social:x",
|
|
86
|
+
"tier": 3,
|
|
87
|
+
"window": "overseas-rss",
|
|
88
|
+
"mode": "draft"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"plugin": "weibo-pub",
|
|
92
|
+
"operation": "post",
|
|
93
|
+
"variant": "social:weibo",
|
|
94
|
+
"tier": 3,
|
|
95
|
+
"window": "overseas-rss",
|
|
96
|
+
"mode": "draft"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"plugin": "threads-pub",
|
|
100
|
+
"operation": "post",
|
|
101
|
+
"variant": "social:threads",
|
|
102
|
+
"tier": 3,
|
|
103
|
+
"window": "overseas-rss",
|
|
104
|
+
"mode": "draft"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"plugin": "shipinhao-pub",
|
|
108
|
+
"operation": "图文",
|
|
109
|
+
"variant": "cards",
|
|
110
|
+
"tier": 3,
|
|
111
|
+
"window": "overseas-rss",
|
|
112
|
+
"mode": "off"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"plugin": "threads-pub",
|
|
116
|
+
"operation": "post-images",
|
|
117
|
+
"variant": "cards",
|
|
118
|
+
"tier": 3,
|
|
119
|
+
"window": "overseas-rss",
|
|
120
|
+
"mode": "off"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# kk-daily-publish3-cn-peak — domestic live window (task 0323 R5).
|
|
2
|
+
#
|
|
3
|
+
# Fires at 17:00 local because the audience window is 09:00 Asia/Shanghai and launchd/cron
|
|
4
|
+
# intervals follow the MACHINE's zone; the first node (`publish-matrix-wait-until 09:00
|
|
5
|
+
# Asia/Shanghai`) sleeps the remainder, which is 1 h from 17:00 PDT and 0 h after the DST flip —
|
|
6
|
+
# so the entry never needs editing twice a year.
|
|
7
|
+
#
|
|
8
|
+
# `live_channels` is baked in: this installed line IS the operator's ADR-022 consent for a live
|
|
9
|
+
# send. Nothing in the workflow or the preset can produce `live` on its own.
|
|
10
|
+
#
|
|
11
|
+
# Rendered by `kk schedule render kk-daily-publish3-cn-peak --platform cron --repo <checkout>`;
|
|
12
|
+
# kk never edits the operator's crontab. MERGE, never replace:
|
|
13
|
+
#
|
|
14
|
+
# crontab -l > /tmp/kk-cn-peak.cron 2>/dev/null || :
|
|
15
|
+
# kk schedule render kk-daily-publish3-cn-peak --platform cron --repo "$PWD" >> /tmp/kk-cn-peak.cron
|
|
16
|
+
# crontab /tmp/kk-cn-peak.cron
|
|
17
|
+
#
|
|
18
|
+
# `run_date` is computed at tick time in America/Los_Angeles: a run that starts at 17:00 PDT is
|
|
19
|
+
# still "today" for the day dir the 04:30 generation wrote.
|
|
20
|
+
0 17 * * * cd {{repo}} && mkdir -p .works/kk-daily-publish3 && PATH="{{home}}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" TZ=America/Los_Angeles spur workflow run plugins/kk/workflows/kk-daily-publish3.yaml --vars "{\"work_dir\":\"{{repo}}/.works/kk-daily-publish3/$(TZ=America/Los_Angeles date +%Y%m%d)\",\"run_date\":\"$(TZ=America/Los_Angeles date +%F)\",\"window\":\"cn-peak\",\"live_channels\":\"wechatmp-pub,xhs-pub,weibo-pub,x-pub\"}" >> {{repo}}/.works/kk-daily-publish3/cn-peak.log 2>&1
|