@gobing-ai/knowledge-kit 0.0.18 → 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/dist/index.js +62 -7
- package/package.json +1 -1
- package/plugins/generations/voice-gen/dist/index.js +60 -3
- package/plugins/generations/voice-gen/src/index.ts +8 -0
- package/plugins/generations/voice-gen/src/voicebox-server.ts +91 -0
- package/plugins/kk/commands/workflow-run.md +10 -6
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/kk-workflow-stages.ts +57 -9
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +42 -130
package/dist/index.js
CHANGED
|
@@ -26420,24 +26420,38 @@ function spawnPluginEntry(opts) {
|
|
|
26420
26420
|
stderr: proc.stderr.toString().trim()
|
|
26421
26421
|
};
|
|
26422
26422
|
}
|
|
26423
|
-
|
|
26423
|
+
var KILL_TREE_SETTLE_MS = 100;
|
|
26424
|
+
function listChildren(pid) {
|
|
26424
26425
|
let stdout;
|
|
26425
26426
|
try {
|
|
26426
26427
|
stdout = Bun.spawnSync(["pgrep", "-P", String(pid)]).stdout.toString();
|
|
26427
26428
|
} catch {
|
|
26428
26429
|
stdout = "";
|
|
26429
26430
|
}
|
|
26430
|
-
|
|
26431
|
-
`))
|
|
26432
|
-
|
|
26433
|
-
|
|
26434
|
-
|
|
26435
|
-
|
|
26431
|
+
return stdout.trim().split(`
|
|
26432
|
+
`).map(Number).filter((child) => Number.isInteger(child) && child > 0);
|
|
26433
|
+
}
|
|
26434
|
+
function killSubtree(pid) {
|
|
26435
|
+
for (const child of listChildren(pid)) {
|
|
26436
|
+
killSubtree(child);
|
|
26436
26437
|
}
|
|
26437
26438
|
try {
|
|
26438
26439
|
process.kill(pid, "SIGKILL");
|
|
26439
26440
|
} catch {}
|
|
26440
26441
|
}
|
|
26442
|
+
async function killProcessTree(pid) {
|
|
26443
|
+
const sweep = () => {
|
|
26444
|
+
for (const child of listChildren(pid)) {
|
|
26445
|
+
killSubtree(child);
|
|
26446
|
+
}
|
|
26447
|
+
};
|
|
26448
|
+
sweep();
|
|
26449
|
+
await Bun.sleep(KILL_TREE_SETTLE_MS);
|
|
26450
|
+
sweep();
|
|
26451
|
+
try {
|
|
26452
|
+
process.kill(pid, "SIGKILL");
|
|
26453
|
+
} catch {}
|
|
26454
|
+
}
|
|
26441
26455
|
async function spawnPluginEntryAsync(opts) {
|
|
26442
26456
|
let timedOut = false;
|
|
26443
26457
|
let timer;
|
|
@@ -27445,6 +27459,46 @@ function registerPluginCommand(program2) {
|
|
|
27445
27459
|
registerStatusVerb(plugins, "doctor", "Alias for `plugin status` (deprecated)", true);
|
|
27446
27460
|
}
|
|
27447
27461
|
|
|
27462
|
+
// src/stage.ts
|
|
27463
|
+
import { spawnSync } from "child_process";
|
|
27464
|
+
import { existsSync as existsSync5, realpathSync as realpathSync3 } from "fs";
|
|
27465
|
+
import { dirname as dirname3, join as join5, resolve as resolve6 } from "path";
|
|
27466
|
+
var STAGES_REL = "plugins/kk/scripts/kk-workflow-stages.ts";
|
|
27467
|
+
function resolveStagesScript(argv1 = process.argv[1]) {
|
|
27468
|
+
let fromPkg = "";
|
|
27469
|
+
if (argv1) {
|
|
27470
|
+
try {
|
|
27471
|
+
fromPkg = join5(dirname3(dirname3(realpathSync3(argv1))), STAGES_REL);
|
|
27472
|
+
} catch {
|
|
27473
|
+
fromPkg = "";
|
|
27474
|
+
}
|
|
27475
|
+
}
|
|
27476
|
+
if (fromPkg && existsSync5(fromPkg))
|
|
27477
|
+
return fromPkg;
|
|
27478
|
+
const fromCwd = resolve6(STAGES_REL);
|
|
27479
|
+
if (existsSync5(fromCwd))
|
|
27480
|
+
return fromCwd;
|
|
27481
|
+
throw new Error(`kk-workflow-stages.ts not found at ${fromPkg || STAGES_REL} \u2014 install or refresh the kk package (bun link in a checkout)`);
|
|
27482
|
+
}
|
|
27483
|
+
function runStage(args, execPath = process.execPath, argv1) {
|
|
27484
|
+
const script = resolveStagesScript(argv1);
|
|
27485
|
+
const res = spawnSync(execPath, [script, ...args], { stdio: "inherit" });
|
|
27486
|
+
if (res.error) {
|
|
27487
|
+
throw new Error(`failed to spawn stage runner at ${script}: ${res.error.message}`);
|
|
27488
|
+
}
|
|
27489
|
+
return res.status ?? 1;
|
|
27490
|
+
}
|
|
27491
|
+
|
|
27492
|
+
// src/commands/stage.ts
|
|
27493
|
+
function registerStageCommand(program2) {
|
|
27494
|
+
program2.command("stage").description("Run a kk-workflow-stages.ts stage (workflow orchestration steps)").argument("<args...>", "Stage name followed by its positional arguments").allowUnknownOption(true).action((args) => {
|
|
27495
|
+
const code = runStage(args);
|
|
27496
|
+
if (code !== 0) {
|
|
27497
|
+
throw new CommanderError(code, "kk.stage.failed", `stage failed with exit code ${code}`);
|
|
27498
|
+
}
|
|
27499
|
+
});
|
|
27500
|
+
}
|
|
27501
|
+
|
|
27448
27502
|
// src/config.ts
|
|
27449
27503
|
import { readFileSync as readFileSync4 } from "fs";
|
|
27450
27504
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -27460,6 +27514,7 @@ function createProgram() {
|
|
|
27460
27514
|
const program2 = new Command().name(CLI_CONFIG.binaryName).description("knowledge-kit CLI").version(CLI_CONFIG.binaryVersion).option("-v, --verbose", "enable verbose diagnostics");
|
|
27461
27515
|
registerExecutorCommand(program2);
|
|
27462
27516
|
registerPluginCommand(program2);
|
|
27517
|
+
registerStageCommand(program2);
|
|
27463
27518
|
return program2;
|
|
27464
27519
|
}
|
|
27465
27520
|
|
package/package.json
CHANGED
|
@@ -21744,7 +21744,7 @@ function findProjectRoot(startDir) {
|
|
|
21744
21744
|
var init_file_system_node = () => {};
|
|
21745
21745
|
|
|
21746
21746
|
// ../../plugins/generations/voice-gen/src/index.ts
|
|
21747
|
-
import { basename, dirname as dirname2, extname, join, resolve } from "path";
|
|
21747
|
+
import { basename, dirname as dirname2, extname, join as join2, resolve } from "path";
|
|
21748
21748
|
import { parseArgs } from "util";
|
|
21749
21749
|
|
|
21750
21750
|
// ../../packages/kk-core/src/kinds.ts
|
|
@@ -22413,6 +22413,60 @@ function createVoiceboxClient(options = {}) {
|
|
|
22413
22413
|
};
|
|
22414
22414
|
}
|
|
22415
22415
|
|
|
22416
|
+
// ../../plugins/generations/voice-gen/src/voicebox-server.ts
|
|
22417
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
22418
|
+
import { existsSync as existsSync2, openSync } from "fs";
|
|
22419
|
+
import { homedir } from "os";
|
|
22420
|
+
import { join } from "path";
|
|
22421
|
+
var BOOT_WAIT_MS = 30000;
|
|
22422
|
+
var POLL_MS = 500;
|
|
22423
|
+
var LOG_PATH = "/tmp/voicebox-server.log";
|
|
22424
|
+
async function probeVoiceboxHealth(url2, customFetch) {
|
|
22425
|
+
try {
|
|
22426
|
+
return (await customFetch(`${url2}/health`)).ok;
|
|
22427
|
+
} catch {
|
|
22428
|
+
return false;
|
|
22429
|
+
}
|
|
22430
|
+
}
|
|
22431
|
+
async function ensureVoiceboxRunning(options = {}) {
|
|
22432
|
+
const rawUrl = options.url ?? process.env.VOICEBOX_URL ?? "http://127.0.0.1:17493";
|
|
22433
|
+
const customFetch = options.fetch ?? fetch;
|
|
22434
|
+
const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
22435
|
+
const customSpawn = options.spawn ?? nodeSpawn;
|
|
22436
|
+
if (await probeVoiceboxHealth(rawUrl, customFetch)) {
|
|
22437
|
+
return;
|
|
22438
|
+
}
|
|
22439
|
+
const url2 = new URL(rawUrl);
|
|
22440
|
+
const local = ["127.0.0.1", "localhost", "::1"].includes(url2.hostname);
|
|
22441
|
+
if (!local) {
|
|
22442
|
+
throw new Error(`voice-gen: Voicebox not reachable at ${rawUrl} \u2014 auto-start only applies to local URLs; is the remote server up? (fix VOICEBOX_URL)`);
|
|
22443
|
+
}
|
|
22444
|
+
const serverBin = options.serverBin ?? process.env.VOICEBOX_SERVER_BIN ?? "/Applications/Voicebox.app/Contents/MacOS/voicebox-server";
|
|
22445
|
+
if (!existsSync2(serverBin)) {
|
|
22446
|
+
throw new Error(`voice-gen: Voicebox not reachable at ${rawUrl} and VOICEBOX_SERVER_BIN is missing (${serverBin}) \u2014 start Voicebox.app or set VOICEBOX_SERVER_BIN`);
|
|
22447
|
+
}
|
|
22448
|
+
const dataDir = options.dataDir ?? process.env.VOICEBOX_DATA_DIR ?? join(homedir(), "Library/Application Support/sh.voicebox.app");
|
|
22449
|
+
const logPath = options.logPath ?? LOG_PATH;
|
|
22450
|
+
const port = url2.port === "" ? 17493 : Number(url2.port);
|
|
22451
|
+
echoError(`voice-gen: Voicebox not reachable at ${rawUrl} \u2014 auto-starting ${serverBin} (log: ${logPath})`);
|
|
22452
|
+
const child = customSpawn(serverBin, ["--port", String(port), "--data-dir", dataDir], {
|
|
22453
|
+
detached: true,
|
|
22454
|
+
stdio: ["ignore", "ignore", openSync(logPath, "a")]
|
|
22455
|
+
});
|
|
22456
|
+
child.unref?.();
|
|
22457
|
+
const bootWaitMs = options.bootWaitMs ?? BOOT_WAIT_MS;
|
|
22458
|
+
const pollMs = options.pollMs ?? POLL_MS;
|
|
22459
|
+
const deadline = Date.now() + bootWaitMs;
|
|
22460
|
+
while (Date.now() < deadline) {
|
|
22461
|
+
await sleep(pollMs);
|
|
22462
|
+
if (await probeVoiceboxHealth(rawUrl, customFetch)) {
|
|
22463
|
+
echoError(`voice-gen: Voicebox ready at ${rawUrl}`);
|
|
22464
|
+
return;
|
|
22465
|
+
}
|
|
22466
|
+
}
|
|
22467
|
+
throw new Error(`voice-gen: Voicebox did not become healthy at ${rawUrl} within ${bootWaitMs}ms of auto-start \u2014 check ${logPath}`);
|
|
22468
|
+
}
|
|
22469
|
+
|
|
22416
22470
|
// ../../plugins/generations/voice-gen/src/voicescript.ts
|
|
22417
22471
|
var VOICEBOX_TEXT_MAX = 50000;
|
|
22418
22472
|
var VOICEBOX_INSTRUCT_MAX = 500;
|
|
@@ -22821,8 +22875,8 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
22821
22875
|
const fs = createNodeFileSystem();
|
|
22822
22876
|
const outDir = dirname2(outputPath);
|
|
22823
22877
|
const outStem = basename(outputPath, extname(outputPath));
|
|
22824
|
-
const audioPath = resolve(
|
|
22825
|
-
const mp3Path = resolve(
|
|
22878
|
+
const audioPath = resolve(join2(outDir, `${outStem}.wav`));
|
|
22879
|
+
const mp3Path = resolve(join2(outDir, `${outStem}.mp3`));
|
|
22826
22880
|
const wantMp3 = isMp3Requested();
|
|
22827
22881
|
await fs.deleteFile(outputPath);
|
|
22828
22882
|
await fs.deleteFile(audioPath);
|
|
@@ -22840,6 +22894,9 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
22840
22894
|
return;
|
|
22841
22895
|
}
|
|
22842
22896
|
const client = clientOverride ?? createVoiceboxClient();
|
|
22897
|
+
if (!clientOverride) {
|
|
22898
|
+
await ensureVoiceboxRunning();
|
|
22899
|
+
}
|
|
22843
22900
|
try {
|
|
22844
22901
|
await client.health();
|
|
22845
22902
|
const envDefaultProfile = process.env.VOICEBOX_DEFAULT_PROFILE;
|
|
@@ -7,6 +7,7 @@ import { applySpeed, concatWavs } from './concat';
|
|
|
7
7
|
import { isMp3Requested, type Mp3Transcoder, transcodeWavToMp3 } from './mp3';
|
|
8
8
|
import { auditVoiceSegments, detectLoudnessDip, TRANSCRIPTION_FIDELITY_FLOOR, transcriptionFidelity } from './qc';
|
|
9
9
|
import { createVoiceboxClient, type VoiceboxClient, type VoiceboxGenerateBody } from './voicebox-client';
|
|
10
|
+
import { ensureVoiceboxRunning } from './voicebox-server';
|
|
10
11
|
import {
|
|
11
12
|
mergeVoiceScripts,
|
|
12
13
|
parseDocToVoiceScript,
|
|
@@ -71,6 +72,13 @@ export async function processGeneratorIO(
|
|
|
71
72
|
|
|
72
73
|
const client = clientOverride ?? createVoiceboxClient();
|
|
73
74
|
|
|
75
|
+
// Auto-start the local server when it is down — only on the default client path;
|
|
76
|
+
// tests inject clientOverride and skip the spawn entirely. With vdriver=ominivoice
|
|
77
|
+
// (the default) this plugin never runs, so no server is needed at all.
|
|
78
|
+
if (!clientOverride) {
|
|
79
|
+
await ensureVoiceboxRunning();
|
|
80
|
+
}
|
|
81
|
+
|
|
74
82
|
try {
|
|
75
83
|
// 1. Health check
|
|
76
84
|
await client.health();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type ChildProcess, spawn as nodeSpawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, openSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { echoError } from '@gobing-ai/ts-utils';
|
|
6
|
+
|
|
7
|
+
/** Boot wait / poll cadence for a freshly spawned local server (Voicebox cold start ≈ 2-5s). */
|
|
8
|
+
const BOOT_WAIT_MS = 30_000;
|
|
9
|
+
const POLL_MS = 500;
|
|
10
|
+
const LOG_PATH = '/tmp/voicebox-server.log';
|
|
11
|
+
|
|
12
|
+
export interface VoiceboxServerOptions {
|
|
13
|
+
url?: string;
|
|
14
|
+
serverBin?: string;
|
|
15
|
+
dataDir?: string;
|
|
16
|
+
logPath?: string;
|
|
17
|
+
bootWaitMs?: number;
|
|
18
|
+
pollMs?: number;
|
|
19
|
+
fetch?: typeof fetch;
|
|
20
|
+
sleep?: (ms: number) => Promise<void>;
|
|
21
|
+
spawn?: typeof nodeSpawn;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function probeVoiceboxHealth(url: string, customFetch: typeof fetch): Promise<boolean> {
|
|
25
|
+
try {
|
|
26
|
+
return (await customFetch(`${url}/health`)).ok;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Ensure a local Voicebox server is reachable: probe `/health`, and when it is down and the
|
|
34
|
+
* URL points at this machine, spawn the app's server binary detached (stderr appended to the
|
|
35
|
+
* log file, survives plugin exit) and poll until healthy — fail loud otherwise. Remote URLs
|
|
36
|
+
* and a missing binary are configuration errors, not auto-start candidates.
|
|
37
|
+
*/
|
|
38
|
+
export async function ensureVoiceboxRunning(options: VoiceboxServerOptions = {}): Promise<void> {
|
|
39
|
+
const rawUrl = options.url ?? process.env.VOICEBOX_URL ?? 'http://127.0.0.1:17493';
|
|
40
|
+
const customFetch = options.fetch ?? fetch;
|
|
41
|
+
const sleep = options.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
|
|
42
|
+
const customSpawn = options.spawn ?? nodeSpawn;
|
|
43
|
+
|
|
44
|
+
if (await probeVoiceboxHealth(rawUrl, customFetch)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const url = new URL(rawUrl);
|
|
49
|
+
const local = ['127.0.0.1', 'localhost', '::1'].includes(url.hostname);
|
|
50
|
+
if (!local) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`voice-gen: Voicebox not reachable at ${rawUrl} — auto-start only applies to local URLs; is the remote server up? (fix VOICEBOX_URL)`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const serverBin =
|
|
56
|
+
options.serverBin ??
|
|
57
|
+
process.env.VOICEBOX_SERVER_BIN ??
|
|
58
|
+
'/Applications/Voicebox.app/Contents/MacOS/voicebox-server';
|
|
59
|
+
if (!existsSync(serverBin)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`voice-gen: Voicebox not reachable at ${rawUrl} and VOICEBOX_SERVER_BIN is missing (${serverBin}) — start Voicebox.app or set VOICEBOX_SERVER_BIN`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const dataDir =
|
|
65
|
+
options.dataDir ??
|
|
66
|
+
process.env.VOICEBOX_DATA_DIR ??
|
|
67
|
+
join(homedir(), 'Library/Application Support/sh.voicebox.app');
|
|
68
|
+
const logPath = options.logPath ?? LOG_PATH;
|
|
69
|
+
const port = url.port === '' ? 17493 : Number(url.port);
|
|
70
|
+
|
|
71
|
+
echoError(`voice-gen: Voicebox not reachable at ${rawUrl} — auto-starting ${serverBin} (log: ${logPath})`);
|
|
72
|
+
const child: ChildProcess = customSpawn(serverBin, ['--port', String(port), '--data-dir', dataDir], {
|
|
73
|
+
detached: true,
|
|
74
|
+
stdio: ['ignore', 'ignore', openSync(logPath, 'a')],
|
|
75
|
+
});
|
|
76
|
+
child.unref?.();
|
|
77
|
+
|
|
78
|
+
const bootWaitMs = options.bootWaitMs ?? BOOT_WAIT_MS;
|
|
79
|
+
const pollMs = options.pollMs ?? POLL_MS;
|
|
80
|
+
const deadline = Date.now() + bootWaitMs;
|
|
81
|
+
while (Date.now() < deadline) {
|
|
82
|
+
await sleep(pollMs);
|
|
83
|
+
if (await probeVoiceboxHealth(rawUrl, customFetch)) {
|
|
84
|
+
echoError(`voice-gen: Voicebox ready at ${rawUrl}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw new Error(
|
|
89
|
+
`voice-gen: Voicebox did not become healthy at ${rawUrl} within ${bootWaitMs}ms of auto-start — check ${logPath}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -246,15 +246,19 @@ spur workflow run "$dest" --vars \
|
|
|
246
246
|
|
|
247
247
|
### Profile `kk-daily-ai-voice`
|
|
248
248
|
|
|
249
|
-
Bind vars: `work_dir`, `publish_enabled`, `last30days_enabled`, `skip_review`,
|
|
250
|
-
(empty → ADR-012 default discovery), `agent`. `$dest` resolves like solo-podcast.
|
|
251
|
-
topicless — no `topic`/`input_file`/`fixture` vars. `run_date` is
|
|
252
|
-
|
|
253
|
-
|
|
249
|
+
Bind vars: `work_dir`, `run_date`, `publish_enabled`, `last30days_enabled`, `skip_review`,
|
|
250
|
+
`plugins_path` (empty → ADR-012 default discovery), `agent`. `$dest` resolves like solo-podcast.
|
|
251
|
+
The run is topicless — no `topic`/`input_file`/`fixture` vars. `run_date` is optional: empty →
|
|
252
|
+
today (manual path), or a dashed/digits date for backfills (headless cron path). `daily-prepare`
|
|
253
|
+
derives the digits-only stamp from it and composes `work_dir` when empty:
|
|
254
|
+
`$works_dir/kk-daily-ai-voice/<yyyymmdd>`; an explicit `work_dir` wins. Either way the effective
|
|
255
|
+
workspace lands in the anchor `.spur/runs/kk-daily-ai-voice/work-dir.txt` and the stamp in
|
|
256
|
+
`$work_dir/run-date.txt`. The `review` state pauses the run for operator approval of the
|
|
257
|
+
VoiceScript; resume with `spur workflow continue [run-id]`.
|
|
254
258
|
|
|
255
259
|
```bash
|
|
256
260
|
spur workflow run "$dest" --vars \
|
|
257
|
-
"{\"work_dir\":\"$work_dir\",\"publish_enabled\":\"$PUBLISH\",\"last30days_enabled\":\"$L30D\",\"skip_review\":\"$SKIP_REVIEW\",\"plugins_path\":\"\",\"agent\":\"$AGENT\"}"
|
|
261
|
+
"{\"work_dir\":\"$work_dir\",\"run_date\":\"\",\"publish_enabled\":\"$PUBLISH\",\"last30days_enabled\":\"$L30D\",\"skip_review\":\"$SKIP_REVIEW\",\"plugins_path\":\"\",\"agent\":\"$AGENT\"}"
|
|
258
262
|
```
|
|
259
263
|
|
|
260
264
|
## 6. Report
|
package/plugins/kk/plugin.json
CHANGED
|
@@ -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;
|
|
@@ -534,15 +537,29 @@ function dailyPrepare(args: string[]): void {
|
|
|
534
537
|
last30daysEnabled = '',
|
|
535
538
|
topic = '',
|
|
536
539
|
horizonHours = '',
|
|
540
|
+
runDateArg = '',
|
|
537
541
|
] = args;
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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', () => {
|
|
541
551
|
for (const dir of DAILY_DIRS)
|
|
542
552
|
sted('mkdir', join(workDir, dir), () => mkdirSync(join(workDir, dir), { recursive: true }));
|
|
543
553
|
sted('write', join(workDir, 'run-date.txt'), () =>
|
|
544
|
-
writeFileSync(join(workDir, 'run-date.txt'), `${
|
|
554
|
+
writeFileSync(join(workDir, 'run-date.txt'), `${dirDate}\n`),
|
|
545
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
|
+
});
|
|
546
563
|
sted('write', join(workDir, '1-ingest/source.json'), () =>
|
|
547
564
|
writeJsonText(join(workDir, '1-ingest/source.json'), {
|
|
548
565
|
limit: Number(limit),
|
|
@@ -572,6 +589,35 @@ function formatRunDate(): string {
|
|
|
572
589
|
return `${now.getFullYear()}${month}${day}`;
|
|
573
590
|
}
|
|
574
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
|
+
|
|
575
621
|
function dailyCollectFacts(args: string[]): void {
|
|
576
622
|
const [workDir = '', runDate = '', pluginsPath = ''] = args;
|
|
577
623
|
const content = join(workDir, '2-facts', `${runDate}_02_collect_content.json`);
|
|
@@ -780,7 +826,9 @@ function dailyArticle(args: string[]): void {
|
|
|
780
826
|
{
|
|
781
827
|
env: {
|
|
782
828
|
ARTICLE_DATE: runDate,
|
|
783
|
-
|
|
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) } : {}),
|
|
784
832
|
},
|
|
785
833
|
},
|
|
786
834
|
);
|
|
@@ -35,6 +35,9 @@ vars:
|
|
|
35
35
|
plan_translate_enabled: "true"
|
|
36
36
|
article_write_enabled: "true"
|
|
37
37
|
plugins_path: ""
|
|
38
|
+
# Projected by the publish state from 3-publish/<run_date>_17_publish_publish-status.txt;
|
|
39
|
+
# guards test this var instead of cat-ing the file (state at decision time).
|
|
40
|
+
publish_status: ""
|
|
38
41
|
last30days_topic: "AI news"
|
|
39
42
|
horizon_hours: "24"
|
|
40
43
|
publish_enabled: "false"
|
|
@@ -62,13 +65,18 @@ states:
|
|
|
62
65
|
onEnter:
|
|
63
66
|
- kind: shell
|
|
64
67
|
options:
|
|
68
|
+
# run_date (dashed or digits; the job worker expands $(date +%F) before spur sees the
|
|
69
|
+
# JSON — var values reach non-shell steps verbatim) only names the workspace:
|
|
70
|
+
# daily-prepare composes the real dir as works_dir/kk-daily-ai-voice/<stamp> when
|
|
71
|
+
# work_dir is empty and always writes the effective work_dir to the cwd-relative
|
|
72
|
+
# anchor below. Shell actions cannot set vars, so the reads project it (then the
|
|
73
|
+
# digits-only stamp) into vars before any later state interpolates them.
|
|
65
74
|
command: >-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
bun "$st" daily-prepare "${vars.work_dir}" "${vars.limit}" "${vars.cursor}" "${vars.state_file}" "${vars.last30days_enabled}" "${vars.last30days_topic}" "${vars.horizon_hours}"
|
|
75
|
+
kk stage daily-prepare "${vars.work_dir}" "${vars.limit}" "${vars.cursor}" "${vars.state_file}" "${vars.last30days_enabled}" "${vars.last30days_topic}" "${vars.horizon_hours}" "${vars.run_date}"
|
|
76
|
+
- kind: file.read.into-var
|
|
77
|
+
options:
|
|
78
|
+
path: .spur/runs/kk-daily-ai-voice/work-dir.txt
|
|
79
|
+
var: work_dir
|
|
72
80
|
- kind: file.read.into-var
|
|
73
81
|
options:
|
|
74
82
|
path: ${vars.work_dir}/run-date.txt
|
|
@@ -89,12 +97,7 @@ states:
|
|
|
89
97
|
- kind: shell
|
|
90
98
|
options:
|
|
91
99
|
command: >-
|
|
92
|
-
|
|
93
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
94
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
95
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
96
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
97
|
-
bun "$st" daily-collect-facts "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
100
|
+
kk stage daily-collect-facts "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
98
101
|
|
|
99
102
|
- id: plan
|
|
100
103
|
description: "Score/dedup/select + annotate the blended Doc[] into the episode plan (episode-plan-gen; annotate rides inside planEpisode)"
|
|
@@ -102,12 +105,7 @@ states:
|
|
|
102
105
|
- kind: shell
|
|
103
106
|
options:
|
|
104
107
|
command: >-
|
|
105
|
-
|
|
106
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
107
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
108
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
109
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
110
|
-
bun "$st" daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}"
|
|
108
|
+
kk stage daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}"
|
|
111
109
|
|
|
112
110
|
- id: quality-control-content
|
|
113
111
|
description: "Content QC: filter the plan's annotated candidates by category allowlist + score thresholds (episode-plan-gen --mode filter); zero candidates route to done-no-content"
|
|
@@ -115,12 +113,7 @@ states:
|
|
|
115
113
|
- kind: shell
|
|
116
114
|
options:
|
|
117
115
|
command: >-
|
|
118
|
-
|
|
119
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
120
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
121
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
122
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
123
|
-
bun "$st" daily-qc-content "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_categories}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
|
|
116
|
+
kk stage daily-qc-content "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_categories}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
|
|
124
117
|
|
|
125
118
|
- id: plan-translate
|
|
126
119
|
description: "Translate needsTranslation plan items into the output plan.json via agent.run"
|
|
@@ -162,12 +155,7 @@ states:
|
|
|
162
155
|
- kind: shell
|
|
163
156
|
options:
|
|
164
157
|
command: >-
|
|
165
|
-
|
|
166
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
167
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
168
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
169
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
170
|
-
bun "$st" daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
158
|
+
kk stage daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
171
159
|
|
|
172
160
|
- id: cover
|
|
173
161
|
description: "Generate the article cover image via the kk:image-authoring skill (agent.run)"
|
|
@@ -186,12 +174,7 @@ states:
|
|
|
186
174
|
- kind: shell
|
|
187
175
|
options:
|
|
188
176
|
command: >-
|
|
189
|
-
|
|
190
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
191
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
192
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
193
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
194
|
-
bun "$st" daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
|
|
177
|
+
kk stage daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
|
|
195
178
|
|
|
196
179
|
- id: script
|
|
197
180
|
description: "Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content"
|
|
@@ -199,12 +182,7 @@ states:
|
|
|
199
182
|
- kind: shell
|
|
200
183
|
options:
|
|
201
184
|
command: >-
|
|
202
|
-
|
|
203
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
204
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
205
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
206
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
207
|
-
bun "$st" daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}"
|
|
185
|
+
kk stage daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}"
|
|
208
186
|
|
|
209
187
|
- id: review
|
|
210
188
|
description: "Operator reviews the generated news script before speech synthesis"
|
|
@@ -221,12 +199,7 @@ states:
|
|
|
221
199
|
- kind: shell
|
|
222
200
|
options:
|
|
223
201
|
command: >-
|
|
224
|
-
|
|
225
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
226
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
227
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
228
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
229
|
-
bun "$st" daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
|
|
202
|
+
kk stage daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
|
|
230
203
|
|
|
231
204
|
- id: generate
|
|
232
205
|
description: "Invoke voice driver plugin (vdriver var: ominivoice -> omni-voice-gen, voicebox -> voice-gen) to generate audio"
|
|
@@ -234,12 +207,7 @@ states:
|
|
|
234
207
|
- kind: shell
|
|
235
208
|
options:
|
|
236
209
|
command: >-
|
|
237
|
-
|
|
238
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
239
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
240
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
241
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
242
|
-
OMNIVOICE_SPEED=1.06 bun "$st" daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.vdriver}"
|
|
210
|
+
OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.vdriver}"
|
|
243
211
|
|
|
244
212
|
- id: intro-music
|
|
245
213
|
description: "Prepend the 8s intro sting (intro_music var; empty = skip) to the generated wav, update duration/audioPath"
|
|
@@ -247,12 +215,7 @@ states:
|
|
|
247
215
|
- kind: shell
|
|
248
216
|
options:
|
|
249
217
|
command: >-
|
|
250
|
-
|
|
251
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
252
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
253
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
254
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
255
|
-
bun "$st" daily-intro "${vars.work_dir}" "${vars.run_date}" "${vars.intro_music}"
|
|
218
|
+
kk stage daily-intro "${vars.work_dir}" "${vars.run_date}" "${vars.intro_music}"
|
|
256
219
|
|
|
257
220
|
- id: quality-control
|
|
258
221
|
description: "Verify audio quality, speech rate, and silence/repetition QC metrics"
|
|
@@ -260,12 +223,7 @@ states:
|
|
|
260
223
|
- kind: shell
|
|
261
224
|
options:
|
|
262
225
|
command: >-
|
|
263
|
-
|
|
264
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
265
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
266
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
267
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
268
|
-
bun "$st" daily-quality-report "${vars.work_dir}" "${vars.run_date}"
|
|
226
|
+
kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
|
|
269
227
|
|
|
270
228
|
- id: publish-prep
|
|
271
229
|
description: "Merge article Content with audio (podcast assembly facts ride the content options channel for podcast-pub's absorbed assembly; runDate kept for surfdash slug)"
|
|
@@ -273,12 +231,7 @@ states:
|
|
|
273
231
|
- kind: shell
|
|
274
232
|
options:
|
|
275
233
|
command: >-
|
|
276
|
-
|
|
277
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
278
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
279
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
280
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
281
|
-
bun "$st" daily-publish-prep "${vars.work_dir}" "${vars.run_date}"
|
|
234
|
+
kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}"
|
|
282
235
|
|
|
283
236
|
- id: publish-surfdash
|
|
284
237
|
description: "Publish the merged Content to surfdash (single target); result lands in result.json — surfdash publishes first so show-notes can link the article (task 0118 seam reorder)"
|
|
@@ -286,12 +239,7 @@ states:
|
|
|
286
239
|
- kind: shell
|
|
287
240
|
options:
|
|
288
241
|
command: >-
|
|
289
|
-
|
|
290
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
291
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
292
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
293
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
294
|
-
bun "$st" daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
242
|
+
kk stage daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
295
243
|
|
|
296
244
|
- id: show-notes
|
|
297
245
|
description: "Render timeline show notes from the episode plan (podcast-pub show-notes op, briefMode; article URL from the surfdash result postPath — surfdash failure degrades to legacy full-body layout)"
|
|
@@ -299,12 +247,7 @@ states:
|
|
|
299
247
|
- kind: shell
|
|
300
248
|
options:
|
|
301
249
|
command: >-
|
|
302
|
-
|
|
303
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
304
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
305
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
306
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
307
|
-
bun "$st" daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
|
|
250
|
+
kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
|
|
308
251
|
|
|
309
252
|
- id: publish-podcast
|
|
310
253
|
description: "Publish the episode to podcast-pub with the rendered show notes (metadata.showNotes carries the brief/timeline markdown; map.ts falls back to content.body when absent — task 0118 R3)"
|
|
@@ -312,12 +255,7 @@ states:
|
|
|
312
255
|
- kind: shell
|
|
313
256
|
options:
|
|
314
257
|
command: >-
|
|
315
|
-
|
|
316
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
317
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
318
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
319
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
320
|
-
bun "$st" daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
258
|
+
kk stage daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
321
259
|
|
|
322
260
|
- id: publish
|
|
323
261
|
description: "Classify the sequenced surfdash → podcast outcomes full|partial|none into publish-status.txt (exit 0 in all three — routing is transition-owned; task 0118: per-target classification over the merged result.json)"
|
|
@@ -325,12 +263,11 @@ states:
|
|
|
325
263
|
- kind: shell
|
|
326
264
|
options:
|
|
327
265
|
command: >-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
bun "$st" daily-publish-classify "${vars.work_dir}" "${vars.run_date}"
|
|
266
|
+
kk stage daily-publish-classify "${vars.work_dir}" "${vars.run_date}"
|
|
267
|
+
- kind: file.read.into-var
|
|
268
|
+
options:
|
|
269
|
+
path: ${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt
|
|
270
|
+
var: publish_status
|
|
334
271
|
|
|
335
272
|
- id: patch-surfdash
|
|
336
273
|
description: "20260917 #4/#6 — patch published surfdash posts: podcast cover URL into image/og_image frontmatter + podcast cross-link (zh/en/ja), commit+push"
|
|
@@ -338,12 +275,7 @@ states:
|
|
|
338
275
|
- kind: shell
|
|
339
276
|
options:
|
|
340
277
|
command: >-
|
|
341
|
-
|
|
342
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
343
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
344
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
345
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
346
|
-
bun "$st" daily-patch-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.surfdash_root}"
|
|
278
|
+
kk stage daily-patch-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.surfdash_root}"
|
|
347
279
|
|
|
348
280
|
- id: publish-partial
|
|
349
281
|
description: "Partial publish — at least one target published, at least one failed; echo warning, write a .spur/run marker, preserve result.json for --retry-from, continue to done (task 0118: surfdash failure = no-URL fallback notes; podcast failure = its own partial)"
|
|
@@ -351,12 +283,7 @@ states:
|
|
|
351
283
|
- kind: shell
|
|
352
284
|
options:
|
|
353
285
|
command: >-
|
|
354
|
-
|
|
355
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
356
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
357
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
358
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
359
|
-
bun "$st" daily-publish-partial "${vars.work_dir}" "${vars.run_date}"
|
|
286
|
+
kk stage daily-publish-partial "${vars.work_dir}" "${vars.run_date}"
|
|
360
287
|
|
|
361
288
|
- id: run-report
|
|
362
289
|
description: "Assemble the per-run report (scores+aggregates+rejected counts+step timing) from existing artifacts — news-report-gen; output-only, never throws; 0117"
|
|
@@ -364,12 +291,7 @@ states:
|
|
|
364
291
|
- kind: shell
|
|
365
292
|
options:
|
|
366
293
|
command: >-
|
|
367
|
-
|
|
368
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
369
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
370
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
371
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
372
|
-
bun "$st" daily-run-report "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
|
|
294
|
+
kk stage daily-run-report "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
|
|
373
295
|
|
|
374
296
|
- id: translate
|
|
375
297
|
description: "Locales — surfdash-pub scaffolds en/ja drafts (deterministic, plugin-owned), agent translates both, sync into the surfdash CMS"
|
|
@@ -377,12 +299,7 @@ states:
|
|
|
377
299
|
- kind: shell
|
|
378
300
|
options:
|
|
379
301
|
command: >-
|
|
380
|
-
|
|
381
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
382
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
383
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
384
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
385
|
-
bun "$st" daily-translate-localize "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
302
|
+
kk stage daily-translate-localize "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
386
303
|
- kind: file.read.into-var
|
|
387
304
|
options:
|
|
388
305
|
path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-localize_en-post-path.txt
|
|
@@ -405,12 +322,7 @@ states:
|
|
|
405
322
|
- kind: shell
|
|
406
323
|
options:
|
|
407
324
|
command: >-
|
|
408
|
-
|
|
409
|
-
pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
|
|
410
|
-
st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
|
|
411
|
-
[ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
|
|
412
|
-
[ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
|
|
413
|
-
bun "$st" daily-translate-sync "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.work_dir}/3-publish/${vars.run_date}_20_translate-localize_zh-post-path.txt"
|
|
325
|
+
kk stage daily-translate-sync "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.work_dir}/3-publish/${vars.run_date}_20_translate-localize_zh-post-path.txt"
|
|
414
326
|
|
|
415
327
|
- id: done
|
|
416
328
|
description: "Terminal — Daily AI news pipeline completed (collect → QC'd plan → article/cover/script/voice → publish/locales)"
|
|
@@ -623,7 +535,7 @@ transitions:
|
|
|
623
535
|
guard:
|
|
624
536
|
kind: shell
|
|
625
537
|
options:
|
|
626
|
-
command: 'test "$
|
|
538
|
+
command: 'test "${vars.publish_status}" = "partial"'
|
|
627
539
|
|
|
628
540
|
- from: publish
|
|
629
541
|
to: failed
|
|
@@ -631,7 +543,7 @@ transitions:
|
|
|
631
543
|
guard:
|
|
632
544
|
kind: shell
|
|
633
545
|
options:
|
|
634
|
-
command: 'test "$
|
|
546
|
+
command: 'test "${vars.publish_status}" = "none"'
|
|
635
547
|
|
|
636
548
|
- from: publish-partial
|
|
637
549
|
to: run-report
|
|
@@ -645,7 +557,7 @@ transitions:
|
|
|
645
557
|
guard:
|
|
646
558
|
kind: shell
|
|
647
559
|
options:
|
|
648
|
-
command: 'test "$
|
|
560
|
+
command: 'test "${vars.publish_status}" = "full"'
|
|
649
561
|
|
|
650
562
|
- from: run-report
|
|
651
563
|
to: done
|
|
@@ -653,7 +565,7 @@ transitions:
|
|
|
653
565
|
guard:
|
|
654
566
|
kind: shell
|
|
655
567
|
options:
|
|
656
|
-
command: 'test "$
|
|
568
|
+
command: 'test "${vars.publish_status}" = "partial"'
|
|
657
569
|
|
|
658
570
|
- from: translate
|
|
659
571
|
to: done
|