@gobing-ai/knowledge-kit 0.0.17 → 0.0.19
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/README.md +74 -0
- package/dist/index.js +108 -107
- package/package.json +19 -1
- package/plugins/generations/content-gen/dist/index.js +42 -94
- package/plugins/generations/core-facts-gen/dist/index.js +51 -100
- package/plugins/generations/daily-article-gen/dist/index.js +57 -99
- package/plugins/generations/daily-article-gen/src/index.ts +12 -4
- package/plugins/generations/dailynews-gen/dist/index.js +57 -102
- package/plugins/generations/dailynews-gen/src/script-builder.ts +9 -1
- package/plugins/generations/episode-plan-gen/dist/index.js +120 -113
- package/plugins/generations/episode-plan-gen/src/index.ts +102 -22
- package/plugins/generations/image-gen/dist/index.js +63 -115
- package/plugins/generations/news-report-gen/dist/index.js +117 -99
- package/plugins/generations/news-report-gen/src/index.ts +117 -1
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +1 -1
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +8 -0
- package/plugins/generations/voice-gen/dist/index.js +161 -120
- package/plugins/generations/voice-gen/src/concat.ts +34 -0
- package/plugins/generations/voice-gen/src/index.ts +14 -4
- package/plugins/generations/voice-gen/src/voicebox-server.ts +91 -0
- package/plugins/generations/voice-gen/src/voicescript.ts +8 -0
- package/plugins/ingestions/aihot-ingest/dist/index.js +47 -97
- package/plugins/ingestions/horizon-ingest/dist/index.js +47 -96
- package/plugins/ingestions/karakeep-local/dist/index.js +217 -266
- package/plugins/ingestions/last30days-ingest/dist/index.js +42 -91
- package/plugins/ingestions/web-search/dist/index.js +68 -117
- package/plugins/kk/commands/workflow-run.md +10 -6
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/kk-workflow-stages.ts +376 -70
- package/plugins/kk/skills/publish/SKILL.md +80 -0
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +136 -224
- package/plugins/kk/workflows/leftover-publish-fanout-example.yaml +53 -0
- package/plugins/publishings/emdash-pub/dist/index.js +42 -91
- package/plugins/publishings/infoq-pub/dist/index.js +21994 -0
- package/plugins/publishings/infoq-pub/package.json +17 -0
- package/plugins/publishings/infoq-pub/plugin.json +7 -0
- package/plugins/publishings/infoq-pub/src/index.ts +123 -0
- package/plugins/publishings/infoq-pub/tsconfig.json +4 -0
- package/plugins/publishings/juejin-pub/dist/index.js +21994 -0
- package/plugins/publishings/juejin-pub/package.json +17 -0
- package/plugins/publishings/juejin-pub/plugin.json +7 -0
- package/plugins/publishings/juejin-pub/src/index.ts +123 -0
- package/plugins/publishings/juejin-pub/tsconfig.json +4 -0
- package/plugins/publishings/medium-pub/dist/index.js +22041 -0
- package/plugins/publishings/medium-pub/package.json +17 -0
- package/plugins/publishings/medium-pub/plugin.json +7 -0
- package/plugins/publishings/medium-pub/src/index.ts +215 -0
- package/plugins/publishings/medium-pub/tsconfig.json +4 -0
- package/plugins/publishings/podcast-pub/dist/index.js +183 -123
- package/plugins/publishings/podcast-pub/src/index.ts +47 -3
- package/plugins/publishings/podcast-pub/src/show-notes.ts +153 -34
- package/plugins/publishings/qiita-pub/dist/index.js +42 -91
- package/plugins/publishings/substack-pub/dist/index.js +21994 -0
- package/plugins/publishings/substack-pub/package.json +17 -0
- package/plugins/publishings/substack-pub/plugin.json +7 -0
- package/plugins/publishings/substack-pub/src/index.ts +123 -0
- package/plugins/publishings/substack-pub/tsconfig.json +4 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +172 -103
- package/plugins/publishings/surfdash-pub/src/index.ts +32 -2
- package/plugins/publishings/wechatmp-pub/dist/index.js +22119 -0
- package/plugins/publishings/wechatmp-pub/package.json +17 -0
- package/plugins/publishings/wechatmp-pub/plugin.json +7 -0
- package/plugins/publishings/wechatmp-pub/src/index.ts +304 -0
- package/plugins/publishings/wechatmp-pub/tsconfig.json +4 -0
- package/plugins/publishings/x-pub/dist/index.js +22110 -0
- package/plugins/publishings/x-pub/package.json +17 -0
- package/plugins/publishings/x-pub/plugin.json +7 -0
- package/plugins/publishings/x-pub/src/index.ts +222 -0
- package/plugins/publishings/x-pub/tsconfig.json +4 -0
- package/plugins/publishings/xhs-pub/dist/index.js +21994 -0
- package/plugins/publishings/xhs-pub/package.json +17 -0
- package/plugins/publishings/xhs-pub/plugin.json +7 -0
- package/plugins/publishings/xhs-pub/src/index.ts +123 -0
- package/plugins/publishings/xhs-pub/tsconfig.json +4 -0
- package/plugins/publishings/zenn-pub/dist/index.js +42 -91
|
@@ -23,11 +23,13 @@
|
|
|
23
23
|
* The YAML prelude resolves this file as `$pkg/plugins/kk/scripts/kk-workflow-stages.ts` where `pkg`
|
|
24
24
|
* comes from `realpath "$(command -v kk)"` (the installed package root), and falls back to the
|
|
25
25
|
* cwd-relative `plugins/kk/scripts/kk-workflow-stages.ts` when that copy is absent — the checkout
|
|
26
|
-
* tier kk-storm-research's render has always relied on, preserved on purpose.
|
|
26
|
+
* tier kk-storm-research's render has always relied on, preserved on purpose. The daily-voice
|
|
27
|
+
* workflow reaches the same script through `kk stage` (apps/cli/src/stage.ts), which applies the
|
|
28
|
+
* same two resolution tiers in TypeScript.
|
|
27
29
|
*
|
|
28
|
-
* SYNC: plugins/kk/workflows/{kk-solo-podcast,kk-itc,kk-storm-research
|
|
29
|
-
*
|
|
30
|
-
* four workflow static tests match them.
|
|
30
|
+
* SYNC: plugins/kk/workflows/{kk-solo-podcast,kk-itc,kk-storm-research}.yaml shell this script
|
|
31
|
+
* (kk-daily-ai-voice goes through `kk stage`). Keep message substrings stable —
|
|
32
|
+
* apps/cli/tests/kk-workflow-stages.test.ts and the four workflow static tests match them.
|
|
31
33
|
*/
|
|
32
34
|
import { spawnSync } from 'node:child_process';
|
|
33
35
|
import { createHash } from 'node:crypto';
|
|
@@ -43,6 +45,7 @@ import {
|
|
|
43
45
|
unlinkSync,
|
|
44
46
|
writeFileSync,
|
|
45
47
|
} from 'node:fs';
|
|
48
|
+
import { homedir } from 'node:os';
|
|
46
49
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
47
50
|
|
|
48
51
|
const SCRIPTS_DIR = import.meta.dir;
|
|
@@ -500,6 +503,16 @@ function metadataOf(value: Record<string, unknown> | null): Record<string, unkno
|
|
|
500
503
|
return metadata !== null && typeof metadata === 'object' ? (metadata as Record<string, unknown>) : {};
|
|
501
504
|
}
|
|
502
505
|
|
|
506
|
+
/**
|
|
507
|
+
* Required stage input: a missing or unparsable file fails the stage naming the path, so a
|
|
508
|
+
* run-date/path mismatch surfaces as its real cause instead of a downstream "field missing" error.
|
|
509
|
+
*/
|
|
510
|
+
function requireJson(path: string, label: string): Record<string, unknown> {
|
|
511
|
+
const parsed = readJson(path);
|
|
512
|
+
if (parsed === null) failWith(1, `${label}: required input not found or unparsable: ${path}`);
|
|
513
|
+
return parsed;
|
|
514
|
+
}
|
|
515
|
+
|
|
503
516
|
/** `jq -e '.targets[]? | select(.target == t and .ok == true and .status == "published")'`. */
|
|
504
517
|
function targetPublished(path: string, target: string): boolean {
|
|
505
518
|
const parsed = readJson(path);
|
|
@@ -524,15 +537,29 @@ function dailyPrepare(args: string[]): void {
|
|
|
524
537
|
last30daysEnabled = '',
|
|
525
538
|
topic = '',
|
|
526
539
|
horizonHours = '',
|
|
540
|
+
runDateArg = '',
|
|
527
541
|
] = args;
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
542
|
+
// Empty work_dir composes the real workspace from the operator's works_dir
|
|
543
|
+
// (workflow-run.md §2 precedence: KK_WORKS_DIR > config works_dir > ~/.config/kk/works):
|
|
544
|
+
// $works_dir/kk-daily-ai-voice/<stamp>
|
|
545
|
+
// The stamp is digits-only (dashes stripped from the caller's run_date, else today) so a
|
|
546
|
+
// resumed/retried run whose persisted run_date var already carries the digits-only
|
|
547
|
+
// run-date.txt value re-derives the same directory.
|
|
548
|
+
const dirDate = runDateArg ? runDateArg.replaceAll('-', '') : formatRunDate();
|
|
549
|
+
const workDir = resolve(workDirArg || join(resolveWorksDir(), 'kk-daily-ai-voice', dirDate));
|
|
550
|
+
timed(workDir, 'prepare', () => {
|
|
531
551
|
for (const dir of DAILY_DIRS)
|
|
532
552
|
sted('mkdir', join(workDir, dir), () => mkdirSync(join(workDir, dir), { recursive: true }));
|
|
533
553
|
sted('write', join(workDir, 'run-date.txt'), () =>
|
|
534
|
-
writeFileSync(join(workDir, 'run-date.txt'), `${
|
|
554
|
+
writeFileSync(join(workDir, 'run-date.txt'), `${dirDate}\n`),
|
|
535
555
|
);
|
|
556
|
+
// Anchor for the workflow: shell actions cannot set vars, so the YAML reads the
|
|
557
|
+
// effective work_dir back into vars.work_dir from this cwd-relative file.
|
|
558
|
+
const anchorDir = join('.spur', 'runs', 'kk-daily-ai-voice');
|
|
559
|
+
sted('write', join(anchorDir, 'work-dir.txt'), () => {
|
|
560
|
+
mkdirSync(anchorDir, { recursive: true });
|
|
561
|
+
writeFileSync(join(anchorDir, 'work-dir.txt'), `${workDir}\n`);
|
|
562
|
+
});
|
|
536
563
|
sted('write', join(workDir, '1-ingest/source.json'), () =>
|
|
537
564
|
writeJsonText(join(workDir, '1-ingest/source.json'), {
|
|
538
565
|
limit: Number(limit),
|
|
@@ -562,6 +589,35 @@ function formatRunDate(): string {
|
|
|
562
589
|
return `${now.getFullYear()}${month}${day}`;
|
|
563
590
|
}
|
|
564
591
|
|
|
592
|
+
/** Expand the `~/` and `$HOME/` prefixes the config examples use. */
|
|
593
|
+
function expandHome(p: string): string {
|
|
594
|
+
if (p.startsWith('~/')) return join(homedir(), p.slice(2));
|
|
595
|
+
if (p.startsWith('$HOME/')) return join(homedir(), p.slice('$HOME/'.length));
|
|
596
|
+
return p;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* works_dir for composed daily workspaces — workflow-run.md §2 precedence:
|
|
601
|
+
* KK_WORKS_DIR env > config works_dir > compiled default ~/.config/kk/works.
|
|
602
|
+
* Unreadable/unparseable config falls through to the default (pluginEnvFromConfig
|
|
603
|
+
* tolerates the same), never fails the run.
|
|
604
|
+
*/
|
|
605
|
+
function resolveWorksDir(): string {
|
|
606
|
+
const fromEnv = process.env.KK_WORKS_DIR;
|
|
607
|
+
if (fromEnv) return fromEnv;
|
|
608
|
+
const configPath = process.env.KK_CONFIG || join(homedir(), '.config/kk/config.yaml');
|
|
609
|
+
if (existsSync(configPath)) {
|
|
610
|
+
try {
|
|
611
|
+
const parsed = Bun.YAML.parse(readFileSync(configPath, 'utf-8')) as Record<string, unknown> | null;
|
|
612
|
+
const worksDir = parsed !== null && typeof parsed === 'object' ? parsed.works_dir : undefined;
|
|
613
|
+
if (typeof worksDir === 'string' && worksDir !== '') return expandHome(worksDir);
|
|
614
|
+
} catch {
|
|
615
|
+
// fall through to the compiled default
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return join(homedir(), '.config/kk/works');
|
|
619
|
+
}
|
|
620
|
+
|
|
565
621
|
function dailyCollectFacts(args: string[]): void {
|
|
566
622
|
const [workDir = '', runDate = '', pluginsPath = ''] = args;
|
|
567
623
|
const content = join(workDir, '2-facts', `${runDate}_02_collect_content.json`);
|
|
@@ -752,6 +808,10 @@ function dailyArticle(args: string[]): void {
|
|
|
752
808
|
console.log(`article cached: ${articleMd}`);
|
|
753
809
|
return;
|
|
754
810
|
}
|
|
811
|
+
// Agent-written prose body (workflow `article-write` state) replaces the mechanical
|
|
812
|
+
// per-item render when present — the 3-format split: surfdash gets an article,
|
|
813
|
+
// not a news list (2026-09-16 feedback).
|
|
814
|
+
const proseBody = join(workDir, '2-article', `${runDate}_06_article-write_body.md`);
|
|
755
815
|
kk(
|
|
756
816
|
[
|
|
757
817
|
'executor',
|
|
@@ -764,7 +824,12 @@ function dailyArticle(args: string[]): void {
|
|
|
764
824
|
...pluginsArgs(pluginsPath),
|
|
765
825
|
],
|
|
766
826
|
{
|
|
767
|
-
env: {
|
|
827
|
+
env: {
|
|
828
|
+
ARTICLE_DATE: runDate,
|
|
829
|
+
// Plugin entries run with cwd = plugin dir (invoke.ts), so relative
|
|
830
|
+
// paths in env must be resolved or the plugin sees ENOENT.
|
|
831
|
+
...(isFile(proseBody) ? { ARTICLE_BODY_FILE: resolve(proseBody) } : {}),
|
|
832
|
+
},
|
|
768
833
|
},
|
|
769
834
|
);
|
|
770
835
|
const body = readJson(articleContent)?.body;
|
|
@@ -792,7 +857,10 @@ function dailyCoverNormalize(args: string[]): void {
|
|
|
792
857
|
function coverNormalizeBody(workDir: string, runDate: string): void {
|
|
793
858
|
const png = join(workDir, '2-cover', 'content-cover-01.png');
|
|
794
859
|
const jpg = join(workDir, '2-cover', 'content-cover-01.jpg');
|
|
795
|
-
|
|
860
|
+
// Must match the name dailyPublishPrep and the article->cover workflow guard read:
|
|
861
|
+
// `${runDate}_07_cover_cover.png` (a plain `cover-${runDate}.png` left options.coverPath
|
|
862
|
+
// undefined and silently dropped the cover from both podcast and surfdash — 2026-09-16 run).
|
|
863
|
+
const out = join(workDir, '2-cover', `${runDate}_07_cover_cover.png`);
|
|
796
864
|
const source = isFile(png) ? png : isFile(jpg) ? jpg : '';
|
|
797
865
|
if (source === '') failWith(1, 'cover normalize: no content-cover-01.{png,jpg} artifact');
|
|
798
866
|
if (source === jpg) {
|
|
@@ -815,7 +883,7 @@ function coverNormalizeBody(workDir: string, runDate: string): void {
|
|
|
815
883
|
// `rm -f` is silent when the file is already gone.
|
|
816
884
|
}
|
|
817
885
|
}
|
|
818
|
-
console.log(`cover normalized:
|
|
886
|
+
console.log(`cover normalized: ${runDate}_07_cover_cover.png`);
|
|
819
887
|
}
|
|
820
888
|
|
|
821
889
|
function dailyScript(args: string[]): void {
|
|
@@ -864,7 +932,17 @@ function dailyWrapDocs(args: string[]): void {
|
|
|
864
932
|
}
|
|
865
933
|
|
|
866
934
|
function dailyGenerate(args: string[]): void {
|
|
867
|
-
const [workDir = '', runDate = '', pluginsPath = '', transcodeMp3 = '',
|
|
935
|
+
const [workDir = '', runDate = '', pluginsPath = '', transcodeMp3 = '', vdriver = ''] = args;
|
|
936
|
+
// 20260917 review: the workflow picks a voice driver by short name; both drivers' envs are
|
|
937
|
+
// passed unconditionally so switching `vdriver` needs no shell changes. Unknown -> fail loud.
|
|
938
|
+
const VOICE_DRIVERS: Record<string, string> = { ominivoice: 'omni-voice-gen', voicebox: 'voice-gen' };
|
|
939
|
+
const voiceGenerator = VOICE_DRIVERS[vdriver];
|
|
940
|
+
if (!voiceGenerator) {
|
|
941
|
+
failWith(
|
|
942
|
+
1,
|
|
943
|
+
`generate: unknown vdriver '${vdriver}' (expected one of: ${Object.keys(VOICE_DRIVERS).join(', ')})`,
|
|
944
|
+
);
|
|
945
|
+
}
|
|
868
946
|
const docs = join(workDir, '3-audio', `${runDate}_10_wrap-docs_docs.json`);
|
|
869
947
|
const content = join(workDir, '3-audio', `${runDate}_11_generate_content.json`);
|
|
870
948
|
timed(workDir, 'generate', () => {
|
|
@@ -881,6 +959,111 @@ function dailyGenerate(args: string[]): void {
|
|
|
881
959
|
});
|
|
882
960
|
}
|
|
883
961
|
|
|
962
|
+
/**
|
|
963
|
+
* 20260917 review #7: prepend the show's 8s intro sting to the generated audio.
|
|
964
|
+
* `intro_music` empty -> skip. Writes `<stem>.intro.wav` (original render kept), points
|
|
965
|
+
* `audioPath` at the merged file, extends `duration`, marks `introMerged` (idempotent).
|
|
966
|
+
*/
|
|
967
|
+
function dailyIntro(args: string[]): void {
|
|
968
|
+
const [workDir = '', runDate = '', introSrc = ''] = args;
|
|
969
|
+
if (!introSrc || introSrc.trim() === '') {
|
|
970
|
+
console.log('intro-music: intro_music var empty — skip');
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
const contentPath = join(workDir, '3-audio', `${runDate}_11_generate_content.json`);
|
|
974
|
+
timed(workDir, 'intro-music', () => {
|
|
975
|
+
const content = requireJson(contentPath, 'intro-music');
|
|
976
|
+
const meta = metadataOf(content);
|
|
977
|
+
if (meta.introMerged === true) {
|
|
978
|
+
console.log(`intro-music cached: ${contentPath}`);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
const rawFile = meta.mp3Path ?? meta.audioPath;
|
|
982
|
+
if (typeof rawFile !== 'string' || rawFile === '') {
|
|
983
|
+
failWith(1, 'intro-music: voice content carries no mp3Path/audioPath');
|
|
984
|
+
}
|
|
985
|
+
const introPath = resolve(introSrc);
|
|
986
|
+
if (!existsSync(introPath)) failWith(1, `intro-music: intro file not found: ${introPath}`);
|
|
987
|
+
const mainPath = resolve(rawFile);
|
|
988
|
+
if (!existsSync(mainPath)) failWith(1, `intro-music: generated audio not found: ${mainPath}`);
|
|
989
|
+
const mergedPath = mainPath.replace(/\.wav$/, '.intro.wav');
|
|
990
|
+
const probe = (file: string, entries: string): string =>
|
|
991
|
+
spawnSync('ffprobe', [
|
|
992
|
+
'-v',
|
|
993
|
+
'error',
|
|
994
|
+
'-select_streams',
|
|
995
|
+
'a:0',
|
|
996
|
+
'-show_entries',
|
|
997
|
+
entries,
|
|
998
|
+
'-of',
|
|
999
|
+
'csv=p=0',
|
|
1000
|
+
file,
|
|
1001
|
+
])
|
|
1002
|
+
.stdout?.toString()
|
|
1003
|
+
.trim() ?? '';
|
|
1004
|
+
const mainDur = Number.parseFloat(probe(mainPath, 'format=duration'));
|
|
1005
|
+
const introDur = Number.parseFloat(probe(introPath, 'format=duration'));
|
|
1006
|
+
if (!Number.isFinite(mainDur) || !Number.isFinite(introDur)) {
|
|
1007
|
+
failWith(1, `intro-music: ffprobe could not read durations (main=${mainDur} intro=${introDur})`);
|
|
1008
|
+
}
|
|
1009
|
+
const rate = probe(mainPath, 'stream=sample_rate') || '24000'; // match the generated audio's rate; mono s16 both sides
|
|
1010
|
+
const norm = (label: string): string =>
|
|
1011
|
+
`[${label}:a]aresample=${rate},aformat=sample_fmts=s16:channel_layouts=mono[${label}]`;
|
|
1012
|
+
const fc = `${norm('0')};${norm('1')};[0][1]concat=n=2:v=0:a=1[out]`;
|
|
1013
|
+
const conv = spawnSync('ffmpeg', [
|
|
1014
|
+
'-y',
|
|
1015
|
+
'-hide_banner',
|
|
1016
|
+
'-loglevel',
|
|
1017
|
+
'error',
|
|
1018
|
+
'-i',
|
|
1019
|
+
introPath,
|
|
1020
|
+
'-i',
|
|
1021
|
+
mainPath,
|
|
1022
|
+
'-filter_complex',
|
|
1023
|
+
fc,
|
|
1024
|
+
'-map',
|
|
1025
|
+
'[out]',
|
|
1026
|
+
'-c:a',
|
|
1027
|
+
'pcm_s16le',
|
|
1028
|
+
mergedPath,
|
|
1029
|
+
]);
|
|
1030
|
+
if (conv.status !== 0 || !existsSync(mergedPath)) {
|
|
1031
|
+
failWith(1, `intro-music: ffmpeg concat failed: ${conv.stderr?.toString().trim()}`);
|
|
1032
|
+
}
|
|
1033
|
+
if (meta.mp3Path && typeof meta.mp3Path === 'string') {
|
|
1034
|
+
// transcode_mp3=true: keep the mp3 path honest by re-transcoding the merged wav.
|
|
1035
|
+
const mp3 = spawnSync('ffmpeg', [
|
|
1036
|
+
'-y',
|
|
1037
|
+
'-hide_banner',
|
|
1038
|
+
'-loglevel',
|
|
1039
|
+
'error',
|
|
1040
|
+
'-i',
|
|
1041
|
+
mergedPath,
|
|
1042
|
+
'-codec:a',
|
|
1043
|
+
'libmp3lame',
|
|
1044
|
+
'-qscale:a',
|
|
1045
|
+
'2',
|
|
1046
|
+
resolve(meta.mp3Path),
|
|
1047
|
+
]);
|
|
1048
|
+
if (mp3.status !== 0) failWith(1, `intro-music: mp3 re-transcode failed: ${mp3.stderr?.toString().trim()}`);
|
|
1049
|
+
}
|
|
1050
|
+
const mergedDur = Number.parseFloat(probe(mergedPath, 'format=duration')) || mainDur + introDur;
|
|
1051
|
+
const updated = {
|
|
1052
|
+
...content,
|
|
1053
|
+
metadata: {
|
|
1054
|
+
...meta,
|
|
1055
|
+
audioPath: mergedPath,
|
|
1056
|
+
duration: mergedDur,
|
|
1057
|
+
introMerged: true,
|
|
1058
|
+
},
|
|
1059
|
+
};
|
|
1060
|
+
sted('write', contentPath, () => writeJsonText(contentPath, updated));
|
|
1061
|
+
console.log(
|
|
1062
|
+
`intro-music: merged ${introDur.toFixed(1)}s intro -> ${mergedPath} (${mergedDur.toFixed(1)}s total)`,
|
|
1063
|
+
);
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
884
1067
|
function dailyQualityReport(args: string[]): void {
|
|
885
1068
|
const [workDir = '', runDate = ''] = args;
|
|
886
1069
|
const content = join(workDir, '3-audio', `${runDate}_11_generate_content.json`);
|
|
@@ -904,7 +1087,7 @@ function dailyPublishPrep(args: string[]): void {
|
|
|
904
1087
|
const prepPath = join(workDir, '3-publish', `${runDate}_13_publish-prep_content.json`);
|
|
905
1088
|
const coverPath = join(workDir, '2-cover', `${runDate}_07_cover_cover.png`);
|
|
906
1089
|
timed(workDir, 'publish-prep', () => {
|
|
907
|
-
const article =
|
|
1090
|
+
const article = requireJson(articlePath, 'publish-prep');
|
|
908
1091
|
const audioMeta = metadataOf(readJson(audioPath));
|
|
909
1092
|
const rawFile = audioMeta.mp3Path ?? audioMeta.audioPath;
|
|
910
1093
|
if (typeof rawFile !== 'string' || rawFile === '') {
|
|
@@ -912,13 +1095,21 @@ function dailyPublishPrep(args: string[]): void {
|
|
|
912
1095
|
}
|
|
913
1096
|
const audioFile = resolve(rawFile);
|
|
914
1097
|
const resolvedCover = resolve(coverPath);
|
|
1098
|
+
const hasCover = existsSync(resolvedCover);
|
|
1099
|
+
// podcast-pub's documented contract reads metadata.coverImage (map.ts); options.coverPath
|
|
1100
|
+
// alone was silently dropped by the mapper — episodes published without a cover.
|
|
915
1101
|
const merged = {
|
|
916
1102
|
...article,
|
|
917
|
-
metadata: {
|
|
1103
|
+
metadata: {
|
|
1104
|
+
...metadataOf(article),
|
|
1105
|
+
audioFile,
|
|
1106
|
+
runDate,
|
|
1107
|
+
...(hasCover ? { coverImage: resolvedCover } : {}),
|
|
1108
|
+
},
|
|
918
1109
|
options: {
|
|
919
1110
|
audioFile,
|
|
920
1111
|
durationSec: audioMeta.duration,
|
|
921
|
-
coverPath:
|
|
1112
|
+
coverPath: hasCover ? resolvedCover : undefined,
|
|
922
1113
|
},
|
|
923
1114
|
};
|
|
924
1115
|
sted('write', prepPath, () => writeJsonText(prepPath, merged));
|
|
@@ -964,6 +1155,9 @@ function dailyShowNotes(args: string[]): void {
|
|
|
964
1155
|
const result = join(workDir, '3-publish', `${runDate}_14_publish-surfdash_result.json`);
|
|
965
1156
|
const out = join(workDir, '3-publish', `${runDate}_15_show-notes_show-notes.md`);
|
|
966
1157
|
const plan = join(workDir, '2-plan', `${runDate}_03_plan_plan.json`);
|
|
1158
|
+
// Generated-episode content carries `metadata.segments[]` — the source of real
|
|
1159
|
+
// audio offsets for the show-notes timeline (jump-to-item timestamps).
|
|
1160
|
+
const audioContent = join(workDir, '3-audio', `${runDate}_11_generate_content.json`);
|
|
967
1161
|
timed(workDir, 'show-notes', () => {
|
|
968
1162
|
const effective = readJson(article)?.title;
|
|
969
1163
|
const titleArg = title !== '' ? title : typeof effective === 'string' ? effective : '';
|
|
@@ -974,6 +1168,7 @@ function dailyShowNotes(args: string[]): void {
|
|
|
974
1168
|
PODCAST_PUB_RUN_DATE: runDate,
|
|
975
1169
|
PODCAST_PUB_BRIEF_MODE: 'true',
|
|
976
1170
|
PODCAST_PUB_ARTICLE_POST_PATH: postPath,
|
|
1171
|
+
...(isFile(audioContent) ? { PODCAST_PUB_AUDIO_CONTENT: audioContent } : {}),
|
|
977
1172
|
},
|
|
978
1173
|
});
|
|
979
1174
|
});
|
|
@@ -1004,7 +1199,7 @@ function dailyPublishPodcast(args: string[]): void {
|
|
|
1004
1199
|
return;
|
|
1005
1200
|
}
|
|
1006
1201
|
try {
|
|
1007
|
-
const content =
|
|
1202
|
+
const content = requireJson(prep, 'publish-podcast');
|
|
1008
1203
|
const showNotes = readFileSync(notes, 'utf-8');
|
|
1009
1204
|
const merged = { ...content, metadata: { ...metadataOf(content), showNotes } };
|
|
1010
1205
|
sted('write', prep, () => writeJsonText(prep, merged));
|
|
@@ -1084,6 +1279,95 @@ function dailyPublishPartial(args: string[]): void {
|
|
|
1084
1279
|
});
|
|
1085
1280
|
}
|
|
1086
1281
|
|
|
1282
|
+
/**
|
|
1283
|
+
* 20260917 review #4/#6: after both publishes land, patch the surfdash disk posts —
|
|
1284
|
+
* reuse the podcast-hosted cover URL in frontmatter (image/og_image; no re-upload; fallback:
|
|
1285
|
+
* copy the local cover into assets/) and append a podcast cross-link at the end of zh/en/ja.
|
|
1286
|
+
* Idempotent per post; commits + pushes the surfdash checkout (SURFDASH_AUTO_PUSH=0 keeps commit-only).
|
|
1287
|
+
*/
|
|
1288
|
+
function dailyPatchSurfdash(args: string[]): void {
|
|
1289
|
+
const [workDir = '', runDate = '', surfdashRoot = ''] = args;
|
|
1290
|
+
if (!surfdashRoot || !existsSync(surfdashRoot)) {
|
|
1291
|
+
console.log(`patch-surfdash: surfdash root '${surfdashRoot}' not found — skip`);
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
timed(workDir, 'patch-surfdash', () => {
|
|
1295
|
+
const sdResult = join(workDir, '3-publish', `${runDate}_14_publish-surfdash_result.json`);
|
|
1296
|
+
const podcastResult = join(workDir, '3-publish', `${runDate}_16_publish-podcast_result.json`);
|
|
1297
|
+
if (!isFile(sdResult) || !isFile(podcastResult)) {
|
|
1298
|
+
failWith(1, 'patch-surfdash: missing surfdash/podcast publish results — run the publish states first');
|
|
1299
|
+
}
|
|
1300
|
+
const targetOf = (path: string): Record<string, unknown> => {
|
|
1301
|
+
const parsed = readJson(path) as { targets?: Array<Record<string, unknown>> };
|
|
1302
|
+
return parsed.targets?.[0] ?? {};
|
|
1303
|
+
};
|
|
1304
|
+
const sd = targetOf(sdResult);
|
|
1305
|
+
const pod = targetOf(podcastResult);
|
|
1306
|
+
const postPath = (sd.metadata as Record<string, unknown> | undefined)?.postPath;
|
|
1307
|
+
if (typeof postPath !== 'string' || postPath === '')
|
|
1308
|
+
failWith(1, 'patch-surfdash: surfdash result carries no postPath');
|
|
1309
|
+
const slugDir = dirname(postPath);
|
|
1310
|
+
const showNotesUrl = typeof pod.url === 'string' ? pod.url : '';
|
|
1311
|
+
const coverUrl = ((pod.metadata as Record<string, unknown> | undefined)?.imageUrl as string | undefined) ?? '';
|
|
1312
|
+
const localCover = join(workDir, '2-cover', `${runDate}_07_cover_cover.png`);
|
|
1313
|
+
const dateIso = `${runDate.slice(0, 4)}-${runDate.slice(4, 6)}-${runDate.slice(6, 8)}`;
|
|
1314
|
+
const crossLink: Record<string, string> = {
|
|
1315
|
+
zh: `🎧 本期已同步制作为播客节目,[点击收听《每日 AI 资讯 · ${dateIso}》](${showNotesUrl})。`,
|
|
1316
|
+
en: `🎧 This episode is also available as a podcast: [listen to the Daily AI Briefing · ${dateIso}](${showNotesUrl}).`,
|
|
1317
|
+
ja: `🎧 本エピソードはポッドキャストでも配信中:[Daily AI Briefing · ${dateIso} を聴く](${showNotesUrl})。`,
|
|
1318
|
+
};
|
|
1319
|
+
let changed = 0;
|
|
1320
|
+
for (const [locale, dir] of [
|
|
1321
|
+
['zh', slugDir],
|
|
1322
|
+
['en', join(dirname(slugDir), 'en', basename(slugDir))],
|
|
1323
|
+
['ja', join(dirname(slugDir), 'ja', basename(slugDir))],
|
|
1324
|
+
] as const) {
|
|
1325
|
+
const indexMd = join(dir, 'index.md');
|
|
1326
|
+
if (!existsSync(indexMd)) {
|
|
1327
|
+
console.log(`patch-surfdash: ${locale} post not published (${indexMd}) — skip`);
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
let md = readFileSync(indexMd, 'utf-8');
|
|
1331
|
+
const fmEnd = md.indexOf('---', 3); // frontmatter is `---\n ... \n---\n`
|
|
1332
|
+
if (fmEnd < 0) failWith(1, `patch-surfdash: no frontmatter in ${indexMd}`);
|
|
1333
|
+
if (!/^image:/m.test(md.slice(0, fmEnd))) {
|
|
1334
|
+
let image = coverUrl;
|
|
1335
|
+
if (image === '' && existsSync(localCover)) {
|
|
1336
|
+
// Fallback for pre-#6 runs: copy the local cover instead of a hosted URL.
|
|
1337
|
+
const assets = join(dir, 'assets');
|
|
1338
|
+
mkdirSync(assets, { recursive: true });
|
|
1339
|
+
copyFileSync(localCover, join(assets, 'cover.png'));
|
|
1340
|
+
image = './assets/cover.png';
|
|
1341
|
+
}
|
|
1342
|
+
if (image !== '') {
|
|
1343
|
+
md = md.replace(/^publishDate: .*$/m, (m0) => `${m0}\nimage: '${image}'\nog_image: '${image}'`);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
if (showNotesUrl !== '' && !md.includes(showNotesUrl)) {
|
|
1347
|
+
md = `${md.replace(/\s*$/, '')}\n\n${crossLink[locale]}\n`;
|
|
1348
|
+
}
|
|
1349
|
+
if (md !== readFileSync(indexMd, 'utf-8')) {
|
|
1350
|
+
sted('write', indexMd, () => writeFileSync(indexMd, md));
|
|
1351
|
+
changed++;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
if (changed === 0) {
|
|
1355
|
+
console.log('patch-surfdash: nothing to change');
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
const git = (...a: string[]): void => {
|
|
1359
|
+
const r = spawnSync('git', ['-C', surfdashRoot, ...a]);
|
|
1360
|
+
if (r.status !== 0) failWith(1, `patch-surfdash: git ${a[0]} failed: ${r.stderr?.toString().trim()}`);
|
|
1361
|
+
};
|
|
1362
|
+
git('add', 'content/posts/articles');
|
|
1363
|
+
git('commit', '-m', `feat: cover + podcast cross-link for ${basename(slugDir)} (kk daily patch)`);
|
|
1364
|
+
if (process.env.SURFDASH_AUTO_PUSH !== '0') git('push');
|
|
1365
|
+
console.log(
|
|
1366
|
+
`patch-surfdash: patched ${changed} post(s), committed${process.env.SURFDASH_AUTO_PUSH !== '0' ? ' + pushed' : ' (push skipped: SURFDASH_AUTO_PUSH=0)'}`,
|
|
1367
|
+
);
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1087
1371
|
function dailyRunReport(args: string[]): void {
|
|
1088
1372
|
const [
|
|
1089
1373
|
workDir = '',
|
|
@@ -1108,6 +1392,9 @@ function dailyRunReport(args: string[]): void {
|
|
|
1108
1392
|
return;
|
|
1109
1393
|
}
|
|
1110
1394
|
sted('write', cands, () => writeJqText(cands, metadataOf(readJson(candidates)).docs ?? []));
|
|
1395
|
+
// Plugin env paths must be absolute: spawnPluginEntry runs plugins with cwd = the
|
|
1396
|
+
// plugin package dir, so a relative workDir silently misses every best-effort read.
|
|
1397
|
+
const absWorkDir = resolve(workDir);
|
|
1111
1398
|
kk(['executor', 'run', 'news-report-gen', '--in', cands, '--out', reportJson, ...pluginsArgs(pluginsPath)], {
|
|
1112
1399
|
env: {
|
|
1113
1400
|
QC_MIN_QUALITY: minQuality,
|
|
@@ -1115,13 +1402,19 @@ function dailyRunReport(args: string[]): void {
|
|
|
1115
1402
|
QC_MIN_URGENCY: minUrgency,
|
|
1116
1403
|
QC_MIN_IMPACT: minImpact,
|
|
1117
1404
|
NEWS_REPORT_REJECTED_FILE: join(
|
|
1118
|
-
|
|
1405
|
+
absWorkDir,
|
|
1119
1406
|
'2-plan',
|
|
1120
1407
|
`${runDate}_04_quality-control-content_candidates.rejected.json`,
|
|
1121
1408
|
),
|
|
1122
|
-
NEWS_REPORT_TIMING_FILE: join(
|
|
1409
|
+
NEWS_REPORT_TIMING_FILE: join(absWorkDir, 'step-timing.json'),
|
|
1123
1410
|
// 0139 R5b: the generate stage's QC audit, read best-effort (absent -> section omitted).
|
|
1124
|
-
NEWS_REPORT_QC_FILE: join(
|
|
1411
|
+
NEWS_REPORT_QC_FILE: join(absWorkDir, '3-audio', `${runDate}_11_generate_content.json`),
|
|
1412
|
+
// 20260917 funnel: the plan stage's full selection trail (every fetched title +
|
|
1413
|
+
// scores + disposition), rendered as the 选稿漏斗 section (absent -> omitted).
|
|
1414
|
+
NEWS_REPORT_PLAN_AUDIT_FILE: join(absWorkDir, '2-plan', `${runDate}_03_plan_plan.json.audit.json`),
|
|
1415
|
+
// 20260917 funnel: the blended fan-in input, rendered as the 语料构成
|
|
1416
|
+
// source/category composition (absent -> omitted).
|
|
1417
|
+
NEWS_REPORT_BLENDED_FILE: join(absWorkDir, '1-ingest', `${runDate}_02_collect_blended.json`),
|
|
1125
1418
|
NEWS_REPORT_DATE: runDate,
|
|
1126
1419
|
},
|
|
1127
1420
|
});
|
|
@@ -1132,68 +1425,78 @@ function dailyRunReport(args: string[]): void {
|
|
|
1132
1425
|
});
|
|
1133
1426
|
}
|
|
1134
1427
|
|
|
1135
|
-
|
|
1428
|
+
/**
|
|
1429
|
+
* Collapsed translate scaffolding (2026-09-16 feedback): surfdash-pub owns the
|
|
1430
|
+
* deterministic mechanics — `scaffold-locale` for both target locales — while the
|
|
1431
|
+
* workflow `translate` state's agent step owns the LLM translation of the drafts.
|
|
1432
|
+
* Writes one marker file per locale (zh post path + en/ja draft paths); empty
|
|
1433
|
+
* marker = locale absent (callers skip). Reuses an existing on-disk draft as the
|
|
1434
|
+
* cache across run retries — re-scaffolding would erase the agent's translation.
|
|
1435
|
+
*/
|
|
1436
|
+
function dailyTranslateLocalize(args: string[]): void {
|
|
1136
1437
|
const [workDir = '', runDate = '', pluginsPath = ''] = args;
|
|
1137
1438
|
const result = join(workDir, '3-publish', `${runDate}_17_publish_result.json`);
|
|
1138
|
-
const zhFile = join(workDir, '3-publish', `${runDate}_20_translate-
|
|
1139
|
-
|
|
1140
|
-
const scaffold = join(workDir, '3-publish', `${runDate}_20_translate-en_scaffold-en.json`);
|
|
1141
|
-
const scaffoldResult = join(workDir, '3-publish', `${runDate}_20_translate-en_scaffold-en-result.json`);
|
|
1142
|
-
timed(workDir, 'translate-en', () => {
|
|
1439
|
+
const zhFile = join(workDir, '3-publish', `${runDate}_20_translate-localize_zh-post-path.txt`);
|
|
1440
|
+
timed(workDir, 'translate-localize', () => {
|
|
1143
1441
|
const postPath = firstPostPath(readJson(result), 'surfdash-pub');
|
|
1144
1442
|
const zh = postPath.endsWith('index.md') ? postPath : '';
|
|
1145
1443
|
sted('write', zhFile, () => writeRaw(zhFile, zh));
|
|
1146
|
-
const en = zh.replace('/zh/', '/en/');
|
|
1147
|
-
sted('write', enFile, () => writeRaw(enFile, en));
|
|
1148
|
-
if (zh === '') {
|
|
1149
|
-
console.log('no zh post path — skip translate-en');
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
if (isFile(en)) {
|
|
1153
|
-
console.log(`en draft cached: ${en}`);
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1156
|
-
sted('write', scaffold, () =>
|
|
1157
|
-
writeJqText(scaffold, { body: '', options: { operation: 'scaffold-locale', postPath: zh, target: 'en' } }),
|
|
1158
|
-
);
|
|
1159
|
-
kk(['executor', 'run', 'surfdash-pub', '--in', scaffold, '--out', scaffoldResult, ...pluginsArgs(pluginsPath)]);
|
|
1160
|
-
const draftPath = metadataOf(readJson(scaffoldResult)).draftPath;
|
|
1161
|
-
sted('write', enFile, () => writeRaw(enFile, typeof draftPath === 'string' ? draftPath : ''));
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
function dailyTranslateJa(args: string[]): void {
|
|
1166
|
-
const [workDir = '', runDate = '', pluginsPath = ''] = args;
|
|
1167
|
-
const zhFile = join(workDir, '3-publish', `${runDate}_20_translate-en_zh-post-path.txt`);
|
|
1168
|
-
const jaFile = join(workDir, '3-publish', `${runDate}_22_translate-ja_ja-post-path.txt`);
|
|
1169
|
-
const scaffold = join(workDir, '3-publish', `${runDate}_22_translate-ja_scaffold-ja.json`);
|
|
1170
|
-
const scaffoldResult = join(workDir, '3-publish', `${runDate}_22_translate-ja_scaffold-ja-result.json`);
|
|
1171
|
-
timed(workDir, 'translate-ja', () => {
|
|
1172
|
-
const zh = isFile(zhFile) ? readFileSync(zhFile, 'utf-8') : '';
|
|
1173
|
-
const ja = zh.replace('/zh/', '/ja/');
|
|
1174
|
-
sted('write', jaFile, () => writeRaw(jaFile, ja));
|
|
1175
1444
|
if (zh === '') {
|
|
1176
|
-
|
|
1445
|
+
for (const target of ['en', 'ja'] as const) {
|
|
1446
|
+
sted(
|
|
1447
|
+
'write',
|
|
1448
|
+
join(workDir, '3-publish', `${runDate}_20_translate-localize_${target}-post-path.txt`),
|
|
1449
|
+
() =>
|
|
1450
|
+
writeRaw(
|
|
1451
|
+
join(workDir, '3-publish', `${runDate}_20_translate-localize_${target}-post-path.txt`),
|
|
1452
|
+
'',
|
|
1453
|
+
),
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
console.log('no zh post path — skip localize');
|
|
1177
1457
|
return;
|
|
1178
1458
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1459
|
+
for (const target of ['en', 'ja'] as const) {
|
|
1460
|
+
const markerFile = join(workDir, '3-publish', `${runDate}_20_translate-localize_${target}-post-path.txt`);
|
|
1461
|
+
const existing = zh.replace('/zh/', `/${target}/`);
|
|
1462
|
+
if (isFile(existing)) {
|
|
1463
|
+
sted('write', markerFile, () => writeRaw(markerFile, existing));
|
|
1464
|
+
console.log(`${target} draft cached: ${existing}`);
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
const scaffold = join(workDir, '3-publish', `${runDate}_20_translate-localize_scaffold-${target}.json`);
|
|
1468
|
+
const scaffoldResult = join(
|
|
1469
|
+
workDir,
|
|
1470
|
+
'3-publish',
|
|
1471
|
+
`${runDate}_20_translate-localize_scaffold-${target}-result.json`,
|
|
1472
|
+
);
|
|
1473
|
+
sted('write', scaffold, () =>
|
|
1474
|
+
writeJqText(scaffold, { body: '', options: { operation: 'scaffold-locale', postPath: zh, target } }),
|
|
1475
|
+
);
|
|
1476
|
+
kk([
|
|
1477
|
+
'executor',
|
|
1478
|
+
'run',
|
|
1479
|
+
'surfdash-pub',
|
|
1480
|
+
'--in',
|
|
1481
|
+
scaffold,
|
|
1482
|
+
'--out',
|
|
1483
|
+
scaffoldResult,
|
|
1484
|
+
...pluginsArgs(pluginsPath),
|
|
1485
|
+
]);
|
|
1486
|
+
const draftPath = metadataOf(readJson(scaffoldResult)).draftPath;
|
|
1487
|
+
sted('write', markerFile, () => writeRaw(markerFile, typeof draftPath === 'string' ? draftPath : ''));
|
|
1182
1488
|
}
|
|
1183
|
-
sted('write', scaffold, () =>
|
|
1184
|
-
writeJqText(scaffold, { body: '', options: { operation: 'scaffold-locale', postPath: zh, target: 'ja' } }),
|
|
1185
|
-
);
|
|
1186
|
-
kk(['executor', 'run', 'surfdash-pub', '--in', scaffold, '--out', scaffoldResult, ...pluginsArgs(pluginsPath)]);
|
|
1187
|
-
const draftPath = metadataOf(readJson(scaffoldResult)).draftPath;
|
|
1188
|
-
sted('write', jaFile, () => writeRaw(jaFile, typeof draftPath === 'string' ? draftPath : ''));
|
|
1189
1489
|
});
|
|
1190
1490
|
}
|
|
1191
1491
|
|
|
1192
1492
|
function dailyTranslateSync(args: string[]): void {
|
|
1193
|
-
const [workDir = '', runDate = '', pluginsPath = '',
|
|
1493
|
+
const [workDir = '', runDate = '', pluginsPath = '', zhPathFile = ''] = args;
|
|
1194
1494
|
const sync = join(workDir, '3-publish', `${runDate}_24_translate-sync_sync.json`);
|
|
1195
1495
|
const syncResult = join(workDir, '3-publish', `${runDate}_24_translate-sync_sync-result.json`);
|
|
1196
1496
|
timed(workDir, 'translate-sync', () => {
|
|
1497
|
+
// Reads the marker file itself — the collapsed `translate` state has no
|
|
1498
|
+
// cross-state var to hand the path through.
|
|
1499
|
+
const zhPostPath = isFile(zhPathFile) ? readFileSync(zhPathFile, 'utf-8') : '';
|
|
1197
1500
|
if (zhPostPath === '') {
|
|
1198
1501
|
console.log('no zh post path — skip translate-sync');
|
|
1199
1502
|
return;
|
|
@@ -1287,6 +1590,9 @@ if (import.meta.main)
|
|
|
1287
1590
|
case 'daily-generate':
|
|
1288
1591
|
dailyGenerate(rest);
|
|
1289
1592
|
break;
|
|
1593
|
+
case 'daily-intro':
|
|
1594
|
+
dailyIntro(rest);
|
|
1595
|
+
break;
|
|
1290
1596
|
case 'daily-quality-report':
|
|
1291
1597
|
dailyQualityReport(rest);
|
|
1292
1598
|
break;
|
|
@@ -1305,17 +1611,17 @@ if (import.meta.main)
|
|
|
1305
1611
|
case 'daily-publish-classify':
|
|
1306
1612
|
dailyPublishClassify(rest);
|
|
1307
1613
|
break;
|
|
1614
|
+
case 'daily-patch-surfdash':
|
|
1615
|
+
dailyPatchSurfdash(rest);
|
|
1616
|
+
break;
|
|
1308
1617
|
case 'daily-publish-partial':
|
|
1309
1618
|
dailyPublishPartial(rest);
|
|
1310
1619
|
break;
|
|
1311
1620
|
case 'daily-run-report':
|
|
1312
1621
|
dailyRunReport(rest);
|
|
1313
1622
|
break;
|
|
1314
|
-
case 'daily-translate-
|
|
1315
|
-
|
|
1316
|
-
break;
|
|
1317
|
-
case 'daily-translate-ja':
|
|
1318
|
-
dailyTranslateJa(rest);
|
|
1623
|
+
case 'daily-translate-localize':
|
|
1624
|
+
dailyTranslateLocalize(rest);
|
|
1319
1625
|
break;
|
|
1320
1626
|
case 'daily-translate-sync':
|
|
1321
1627
|
dailyTranslateSync(rest);
|