@gobing-ai/knowledge-kit 0.0.11 → 0.0.13
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/dist/index.js +451 -152
- package/package.json +1 -1
- package/plugins/generations/content-gen/dist/index.js +22167 -0
- package/plugins/generations/content-gen/plugin.json +1 -1
- package/plugins/generations/content-gen/src/storm.ts +99 -50
- package/plugins/generations/core-facts-gen/dist/index.js +22048 -0
- package/plugins/generations/core-facts-gen/package.json +17 -0
- package/plugins/generations/core-facts-gen/plugin.json +7 -0
- package/plugins/generations/core-facts-gen/src/index.ts +116 -0
- package/plugins/generations/core-facts-gen/tsconfig.json +4 -0
- package/plugins/generations/daily-article-gen/dist/index.js +22022 -0
- package/plugins/generations/daily-article-gen/package.json +17 -0
- package/plugins/generations/daily-article-gen/plugin.json +7 -0
- package/plugins/generations/daily-article-gen/src/index.ts +91 -0
- package/plugins/generations/daily-article-gen/tsconfig.json +4 -0
- package/plugins/generations/dailynews-gen/dist/index.js +22324 -0
- package/plugins/generations/dailynews-gen/plugin.json +1 -1
- package/plugins/generations/dailynews-gen/src/index.ts +11 -0
- package/plugins/generations/dailynews-gen/src/script-builder.ts +1 -1
- package/plugins/generations/episode-plan-gen/dist/index.js +22479 -0
- package/plugins/generations/episode-plan-gen/package.json +17 -0
- package/plugins/generations/episode-plan-gen/plugin.json +7 -0
- package/plugins/generations/episode-plan-gen/src/index.ts +726 -0
- package/plugins/generations/episode-plan-gen/tsconfig.json +4 -0
- package/plugins/generations/image-gen/config.example.yaml +75 -0
- package/plugins/generations/image-gen/dist/index.js +22619 -0
- package/plugins/generations/image-gen/package.json +17 -0
- package/plugins/generations/image-gen/plugin.json +7 -0
- package/plugins/generations/image-gen/presets/formats/cover.yaml +57 -0
- package/plugins/generations/image-gen/presets/formats/free.yaml +46 -0
- package/plugins/generations/image-gen/presets/formats/illustration.yaml +48 -0
- package/plugins/generations/image-gen/presets/styles/clean-webapp-ui.yaml +28 -0
- package/plugins/generations/image-gen/presets/styles/cute.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/editorial.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/fresh.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/minimalist.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/photorealistic.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/sketch.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/technical-diagram.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/vibrant.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/warm.yaml +3 -0
- package/plugins/generations/image-gen/src/bytes.ts +19 -0
- package/plugins/generations/image-gen/src/index.ts +319 -0
- package/plugins/generations/image-gen/src/job.ts +143 -0
- package/plugins/generations/image-gen/src/paths.ts +31 -0
- package/plugins/generations/image-gen/src/presets.ts +344 -0
- package/plugins/generations/image-gen/src/providers/seedream.ts +128 -0
- package/plugins/generations/image-gen/src/providers/types.ts +285 -0
- package/plugins/generations/image-gen/tsconfig.json +8 -0
- package/plugins/generations/voice-gen/dist/index.js +23027 -0
- package/plugins/generations/voice-gen/plugin.json +1 -1
- package/plugins/generations/voice-gen/src/index.ts +102 -11
- package/plugins/generations/voice-gen/src/qc.ts +154 -9
- package/plugins/ingestions/aihot-ingest/dist/index.js +22358 -0
- package/plugins/ingestions/aihot-ingest/plugin.json +2 -2
- package/plugins/ingestions/aihot-ingest/src/index.ts +72 -13
- package/plugins/ingestions/aihot-ingest/src/mapper.ts +1 -0
- package/plugins/ingestions/aihot-ingest/src/rss.ts +151 -0
- package/plugins/ingestions/horizon-ingest/dist/index.js +22105 -0
- package/plugins/ingestions/horizon-ingest/package.json +17 -0
- package/plugins/ingestions/horizon-ingest/plugin.json +7 -0
- package/plugins/ingestions/horizon-ingest/src/index.ts +205 -0
- package/plugins/ingestions/horizon-ingest/tsconfig.json +4 -0
- package/plugins/ingestions/karakeep-local/dist/index.js +24204 -0
- package/plugins/ingestions/karakeep-local/plugin.json +1 -1
- package/plugins/ingestions/last30days-ingest/dist/index.js +22050 -0
- package/plugins/ingestions/last30days-ingest/package.json +17 -0
- package/plugins/ingestions/last30days-ingest/plugin.json +7 -0
- package/plugins/ingestions/last30days-ingest/src/index.ts +148 -0
- package/plugins/ingestions/last30days-ingest/tsconfig.json +4 -0
- package/plugins/ingestions/web-search/dist/index.js +24379 -0
- package/plugins/ingestions/web-search/plugin.json +1 -1
- package/plugins/kk/commands/image-generate.md +15 -0
- package/plugins/kk/config.example.yaml +80 -0
- package/plugins/kk/skills/image-authoring/SKILL.md +250 -0
- package/plugins/kk/skills/image-authoring/references/illustration-positions.md +87 -0
- package/plugins/kk/skills/image-authoring/references/migrating-from-wt.md +31 -0
- package/plugins/kk/skills/image-authoring/references/providers.md +52 -0
- package/plugins/kk/skills/image-authoring/references/style-extraction.md +134 -0
- package/plugins/kk/skills/taste-unslop/SKILL.md +12 -6
- package/plugins/kk/skills/taste-unslop/references/pattern-guide.md +128 -48
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +432 -19
- package/plugins/publishings/emdash-pub/dist/index.js +22243 -0
- package/plugins/publishings/emdash-pub/plugin.json +1 -1
- package/plugins/publishings/podcast-pub/dist/index.js +22596 -0
- package/plugins/publishings/podcast-pub/package.json +17 -0
- package/plugins/publishings/podcast-pub/plugin.json +13 -0
- package/plugins/publishings/podcast-pub/src/index.ts +538 -0
- package/plugins/publishings/podcast-pub/src/map.ts +165 -0
- package/plugins/publishings/podcast-pub/src/microfeed-client.ts +196 -0
- package/plugins/publishings/podcast-pub/src/show-notes.ts +132 -0
- package/plugins/publishings/podcast-pub/tsconfig.json +4 -0
- package/plugins/publishings/qiita-pub/dist/index.js +22081 -0
- package/plugins/publishings/qiita-pub/plugin.json +1 -1
- package/plugins/publishings/surfdash-pub/dist/index.js +22233 -0
- package/plugins/publishings/surfdash-pub/plugin.json +1 -1
- package/plugins/publishings/surfdash-pub/src/index.ts +328 -62
- package/plugins/publishings/zenn-pub/dist/index.js +22122 -0
- package/plugins/publishings/zenn-pub/plugin.json +1 -1
package/dist/index.js
CHANGED
|
@@ -26016,7 +26016,8 @@ function useColor() {
|
|
|
26016
26016
|
var program = new Command;
|
|
26017
26017
|
|
|
26018
26018
|
// src/fanin.ts
|
|
26019
|
-
import {
|
|
26019
|
+
import { tmpdir } from "os";
|
|
26020
|
+
import { isAbsolute as isAbsolute2, join as join3, resolve as resolve3 } from "path";
|
|
26020
26021
|
|
|
26021
26022
|
// ../../packages/kk-core/src/kinds.ts
|
|
26022
26023
|
init_zod();
|
|
@@ -26093,12 +26094,14 @@ function isPluginKind(value) {
|
|
|
26093
26094
|
}
|
|
26094
26095
|
// ../../packages/kk-core/src/manifest.ts
|
|
26095
26096
|
init_zod();
|
|
26097
|
+
var OperationIOSchema = exports_external.enum(["doc-list", "markdown"]);
|
|
26096
26098
|
var PluginManifestSchema = exports_external.object({
|
|
26097
26099
|
name: exports_external.string().min(1),
|
|
26098
26100
|
kind: PluginKindSchema,
|
|
26099
26101
|
entry: exports_external.string().min(1),
|
|
26100
26102
|
version: exports_external.string().optional(),
|
|
26101
|
-
description: exports_external.string().optional()
|
|
26103
|
+
description: exports_external.string().optional(),
|
|
26104
|
+
operations: exports_external.record(exports_external.string(), exports_external.object({ input: OperationIOSchema, output: OperationIOSchema })).optional()
|
|
26102
26105
|
}).passthrough();
|
|
26103
26106
|
// ../../packages/kk-core/src/registry.ts
|
|
26104
26107
|
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
|
|
@@ -26152,7 +26155,7 @@ function resolvePluginRoots(opts = {}) {
|
|
|
26152
26155
|
if (builtinRoot !== undefined) {
|
|
26153
26156
|
roots.push({ origin: "builtin", root: builtinRoot });
|
|
26154
26157
|
}
|
|
26155
|
-
const userRoot = join(opts.homeDir ?? homedir(), ".kk", "plugins");
|
|
26158
|
+
const userRoot = join(opts.homeDir ?? homedir(), ".config", "kk", "plugins");
|
|
26156
26159
|
if (isDirectory(userRoot)) {
|
|
26157
26160
|
roots.push({ origin: "user", root: userRoot });
|
|
26158
26161
|
}
|
|
@@ -26330,7 +26333,8 @@ function consumeManifest(dirPath, expectedKind, origin, plugins, diagnostics, se
|
|
|
26330
26333
|
manifestPath,
|
|
26331
26334
|
origin,
|
|
26332
26335
|
version: manifest.version,
|
|
26333
|
-
description: manifest.description
|
|
26336
|
+
description: manifest.description,
|
|
26337
|
+
...manifest.operations !== undefined ? { operations: manifest.operations } : {}
|
|
26334
26338
|
});
|
|
26335
26339
|
}
|
|
26336
26340
|
function listSortedDir(absDir) {
|
|
@@ -26365,134 +26369,11 @@ init_config();
|
|
|
26365
26369
|
if (false) {}
|
|
26366
26370
|
// src/fanin.ts
|
|
26367
26371
|
init_dist();
|
|
26368
|
-
var EXIT_USAGE = 2;
|
|
26369
|
-
var EXIT_INPUT = 1;
|
|
26370
|
-
function mergeDocLists(inputs, sources) {
|
|
26371
|
-
const merged = [];
|
|
26372
|
-
const byId = new Map;
|
|
26373
|
-
for (const [i, docList] of inputs.entries()) {
|
|
26374
|
-
const file2 = sources[i] ?? `input[${i}]`;
|
|
26375
|
-
for (const doc2 of docList) {
|
|
26376
|
-
const entry = { file: file2 };
|
|
26377
|
-
if (doc2.sourceUri !== undefined) {
|
|
26378
|
-
entry.sourceUri = doc2.sourceUri;
|
|
26379
|
-
}
|
|
26380
|
-
const existing = byId.get(doc2.id);
|
|
26381
|
-
if (existing) {
|
|
26382
|
-
const list = existing.metadata?.sources;
|
|
26383
|
-
if (Array.isArray(list)) {
|
|
26384
|
-
list.push(entry);
|
|
26385
|
-
}
|
|
26386
|
-
continue;
|
|
26387
|
-
}
|
|
26388
|
-
const mergedDoc = {
|
|
26389
|
-
...doc2,
|
|
26390
|
-
metadata: { ...doc2.metadata ?? {}, sources: [entry] }
|
|
26391
|
-
};
|
|
26392
|
-
byId.set(doc2.id, mergedDoc);
|
|
26393
|
-
merged.push(mergedDoc);
|
|
26394
|
-
}
|
|
26395
|
-
}
|
|
26396
|
-
return merged;
|
|
26397
|
-
}
|
|
26398
|
-
async function readDocList(fs2, path3) {
|
|
26399
|
-
if (!await fs2.exists(path3)) {
|
|
26400
|
-
echoError(`Input file does not exist: ${path3}`);
|
|
26401
|
-
return null;
|
|
26402
|
-
}
|
|
26403
|
-
let content;
|
|
26404
|
-
try {
|
|
26405
|
-
content = await fs2.readFile(path3);
|
|
26406
|
-
} catch (err) {
|
|
26407
|
-
echoError(`Failed to read input file ${path3}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26408
|
-
return null;
|
|
26409
|
-
}
|
|
26410
|
-
let json2;
|
|
26411
|
-
try {
|
|
26412
|
-
json2 = JSON.parse(content);
|
|
26413
|
-
} catch (err) {
|
|
26414
|
-
echoError(`Input file ${path3} is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
26415
|
-
return null;
|
|
26416
|
-
}
|
|
26417
|
-
const result = DocListSchema.safeParse(json2);
|
|
26418
|
-
if (!result.success) {
|
|
26419
|
-
const issues = result.error.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ");
|
|
26420
|
-
echoError(`Input file ${path3} violates the Doc list schema: ${issues}`);
|
|
26421
|
-
return null;
|
|
26422
|
-
}
|
|
26423
|
-
return result.data;
|
|
26424
|
-
}
|
|
26425
|
-
function parseArgs(argv) {
|
|
26426
|
-
const inPaths = [];
|
|
26427
|
-
let outPath;
|
|
26428
|
-
for (let i = 0;i < argv.length; i++) {
|
|
26429
|
-
const arg = argv[i];
|
|
26430
|
-
if (arg === "--in") {
|
|
26431
|
-
const value = argv[++i];
|
|
26432
|
-
if (value === undefined) {
|
|
26433
|
-
throw new Error("--in requires a file path");
|
|
26434
|
-
}
|
|
26435
|
-
inPaths.push(value);
|
|
26436
|
-
} else if (arg === "--out") {
|
|
26437
|
-
const value = argv[++i];
|
|
26438
|
-
if (value === undefined) {
|
|
26439
|
-
throw new Error("--out requires a file path");
|
|
26440
|
-
}
|
|
26441
|
-
if (outPath !== undefined) {
|
|
26442
|
-
throw new Error("--out may be specified only once");
|
|
26443
|
-
}
|
|
26444
|
-
outPath = value;
|
|
26445
|
-
} else {
|
|
26446
|
-
throw new Error(`Unknown argument: ${arg}`);
|
|
26447
|
-
}
|
|
26448
|
-
}
|
|
26449
|
-
if (inPaths.length === 0) {
|
|
26450
|
-
throw new Error("at least one --in file is required");
|
|
26451
|
-
}
|
|
26452
|
-
if (outPath === undefined) {
|
|
26453
|
-
throw new Error("--out file is required");
|
|
26454
|
-
}
|
|
26455
|
-
return { inPaths, outPath };
|
|
26456
|
-
}
|
|
26457
|
-
async function runFanin(argv) {
|
|
26458
|
-
let opts;
|
|
26459
|
-
try {
|
|
26460
|
-
opts = parseArgs(argv);
|
|
26461
|
-
} catch (err) {
|
|
26462
|
-
echoError(`fanin: ${err instanceof Error ? err.message : String(err)}`);
|
|
26463
|
-
return EXIT_USAGE;
|
|
26464
|
-
}
|
|
26465
|
-
const fs2 = createNodeFileSystem();
|
|
26466
|
-
const inputs = [];
|
|
26467
|
-
const sources = [];
|
|
26468
|
-
for (const inPath of opts.inPaths) {
|
|
26469
|
-
const docList = await readDocList(fs2, inPath);
|
|
26470
|
-
if (docList === null) {
|
|
26471
|
-
return EXIT_INPUT;
|
|
26472
|
-
}
|
|
26473
|
-
inputs.push(docList);
|
|
26474
|
-
sources.push(inPath);
|
|
26475
|
-
}
|
|
26476
|
-
const merged = mergeDocLists(inputs, sources);
|
|
26477
|
-
try {
|
|
26478
|
-
await fs2.writeFile(resolve2(opts.outPath), `${JSON.stringify(merged, null, 2)}
|
|
26479
|
-
`);
|
|
26480
|
-
} catch (err) {
|
|
26481
|
-
echoError(`Failed to write output file ${resolve2(opts.outPath)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26482
|
-
return EXIT_INPUT;
|
|
26483
|
-
}
|
|
26484
|
-
return 0;
|
|
26485
|
-
}
|
|
26486
|
-
|
|
26487
|
-
// src/fanout.ts
|
|
26488
|
-
import { tmpdir } from "os";
|
|
26489
|
-
import { isAbsolute as isAbsolute2, join as join3, resolve as resolve4 } from "path";
|
|
26490
|
-
init_dist();
|
|
26491
26372
|
|
|
26492
26373
|
// src/invoke.ts
|
|
26493
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
26374
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync3, unlinkSync } from "fs";
|
|
26494
26375
|
import { homedir as homedir2 } from "os";
|
|
26495
|
-
import { isAbsolute, join as join2, resolve as
|
|
26376
|
+
import { isAbsolute, join as join2, resolve as resolve2 } from "path";
|
|
26496
26377
|
init_dist();
|
|
26497
26378
|
function resolveKkConfigPath() {
|
|
26498
26379
|
return process.env.KK_CONFIG || join2(homedir2(), ".config/kk/config.yaml");
|
|
@@ -26546,12 +26427,21 @@ function pluginEnvFromConfig(kind, name, configFile = resolveKkConfigPath()) {
|
|
|
26546
26427
|
}
|
|
26547
26428
|
return out;
|
|
26548
26429
|
}
|
|
26549
|
-
function
|
|
26550
|
-
|
|
26430
|
+
function pluginEntryCmd(opts) {
|
|
26431
|
+
return opts.entryPath.endsWith(".ts") || opts.entryPath.endsWith(".js") ? [process.execPath, opts.entryPath, "--in", opts.inPath, "--out", opts.outPath] : [opts.entryPath, "--in", opts.inPath, "--out", opts.outPath];
|
|
26432
|
+
}
|
|
26433
|
+
function pluginEntryEnv(opts) {
|
|
26551
26434
|
const fromConfig = opts.pluginKind !== undefined && opts.pluginName !== undefined ? pluginEnvFromConfig(opts.pluginKind, opts.pluginName) : {};
|
|
26552
|
-
|
|
26435
|
+
return {
|
|
26436
|
+
...fromConfig,
|
|
26437
|
+
...process.env,
|
|
26438
|
+
KNOWLEDGE_KIT_PROJECT_ROOT: process.env.KNOWLEDGE_KIT_PROJECT_ROOT ?? process.cwd()
|
|
26439
|
+
};
|
|
26440
|
+
}
|
|
26441
|
+
function spawnPluginEntry(opts) {
|
|
26442
|
+
const proc = Bun.spawnSync(pluginEntryCmd(opts), {
|
|
26553
26443
|
cwd: opts.cwd,
|
|
26554
|
-
env:
|
|
26444
|
+
env: pluginEntryEnv(opts),
|
|
26555
26445
|
stdout: "pipe",
|
|
26556
26446
|
stderr: "pipe"
|
|
26557
26447
|
});
|
|
@@ -26561,6 +26451,51 @@ function spawnPluginEntry(opts) {
|
|
|
26561
26451
|
stderr: proc.stderr.toString().trim()
|
|
26562
26452
|
};
|
|
26563
26453
|
}
|
|
26454
|
+
function killProcessTree(pid) {
|
|
26455
|
+
let stdout;
|
|
26456
|
+
try {
|
|
26457
|
+
stdout = Bun.spawnSync(["pgrep", "-P", String(pid)]).stdout.toString();
|
|
26458
|
+
} catch {
|
|
26459
|
+
stdout = "";
|
|
26460
|
+
}
|
|
26461
|
+
for (const line of stdout.trim().split(`
|
|
26462
|
+
`)) {
|
|
26463
|
+
const child = Number(line);
|
|
26464
|
+
if (Number.isInteger(child) && child > 0) {
|
|
26465
|
+
killProcessTree(child);
|
|
26466
|
+
}
|
|
26467
|
+
}
|
|
26468
|
+
try {
|
|
26469
|
+
process.kill(pid, "SIGKILL");
|
|
26470
|
+
} catch {}
|
|
26471
|
+
}
|
|
26472
|
+
async function spawnPluginEntryAsync(opts) {
|
|
26473
|
+
let timedOut = false;
|
|
26474
|
+
let timer;
|
|
26475
|
+
const proc = Bun.spawn(pluginEntryCmd(opts), {
|
|
26476
|
+
cwd: opts.cwd,
|
|
26477
|
+
env: pluginEntryEnv(opts),
|
|
26478
|
+
stdout: "pipe",
|
|
26479
|
+
stderr: "pipe"
|
|
26480
|
+
});
|
|
26481
|
+
if (opts.timeoutMs !== undefined) {
|
|
26482
|
+
timer = setTimeout(() => {
|
|
26483
|
+
timedOut = true;
|
|
26484
|
+
killProcessTree(proc.pid);
|
|
26485
|
+
}, opts.timeoutMs);
|
|
26486
|
+
}
|
|
26487
|
+
const [stdout, stderr] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text()]);
|
|
26488
|
+
await proc.exited;
|
|
26489
|
+
if (timer !== undefined) {
|
|
26490
|
+
clearTimeout(timer);
|
|
26491
|
+
}
|
|
26492
|
+
return {
|
|
26493
|
+
exitCode: proc.exitCode === null ? 1 : proc.exitCode,
|
|
26494
|
+
stdout: stdout.trim(),
|
|
26495
|
+
stderr: stderr.trim(),
|
|
26496
|
+
...timedOut ? { timedOut: true } : {}
|
|
26497
|
+
};
|
|
26498
|
+
}
|
|
26564
26499
|
async function runInvoke(opts) {
|
|
26565
26500
|
const discovery = discoverAllPlugins({ pluginsRoot: opts.pluginsRoot, extraPaths: opts.pluginsPath });
|
|
26566
26501
|
const record2 = getByName(discovery, opts.plugin);
|
|
@@ -26574,8 +26509,13 @@ async function runInvoke(opts) {
|
|
|
26574
26509
|
return 1;
|
|
26575
26510
|
}
|
|
26576
26511
|
const contract = kindContract(record2.kind);
|
|
26577
|
-
const absInPath =
|
|
26578
|
-
const absOutPath =
|
|
26512
|
+
const absInPath = resolve2(opts.inPath);
|
|
26513
|
+
const absOutPath = resolve2(opts.outPath);
|
|
26514
|
+
if (existsSync3(absInPath) && absInPath === absOutPath) {
|
|
26515
|
+
echoError(`--in and --out alias the same path (${absInPath}); refusing to overwrite the input`);
|
|
26516
|
+
return 1;
|
|
26517
|
+
}
|
|
26518
|
+
let artifactOutput = false;
|
|
26579
26519
|
if (contract.input !== null) {
|
|
26580
26520
|
if (!existsSync3(absInPath)) {
|
|
26581
26521
|
echoError(`Input file does not exist: ${absInPath}`);
|
|
@@ -26595,11 +26535,26 @@ async function runInvoke(opts) {
|
|
|
26595
26535
|
echoError(`Input file ${absInPath} is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
26596
26536
|
return 1;
|
|
26597
26537
|
}
|
|
26598
|
-
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
|
|
26602
|
-
|
|
26538
|
+
if (record2.kind === "publish" && Array.isArray(json2)) {
|
|
26539
|
+
const op = Object.values(record2.operations ?? {}).find((o) => o.input === "doc-list");
|
|
26540
|
+
if (!op) {
|
|
26541
|
+
echoError(`Publish plugin '${record2.name}' received a Doc[] array but declares no doc-list operation; a Content object is required`);
|
|
26542
|
+
return 1;
|
|
26543
|
+
}
|
|
26544
|
+
const docs = DocListSchema.safeParse(json2);
|
|
26545
|
+
if (!docs.success) {
|
|
26546
|
+
const issues = docs.error.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ");
|
|
26547
|
+
echoError(`Input schema violation for the declared doc-list operation of '${record2.name}': ${issues}`);
|
|
26548
|
+
return 1;
|
|
26549
|
+
}
|
|
26550
|
+
artifactOutput = op.output === "markdown";
|
|
26551
|
+
} else {
|
|
26552
|
+
const parseResult = contract.input.safeParse(json2);
|
|
26553
|
+
if (!parseResult.success) {
|
|
26554
|
+
const issues = parseResult.error.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ");
|
|
26555
|
+
echoError(`Input schema violation for kind '${record2.kind}': ${issues}`);
|
|
26556
|
+
return 1;
|
|
26557
|
+
}
|
|
26603
26558
|
}
|
|
26604
26559
|
} else {
|
|
26605
26560
|
if (!existsSync3(absInPath)) {
|
|
@@ -26607,11 +26562,19 @@ async function runInvoke(opts) {
|
|
|
26607
26562
|
return 1;
|
|
26608
26563
|
}
|
|
26609
26564
|
}
|
|
26610
|
-
const entryPath = isAbsolute(record2.entry) ? record2.entry :
|
|
26565
|
+
const entryPath = isAbsolute(record2.entry) ? record2.entry : resolve2(record2.dir, record2.entry);
|
|
26611
26566
|
if (!existsSync3(entryPath)) {
|
|
26612
26567
|
echoError(`Plugin entry script does not exist: ${entryPath}`);
|
|
26613
26568
|
return 1;
|
|
26614
26569
|
}
|
|
26570
|
+
if (existsSync3(absOutPath)) {
|
|
26571
|
+
try {
|
|
26572
|
+
unlinkSync(absOutPath);
|
|
26573
|
+
} catch (err) {
|
|
26574
|
+
echoError(`Failed to clear stale output ${absOutPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26575
|
+
return 1;
|
|
26576
|
+
}
|
|
26577
|
+
}
|
|
26615
26578
|
const proc = spawnPluginEntry({
|
|
26616
26579
|
entryPath,
|
|
26617
26580
|
cwd: record2.dir,
|
|
@@ -26630,6 +26593,13 @@ async function runInvoke(opts) {
|
|
|
26630
26593
|
echoError(`Plugin entry succeeded but did not write output file: ${absOutPath}`);
|
|
26631
26594
|
return 1;
|
|
26632
26595
|
}
|
|
26596
|
+
if (artifactOutput) {
|
|
26597
|
+
if (statSync3(absOutPath).size === 0) {
|
|
26598
|
+
echoError(`Plugin entry succeeded but wrote an empty output file: ${absOutPath}`);
|
|
26599
|
+
return 1;
|
|
26600
|
+
}
|
|
26601
|
+
return 0;
|
|
26602
|
+
}
|
|
26633
26603
|
let outContent;
|
|
26634
26604
|
try {
|
|
26635
26605
|
outContent = readFileSync3(absOutPath, "utf-8");
|
|
@@ -26650,10 +26620,305 @@ async function runInvoke(opts) {
|
|
|
26650
26620
|
echoError(`Output schema violation for kind '${record2.kind}': ${issues}`);
|
|
26651
26621
|
return 1;
|
|
26652
26622
|
}
|
|
26623
|
+
const parsed = outResult.data;
|
|
26624
|
+
if (record2.kind === "publish" && parsed.ok === false) {
|
|
26625
|
+
const reason = parsed.error ?? parsed.message ?? "publish failed";
|
|
26626
|
+
echoError(`Plugin '${record2.name}' reported failure: ${reason}`);
|
|
26627
|
+
return 1;
|
|
26628
|
+
}
|
|
26629
|
+
return 0;
|
|
26630
|
+
}
|
|
26631
|
+
|
|
26632
|
+
// src/fanin.ts
|
|
26633
|
+
var DEFAULT_SOURCE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
26634
|
+
var SOURCE_ERROR_MAX_CHARS = 2048;
|
|
26635
|
+
function capSourceError(error51) {
|
|
26636
|
+
if (error51.length <= SOURCE_ERROR_MAX_CHARS) {
|
|
26637
|
+
return error51;
|
|
26638
|
+
}
|
|
26639
|
+
return `${error51.slice(0, SOURCE_ERROR_MAX_CHARS)}... [truncated; ${error51.length} chars total]`;
|
|
26640
|
+
}
|
|
26641
|
+
var EXIT_USAGE = 2;
|
|
26642
|
+
var EXIT_INPUT = 1;
|
|
26643
|
+
function mergeDocLists(inputs, sources) {
|
|
26644
|
+
const merged = [];
|
|
26645
|
+
const byId = new Map;
|
|
26646
|
+
for (const [i, docList] of inputs.entries()) {
|
|
26647
|
+
const file2 = sources[i] ?? `input[${i}]`;
|
|
26648
|
+
for (const doc2 of docList) {
|
|
26649
|
+
const entry = { file: file2 };
|
|
26650
|
+
if (doc2.sourceUri !== undefined) {
|
|
26651
|
+
entry.sourceUri = doc2.sourceUri;
|
|
26652
|
+
}
|
|
26653
|
+
const existing = byId.get(doc2.id);
|
|
26654
|
+
if (existing) {
|
|
26655
|
+
const list = existing.metadata?.sources;
|
|
26656
|
+
if (Array.isArray(list)) {
|
|
26657
|
+
list.push(entry);
|
|
26658
|
+
}
|
|
26659
|
+
continue;
|
|
26660
|
+
}
|
|
26661
|
+
const mergedDoc = {
|
|
26662
|
+
...doc2,
|
|
26663
|
+
metadata: { ...doc2.metadata ?? {}, sources: [entry] }
|
|
26664
|
+
};
|
|
26665
|
+
byId.set(doc2.id, mergedDoc);
|
|
26666
|
+
merged.push(mergedDoc);
|
|
26667
|
+
}
|
|
26668
|
+
}
|
|
26669
|
+
return merged;
|
|
26670
|
+
}
|
|
26671
|
+
async function readDocList(fs2, path3) {
|
|
26672
|
+
if (!await fs2.exists(path3)) {
|
|
26673
|
+
echoError(`Input file does not exist: ${path3}`);
|
|
26674
|
+
return null;
|
|
26675
|
+
}
|
|
26676
|
+
let content;
|
|
26677
|
+
try {
|
|
26678
|
+
content = await fs2.readFile(path3);
|
|
26679
|
+
} catch (err) {
|
|
26680
|
+
echoError(`Failed to read input file ${path3}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26681
|
+
return null;
|
|
26682
|
+
}
|
|
26683
|
+
let json2;
|
|
26684
|
+
try {
|
|
26685
|
+
json2 = JSON.parse(content);
|
|
26686
|
+
} catch (err) {
|
|
26687
|
+
echoError(`Input file ${path3} is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
26688
|
+
return null;
|
|
26689
|
+
}
|
|
26690
|
+
const result = DocListSchema.safeParse(json2);
|
|
26691
|
+
if (!result.success) {
|
|
26692
|
+
const issues = result.error.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ");
|
|
26693
|
+
echoError(`Input file ${path3} violates the Doc list schema: ${issues}`);
|
|
26694
|
+
return null;
|
|
26695
|
+
}
|
|
26696
|
+
return result.data;
|
|
26697
|
+
}
|
|
26698
|
+
function parseArgs(argv) {
|
|
26699
|
+
const inPaths = [];
|
|
26700
|
+
const sources = [];
|
|
26701
|
+
let outPath;
|
|
26702
|
+
let sourceTimeoutMs;
|
|
26703
|
+
const sourceTimeoutOverrides = {};
|
|
26704
|
+
for (let i = 0;i < argv.length; i++) {
|
|
26705
|
+
const arg = argv[i];
|
|
26706
|
+
if (arg === "--in") {
|
|
26707
|
+
const value = argv[++i];
|
|
26708
|
+
if (value === undefined) {
|
|
26709
|
+
throw new Error("--in requires a file path");
|
|
26710
|
+
}
|
|
26711
|
+
inPaths.push(value);
|
|
26712
|
+
} else if (arg === "--source") {
|
|
26713
|
+
const value = argv[++i];
|
|
26714
|
+
if (value === undefined) {
|
|
26715
|
+
throw new Error("--source requires <plugin>:<inputFile>");
|
|
26716
|
+
}
|
|
26717
|
+
const sep = value.indexOf(":");
|
|
26718
|
+
if (sep <= 0 || sep === value.length - 1) {
|
|
26719
|
+
throw new Error(`--source requires <plugin>:<inputFile>, got '${value}'`);
|
|
26720
|
+
}
|
|
26721
|
+
sources.push({ plugin: value.slice(0, sep), inputFile: value.slice(sep + 1) });
|
|
26722
|
+
} else if (arg === "--source-timeout") {
|
|
26723
|
+
const value = argv[++i];
|
|
26724
|
+
if (value === undefined) {
|
|
26725
|
+
throw new Error("--source-timeout requires a millisecond value");
|
|
26726
|
+
}
|
|
26727
|
+
if (sourceTimeoutMs !== undefined) {
|
|
26728
|
+
throw new Error("--source-timeout may be specified only once");
|
|
26729
|
+
}
|
|
26730
|
+
const parsed = Number(value);
|
|
26731
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
26732
|
+
throw new Error(`--source-timeout must be a positive integer (ms), got '${value}'`);
|
|
26733
|
+
}
|
|
26734
|
+
sourceTimeoutMs = parsed;
|
|
26735
|
+
} else if (arg === "--source-timeout-override") {
|
|
26736
|
+
const value = argv[++i];
|
|
26737
|
+
if (value === undefined) {
|
|
26738
|
+
throw new Error("--source-timeout-override requires <plugin>:<ms>");
|
|
26739
|
+
}
|
|
26740
|
+
const sep = value.indexOf(":");
|
|
26741
|
+
if (sep <= 0 || sep === value.length - 1) {
|
|
26742
|
+
throw new Error(`--source-timeout-override requires <plugin>:<ms>, got '${value}'`);
|
|
26743
|
+
}
|
|
26744
|
+
const plugin = value.slice(0, sep);
|
|
26745
|
+
if (plugin in sourceTimeoutOverrides) {
|
|
26746
|
+
throw new Error(`--source-timeout-override: plugin '${plugin}' may be specified only once`);
|
|
26747
|
+
}
|
|
26748
|
+
const parsed = Number(value.slice(sep + 1));
|
|
26749
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
26750
|
+
throw new Error(`--source-timeout-override ms must be a positive integer, got '${value.slice(sep + 1)}'`);
|
|
26751
|
+
}
|
|
26752
|
+
sourceTimeoutOverrides[plugin] = parsed;
|
|
26753
|
+
} else if (arg === "--out") {
|
|
26754
|
+
const value = argv[++i];
|
|
26755
|
+
if (value === undefined) {
|
|
26756
|
+
throw new Error("--out requires a file path");
|
|
26757
|
+
}
|
|
26758
|
+
if (outPath !== undefined) {
|
|
26759
|
+
throw new Error("--out may be specified only once");
|
|
26760
|
+
}
|
|
26761
|
+
outPath = value;
|
|
26762
|
+
} else {
|
|
26763
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
26764
|
+
}
|
|
26765
|
+
}
|
|
26766
|
+
if (inPaths.length === 0 && sources.length === 0) {
|
|
26767
|
+
throw new Error("at least one --in file or --source is required");
|
|
26768
|
+
}
|
|
26769
|
+
if (sourceTimeoutMs !== undefined && sources.length === 0) {
|
|
26770
|
+
throw new Error("--source-timeout requires at least one --source");
|
|
26771
|
+
}
|
|
26772
|
+
if (Object.keys(sourceTimeoutOverrides).length > 0 && sources.length === 0) {
|
|
26773
|
+
throw new Error("--source-timeout-override requires at least one --source");
|
|
26774
|
+
}
|
|
26775
|
+
for (const plugin of Object.keys(sourceTimeoutOverrides)) {
|
|
26776
|
+
if (!sources.some((candidate) => candidate.plugin === plugin)) {
|
|
26777
|
+
throw new Error(`--source-timeout-override names plugin '${plugin}' which is not among the --source plugins`);
|
|
26778
|
+
}
|
|
26779
|
+
}
|
|
26780
|
+
if (outPath === undefined) {
|
|
26781
|
+
throw new Error("--out file is required");
|
|
26782
|
+
}
|
|
26783
|
+
return {
|
|
26784
|
+
inPaths,
|
|
26785
|
+
outPath,
|
|
26786
|
+
...sources.length > 0 ? { sources } : {},
|
|
26787
|
+
...sourceTimeoutMs !== undefined ? { sourceTimeoutMs } : {},
|
|
26788
|
+
...Object.keys(sourceTimeoutOverrides).length > 0 ? { sourceTimeoutOverrides } : {}
|
|
26789
|
+
};
|
|
26790
|
+
}
|
|
26791
|
+
async function collectSourceDocs(discovery, source, timeoutMs) {
|
|
26792
|
+
const record2 = getByName(discovery, source.plugin);
|
|
26793
|
+
if (!record2) {
|
|
26794
|
+
throw new Error(`Plugin '${source.plugin}' not found in the merged plugin registry`);
|
|
26795
|
+
}
|
|
26796
|
+
if (record2.kind !== "ingestion") {
|
|
26797
|
+
throw new Error(`Plugin '${source.plugin}' is kind '${record2.kind}', not 'ingestion'`);
|
|
26798
|
+
}
|
|
26799
|
+
const fs2 = createNodeFileSystem();
|
|
26800
|
+
const absInPath = resolve3(source.inputFile);
|
|
26801
|
+
if (!await fs2.exists(absInPath)) {
|
|
26802
|
+
throw new Error(`Input file does not exist: ${absInPath}`);
|
|
26803
|
+
}
|
|
26804
|
+
const entryPath = isAbsolute2(record2.entry) ? record2.entry : resolve3(record2.dir, record2.entry);
|
|
26805
|
+
if (!await fs2.exists(entryPath)) {
|
|
26806
|
+
throw new Error(`Plugin entry script does not exist: ${entryPath}`);
|
|
26807
|
+
}
|
|
26808
|
+
const outTmp = join3(tmpdir(), `kk-fanin-${source.plugin}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
26809
|
+
try {
|
|
26810
|
+
const proc = await spawnPluginEntryAsync({
|
|
26811
|
+
entryPath,
|
|
26812
|
+
cwd: record2.dir,
|
|
26813
|
+
inPath: absInPath,
|
|
26814
|
+
outPath: outTmp,
|
|
26815
|
+
pluginKind: record2.kind,
|
|
26816
|
+
pluginName: record2.name,
|
|
26817
|
+
timeoutMs
|
|
26818
|
+
});
|
|
26819
|
+
if (proc.exitCode !== 0) {
|
|
26820
|
+
const why = proc.timedOut === true ? `timed out after ${timeoutMs}ms` : `exited with code ${proc.exitCode}`;
|
|
26821
|
+
throw new Error(`Plugin '${source.plugin}' ${why}: ${proc.stderr || proc.stdout || "no output"}`);
|
|
26822
|
+
}
|
|
26823
|
+
if (!await fs2.exists(outTmp)) {
|
|
26824
|
+
throw new Error(`Plugin '${source.plugin}' succeeded but did not write an output file`);
|
|
26825
|
+
}
|
|
26826
|
+
let json2;
|
|
26827
|
+
try {
|
|
26828
|
+
json2 = JSON.parse(await fs2.readFile(outTmp));
|
|
26829
|
+
} catch (err) {
|
|
26830
|
+
throw new Error(`Plugin '${source.plugin}' wrote invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
26831
|
+
}
|
|
26832
|
+
const parsed = DocListSchema.safeParse(json2);
|
|
26833
|
+
if (!parsed.success) {
|
|
26834
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ");
|
|
26835
|
+
throw new Error(`Plugin '${source.plugin}' output violates the Doc list schema: ${issues}`);
|
|
26836
|
+
}
|
|
26837
|
+
return parsed.data;
|
|
26838
|
+
} finally {
|
|
26839
|
+
await fs2.deleteFile(outTmp);
|
|
26840
|
+
}
|
|
26841
|
+
}
|
|
26842
|
+
async function runFanin(argv) {
|
|
26843
|
+
let opts;
|
|
26844
|
+
try {
|
|
26845
|
+
opts = parseArgs(argv);
|
|
26846
|
+
} catch (err) {
|
|
26847
|
+
echoError(`fanin: ${err instanceof Error ? err.message : String(err)}`);
|
|
26848
|
+
return EXIT_USAGE;
|
|
26849
|
+
}
|
|
26850
|
+
const fs2 = createNodeFileSystem();
|
|
26851
|
+
const inputs = [];
|
|
26852
|
+
const sources = [];
|
|
26853
|
+
const sourceErrors = [];
|
|
26854
|
+
for (const inPath of opts.inPaths) {
|
|
26855
|
+
const docList = await readDocList(fs2, inPath);
|
|
26856
|
+
if (docList === null) {
|
|
26857
|
+
return EXIT_INPUT;
|
|
26858
|
+
}
|
|
26859
|
+
inputs.push(docList);
|
|
26860
|
+
sources.push(inPath);
|
|
26861
|
+
}
|
|
26862
|
+
if (opts.sources !== undefined && opts.sources.length > 0) {
|
|
26863
|
+
const discovery = discoverAllPlugins({});
|
|
26864
|
+
const settled = await Promise.allSettled(opts.sources.map(async (source) => {
|
|
26865
|
+
const started = Date.now();
|
|
26866
|
+
try {
|
|
26867
|
+
const timeoutMs = opts.sourceTimeoutOverrides?.[source.plugin] ?? opts.sourceTimeoutMs ?? DEFAULT_SOURCE_TIMEOUT_MS;
|
|
26868
|
+
return { ok: true, docs: await collectSourceDocs(discovery, source, timeoutMs) };
|
|
26869
|
+
} catch (err) {
|
|
26870
|
+
return {
|
|
26871
|
+
ok: false,
|
|
26872
|
+
error: err instanceof Error ? err.message : String(err),
|
|
26873
|
+
durationMs: Date.now() - started
|
|
26874
|
+
};
|
|
26875
|
+
}
|
|
26876
|
+
}));
|
|
26877
|
+
for (const [i, outcome] of settled.entries()) {
|
|
26878
|
+
const source = opts.sources[i];
|
|
26879
|
+
if (source === undefined) {
|
|
26880
|
+
continue;
|
|
26881
|
+
}
|
|
26882
|
+
const result = outcome.status === "fulfilled" ? outcome.value : { ok: false, error: String(outcome.reason), durationMs: 0 };
|
|
26883
|
+
if (result.ok) {
|
|
26884
|
+
inputs.push(result.docs);
|
|
26885
|
+
sources.push(source.plugin);
|
|
26886
|
+
} else {
|
|
26887
|
+
sourceErrors.push({
|
|
26888
|
+
plugin: source.plugin,
|
|
26889
|
+
error: capSourceError(result.error),
|
|
26890
|
+
durationMs: result.durationMs
|
|
26891
|
+
});
|
|
26892
|
+
}
|
|
26893
|
+
}
|
|
26894
|
+
for (const err of sourceErrors) {
|
|
26895
|
+
echoError(`fanin: source '${err.plugin}' failed after ${err.durationMs}ms: ${err.error}`);
|
|
26896
|
+
}
|
|
26897
|
+
if (sourceErrors.length === opts.sources.length) {
|
|
26898
|
+
echoError(`fanin: all ${opts.sources.length} source(s) failed; no output written`);
|
|
26899
|
+
return EXIT_INPUT;
|
|
26900
|
+
}
|
|
26901
|
+
}
|
|
26902
|
+
const merged = mergeDocLists(inputs, sources);
|
|
26903
|
+
if (sourceErrors.length > 0) {
|
|
26904
|
+
for (const doc2 of merged) {
|
|
26905
|
+
doc2.metadata = { ...doc2.metadata ?? {}, sourceErrors };
|
|
26906
|
+
}
|
|
26907
|
+
}
|
|
26908
|
+
try {
|
|
26909
|
+
await fs2.writeFile(resolve3(opts.outPath), `${JSON.stringify(merged, null, 2)}
|
|
26910
|
+
`);
|
|
26911
|
+
} catch (err) {
|
|
26912
|
+
echoError(`Failed to write output file ${resolve3(opts.outPath)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26913
|
+
return EXIT_INPUT;
|
|
26914
|
+
}
|
|
26653
26915
|
return 0;
|
|
26654
26916
|
}
|
|
26655
26917
|
|
|
26656
26918
|
// src/fanout.ts
|
|
26919
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
26920
|
+
import { isAbsolute as isAbsolute3, join as join4, resolve as resolve4 } from "path";
|
|
26921
|
+
init_dist();
|
|
26657
26922
|
var EXIT_FAILURE = 1;
|
|
26658
26923
|
var EXIT_INPUT2 = 2;
|
|
26659
26924
|
function aggregate(entries) {
|
|
@@ -26683,6 +26948,7 @@ async function fanout(content, targets, dispatch) {
|
|
|
26683
26948
|
status: "published",
|
|
26684
26949
|
...result.url !== undefined ? { url: result.url } : {},
|
|
26685
26950
|
...result.id !== undefined ? { id: result.id } : {},
|
|
26951
|
+
...result.metadata !== undefined ? { metadata: result.metadata } : {},
|
|
26686
26952
|
durationMs
|
|
26687
26953
|
} : {
|
|
26688
26954
|
target,
|
|
@@ -26704,9 +26970,6 @@ async function fanout(content, targets, dispatch) {
|
|
|
26704
26970
|
}
|
|
26705
26971
|
return aggregate(entries);
|
|
26706
26972
|
}
|
|
26707
|
-
function selectRetryTargets(prior) {
|
|
26708
|
-
return prior.targets.filter((t) => t.status === "failed").map((t) => t.target);
|
|
26709
|
-
}
|
|
26710
26973
|
async function readContent(fs2, path3) {
|
|
26711
26974
|
if (!await fs2.exists(path3)) {
|
|
26712
26975
|
echoError(`Content file does not exist: ${path3}`);
|
|
@@ -26781,6 +27044,14 @@ async function readAggregateResult(fs2, path3) {
|
|
|
26781
27044
|
echoError(`Result file ${path3} violates the aggregate result schema (target ${String(t.target)})`);
|
|
26782
27045
|
return null;
|
|
26783
27046
|
}
|
|
27047
|
+
let metadata;
|
|
27048
|
+
if (t.metadata !== undefined) {
|
|
27049
|
+
if (typeof t.metadata !== "object" || t.metadata === null || Array.isArray(t.metadata)) {
|
|
27050
|
+
echoError(`Result file ${path3} violates the aggregate result schema: target '${String(t.target)}' metadata must be an object`);
|
|
27051
|
+
return null;
|
|
27052
|
+
}
|
|
27053
|
+
metadata = t.metadata;
|
|
27054
|
+
}
|
|
26784
27055
|
targets.push({
|
|
26785
27056
|
target: t.target,
|
|
26786
27057
|
ok: t.ok,
|
|
@@ -26788,7 +27059,8 @@ async function readAggregateResult(fs2, path3) {
|
|
|
26788
27059
|
...typeof t.url === "string" ? { url: t.url } : {},
|
|
26789
27060
|
...typeof t.id === "string" ? { id: t.id } : {},
|
|
26790
27061
|
...typeof t.error === "string" ? { error: t.error } : {},
|
|
26791
|
-
...typeof t.durationMs === "number" ? { durationMs: t.durationMs } : {}
|
|
27062
|
+
...typeof t.durationMs === "number" ? { durationMs: t.durationMs } : {},
|
|
27063
|
+
...metadata !== undefined ? { metadata } : {}
|
|
26792
27064
|
});
|
|
26793
27065
|
}
|
|
26794
27066
|
return {
|
|
@@ -26887,11 +27159,11 @@ async function pluginDispatch(target, contentPath, discovery) {
|
|
|
26887
27159
|
throw new Error(`Publish plugin '${target}' not found in the merged plugin registry`);
|
|
26888
27160
|
}
|
|
26889
27161
|
const fs2 = createNodeFileSystem();
|
|
26890
|
-
const entryPath =
|
|
27162
|
+
const entryPath = isAbsolute3(record2.entry) ? record2.entry : resolve4(record2.dir, record2.entry);
|
|
26891
27163
|
if (!await fs2.exists(entryPath)) {
|
|
26892
27164
|
throw new Error(`Plugin entry script does not exist: ${entryPath}`);
|
|
26893
27165
|
}
|
|
26894
|
-
const outTmp =
|
|
27166
|
+
const outTmp = join4(tmpdir2(), `kk-fanout-${target}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
26895
27167
|
try {
|
|
26896
27168
|
const proc = spawnPluginEntry({
|
|
26897
27169
|
entryPath,
|
|
@@ -26956,6 +27228,7 @@ async function runFanout(argv, deps = {}) {
|
|
|
26956
27228
|
let prior;
|
|
26957
27229
|
let skipped = [];
|
|
26958
27230
|
let targets = opts.targets;
|
|
27231
|
+
let retainedFailed = [];
|
|
26959
27232
|
if (opts.retryFromPath !== undefined) {
|
|
26960
27233
|
const parsed = await readAggregateResult(fs2, opts.retryFromPath);
|
|
26961
27234
|
if (parsed === null) {
|
|
@@ -26967,17 +27240,26 @@ async function runFanout(argv, deps = {}) {
|
|
|
26967
27240
|
ok: true,
|
|
26968
27241
|
status: "skipped",
|
|
26969
27242
|
...t.url !== undefined ? { url: t.url } : {},
|
|
26970
|
-
...t.id !== undefined ? { id: t.id } : {}
|
|
27243
|
+
...t.id !== undefined ? { id: t.id } : {},
|
|
27244
|
+
...t.metadata !== undefined ? { metadata: t.metadata } : {}
|
|
26971
27245
|
}));
|
|
26972
27246
|
const carried = new Set(skipped.map((t) => t.target));
|
|
26973
|
-
|
|
27247
|
+
const priorFailed = prior.targets.filter((t) => t.status === "failed");
|
|
27248
|
+
if (opts.targets.length > 0) {
|
|
27249
|
+
const attempt = [...new Set(opts.targets.filter((t) => !carried.has(t)))];
|
|
27250
|
+
const attemptSet = new Set(attempt);
|
|
27251
|
+
targets = attempt;
|
|
27252
|
+
retainedFailed = priorFailed.filter((t) => !attemptSet.has(t.target));
|
|
27253
|
+
} else {
|
|
27254
|
+
targets = priorFailed.map((t) => t.target);
|
|
27255
|
+
}
|
|
26974
27256
|
}
|
|
26975
27257
|
const dispatch = deps.dispatch ?? ((t) => pluginDispatch(t, opts.contentPath, discovery));
|
|
26976
27258
|
if (deps.dispatch === undefined && !validateTargets(targets, discovery)) {
|
|
26977
27259
|
return EXIT_INPUT2;
|
|
26978
27260
|
}
|
|
26979
27261
|
const fresh = await fanout(content, targets, dispatch);
|
|
26980
|
-
const result = prior !== undefined ? aggregate([...skipped, ...fresh.targets]) : fresh;
|
|
27262
|
+
const result = prior !== undefined ? aggregate([...skipped, ...retainedFailed, ...fresh.targets]) : fresh;
|
|
26981
27263
|
try {
|
|
26982
27264
|
await fs2.writeFile(resolve4(opts.outPath), `${JSON.stringify(result, null, 2)}
|
|
26983
27265
|
`);
|
|
@@ -27012,11 +27294,24 @@ function collectIn(value, acc) {
|
|
|
27012
27294
|
acc.push(value);
|
|
27013
27295
|
return acc;
|
|
27014
27296
|
}
|
|
27297
|
+
function collectSource(value, acc) {
|
|
27298
|
+
acc.push(value);
|
|
27299
|
+
return acc;
|
|
27300
|
+
}
|
|
27015
27301
|
async function runFaninAction(options) {
|
|
27016
27302
|
const argv = [];
|
|
27017
27303
|
for (const inPath of options.in) {
|
|
27018
27304
|
argv.push("--in", inPath);
|
|
27019
27305
|
}
|
|
27306
|
+
for (const source of options.source ?? []) {
|
|
27307
|
+
argv.push("--source", source);
|
|
27308
|
+
}
|
|
27309
|
+
if (options.sourceTimeout !== undefined) {
|
|
27310
|
+
argv.push("--source-timeout", options.sourceTimeout);
|
|
27311
|
+
}
|
|
27312
|
+
for (const override of options.sourceTimeoutOverride ?? []) {
|
|
27313
|
+
argv.push("--source-timeout-override", override);
|
|
27314
|
+
}
|
|
27020
27315
|
argv.push("--out", options.out);
|
|
27021
27316
|
const code = await runFanin(argv);
|
|
27022
27317
|
if (code !== 0) {
|
|
@@ -27027,6 +27322,10 @@ function collectTarget(value, acc) {
|
|
|
27027
27322
|
acc.push(value);
|
|
27028
27323
|
return acc;
|
|
27029
27324
|
}
|
|
27325
|
+
function collectOverride(value, acc) {
|
|
27326
|
+
acc.push(value);
|
|
27327
|
+
return acc;
|
|
27328
|
+
}
|
|
27030
27329
|
async function runFanoutAction(options) {
|
|
27031
27330
|
const argv = ["--content", options.content, "--out", options.out];
|
|
27032
27331
|
for (const target of options.target) {
|
|
@@ -27049,10 +27348,10 @@ async function runFanoutAction(options) {
|
|
|
27049
27348
|
function registerExecutorCommand(program2) {
|
|
27050
27349
|
const executor = program2.command("executor").description("Run knowledge-kit plugin steps (invoke, fan-in, fan-out)");
|
|
27051
27350
|
executor.command("run").description("Invoke a plugin with input/output files and contract validation").argument("<plugin>", "Name of the plugin to invoke").requiredOption("--in <path>", "Input file path (or source path for ingestion)").requiredOption("--out <path>", "Output file path").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runInvokeAction);
|
|
27052
|
-
executor.command("fan-in").alias("fanin").description("Merge N Doc[] JSON inputs into one deduplicated Doc[] output").
|
|
27351
|
+
executor.command("fan-in").alias("fanin").description("Merge N Doc[] JSON inputs into one deduplicated Doc[] output").option("--in <path>", "Input Doc[] JSON file (repeatable)", collectIn, []).option("--source <plugin>:<file>", "Ingestion source to spawn concurrently: plugin name and its input file (repeatable)", collectSource, []).option("--source-timeout <ms>", "Per-source timeout in milliseconds (default 1800000)").option("--source-timeout-override <plugin>:<ms>", "Per-source timeout override for one named --source plugin, e.g. horizon-ingest:2700000 (repeatable; wins over --source-timeout/default)", collectOverride, []).requiredOption("--out <path>", "Output merged Doc[] JSON file path").action(runFaninAction);
|
|
27053
27352
|
executor.command("fan-out").alias("fanout").description("Publish one Content JSON to N targets, aggregating per-target results").requiredOption("--content <path>", "Source ContentSchema JSON file path").option("--target <plugin>", "Target publish plugin name (repeatable)", collectTarget, []).requiredOption("--out <path>", "Aggregate Result JSON file path").option("--retry-from <path>", "Prior Result JSON file to retry only previously failed targets").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runFanoutAction);
|
|
27054
27353
|
program2.command("invoke", { hidden: true }).description("Alias for `executor run` (deprecated)").argument("<plugin>", "Name of the plugin to invoke").requiredOption("--in <path>", "Input file path (or source path for ingestion)").requiredOption("--out <path>", "Output file path").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runInvokeAction);
|
|
27055
|
-
program2.command("fanin", { hidden: true }).description("Alias for `executor fan-in` (deprecated)").
|
|
27354
|
+
program2.command("fanin", { hidden: true }).description("Alias for `executor fan-in` (deprecated)").option("--in <path>", "Input Doc[] JSON file (repeatable)", collectIn, []).option("--source <plugin>:<file>", "Ingestion source to spawn concurrently: plugin name and its input file (repeatable)", collectSource, []).option("--source-timeout <ms>", "Per-source timeout in milliseconds (default 1800000)").option("--source-timeout-override <plugin>:<ms>", "Per-source timeout override for one named --source plugin, e.g. horizon-ingest:2700000 (repeatable; wins over --source-timeout/default)", collectOverride, []).requiredOption("--out <path>", "Output merged Doc[] JSON file path").action(runFaninAction);
|
|
27056
27355
|
program2.command("fanout", { hidden: true }).description("Alias for `executor fan-out` (deprecated)").requiredOption("--content <path>", "Source ContentSchema JSON file path").option("--target <plugin>", "Target publish plugin name (repeatable)", collectTarget, []).requiredOption("--out <path>", "Aggregate Result JSON file path").option("--retry-from <path>", "Prior Result JSON file to retry only previously failed targets").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runFanoutAction);
|
|
27057
27356
|
}
|
|
27058
27357
|
|