@gobing-ai/knowledge-kit 0.1.14 → 0.1.15
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 +796 -176
- package/package.json +1 -1
- package/plugins/generations/content-gen/dist/index.js +2 -2
- package/plugins/generations/core-facts-gen/dist/index.js +2 -2
- package/plugins/generations/daily-article-gen/dist/index.js +2 -2
- package/plugins/generations/dailynews-gen/dist/index.js +72 -9
- package/plugins/generations/dailynews-gen/src/index.ts +23 -1
- package/plugins/generations/dailynews-gen/src/script-plan.ts +103 -5
- package/plugins/generations/episode-plan-gen/dist/index.js +2 -2
- package/plugins/generations/gemini-tts/dist/index.js +2 -2
- package/plugins/generations/image-gen/dist/index.js +2 -2
- package/plugins/generations/news-report-gen/dist/index.js +2 -2
- package/plugins/generations/voice-gen/dist/index.js +2 -2
- package/plugins/generations/voxcpm-tts/dist/index.js +2 -2
- package/plugins/ingestions/aihot-ingest/dist/index.js +2 -2
- package/plugins/ingestions/horizon-ingest/dist/index.js +2 -2
- package/plugins/ingestions/last30days-ingest/dist/index.js +2 -2
- package/plugins/ingestions/web-search/dist/index.js +2 -2
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/daily-scheduled-run.ts +4 -0
- package/plugins/kk/scripts/daily-stages.ts +476 -12
- package/plugins/kk/scripts/kk-workflow-stages.ts +20 -4
- package/plugins/kk/skills/content-judge/SKILL.md +5 -4
- package/plugins/kk/skills/content-judge/references/rubrics.md +14 -0
- package/plugins/kk/skills/news-editorial/SKILL.md +122 -0
- package/plugins/kk/skills/news-editorial/references/selection.md +47 -0
- package/plugins/kk/skills/news-editorial/references/style-contract.zh.md +99 -0
- package/plugins/kk/skills/publish/SKILL.md +3 -3
- package/plugins/kk/skills/web-automation/SKILL.md +229 -77
- package/plugins/kk/skills/web-automation/references/browser-launch.md +127 -36
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +4 -6
- package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +183 -222
- package/plugins/kk/workflows/kk-daily-publish3.yaml +16 -8
- package/plugins/publishings/bilibili-pub/dist/index.js +5 -3
- package/plugins/publishings/bilibili-pub/dist/profile-check.js +3 -1
- package/plugins/publishings/cnblogs-pub/dist/index.js +5 -3
- package/plugins/publishings/cnblogs-pub/dist/profile-check.js +3 -1
- package/plugins/publishings/csdn-pub/dist/index.js +5 -3
- package/plugins/publishings/csdn-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/emdash-pub/dist/index.js +2 -2
- package/plugins/publishings/infoq-pub/dist/index.js +5 -3
- package/plugins/publishings/infoq-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/jike-pub/dist/index.js +5 -3
- package/plugins/publishings/jike-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/juejin-pub/dist/index.js +5 -3
- package/plugins/publishings/juejin-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/linkedin-pub/dist/index.js +5 -3
- package/plugins/publishings/linkedin-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/medium-pub/dist/index.js +2 -2
- package/plugins/publishings/podcast-pub/dist/index.js +147 -4
- package/plugins/publishings/podcast-pub/src/index.ts +71 -2
- package/plugins/publishings/podcast-pub/src/map.ts +3 -0
- package/plugins/publishings/podcast-pub/src/transcript.ts +195 -0
- package/plugins/publishings/qiita-pub/dist/index.js +2 -2
- package/plugins/publishings/shipinhao-pub/dist/index.js +5 -3
- package/plugins/publishings/shipinhao-pub/dist/profile-check.js +3 -1
- package/plugins/publishings/sspai-pub/dist/index.js +5 -3
- package/plugins/publishings/sspai-pub/dist/profile-check.js +3 -1
- package/plugins/publishings/substack-pub/dist/index.js +5 -3
- package/plugins/publishings/substack-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/surfdash-pub/dist/index.js +2 -2
- package/plugins/publishings/threads-pub/dist/index.js +2 -2
- package/plugins/publishings/wechatmp-pub/dist/index.js +52 -33
- package/plugins/publishings/wechatmp-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/wechatmp-pub/src/flow.ts +70 -33
- package/plugins/publishings/weibo-pub/dist/index.js +5 -3
- package/plugins/publishings/weibo-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/x-pub/dist/index.js +5 -3
- package/plugins/publishings/x-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/xhs-pub/dist/index.js +5 -3
- package/plugins/publishings/xhs-pub/dist/profile-check.js +5 -3
- package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +5 -3
- package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +3 -1
- package/plugins/publishings/zenn-pub/dist/index.js +2 -2
- package/plugins/kk/scripts/daily-voice-style.zh.md +0 -70
package/dist/index.js
CHANGED
|
@@ -38526,10 +38526,12 @@ var init_bot = __esm(() => {
|
|
|
38526
38526
|
CONTROL_CHARACTERS = /\p{Cc}/u;
|
|
38527
38527
|
BotError = class BotError extends Error {
|
|
38528
38528
|
code;
|
|
38529
|
-
|
|
38529
|
+
details;
|
|
38530
|
+
constructor(code, message, details = {}) {
|
|
38530
38531
|
super(message);
|
|
38531
38532
|
this.name = "BotError";
|
|
38532
38533
|
this.code = code;
|
|
38534
|
+
this.details = details;
|
|
38533
38535
|
}
|
|
38534
38536
|
};
|
|
38535
38537
|
});
|
|
@@ -38796,11 +38798,13 @@ __export(exports_browser, {
|
|
|
38796
38798
|
captureSessionFacts: () => captureSessionFacts,
|
|
38797
38799
|
checkChannelSession: () => checkChannelSession,
|
|
38798
38800
|
chmod: () => chmod,
|
|
38801
|
+
closeBotContext: () => closeBotContext,
|
|
38799
38802
|
createExclusiveFile: () => createExclusiveFile,
|
|
38800
38803
|
createKkBrowserLauncher: () => createKkBrowserLauncher,
|
|
38801
38804
|
createShadowFinder: () => createShadowFinder,
|
|
38802
38805
|
enterDelegatedLease: () => enterDelegatedLease,
|
|
38803
38806
|
execBot: () => execBot,
|
|
38807
|
+
execFailureDetail: () => execFailureDetail,
|
|
38804
38808
|
expectOutcome: () => expectOutcome,
|
|
38805
38809
|
fillCodeMirror: () => fillCodeMirror,
|
|
38806
38810
|
findLeftoverTransaction: () => findLeftoverTransaction,
|
|
@@ -38814,11 +38818,13 @@ __export(exports_browser, {
|
|
|
38814
38818
|
loginBot: () => loginBot,
|
|
38815
38819
|
logoutBot: () => logoutBot,
|
|
38816
38820
|
markdownToHtml: () => markdownToHtml,
|
|
38821
|
+
missingBotProfile: () => missingBotProfile,
|
|
38817
38822
|
mkdirExclusive: () => mkdirExclusive,
|
|
38818
38823
|
normalizeBotDefaultUrl: () => normalizeBotDefaultUrl,
|
|
38819
38824
|
normalizeSessionExpiry: () => normalizeSessionExpiry,
|
|
38820
38825
|
normalizeSessionIdentity: () => normalizeSessionIdentity,
|
|
38821
38826
|
openBot: () => openBot,
|
|
38827
|
+
openBotPage: () => openBotPage,
|
|
38822
38828
|
openChannelSession: () => openChannelSession,
|
|
38823
38829
|
parseBotMetadata: () => parseBotMetadata,
|
|
38824
38830
|
pasteHtml: () => pasteHtml,
|
|
@@ -38832,6 +38838,7 @@ __export(exports_browser, {
|
|
|
38832
38838
|
rmdir: () => rmdir,
|
|
38833
38839
|
runProfileCli: () => runProfileCli,
|
|
38834
38840
|
uploadFile: () => uploadFile,
|
|
38841
|
+
validateExecUrl: () => validateExecUrl,
|
|
38835
38842
|
verifyLeaseHeld: () => verifyLeaseHeld,
|
|
38836
38843
|
waitUntil: () => waitUntil
|
|
38837
38844
|
});
|
|
@@ -39652,6 +39659,539 @@ var init_browser = __esm(() => {
|
|
|
39652
39659
|
];
|
|
39653
39660
|
});
|
|
39654
39661
|
|
|
39662
|
+
// ../../packages/publish-harness/src/bot-collection.ts
|
|
39663
|
+
var exports_bot_collection = {};
|
|
39664
|
+
__export(exports_bot_collection, {
|
|
39665
|
+
runBotCollection: () => runBotCollection
|
|
39666
|
+
});
|
|
39667
|
+
function isRecord3(value) {
|
|
39668
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39669
|
+
}
|
|
39670
|
+
function usage(path) {
|
|
39671
|
+
return new BotError("usage", `invalid collection plan: ${path}`);
|
|
39672
|
+
}
|
|
39673
|
+
function requireSafeId(value, path, seen) {
|
|
39674
|
+
if (typeof value !== "string" || !SAFE_ID.test(value))
|
|
39675
|
+
throw usage(`${path} must match [A-Za-z0-9][A-Za-z0-9_-]{0,63}`);
|
|
39676
|
+
if (seen.has(value))
|
|
39677
|
+
throw usage(`${path} duplicates an id already declared in this plan`);
|
|
39678
|
+
seen.add(value);
|
|
39679
|
+
return value;
|
|
39680
|
+
}
|
|
39681
|
+
function requireString(value, path) {
|
|
39682
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
39683
|
+
throw usage(`${path} must be a non-empty string`);
|
|
39684
|
+
return value;
|
|
39685
|
+
}
|
|
39686
|
+
function optionalString(value, path) {
|
|
39687
|
+
return value === undefined ? undefined : requireString(value, path);
|
|
39688
|
+
}
|
|
39689
|
+
function optionalTimeout(value, path) {
|
|
39690
|
+
return value === undefined ? undefined : requirePositiveInt(value, path);
|
|
39691
|
+
}
|
|
39692
|
+
function requirePositiveInt(value, path) {
|
|
39693
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
39694
|
+
throw usage(`${path} must be a positive integer`);
|
|
39695
|
+
}
|
|
39696
|
+
return value;
|
|
39697
|
+
}
|
|
39698
|
+
function requireHeaders(value, path) {
|
|
39699
|
+
if (!isRecord3(value))
|
|
39700
|
+
throw usage(`${path} must be an object of string header values`);
|
|
39701
|
+
const headers = {};
|
|
39702
|
+
for (const [name, headerValue] of Object.entries(value)) {
|
|
39703
|
+
headers[name] = requireString(headerValue, `${path}.${name}`);
|
|
39704
|
+
}
|
|
39705
|
+
return headers;
|
|
39706
|
+
}
|
|
39707
|
+
function requireUrl(value, path) {
|
|
39708
|
+
try {
|
|
39709
|
+
return validateExecUrl(requireString(value, path));
|
|
39710
|
+
} catch {
|
|
39711
|
+
throw usage(`${path} must be an absolute http(s) URL without credentials`);
|
|
39712
|
+
}
|
|
39713
|
+
}
|
|
39714
|
+
function normalizeOrigin(value, path) {
|
|
39715
|
+
const raw = requireString(value, path);
|
|
39716
|
+
let parsed;
|
|
39717
|
+
try {
|
|
39718
|
+
parsed = new URL(raw);
|
|
39719
|
+
} catch {
|
|
39720
|
+
throw usage(`${path} must be an absolute http(s) origin`);
|
|
39721
|
+
}
|
|
39722
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.origin !== raw) {
|
|
39723
|
+
throw usage(`${path} must be an exact http(s) scheme/host/port origin`);
|
|
39724
|
+
}
|
|
39725
|
+
return parsed.origin;
|
|
39726
|
+
}
|
|
39727
|
+
function normalizePathPrefix(value, path) {
|
|
39728
|
+
const prefix = requireString(value, path);
|
|
39729
|
+
if (!prefix.startsWith("/") || prefix.includes("?") || prefix.includes("#")) {
|
|
39730
|
+
throw usage(`${path} must be an absolute URL path without query or fragment`);
|
|
39731
|
+
}
|
|
39732
|
+
return prefix;
|
|
39733
|
+
}
|
|
39734
|
+
function normalizeStep(raw, path, seen) {
|
|
39735
|
+
if (!isRecord3(raw))
|
|
39736
|
+
throw usage(path);
|
|
39737
|
+
const id = requireSafeId(raw.id, `${path}.id`, seen);
|
|
39738
|
+
switch (raw.type) {
|
|
39739
|
+
case "request": {
|
|
39740
|
+
if (raw.data !== undefined && typeof raw.data !== "string")
|
|
39741
|
+
throw usage(`${path}.data must be a string`);
|
|
39742
|
+
if (raw.followRedirects !== undefined && typeof raw.followRedirects !== "boolean") {
|
|
39743
|
+
throw usage(`${path}.followRedirects must be a boolean`);
|
|
39744
|
+
}
|
|
39745
|
+
return {
|
|
39746
|
+
id,
|
|
39747
|
+
type: "request",
|
|
39748
|
+
url: requireUrl(raw.url, `${path}.url`),
|
|
39749
|
+
...raw.method === undefined ? {} : { method: requireString(raw.method, `${path}.method`) },
|
|
39750
|
+
...raw.headers === undefined ? {} : { headers: requireHeaders(raw.headers, `${path}.headers`) },
|
|
39751
|
+
...raw.data === undefined ? {} : { data: raw.data },
|
|
39752
|
+
...raw.followRedirects === undefined ? {} : { followRedirects: raw.followRedirects },
|
|
39753
|
+
...rawTimeout(raw.timeoutMs, path) ?? {}
|
|
39754
|
+
};
|
|
39755
|
+
}
|
|
39756
|
+
case "navigate": {
|
|
39757
|
+
const waitUntil = raw.waitUntil;
|
|
39758
|
+
if (waitUntil !== undefined && !NAVIGATION_WAIT_UNTIL.includes(waitUntil)) {
|
|
39759
|
+
throw usage(`${path}.waitUntil must be one of ${NAVIGATION_WAIT_UNTIL.join(", ")}`);
|
|
39760
|
+
}
|
|
39761
|
+
return {
|
|
39762
|
+
id,
|
|
39763
|
+
type: "navigate",
|
|
39764
|
+
url: requireUrl(raw.url, `${path}.url`),
|
|
39765
|
+
...waitUntil === undefined ? {} : { waitUntil },
|
|
39766
|
+
...rawTimeout(raw.timeoutMs, path) ?? {}
|
|
39767
|
+
};
|
|
39768
|
+
}
|
|
39769
|
+
case "wait":
|
|
39770
|
+
case "click":
|
|
39771
|
+
return {
|
|
39772
|
+
id,
|
|
39773
|
+
type: raw.type,
|
|
39774
|
+
selector: requireString(raw.selector, `${path}.selector`),
|
|
39775
|
+
...rawTimeout(raw.timeoutMs, path) ?? {}
|
|
39776
|
+
};
|
|
39777
|
+
case "scroll": {
|
|
39778
|
+
const deltaY = raw.deltaY;
|
|
39779
|
+
if (typeof deltaY !== "number" || !Number.isFinite(deltaY) || deltaY === 0) {
|
|
39780
|
+
throw usage(`${path}.deltaY must be a finite non-zero number`);
|
|
39781
|
+
}
|
|
39782
|
+
return { id, type: "scroll", deltaY, ...rawTimeout(raw.timeoutMs, path) ?? {} };
|
|
39783
|
+
}
|
|
39784
|
+
case "extract": {
|
|
39785
|
+
if (raw.format !== "html" && raw.format !== "text")
|
|
39786
|
+
throw usage(`${path}.format must be "html" or "text"`);
|
|
39787
|
+
const selector = optionalString(raw.selector, `${path}.selector`);
|
|
39788
|
+
return {
|
|
39789
|
+
id,
|
|
39790
|
+
type: "extract",
|
|
39791
|
+
format: raw.format,
|
|
39792
|
+
...selector === undefined ? {} : { selector },
|
|
39793
|
+
...rawTimeout(raw.timeoutMs, path) ?? {}
|
|
39794
|
+
};
|
|
39795
|
+
}
|
|
39796
|
+
default:
|
|
39797
|
+
throw usage(`${path}.type must be request, navigate, wait, click, scroll or extract`);
|
|
39798
|
+
}
|
|
39799
|
+
}
|
|
39800
|
+
function rawTimeout(value, path) {
|
|
39801
|
+
const timeoutMs = optionalTimeout(value, `${path}.timeoutMs`);
|
|
39802
|
+
return timeoutMs === undefined ? undefined : { timeoutMs };
|
|
39803
|
+
}
|
|
39804
|
+
function normalizePlan(plan) {
|
|
39805
|
+
if (!isRecord3(plan))
|
|
39806
|
+
throw usage("plan must be a JSON object");
|
|
39807
|
+
if (!Array.isArray(plan.steps))
|
|
39808
|
+
throw usage("plan.steps must be an array");
|
|
39809
|
+
if (plan.responses !== undefined && !Array.isArray(plan.responses))
|
|
39810
|
+
throw usage("plan.responses must be an array");
|
|
39811
|
+
const seen = new Set;
|
|
39812
|
+
const steps = plan.steps.map((step, index) => normalizeStep(step, `plan.steps[${index}]`, seen));
|
|
39813
|
+
const responses = (plan.responses ?? []).map((raw, index) => {
|
|
39814
|
+
const path = `plan.responses[${index}]`;
|
|
39815
|
+
if (!isRecord3(raw))
|
|
39816
|
+
throw usage(path);
|
|
39817
|
+
return {
|
|
39818
|
+
id: requireSafeId(raw.id, `${path}.id`, seen),
|
|
39819
|
+
...raw.origin === undefined ? {} : { origin: normalizeOrigin(raw.origin, `${path}.origin`) },
|
|
39820
|
+
pathPrefix: normalizePathPrefix(raw.pathPrefix, `${path}.pathPrefix`)
|
|
39821
|
+
};
|
|
39822
|
+
});
|
|
39823
|
+
const produces = steps.some((step) => step.type === "request" || step.type === "extract");
|
|
39824
|
+
if (!produces && responses.length === 0) {
|
|
39825
|
+
throw usage("plan declares no artifact source: add a request/extract step or a response capture");
|
|
39826
|
+
}
|
|
39827
|
+
let timeoutMs = DEFAULT_RUN_TIMEOUT_MS;
|
|
39828
|
+
if (plan.timeoutMs !== undefined) {
|
|
39829
|
+
timeoutMs = requirePositiveInt(plan.timeoutMs, "plan.timeoutMs");
|
|
39830
|
+
if (timeoutMs > MAX_RUN_TIMEOUT_MS)
|
|
39831
|
+
throw usage(`plan.timeoutMs must not exceed ${MAX_RUN_TIMEOUT_MS}`);
|
|
39832
|
+
}
|
|
39833
|
+
return { steps, responses, timeoutMs };
|
|
39834
|
+
}
|
|
39835
|
+
function safeUrl(input) {
|
|
39836
|
+
try {
|
|
39837
|
+
const parsed = new URL(input);
|
|
39838
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
39839
|
+
return;
|
|
39840
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
39841
|
+
} catch {
|
|
39842
|
+
return;
|
|
39843
|
+
}
|
|
39844
|
+
}
|
|
39845
|
+
function looksLikeAuthWall(url) {
|
|
39846
|
+
if (url === undefined)
|
|
39847
|
+
return false;
|
|
39848
|
+
try {
|
|
39849
|
+
return AUTH_PATH.test(new URL(url).pathname);
|
|
39850
|
+
} catch {
|
|
39851
|
+
return false;
|
|
39852
|
+
}
|
|
39853
|
+
}
|
|
39854
|
+
function classifyStatus(status, stepIndex, stepId, what = "step") {
|
|
39855
|
+
const details = {
|
|
39856
|
+
...stepIndex === undefined ? {} : { stepIndex },
|
|
39857
|
+
stepId
|
|
39858
|
+
};
|
|
39859
|
+
if (status === 401 || status === 403) {
|
|
39860
|
+
return new BotError("authentication", `${what} '${stepId}' hit an authentication wall (status ${status}) \u2014 ` + "run `kk bot login <url> --save <profileName>` to renew the session", details);
|
|
39861
|
+
}
|
|
39862
|
+
if (status >= 400) {
|
|
39863
|
+
return new BotError("browser", `${what} '${stepId}' returned status ${status}`, details);
|
|
39864
|
+
}
|
|
39865
|
+
return;
|
|
39866
|
+
}
|
|
39867
|
+
function emptyFailure(id, what, stepIndex) {
|
|
39868
|
+
return new BotError("empty", `${what} '${id}' produced no content`, {
|
|
39869
|
+
...stepIndex === undefined ? {} : { stepIndex },
|
|
39870
|
+
stepId: id
|
|
39871
|
+
});
|
|
39872
|
+
}
|
|
39873
|
+
async function readBody(response) {
|
|
39874
|
+
return await response.body();
|
|
39875
|
+
}
|
|
39876
|
+
function stepTimeout(step, remaining, stepIndex) {
|
|
39877
|
+
if (remaining <= 0) {
|
|
39878
|
+
throw new BotError("browser", `step '${step.id}' exceeded the collection deadline`, {
|
|
39879
|
+
stepIndex,
|
|
39880
|
+
stepId: step.id
|
|
39881
|
+
});
|
|
39882
|
+
}
|
|
39883
|
+
return Math.min(step.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS2, remaining);
|
|
39884
|
+
}
|
|
39885
|
+
function requestFailureDetail(error, step) {
|
|
39886
|
+
let detail = execFailureDetail(error, step.url);
|
|
39887
|
+
const secrets = [...Object.values(step.headers ?? {}), ...step.data === undefined ? [] : [step.data]];
|
|
39888
|
+
for (const secret of secrets) {
|
|
39889
|
+
if (secret.length > 0)
|
|
39890
|
+
detail = detail.split(secret).join("<redacted>");
|
|
39891
|
+
}
|
|
39892
|
+
return detail.slice(0, 200);
|
|
39893
|
+
}
|
|
39894
|
+
async function runStep(step, index, context, page, timeoutMs) {
|
|
39895
|
+
const details = { stepIndex: index, stepId: step.id };
|
|
39896
|
+
switch (step.type) {
|
|
39897
|
+
case "request": {
|
|
39898
|
+
let response;
|
|
39899
|
+
try {
|
|
39900
|
+
response = await context.request.fetch(step.url, {
|
|
39901
|
+
method: step.method ?? (step.data === undefined ? "GET" : "POST"),
|
|
39902
|
+
...step.headers === undefined ? {} : { headers: step.headers },
|
|
39903
|
+
...step.data === undefined ? {} : { data: Buffer.from(step.data, "utf8") },
|
|
39904
|
+
failOnStatusCode: false,
|
|
39905
|
+
...step.followRedirects === true ? {} : { maxRedirects: 0 },
|
|
39906
|
+
timeout: timeoutMs
|
|
39907
|
+
});
|
|
39908
|
+
} catch (error) {
|
|
39909
|
+
throw new BotError("browser", `step '${step.id}' request failed (${requestFailureDetail(error, step)})`, details);
|
|
39910
|
+
}
|
|
39911
|
+
const status = response.status();
|
|
39912
|
+
const wall = classifyStatus(status, index, step.id);
|
|
39913
|
+
if (wall !== undefined)
|
|
39914
|
+
throw wall;
|
|
39915
|
+
let body;
|
|
39916
|
+
try {
|
|
39917
|
+
body = await readBody(response);
|
|
39918
|
+
} catch {
|
|
39919
|
+
throw new BotError("browser", `step '${step.id}' response body could not be read`, details);
|
|
39920
|
+
}
|
|
39921
|
+
const text = body.toString("utf8");
|
|
39922
|
+
if (text.trim() === "")
|
|
39923
|
+
throw emptyFailure(step.id, "step", index);
|
|
39924
|
+
const requestUrl = safeUrl(step.url);
|
|
39925
|
+
return {
|
|
39926
|
+
result: {
|
|
39927
|
+
index,
|
|
39928
|
+
id: step.id,
|
|
39929
|
+
type: step.type,
|
|
39930
|
+
...requestUrl === undefined ? {} : { url: requestUrl }
|
|
39931
|
+
},
|
|
39932
|
+
artifact: {
|
|
39933
|
+
id: step.id,
|
|
39934
|
+
kind: "request",
|
|
39935
|
+
body: text,
|
|
39936
|
+
status,
|
|
39937
|
+
...requestUrl === undefined ? {} : { url: requestUrl },
|
|
39938
|
+
stepIndex: index
|
|
39939
|
+
}
|
|
39940
|
+
};
|
|
39941
|
+
}
|
|
39942
|
+
case "navigate": {
|
|
39943
|
+
let status;
|
|
39944
|
+
try {
|
|
39945
|
+
const response = await page.goto(step.url, {
|
|
39946
|
+
waitUntil: step.waitUntil ?? "load",
|
|
39947
|
+
timeout: timeoutMs
|
|
39948
|
+
});
|
|
39949
|
+
status = response?.status();
|
|
39950
|
+
} catch (error) {
|
|
39951
|
+
throw new BotError("browser", `step '${step.id}' navigation failed (${execFailureDetail(error, step.url)})`, details);
|
|
39952
|
+
}
|
|
39953
|
+
const finalUrl = safeUrl(page.url()) ?? safeUrl(step.url);
|
|
39954
|
+
if (looksLikeAuthWall(page.url())) {
|
|
39955
|
+
throw new BotError("authentication", `step '${step.id}' landed on an authentication page \u2014 ` + "run `kk bot login <url> --save <profileName>` to renew the session", details);
|
|
39956
|
+
}
|
|
39957
|
+
if (status !== undefined) {
|
|
39958
|
+
const wall = classifyStatus(status, index, step.id);
|
|
39959
|
+
if (wall !== undefined)
|
|
39960
|
+
throw wall;
|
|
39961
|
+
}
|
|
39962
|
+
return {
|
|
39963
|
+
result: { index, id: step.id, type: step.type, ...finalUrl === undefined ? {} : { url: finalUrl } }
|
|
39964
|
+
};
|
|
39965
|
+
}
|
|
39966
|
+
case "wait": {
|
|
39967
|
+
try {
|
|
39968
|
+
await page.waitForSelector(step.selector, { timeout: timeoutMs });
|
|
39969
|
+
} catch {
|
|
39970
|
+
throw new BotError("browser", `step '${step.id}' timed out waiting for its selector`, details);
|
|
39971
|
+
}
|
|
39972
|
+
return { result: { index, id: step.id, type: step.type } };
|
|
39973
|
+
}
|
|
39974
|
+
case "click": {
|
|
39975
|
+
try {
|
|
39976
|
+
await page.locator(step.selector).first().click({ timeout: timeoutMs });
|
|
39977
|
+
} catch {
|
|
39978
|
+
throw new BotError("browser", `step '${step.id}' could not click its selector`, details);
|
|
39979
|
+
}
|
|
39980
|
+
return { result: { index, id: step.id, type: step.type } };
|
|
39981
|
+
}
|
|
39982
|
+
case "scroll": {
|
|
39983
|
+
try {
|
|
39984
|
+
await page.evaluate(`window.scrollBy(0, ${JSON.stringify(step.deltaY)})`);
|
|
39985
|
+
} catch {
|
|
39986
|
+
throw new BotError("browser", `step '${step.id}' could not scroll the page`, details);
|
|
39987
|
+
}
|
|
39988
|
+
return { result: { index, id: step.id, type: step.type } };
|
|
39989
|
+
}
|
|
39990
|
+
case "extract": {
|
|
39991
|
+
let text;
|
|
39992
|
+
try {
|
|
39993
|
+
const target = step.selector === undefined ? null : page.locator(step.selector).first();
|
|
39994
|
+
text = await applyTimeout(step.format === "html" ? target === null ? page.content() : target.evaluate("(element) => element.outerHTML").then((value) => String(value)) : target === null ? page.evaluate('document.body ? document.body.innerText : ""').then((value) => String(value)) : target.innerText(), timeoutMs);
|
|
39995
|
+
} catch {
|
|
39996
|
+
throw new BotError("browser", `step '${step.id}' could not extract its content`, details);
|
|
39997
|
+
}
|
|
39998
|
+
if (text.trim() === "")
|
|
39999
|
+
throw emptyFailure(step.id, "step", index);
|
|
40000
|
+
const pageUrl = safeUrl(page.url());
|
|
40001
|
+
return {
|
|
40002
|
+
result: { index, id: step.id, type: step.type, ...pageUrl === undefined ? {} : { url: pageUrl } },
|
|
40003
|
+
artifact: {
|
|
40004
|
+
id: step.id,
|
|
40005
|
+
kind: step.format,
|
|
40006
|
+
body: text,
|
|
40007
|
+
...pageUrl === undefined ? {} : { url: pageUrl },
|
|
40008
|
+
stepIndex: index
|
|
40009
|
+
}
|
|
40010
|
+
};
|
|
40011
|
+
}
|
|
40012
|
+
}
|
|
40013
|
+
}
|
|
40014
|
+
function enforceCaps(artifact, artifacts, aggregate) {
|
|
40015
|
+
if (artifacts.length >= MAX_ARTIFACTS) {
|
|
40016
|
+
throw new BotError("browser", `collection produced more than ${MAX_ARTIFACTS} artifacts`, {
|
|
40017
|
+
...artifact.stepIndex === undefined ? {} : { stepIndex: artifact.stepIndex },
|
|
40018
|
+
stepId: artifact.id
|
|
40019
|
+
});
|
|
40020
|
+
}
|
|
40021
|
+
const bytes = Buffer.byteLength(artifact.body, "utf8");
|
|
40022
|
+
if (bytes > MAX_ARTIFACT_BYTES) {
|
|
40023
|
+
throw new BotError("browser", `artifact '${artifact.id}' exceeds the per-artifact body cap`, {
|
|
40024
|
+
...artifact.stepIndex === undefined ? {} : { stepIndex: artifact.stepIndex },
|
|
40025
|
+
stepId: artifact.id
|
|
40026
|
+
});
|
|
40027
|
+
}
|
|
40028
|
+
const total = aggregate + bytes;
|
|
40029
|
+
if (total > MAX_AGGREGATE_BYTES) {
|
|
40030
|
+
throw new BotError("browser", `collection exceeded the aggregate body cap`, { stepId: artifact.id });
|
|
40031
|
+
}
|
|
40032
|
+
return total;
|
|
40033
|
+
}
|
|
40034
|
+
async function applyTimeout(promise, timeoutMs) {
|
|
40035
|
+
let timer;
|
|
40036
|
+
const expiry = new Promise((_resolve, reject) => {
|
|
40037
|
+
timer = setTimeout(() => reject(new StepTimeoutError), timeoutMs);
|
|
40038
|
+
});
|
|
40039
|
+
try {
|
|
40040
|
+
return await Promise.race([promise, expiry]);
|
|
40041
|
+
} finally {
|
|
40042
|
+
if (timer !== undefined)
|
|
40043
|
+
clearTimeout(timer);
|
|
40044
|
+
}
|
|
40045
|
+
}
|
|
40046
|
+
async function waitWithin(promise, budgetMs) {
|
|
40047
|
+
if (budgetMs <= 0)
|
|
40048
|
+
return false;
|
|
40049
|
+
let timer;
|
|
40050
|
+
const expiry = new Promise((resolve) => {
|
|
40051
|
+
timer = setTimeout(() => resolve(false), budgetMs);
|
|
40052
|
+
});
|
|
40053
|
+
try {
|
|
40054
|
+
return await Promise.race([promise.then(() => true), expiry]);
|
|
40055
|
+
} finally {
|
|
40056
|
+
if (timer !== undefined)
|
|
40057
|
+
clearTimeout(timer);
|
|
40058
|
+
}
|
|
40059
|
+
}
|
|
40060
|
+
async function runBotCollection(profileName, plan, options = {}) {
|
|
40061
|
+
const normalized = normalizePlan(plan);
|
|
40062
|
+
const name = assertSafeBotName(profileName);
|
|
40063
|
+
const fileSystem = options.fileSystem ?? createNodeFileSystem();
|
|
40064
|
+
const now = options.now ?? Date.now;
|
|
40065
|
+
const deadline = now() + normalized.timeoutMs;
|
|
40066
|
+
const profileDir = resolveBotProfileDir(name, { root: options.root });
|
|
40067
|
+
if (lstat(profileDir) === null)
|
|
40068
|
+
throw missingBotProfile(name, false);
|
|
40069
|
+
const acquire = options.acquireLease ?? acquireProfileLease;
|
|
40070
|
+
const lease = await acquire(profileDir, { leaseRoot: options.leaseRoot, fileSystem });
|
|
40071
|
+
const captures = normalized.responses.map((declaration) => ({
|
|
40072
|
+
declaration,
|
|
40073
|
+
matches: [],
|
|
40074
|
+
pending: [],
|
|
40075
|
+
readFailures: 0
|
|
40076
|
+
}));
|
|
40077
|
+
let context;
|
|
40078
|
+
try {
|
|
40079
|
+
const launcher = options.launcher ?? createKkBrowserLauncher(undefined, { lease, physicalProfileDir: profileDir }, { leaseRoot: options.leaseRoot, fileSystem });
|
|
40080
|
+
context = await launcher.launch({ profileDir, headless: true });
|
|
40081
|
+
const activeContext = context;
|
|
40082
|
+
if (captures.length > 0) {
|
|
40083
|
+
activeContext.on("response", (response) => {
|
|
40084
|
+
const raw = response.url();
|
|
40085
|
+
let parsed;
|
|
40086
|
+
try {
|
|
40087
|
+
parsed = new URL(raw);
|
|
40088
|
+
} catch {
|
|
40089
|
+
return;
|
|
40090
|
+
}
|
|
40091
|
+
for (const state of captures) {
|
|
40092
|
+
const { origin, pathPrefix } = state.declaration;
|
|
40093
|
+
if (origin !== undefined && parsed.origin !== origin)
|
|
40094
|
+
continue;
|
|
40095
|
+
if (!parsed.pathname.startsWith(pathPrefix))
|
|
40096
|
+
continue;
|
|
40097
|
+
const status = response.status();
|
|
40098
|
+
const failure = classifyStatus(status, undefined, state.declaration.id, "capture");
|
|
40099
|
+
if (failure !== undefined) {
|
|
40100
|
+
state.failure ??= failure;
|
|
40101
|
+
continue;
|
|
40102
|
+
}
|
|
40103
|
+
state.pending.push(readBody(response).then((body) => {
|
|
40104
|
+
state.matches.push({
|
|
40105
|
+
status,
|
|
40106
|
+
text: body.toString("utf8"),
|
|
40107
|
+
url: `${parsed.origin}${parsed.pathname}`
|
|
40108
|
+
});
|
|
40109
|
+
}, () => {
|
|
40110
|
+
state.readFailures += 1;
|
|
40111
|
+
}));
|
|
40112
|
+
}
|
|
40113
|
+
});
|
|
40114
|
+
}
|
|
40115
|
+
const page = await openBotPage(activeContext);
|
|
40116
|
+
const steps = [];
|
|
40117
|
+
const artifacts = [];
|
|
40118
|
+
let aggregate = 0;
|
|
40119
|
+
for (const [index, step] of normalized.steps.entries()) {
|
|
40120
|
+
const timeoutMs = stepTimeout(step, deadline - now(), index);
|
|
40121
|
+
let outcome;
|
|
40122
|
+
try {
|
|
40123
|
+
outcome = await applyTimeout(runStep(step, index, activeContext, page, timeoutMs), timeoutMs);
|
|
40124
|
+
} catch (error) {
|
|
40125
|
+
if (error instanceof StepTimeoutError) {
|
|
40126
|
+
throw new BotError("browser", `step '${step.id}' exceeded the collection deadline`, {
|
|
40127
|
+
stepIndex: index,
|
|
40128
|
+
stepId: step.id
|
|
40129
|
+
});
|
|
40130
|
+
}
|
|
40131
|
+
throw error;
|
|
40132
|
+
}
|
|
40133
|
+
steps.push(outcome.result);
|
|
40134
|
+
if (outcome.artifact !== undefined) {
|
|
40135
|
+
aggregate = enforceCaps(outcome.artifact, artifacts, aggregate);
|
|
40136
|
+
artifacts.push(outcome.artifact);
|
|
40137
|
+
}
|
|
40138
|
+
}
|
|
40139
|
+
for (const state of captures) {
|
|
40140
|
+
if (state.failure !== undefined)
|
|
40141
|
+
throw state.failure;
|
|
40142
|
+
const settled = await waitWithin(Promise.all(state.pending).then(() => {
|
|
40143
|
+
return;
|
|
40144
|
+
}), deadline - now());
|
|
40145
|
+
if (!settled) {
|
|
40146
|
+
throw new BotError("browser", `response capture '${state.declaration.id}' exceeded the collection deadline`, {
|
|
40147
|
+
stepId: state.declaration.id
|
|
40148
|
+
});
|
|
40149
|
+
}
|
|
40150
|
+
if (state.failure !== undefined)
|
|
40151
|
+
throw state.failure;
|
|
40152
|
+
const found = state.matches.filter((match) => match.text.trim() !== "");
|
|
40153
|
+
if (found.length === 0 && state.readFailures > 0) {
|
|
40154
|
+
throw new BotError("browser", `response capture '${state.declaration.id}' could not be read`, {
|
|
40155
|
+
stepId: state.declaration.id
|
|
40156
|
+
});
|
|
40157
|
+
}
|
|
40158
|
+
if (found.length === 0)
|
|
40159
|
+
throw emptyFailure(state.declaration.id, "capture");
|
|
40160
|
+
for (const match of found) {
|
|
40161
|
+
const artifact = {
|
|
40162
|
+
id: state.declaration.id,
|
|
40163
|
+
kind: "response",
|
|
40164
|
+
body: match.text,
|
|
40165
|
+
status: match.status,
|
|
40166
|
+
url: match.url
|
|
40167
|
+
};
|
|
40168
|
+
aggregate = enforceCaps(artifact, artifacts, aggregate);
|
|
40169
|
+
artifacts.push(artifact);
|
|
40170
|
+
}
|
|
40171
|
+
}
|
|
40172
|
+
return { steps, artifacts };
|
|
40173
|
+
} finally {
|
|
40174
|
+
await closeBotContext(context);
|
|
40175
|
+
await Promise.allSettled(captures.flatMap((state) => state.pending));
|
|
40176
|
+
await lease.release();
|
|
40177
|
+
}
|
|
40178
|
+
}
|
|
40179
|
+
var DEFAULT_STEP_TIMEOUT_MS2 = 30000, DEFAULT_RUN_TIMEOUT_MS = 120000, MAX_RUN_TIMEOUT_MS = 600000, MAX_ARTIFACT_BYTES, MAX_AGGREGATE_BYTES, MAX_ARTIFACTS = 100, SAFE_ID, AUTH_PATH, NAVIGATION_WAIT_UNTIL, StepTimeoutError;
|
|
40180
|
+
var init_bot_collection = __esm(() => {
|
|
40181
|
+
init_dist3();
|
|
40182
|
+
init_bot();
|
|
40183
|
+
init_bot_fs();
|
|
40184
|
+
init_browser();
|
|
40185
|
+
init_profile_lease();
|
|
40186
|
+
MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
|
|
40187
|
+
MAX_AGGREGATE_BYTES = 20 * 1024 * 1024;
|
|
40188
|
+
SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
40189
|
+
AUTH_PATH = /(?:^|\/)(?:login|signin|sign-in|auth|sso)(?:\/|$)/i;
|
|
40190
|
+
NAVIGATION_WAIT_UNTIL = ["load", "domcontentloaded", "networkidle"];
|
|
40191
|
+
StepTimeoutError = class StepTimeoutError extends Error {
|
|
40192
|
+
};
|
|
40193
|
+
});
|
|
40194
|
+
|
|
39655
40195
|
// ../../node_modules/.bun/@logtape+logtape@2.3.1/node_modules/@logtape/logtape/dist/util.node.js
|
|
39656
40196
|
var exports_util_node = {};
|
|
39657
40197
|
__export(exports_util_node, {
|
|
@@ -42103,11 +42643,11 @@ var PluginManifestSchemaBase = looseObject({
|
|
|
42103
42643
|
});
|
|
42104
42644
|
var PluginManifestSchema = PluginManifestSchemaBase.superRefine((manifest, ctx) => {
|
|
42105
42645
|
if (manifest.browserSession !== undefined) {
|
|
42106
|
-
if (manifest.kind !== "publish") {
|
|
42646
|
+
if (manifest.kind !== "publish" && manifest.kind !== "ingestion") {
|
|
42107
42647
|
ctx.addIssue({
|
|
42108
42648
|
code: "custom",
|
|
42109
42649
|
path: ["browserSession"],
|
|
42110
|
-
message: `browserSession requires kind "publish", not "${manifest.kind}"`
|
|
42650
|
+
message: `browserSession requires kind "publish" or "ingestion", not "${manifest.kind}"`
|
|
42111
42651
|
});
|
|
42112
42652
|
}
|
|
42113
42653
|
if (manifest.check === undefined) {
|
|
@@ -48567,6 +49107,8 @@ function collectSetting2(value, previous) {
|
|
|
48567
49107
|
}
|
|
48568
49108
|
|
|
48569
49109
|
// src/commands/bot.ts
|
|
49110
|
+
import { join as join14 } from "path";
|
|
49111
|
+
init_dist3();
|
|
48570
49112
|
init_commander();
|
|
48571
49113
|
|
|
48572
49114
|
// src/bot-adapter.ts
|
|
@@ -48643,6 +49185,20 @@ function bindAdapterSession(candidate) {
|
|
|
48643
49185
|
};
|
|
48644
49186
|
}
|
|
48645
49187
|
|
|
49188
|
+
// src/bot-plan.ts
|
|
49189
|
+
function decodeBotPlan(raw) {
|
|
49190
|
+
let parsed;
|
|
49191
|
+
try {
|
|
49192
|
+
parsed = JSON.parse(raw);
|
|
49193
|
+
} catch {
|
|
49194
|
+
return { ok: false, reason: "invalid-json" };
|
|
49195
|
+
}
|
|
49196
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
49197
|
+
return { ok: false, reason: "not-object" };
|
|
49198
|
+
}
|
|
49199
|
+
return { ok: true, plan: parsed };
|
|
49200
|
+
}
|
|
49201
|
+
|
|
48646
49202
|
// src/byte-output.ts
|
|
48647
49203
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
48648
49204
|
function writeBytes(chunk) {
|
|
@@ -48661,7 +49217,10 @@ function writeFileBytes(path, bytes) {
|
|
|
48661
49217
|
init_output2();
|
|
48662
49218
|
var botHarness;
|
|
48663
49219
|
function loadBotHarness() {
|
|
48664
|
-
botHarness ??= Promise.
|
|
49220
|
+
botHarness ??= Promise.all([
|
|
49221
|
+
Promise.resolve().then(() => (init_browser(), exports_browser)),
|
|
49222
|
+
Promise.resolve().then(() => (init_bot_collection(), exports_bot_collection))
|
|
49223
|
+
]).then(([browser, collection]) => ({ ...browser, ...collection }));
|
|
48665
49224
|
return botHarness;
|
|
48666
49225
|
}
|
|
48667
49226
|
async function runBotAction(load, action) {
|
|
@@ -48805,16 +49364,77 @@ function execHeaderBlock(status, statusText, headers) {
|
|
|
48805
49364
|
|
|
48806
49365
|
`;
|
|
48807
49366
|
}
|
|
49367
|
+
function parsePlanMaxTime(harness, value) {
|
|
49368
|
+
const seconds = Number(value);
|
|
49369
|
+
if (!Number.isFinite(seconds) || seconds <= 0 || seconds > 600) {
|
|
49370
|
+
throw new harness.BotError("usage", "--max-time must be a positive number of seconds (up to 600)");
|
|
49371
|
+
}
|
|
49372
|
+
return Math.round(seconds * 1000);
|
|
49373
|
+
}
|
|
49374
|
+
async function readPlanFile(harness, planPath) {
|
|
49375
|
+
let raw;
|
|
49376
|
+
try {
|
|
49377
|
+
raw = await createNodeFileSystem().readFile(planPath);
|
|
49378
|
+
} catch {
|
|
49379
|
+
throw new harness.BotError("usage", `could not read plan file '${planPath}'`);
|
|
49380
|
+
}
|
|
49381
|
+
const decoded = decodeBotPlan(raw);
|
|
49382
|
+
if (!decoded.ok) {
|
|
49383
|
+
throw new harness.BotError("usage", decoded.reason === "not-object" ? "plan must be a JSON object" : "plan file is not valid JSON");
|
|
49384
|
+
}
|
|
49385
|
+
return decoded.plan;
|
|
49386
|
+
}
|
|
49387
|
+
async function botPlanAction(load, name, planPath, options) {
|
|
49388
|
+
await runBotAction(load, async (harness) => {
|
|
49389
|
+
const plan = await readPlanFile(harness, planPath);
|
|
49390
|
+
const timeoutMs = options.maxTime === undefined ? undefined : parsePlanMaxTime(harness, options.maxTime);
|
|
49391
|
+
const result = await harness.runBotCollection(name, plan, {
|
|
49392
|
+
...timeoutMs === undefined ? {} : { timeoutMs }
|
|
49393
|
+
});
|
|
49394
|
+
if (options.output !== undefined) {
|
|
49395
|
+
const fs = createNodeFileSystem();
|
|
49396
|
+
await fs.ensureDir(options.output);
|
|
49397
|
+
harness.chmod(options.output, 448);
|
|
49398
|
+
const sanitizedArtifacts = [];
|
|
49399
|
+
for (const [index, artifact] of result.artifacts.entries()) {
|
|
49400
|
+
const safeName = `${String(index).padStart(2, "0")}-${artifact.id.replace(/[^A-Za-z0-9_-]/g, "-")}.txt`;
|
|
49401
|
+
const filePath = join14(options.output, safeName);
|
|
49402
|
+
await fs.writeFile(filePath, artifact.body);
|
|
49403
|
+
harness.chmod(filePath, 384);
|
|
49404
|
+
sanitizedArtifacts.push({
|
|
49405
|
+
id: artifact.id,
|
|
49406
|
+
kind: artifact.kind,
|
|
49407
|
+
file: safeName,
|
|
49408
|
+
...artifact.status !== undefined ? { status: artifact.status } : {},
|
|
49409
|
+
...artifact.url !== undefined ? { url: artifact.url } : {},
|
|
49410
|
+
...artifact.stepIndex !== undefined ? { stepIndex: artifact.stepIndex } : {}
|
|
49411
|
+
});
|
|
49412
|
+
}
|
|
49413
|
+
const summary = {
|
|
49414
|
+
ok: true,
|
|
49415
|
+
steps: result.steps,
|
|
49416
|
+
artifacts: sanitizedArtifacts
|
|
49417
|
+
};
|
|
49418
|
+
const resultPath = join14(options.output, "result.json");
|
|
49419
|
+
await fs.writeFile(resultPath, JSON.stringify(summary, null, 2));
|
|
49420
|
+
harness.chmod(resultPath, 384);
|
|
49421
|
+
consoleOutput.write(JSON.stringify(summary, null, 2));
|
|
49422
|
+
} else {
|
|
49423
|
+
consoleOutput.write(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
49424
|
+
}
|
|
49425
|
+
});
|
|
49426
|
+
}
|
|
48808
49427
|
function registerBotCommand(program, deps = {}) {
|
|
48809
49428
|
const load = deps.load ?? loadBotHarness;
|
|
48810
|
-
const bot = program.command("bot").description("Named local browser sessions (list, login, logout, open, exec)");
|
|
49429
|
+
const bot = program.command("bot").description("Named local browser sessions (list, login, logout, open, exec, plan)");
|
|
48811
49430
|
bot.command("list").description("List cached local bot profiles: name, cached, default URL, identity, last login, expires").action(() => botListAction(load));
|
|
48812
49431
|
bot.command("login").description("Open a site headed and save it as a named bot session after terminal confirmation").argument("<url>", "Login URL to open (http/https; its query is never saved)").requiredOption("--save <name>", "Bot name matching [A-Za-z0-9][A-Za-z0-9_-]{0,63}").action((url, options) => botLoginAction(load, url, options));
|
|
48813
49432
|
bot.command("logout").description("Remove a named bot's local profile and credentials (no server-side revocation)").argument("<name>", "Bot name").action((name) => botLogoutAction(load, name));
|
|
48814
49433
|
bot.command("open").description("Open a named bot's saved page headed until the browser closes").argument("<name>", "Bot name").option("--login", "Reauthenticate first, then reopen the saved page for interaction").action((name, options) => botOpenAction(load, name, options));
|
|
48815
49434
|
const exec = bot.command("exec").description("Send one request with a named bot's cached cookie session (curl option subset)").argument("<name>", "Bot name").argument("<url>", "Request URL (http/https, no credentials; sent as given, query kept)").option("-X, --request <method>", "HTTP method (default GET, or POST when -d is given)").option("-H, --header <header>", 'Request header "Name: value" (repeatable)', collectHeader).option("-d, --data <data>", "Request body: a literal, or @file read raw").option("-o, --output <file>", "Write the response body to a file instead of stdout").option("-i, --include", "Include the status line and response headers before the body (stdout or the -o file)").option("-L, --location", "Follow redirects (default: return the 3xx response as-is)").option("-f, --fail", "Exit 1 without the body when the status is 400 or above").option("-m, --max-time <seconds>", "Whole-request timeout in seconds (default: Playwright 30s)").action((name, url, options) => botExecAction(load, name, url, options));
|
|
49435
|
+
const plan = bot.command("plan").description("Execute a declarative collection plan with a named bot session").argument("<name>", "Bot name").argument("<plan>", "Path to plan JSON file").option("-o, --output <dir>", "Directory to write captured artifact bodies (0600 mode)").option("-m, --max-time <seconds>", "Whole-run timeout in seconds (default: 120s, max: 600s)").action((name, planPath, options) => botPlanAction(load, name, planPath, options));
|
|
48816
49436
|
bot.hook("preSubcommand", (_parent, subcommand) => {
|
|
48817
|
-
if (subcommand === exec) {
|
|
49437
|
+
if (subcommand === exec || subcommand === plan) {
|
|
48818
49438
|
subcommand.exitOverride((error) => {
|
|
48819
49439
|
throw error.exitCode === 1 ? new CommanderError(2, error.code, error.message) : error;
|
|
48820
49440
|
});
|
|
@@ -48827,7 +49447,7 @@ init_commander();
|
|
|
48827
49447
|
|
|
48828
49448
|
// src/fanin.ts
|
|
48829
49449
|
import { tmpdir } from "os";
|
|
48830
|
-
import { isAbsolute as isAbsolute6, join as
|
|
49450
|
+
import { isAbsolute as isAbsolute6, join as join15, resolve as resolve14 } from "path";
|
|
48831
49451
|
init_dist3();
|
|
48832
49452
|
init_dist();
|
|
48833
49453
|
var DEFAULT_SOURCE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
@@ -49023,7 +49643,7 @@ async function collectSourceDocs(discovery, source, timeoutMs) {
|
|
|
49023
49643
|
if (!await fs.exists(entryPath)) {
|
|
49024
49644
|
throw new Error(`Plugin entry script does not exist: ${entryPath}`);
|
|
49025
49645
|
}
|
|
49026
|
-
const outTmp =
|
|
49646
|
+
const outTmp = join15(tmpdir(), `kk-fanin-${source.plugin}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
49027
49647
|
try {
|
|
49028
49648
|
const proc = await spawnPluginEntryAsync({
|
|
49029
49649
|
entryPath,
|
|
@@ -49131,7 +49751,7 @@ async function runFanin(argv) {
|
|
|
49131
49751
|
|
|
49132
49752
|
// src/fanout.ts
|
|
49133
49753
|
import { tmpdir as tmpdir2 } from "os";
|
|
49134
|
-
import { isAbsolute as isAbsolute7, join as
|
|
49754
|
+
import { isAbsolute as isAbsolute7, join as join16, resolve as resolve15 } from "path";
|
|
49135
49755
|
init_dist3();
|
|
49136
49756
|
init_dist();
|
|
49137
49757
|
var EXIT_FAILURE = 1;
|
|
@@ -49419,7 +50039,7 @@ async function pluginDispatch(target, contentPath, discovery, targetTimeoutMs) {
|
|
|
49419
50039
|
if (!await fs.exists(entryPath)) {
|
|
49420
50040
|
throw new Error(`Plugin entry script does not exist: ${entryPath}`);
|
|
49421
50041
|
}
|
|
49422
|
-
const outTmp =
|
|
50042
|
+
const outTmp = join16(tmpdir2(), `kk-fanout-${target}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
49423
50043
|
try {
|
|
49424
50044
|
const proc = await spawnPluginEntryAsync({
|
|
49425
50045
|
entryPath,
|
|
@@ -49632,7 +50252,7 @@ function registerExecutorCommand(program) {
|
|
|
49632
50252
|
init_commander();
|
|
49633
50253
|
|
|
49634
50254
|
// src/itc-candidates.ts
|
|
49635
|
-
import { join as
|
|
50255
|
+
import { join as join19 } from "path";
|
|
49636
50256
|
init_dist3();
|
|
49637
50257
|
init_dist();
|
|
49638
50258
|
init_zod();
|
|
@@ -49805,19 +50425,19 @@ async function itcDecision(request, fs = createNodeFileSystem(), executor = new
|
|
|
49805
50425
|
|
|
49806
50426
|
// src/itc-topic.ts
|
|
49807
50427
|
import { createHash as createHash3 } from "crypto";
|
|
49808
|
-
import { join as
|
|
50428
|
+
import { join as join18, resolve as resolve17 } from "path";
|
|
49809
50429
|
init_dist3();
|
|
49810
50430
|
init_dist();
|
|
49811
50431
|
init_zod();
|
|
49812
50432
|
|
|
49813
50433
|
// src/package-root.ts
|
|
49814
50434
|
init_dist3();
|
|
49815
|
-
import { dirname as dirname11, join as
|
|
50435
|
+
import { dirname as dirname11, join as join17, resolve as resolve16 } from "path";
|
|
49816
50436
|
var SHIPPED_DIR = "plugins/kk";
|
|
49817
50437
|
async function findPackageRoot(fs, start) {
|
|
49818
50438
|
let dir = resolve16(start);
|
|
49819
50439
|
for (;; ) {
|
|
49820
|
-
if (await fs.exists(
|
|
50440
|
+
if (await fs.exists(join17(dir, SHIPPED_DIR)))
|
|
49821
50441
|
return dir;
|
|
49822
50442
|
const parent = resolve16(dir, "..");
|
|
49823
50443
|
if (parent === dir)
|
|
@@ -49873,7 +50493,7 @@ async function resolveTopicProfilePath(fs, topic, customPath) {
|
|
|
49873
50493
|
if (customPath !== undefined && await fs.exists(customPath)) {
|
|
49874
50494
|
return customPath;
|
|
49875
50495
|
}
|
|
49876
|
-
const candidate =
|
|
50496
|
+
const candidate = join18(await resolvePackageRoot(fs), "plugins/kk/decisions/extract", `${topic}.yaml`);
|
|
49877
50497
|
if (await fs.exists(candidate))
|
|
49878
50498
|
return candidate;
|
|
49879
50499
|
throw new Error(`no profile for topic '${topic}': expected plugins/kk/decisions/extract/${topic}.yaml under the package root (cwd or script tree)`);
|
|
@@ -49943,9 +50563,9 @@ async function itcTopicExtract(request, fs = createNodeFileSystem()) {
|
|
|
49943
50563
|
poolDigest,
|
|
49944
50564
|
...request.refreshRun?.runId ? { collectRunId: request.refreshRun.runId } : {}
|
|
49945
50565
|
};
|
|
49946
|
-
await fs.writeFile(
|
|
50566
|
+
await fs.writeFile(join18(request.intakeDir, "topic-pool.json"), `${JSON.stringify(docs, null, 2)}
|
|
49947
50567
|
`);
|
|
49948
|
-
await fs.writeFile(
|
|
50568
|
+
await fs.writeFile(join18(request.intakeDir, "topic-pool-manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
49949
50569
|
`);
|
|
49950
50570
|
return { count: docs.length, poolDigest };
|
|
49951
50571
|
}
|
|
@@ -49968,9 +50588,9 @@ var ItcTopicValidateRequestSchema = object2({
|
|
|
49968
50588
|
parentRunId: string2().min(1)
|
|
49969
50589
|
});
|
|
49970
50590
|
async function itcTopicValidate(request, fs = createNodeFileSystem()) {
|
|
49971
|
-
const manifestPath =
|
|
49972
|
-
const poolPath =
|
|
49973
|
-
const candidatesPath =
|
|
50591
|
+
const manifestPath = join18(request.intakeDir, "topic-pool-manifest.json");
|
|
50592
|
+
const poolPath = join18(request.intakeDir, "topic-pool.json");
|
|
50593
|
+
const candidatesPath = join18(request.intakeDir, "topic-candidates.json");
|
|
49974
50594
|
if (!await fs.exists(manifestPath) || !await fs.exists(poolPath)) {
|
|
49975
50595
|
throw new Error(`topic pool artifacts not found in ${request.intakeDir}`);
|
|
49976
50596
|
}
|
|
@@ -50014,7 +50634,7 @@ async function itcTopicValidate(request, fs = createNodeFileSystem()) {
|
|
|
50014
50634
|
}
|
|
50015
50635
|
}
|
|
50016
50636
|
}
|
|
50017
|
-
await fs.writeFile(
|
|
50637
|
+
await fs.writeFile(join18(request.intakeDir, ".topic-candidates-valid"), `${new Date().toISOString()}
|
|
50018
50638
|
`);
|
|
50019
50639
|
return { valid: true };
|
|
50020
50640
|
}
|
|
@@ -50027,9 +50647,9 @@ var ItcTopicSelectRequestSchema = object2({
|
|
|
50027
50647
|
});
|
|
50028
50648
|
async function itcTopicSelect(request, fs = createNodeFileSystem()) {
|
|
50029
50649
|
await itcTopicValidate(request, fs);
|
|
50030
|
-
const manifestPath =
|
|
50031
|
-
const poolPath =
|
|
50032
|
-
const candidatesPath =
|
|
50650
|
+
const manifestPath = join18(request.intakeDir, "topic-pool-manifest.json");
|
|
50651
|
+
const poolPath = join18(request.intakeDir, "topic-pool.json");
|
|
50652
|
+
const candidatesPath = join18(request.intakeDir, "topic-candidates.json");
|
|
50033
50653
|
if (!await fs.exists(manifestPath) || !await fs.exists(poolPath) || !await fs.exists(candidatesPath)) {
|
|
50034
50654
|
throw new Error(`intake artifacts missing in ${request.intakeDir}`);
|
|
50035
50655
|
}
|
|
@@ -50042,7 +50662,7 @@ async function itcTopicSelect(request, fs = createNodeFileSystem()) {
|
|
|
50042
50662
|
}
|
|
50043
50663
|
const selectedDocSet = new Set(candidate.itemIds);
|
|
50044
50664
|
const selectedDocs = poolDocs.filter((d) => selectedDocSet.has(d.id));
|
|
50045
|
-
const selectedDocsPath =
|
|
50665
|
+
const selectedDocsPath = join18(request.intakeDir, "selected-docs.json");
|
|
50046
50666
|
await fs.writeFile(selectedDocsPath, `${JSON.stringify(selectedDocs, null, 2)}
|
|
50047
50667
|
`);
|
|
50048
50668
|
const chosenSource = {
|
|
@@ -50056,7 +50676,7 @@ async function itcTopicSelect(request, fs = createNodeFileSystem()) {
|
|
|
50056
50676
|
itemIds: candidate.itemIds,
|
|
50057
50677
|
selectedDocsPath
|
|
50058
50678
|
};
|
|
50059
|
-
await fs.writeFile(
|
|
50679
|
+
await fs.writeFile(join18(request.intakeDir, "chosen-source.json"), `${JSON.stringify(chosenSource, null, 2)}
|
|
50060
50680
|
`);
|
|
50061
50681
|
const storePath = await resolveStorePath(resolveKkConfigPath2(), request.dbPath);
|
|
50062
50682
|
const store = await openStore({ path: storePath });
|
|
@@ -50095,7 +50715,7 @@ async function itcSelectionSettle(request, fs = createNodeFileSystem()) {
|
|
|
50095
50715
|
const storePath = await resolveStorePath(resolveKkConfigPath2(), request.dbPath);
|
|
50096
50716
|
if (request.outcome === "published") {
|
|
50097
50717
|
let itemIds;
|
|
50098
|
-
const chosenSourcePath =
|
|
50718
|
+
const chosenSourcePath = join18(request.intakeDir, "chosen-source.json");
|
|
50099
50719
|
if (await fs.exists(chosenSourcePath)) {
|
|
50100
50720
|
try {
|
|
50101
50721
|
const chosen = JSON.parse(await fs.readFile(chosenSourcePath));
|
|
@@ -50125,7 +50745,7 @@ async function itcSelectionSettle(request, fs = createNodeFileSystem()) {
|
|
|
50125
50745
|
guidance: "reconcile status manually via kk store mark-published or kk store release"
|
|
50126
50746
|
};
|
|
50127
50747
|
await fs.ensureDir(request.intakeDir);
|
|
50128
|
-
await fs.writeFile(
|
|
50748
|
+
await fs.writeFile(join18(request.intakeDir, ".selection-reconcile.json"), `${JSON.stringify(reconcileInfo, null, 2)}
|
|
50129
50749
|
`);
|
|
50130
50750
|
return { settled: "retained", action: "reconcile_retained" };
|
|
50131
50751
|
}
|
|
@@ -50210,7 +50830,7 @@ function buildTopicCandidates(pool) {
|
|
|
50210
50830
|
return candidates;
|
|
50211
50831
|
}
|
|
50212
50832
|
async function runItcCandidates(options, fs = createNodeFileSystem(), executor) {
|
|
50213
|
-
const intakeDir = options.intakeDir ??
|
|
50833
|
+
const intakeDir = options.intakeDir ?? join19(process.cwd(), ".itc-intake", options.parentRunId);
|
|
50214
50834
|
const { count: poolCount } = await itcTopicExtract({
|
|
50215
50835
|
schemaVersion: 1,
|
|
50216
50836
|
parentRunId: options.parentRunId,
|
|
@@ -50221,13 +50841,13 @@ async function runItcCandidates(options, fs = createNodeFileSystem(), executor)
|
|
|
50221
50841
|
...options.limit ? { limit: options.limit } : {},
|
|
50222
50842
|
...options.dbPath ? { dbPath: options.dbPath } : {}
|
|
50223
50843
|
}, fs);
|
|
50224
|
-
const pool = array(DocSchema).parse(JSON.parse(await fs.readFile(
|
|
50225
|
-
const manifest = JSON.parse(await fs.readFile(
|
|
50844
|
+
const pool = array(DocSchema).parse(JSON.parse(await fs.readFile(join19(intakeDir, "topic-pool.json"))));
|
|
50845
|
+
const manifest = JSON.parse(await fs.readFile(join19(intakeDir, "topic-pool-manifest.json")));
|
|
50226
50846
|
const candidates = buildTopicCandidates(pool);
|
|
50227
50847
|
if (candidates.length === 0) {
|
|
50228
50848
|
throw new Error(`no citable pool item carries a source URI: cannot build topic candidates from ${poolCount} items`);
|
|
50229
50849
|
}
|
|
50230
|
-
await fs.writeFile(
|
|
50850
|
+
await fs.writeFile(join19(intakeDir, "topic-candidates.json"), `${JSON.stringify({
|
|
50231
50851
|
schemaVersion: 1,
|
|
50232
50852
|
parentRunId: options.parentRunId,
|
|
50233
50853
|
poolDigest: manifest.poolDigest,
|
|
@@ -50239,8 +50859,8 @@ async function runItcCandidates(options, fs = createNodeFileSystem(), executor)
|
|
|
50239
50859
|
parentRunId: options.parentRunId,
|
|
50240
50860
|
decisionId: "kk-itc-topic-choice",
|
|
50241
50861
|
candidates: candidates.map((c) => ({ id: c.id, title: c.title, description: c.reason })),
|
|
50242
|
-
evidenceFiles: [
|
|
50243
|
-
outPath:
|
|
50862
|
+
evidenceFiles: [join19(intakeDir, "topic-candidates.json")],
|
|
50863
|
+
outPath: join19(intakeDir, "topic-decision.json")
|
|
50244
50864
|
}, fs, executor);
|
|
50245
50865
|
for (const c of candidates) {
|
|
50246
50866
|
echo(`${c.id} \u2014 ${c.title}`);
|
|
@@ -50686,14 +51306,14 @@ init_commander();
|
|
|
50686
51306
|
|
|
50687
51307
|
// src/itc-stage.ts
|
|
50688
51308
|
import { createHash as createHash7 } from "crypto";
|
|
50689
|
-
import { join as
|
|
51309
|
+
import { join as join23 } from "path";
|
|
50690
51310
|
init_dist3();
|
|
50691
51311
|
init_dist();
|
|
50692
51312
|
|
|
50693
51313
|
// src/itc-config.ts
|
|
50694
51314
|
init_dist3();
|
|
50695
51315
|
import { createHash as createHash4 } from "crypto";
|
|
50696
|
-
import { dirname as dirname12, isAbsolute as isAbsolute8, join as
|
|
51316
|
+
import { dirname as dirname12, isAbsolute as isAbsolute8, join as join20, resolve as resolve20 } from "path";
|
|
50697
51317
|
|
|
50698
51318
|
class ItcConfigInvalidError extends Error {
|
|
50699
51319
|
constructor(message) {
|
|
@@ -50762,7 +51382,7 @@ function expandHome3(value, homeDir) {
|
|
|
50762
51382
|
if (value === "~")
|
|
50763
51383
|
return homeDir;
|
|
50764
51384
|
if (value.startsWith("~/"))
|
|
50765
|
-
return
|
|
51385
|
+
return join20(homeDir, value.slice(2));
|
|
50766
51386
|
return value;
|
|
50767
51387
|
}
|
|
50768
51388
|
function canonicalJson(value) {
|
|
@@ -50780,7 +51400,7 @@ function resolveItcConfigPath(env, homeDir) {
|
|
|
50780
51400
|
const explicit = env.KK_CONFIG;
|
|
50781
51401
|
if (explicit !== undefined && explicit.trim() !== "")
|
|
50782
51402
|
return expandHome3(explicit.trim(), homeDir);
|
|
50783
|
-
return
|
|
51403
|
+
return join20(homeDir, ".config", "kk", "config.yaml");
|
|
50784
51404
|
}
|
|
50785
51405
|
async function readItcConfigBlock(configPath, fs = createNodeFileSystem()) {
|
|
50786
51406
|
if (!await fs.exists(configPath))
|
|
@@ -51124,18 +51744,18 @@ function resolveItcWorkspace(options, topic, input = {}) {
|
|
|
51124
51744
|
dated: input.runDate !== undefined
|
|
51125
51745
|
};
|
|
51126
51746
|
}
|
|
51127
|
-
const leaf = options.runDate !== null ?
|
|
51128
|
-
return { workDir:
|
|
51747
|
+
const leaf = options.runDate !== null ? join20(slug, options.runDate) : slug;
|
|
51748
|
+
return { workDir: join20(options.worksRoot, "kk-itc", leaf), topicName: slug, dated: options.runDate !== null };
|
|
51129
51749
|
}
|
|
51130
51750
|
async function writeItcOptionsSnapshot(workDir, options, fs = createNodeFileSystem()) {
|
|
51131
|
-
const path =
|
|
51751
|
+
const path = join20(workDir, "itc-options.json");
|
|
51132
51752
|
await fs.ensureDir(workDir);
|
|
51133
51753
|
await fs.writeFile(path, `${JSON.stringify(options, null, 2)}
|
|
51134
51754
|
`);
|
|
51135
51755
|
return path;
|
|
51136
51756
|
}
|
|
51137
51757
|
async function verifyItcOptionsSnapshot(workDir, options, fs = createNodeFileSystem()) {
|
|
51138
|
-
const path =
|
|
51758
|
+
const path = join20(workDir, "itc-options.json");
|
|
51139
51759
|
if (!await fs.exists(path))
|
|
51140
51760
|
return { ok: true };
|
|
51141
51761
|
let saved;
|
|
@@ -51170,7 +51790,7 @@ async function verifyItcOptionsSnapshot(workDir, options, fs = createNodeFileSys
|
|
|
51170
51790
|
|
|
51171
51791
|
// src/itc-plan.ts
|
|
51172
51792
|
import { createHash as createHash5 } from "crypto";
|
|
51173
|
-
import { join as
|
|
51793
|
+
import { join as join21, resolve as resolve21 } from "path";
|
|
51174
51794
|
init_dist3();
|
|
51175
51795
|
init_zod();
|
|
51176
51796
|
var ItcTargetsOriginSchema = _enum2([
|
|
@@ -51218,7 +51838,7 @@ var SLUG_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
|
51218
51838
|
async function resolveDefaultPackageRoot(fs) {
|
|
51219
51839
|
let dir = resolve21(process.cwd());
|
|
51220
51840
|
for (;; ) {
|
|
51221
|
-
if (await fs.exists(
|
|
51841
|
+
if (await fs.exists(join21(dir, "plugins/kk/publish-presets")))
|
|
51222
51842
|
return dir;
|
|
51223
51843
|
const parent = resolve21(dir, "..");
|
|
51224
51844
|
if (parent === dir)
|
|
@@ -51376,7 +51996,7 @@ async function loadPublishPreset(slug, roots = {}, fs = createNodeFileSystem())
|
|
|
51376
51996
|
return resolvePresetFromRoots(fs, slug.trim(), projectRoot, packageRoot);
|
|
51377
51997
|
}
|
|
51378
51998
|
async function hasCoverImage(fs, workDir) {
|
|
51379
|
-
const dir =
|
|
51999
|
+
const dir = join21(workDir, "4-illustrations");
|
|
51380
52000
|
if (!await fs.exists(dir))
|
|
51381
52001
|
return false;
|
|
51382
52002
|
try {
|
|
@@ -51387,7 +52007,7 @@ async function hasCoverImage(fs, workDir) {
|
|
|
51387
52007
|
}
|
|
51388
52008
|
}
|
|
51389
52009
|
async function hasOrderedImages(fs, workDir) {
|
|
51390
|
-
const path =
|
|
52010
|
+
const path = join21(workDir, "4-publish", "content.json");
|
|
51391
52011
|
if (!await fs.exists(path))
|
|
51392
52012
|
return false;
|
|
51393
52013
|
try {
|
|
@@ -51501,7 +52121,7 @@ async function buildItcPublishPlan(request, plugins, fs = createNodeFileSystem()
|
|
|
51501
52121
|
|
|
51502
52122
|
// src/itc-publish.ts
|
|
51503
52123
|
import { createHash as createHash6 } from "crypto";
|
|
51504
|
-
import { join as
|
|
52124
|
+
import { join as join22 } from "path";
|
|
51505
52125
|
init_dist3();
|
|
51506
52126
|
init_dist();
|
|
51507
52127
|
init_zod();
|
|
@@ -51535,11 +52155,11 @@ var ItcReceiptSchema = object2({
|
|
|
51535
52155
|
finishedAt: string2().optional()
|
|
51536
52156
|
}).strict();
|
|
51537
52157
|
async function findCover(fs, workDir) {
|
|
51538
|
-
const dir =
|
|
52158
|
+
const dir = join22(workDir, "4-illustrations");
|
|
51539
52159
|
if (!await fs.exists(dir))
|
|
51540
52160
|
return;
|
|
51541
52161
|
const cover = (await fs.readDir(dir)).find((file) => file.startsWith("cover."));
|
|
51542
|
-
return cover ?
|
|
52162
|
+
return cover ? join22(dir, cover) : undefined;
|
|
51543
52163
|
}
|
|
51544
52164
|
async function readFrontmatter(fs, path) {
|
|
51545
52165
|
if (!await fs.exists(path))
|
|
@@ -51572,7 +52192,7 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51572
52192
|
const snapshot = await readOptionsSnapshot(fs, workDir);
|
|
51573
52193
|
const [targetsArg = "", sourceLocale = "", liveArg = "", presetArg = ""] = snapshot ? [snapshot.publishTargets, snapshot.sourceLocale, String(snapshot.publishLive), ""] : positional;
|
|
51574
52194
|
const live = liveArg === "true";
|
|
51575
|
-
const draftPath =
|
|
52195
|
+
const draftPath = join22(workDir, "3-draft-draft-article.md");
|
|
51576
52196
|
if (!await fs.exists(draftPath)) {
|
|
51577
52197
|
echoError(`final draft not found: ${draftPath}`);
|
|
51578
52198
|
return 1;
|
|
@@ -51592,10 +52212,10 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51592
52212
|
return 1;
|
|
51593
52213
|
}
|
|
51594
52214
|
const content = { title, format: "markdown", body };
|
|
51595
|
-
const outlinePath =
|
|
52215
|
+
const outlinePath = join22(workDir, "2-outline", "outline-approved.md");
|
|
51596
52216
|
if (await fs.exists(outlinePath))
|
|
51597
52217
|
content.outline = (await fs.readFile(outlinePath)).trim();
|
|
51598
|
-
const docsPath =
|
|
52218
|
+
const docsPath = join22(workDir, "docs.json");
|
|
51599
52219
|
if (await fs.exists(docsPath)) {
|
|
51600
52220
|
try {
|
|
51601
52221
|
const docs = JSON.parse(await fs.readFile(docsPath));
|
|
@@ -51612,7 +52232,7 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51612
52232
|
const cover = await findCover(fs, workDir);
|
|
51613
52233
|
if (cover)
|
|
51614
52234
|
metadata.coverImage = cover;
|
|
51615
|
-
const brief = await readFrontmatter(fs,
|
|
52235
|
+
const brief = await readFrontmatter(fs, join22(workDir, "brief.md"));
|
|
51616
52236
|
if (brief.tags)
|
|
51617
52237
|
metadata.tags = splitList(brief.tags);
|
|
51618
52238
|
if (brief.keywords)
|
|
@@ -51620,9 +52240,9 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51620
52240
|
if (brief.description)
|
|
51621
52241
|
metadata.description = brief.description;
|
|
51622
52242
|
content.metadata = metadata;
|
|
51623
|
-
const publishDir =
|
|
52243
|
+
const publishDir = join22(workDir, "4-publish");
|
|
51624
52244
|
await fs.ensureDir(publishDir);
|
|
51625
|
-
await fs.writeFile(
|
|
52245
|
+
await fs.writeFile(join22(publishDir, "content.json"), `${JSON.stringify(content, null, 2)}
|
|
51626
52246
|
`);
|
|
51627
52247
|
const fromLaunch = snapshot?.publishPresetSource === "launch" ? snapshot.publishPreset : null;
|
|
51628
52248
|
const fromConfig = snapshot?.publishPresetSource === "config" ? snapshot.publishPreset : null;
|
|
@@ -51649,7 +52269,7 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51649
52269
|
title: `${resolvedTargets.presetName}: ${resolvedTargets.targets.map((t) => t.plugin).join(", ")}`
|
|
51650
52270
|
}
|
|
51651
52271
|
],
|
|
51652
|
-
outPath:
|
|
52272
|
+
outPath: join22(workDir, "4-publish", "preset-decision.json")
|
|
51653
52273
|
}, fs);
|
|
51654
52274
|
if (decision.value !== "suggested")
|
|
51655
52275
|
resolvedTargets = { targets: [], origin: "none" };
|
|
@@ -51665,7 +52285,7 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51665
52285
|
browser: [],
|
|
51666
52286
|
targets: []
|
|
51667
52287
|
};
|
|
51668
|
-
await fs.writeFile(
|
|
52288
|
+
await fs.writeFile(join22(publishDir, "publish-plan.json"), `${JSON.stringify(emptyPlan, null, 2)}
|
|
51669
52289
|
`);
|
|
51670
52290
|
return 0;
|
|
51671
52291
|
}
|
|
@@ -51682,7 +52302,7 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51682
52302
|
};
|
|
51683
52303
|
try {
|
|
51684
52304
|
const plan = await buildItcPublishPlan(req, plugins, fs);
|
|
51685
|
-
const planPath =
|
|
52305
|
+
const planPath = join22(publishDir, "publish-plan.json");
|
|
51686
52306
|
await fs.writeFile(planPath, `${JSON.stringify(plan, null, 2)}
|
|
51687
52307
|
`);
|
|
51688
52308
|
await prepareTargetPayloads(fs, workDir, plan, plugins);
|
|
@@ -51693,13 +52313,13 @@ async function itcMaterializePlan(args, fs = createNodeFileSystem()) {
|
|
|
51693
52313
|
}
|
|
51694
52314
|
}
|
|
51695
52315
|
async function readOptionsSnapshot(fs, workDir) {
|
|
51696
|
-
const path =
|
|
52316
|
+
const path = join22(workDir, "itc-options.json");
|
|
51697
52317
|
if (!await fs.exists(path))
|
|
51698
52318
|
return;
|
|
51699
52319
|
return JSON.parse(await fs.readFile(path));
|
|
51700
52320
|
}
|
|
51701
52321
|
async function prepareTargetPayloads(fs, workDir, plan, plugins) {
|
|
51702
|
-
const publishDir =
|
|
52322
|
+
const publishDir = join22(workDir, "4-publish");
|
|
51703
52323
|
if (!plan.targets || plan.targets.length === 0)
|
|
51704
52324
|
return;
|
|
51705
52325
|
const planned = new Set(plan.targets.map((t) => t.plugin));
|
|
@@ -51717,9 +52337,9 @@ async function prepareTargetPayloads(fs, workDir, plan, plugins) {
|
|
|
51717
52337
|
for (const target of plan.targets) {
|
|
51718
52338
|
const variantEntry = plan.variants[target.variant];
|
|
51719
52339
|
const baseFile = variantEntry?.file ?? "content.json";
|
|
51720
|
-
let basePath =
|
|
52340
|
+
let basePath = join22(publishDir, baseFile);
|
|
51721
52341
|
if (!await fs.exists(basePath)) {
|
|
51722
|
-
basePath =
|
|
52342
|
+
basePath = join22(publishDir, "content.json");
|
|
51723
52343
|
}
|
|
51724
52344
|
if (!await fs.exists(basePath))
|
|
51725
52345
|
continue;
|
|
@@ -51736,7 +52356,7 @@ async function prepareTargetPayloads(fs, workDir, plan, plugins) {
|
|
|
51736
52356
|
}
|
|
51737
52357
|
};
|
|
51738
52358
|
const targetFileName = `content.${target.plugin}.json`;
|
|
51739
|
-
const targetFilePath =
|
|
52359
|
+
const targetFilePath = join22(publishDir, targetFileName);
|
|
51740
52360
|
const contentStr = `${JSON.stringify(isolatedContent, null, 2)}
|
|
51741
52361
|
`;
|
|
51742
52362
|
await fs.writeFile(targetFilePath, contentStr);
|
|
@@ -51744,7 +52364,7 @@ async function prepareTargetPayloads(fs, workDir, plan, plugins) {
|
|
|
51744
52364
|
target.payloadPath = `4-publish/${targetFileName}`;
|
|
51745
52365
|
target.payloadDigest = payloadDigest;
|
|
51746
52366
|
}
|
|
51747
|
-
await fs.writeFile(
|
|
52367
|
+
await fs.writeFile(join22(publishDir, "publish-plan.json"), `${JSON.stringify(plan, null, 2)}
|
|
51748
52368
|
`);
|
|
51749
52369
|
}
|
|
51750
52370
|
async function probePlugin(plugin, executor = new NodeProcessExecutor) {
|
|
@@ -51779,7 +52399,7 @@ async function itcCheckTargets(args, fs = createNodeFileSystem(), executor = new
|
|
|
51779
52399
|
echoError("usage: itc-check-targets <work_dir>");
|
|
51780
52400
|
return 1;
|
|
51781
52401
|
}
|
|
51782
|
-
const planPath =
|
|
52402
|
+
const planPath = join22(workDir, "4-publish/publish-plan.json");
|
|
51783
52403
|
if (!await fs.exists(planPath))
|
|
51784
52404
|
return 0;
|
|
51785
52405
|
const plan = JSON.parse(await fs.readFile(planPath));
|
|
@@ -51808,7 +52428,7 @@ async function sendRows(options) {
|
|
|
51808
52428
|
const sameDigest = (priorDigest, digest) => priorDigest !== undefined && digest !== undefined && priorDigest === digest;
|
|
51809
52429
|
for (const row of rows) {
|
|
51810
52430
|
const startedAt = new Date().toISOString();
|
|
51811
|
-
const targetReceiptPath =
|
|
52431
|
+
const targetReceiptPath = join22(receiptsDir, `${row.key}.json`);
|
|
51812
52432
|
let reattempt = false;
|
|
51813
52433
|
let payloadPath = row.payloadPath;
|
|
51814
52434
|
let preparedError;
|
|
@@ -51916,7 +52536,7 @@ async function sendRows(options) {
|
|
|
51916
52536
|
await options.onReceipt(receipt);
|
|
51917
52537
|
continue;
|
|
51918
52538
|
}
|
|
51919
|
-
const outPath =
|
|
52539
|
+
const outPath = join22(publishDir, `fanout-result.${row.key}${retry ? ".retry" : ""}.json`);
|
|
51920
52540
|
if (reattempt && await fs.exists(outPath))
|
|
51921
52541
|
await fs.deleteFile(outPath);
|
|
51922
52542
|
const fanoutArgs = [
|
|
@@ -51995,8 +52615,8 @@ async function itcSendTargets(args, fs = createNodeFileSystem(), dispatch = runF
|
|
|
51995
52615
|
echoError("usage: itc-send-targets <work_dir> [--retry]");
|
|
51996
52616
|
return 1;
|
|
51997
52617
|
}
|
|
51998
|
-
const publishDir =
|
|
51999
|
-
const planPath =
|
|
52618
|
+
const publishDir = join22(workDir, "4-publish");
|
|
52619
|
+
const planPath = join22(publishDir, "publish-plan.json");
|
|
52000
52620
|
if (!await fs.exists(planPath))
|
|
52001
52621
|
return 0;
|
|
52002
52622
|
const plan = JSON.parse(await fs.readFile(planPath));
|
|
@@ -52004,10 +52624,10 @@ async function itcSendTargets(args, fs = createNodeFileSystem(), dispatch = runF
|
|
|
52004
52624
|
return 0;
|
|
52005
52625
|
const parentRunId = (await readOptionsSnapshot(fs, workDir))?.parentRunId;
|
|
52006
52626
|
if (!parentRunId) {
|
|
52007
|
-
echoError(`itc-send-targets: ${
|
|
52627
|
+
echoError(`itc-send-targets: ${join22(workDir, "itc-options.json")} has no parentRunId`);
|
|
52008
52628
|
return 1;
|
|
52009
52629
|
}
|
|
52010
|
-
const receiptsDir =
|
|
52630
|
+
const receiptsDir = join22(publishDir, "receipts");
|
|
52011
52631
|
await fs.ensureDir(receiptsDir);
|
|
52012
52632
|
const planRaw = await fs.readFile(planPath);
|
|
52013
52633
|
const planDigest = createHash6("sha256").update(planRaw).digest("hex");
|
|
@@ -52016,7 +52636,7 @@ async function itcSendTargets(args, fs = createNodeFileSystem(), dispatch = runF
|
|
|
52016
52636
|
plugin: target.plugin,
|
|
52017
52637
|
profile: target.profile,
|
|
52018
52638
|
mode: target.mode,
|
|
52019
|
-
payloadPath:
|
|
52639
|
+
payloadPath: join22(workDir, target.payloadPath ?? `4-publish/content.${target.plugin}.json`),
|
|
52020
52640
|
transport: target.transport
|
|
52021
52641
|
}));
|
|
52022
52642
|
const outcome = await sendRows({
|
|
@@ -52040,12 +52660,12 @@ async function itcReconcileReceipts(args, fs = createNodeFileSystem()) {
|
|
|
52040
52660
|
echoError("usage: itc-reconcile-receipts <work_dir>");
|
|
52041
52661
|
return 1;
|
|
52042
52662
|
}
|
|
52043
|
-
const publishDir =
|
|
52044
|
-
const planPath =
|
|
52663
|
+
const publishDir = join22(workDir, "4-publish");
|
|
52664
|
+
const planPath = join22(publishDir, "publish-plan.json");
|
|
52045
52665
|
if (!await fs.exists(planPath))
|
|
52046
52666
|
return 0;
|
|
52047
52667
|
const plan = JSON.parse(await fs.readFile(planPath));
|
|
52048
|
-
const chosenPath =
|
|
52668
|
+
const chosenPath = join22(workDir, "chosen-source.json");
|
|
52049
52669
|
const chosen = await fs.exists(chosenPath) ? JSON.parse(await fs.readFile(chosenPath)) : undefined;
|
|
52050
52670
|
const settle = async (outcome) => {
|
|
52051
52671
|
if (chosen === undefined)
|
|
@@ -52062,10 +52682,10 @@ async function itcReconcileReceipts(args, fs = createNodeFileSystem()) {
|
|
|
52062
52682
|
await settle("author_only");
|
|
52063
52683
|
return 0;
|
|
52064
52684
|
}
|
|
52065
|
-
const receiptsDir =
|
|
52685
|
+
const receiptsDir = join22(publishDir, "receipts");
|
|
52066
52686
|
const receipts = [];
|
|
52067
52687
|
for (const target of plan.targets) {
|
|
52068
|
-
const rPath =
|
|
52688
|
+
const rPath = join22(receiptsDir, `${target.plugin}.json`);
|
|
52069
52689
|
if (!await fs.exists(rPath)) {
|
|
52070
52690
|
echoError(`itc-reconcile-receipts: missing receipt for target '${target.plugin}'`);
|
|
52071
52691
|
return 1;
|
|
@@ -52090,12 +52710,12 @@ async function itcExportSeo(args, fs = createNodeFileSystem()) {
|
|
|
52090
52710
|
echoError("usage: itc-export-seo <work_dir> [mode]");
|
|
52091
52711
|
return 1;
|
|
52092
52712
|
}
|
|
52093
|
-
const seoDir =
|
|
52713
|
+
const seoDir = join22(workDir, "seo");
|
|
52094
52714
|
await fs.ensureDir(seoDir);
|
|
52095
52715
|
if (mode === "off") {
|
|
52096
|
-
await fs.writeFile(
|
|
52716
|
+
await fs.writeFile(join22(seoDir, "prepublish-request.json"), `${JSON.stringify({ schemaVersion: 1, mode: "off", status: "not-assessed" }, null, 2)}
|
|
52097
52717
|
`);
|
|
52098
|
-
await fs.writeFile(
|
|
52718
|
+
await fs.writeFile(join22(seoDir, "published-pages.json"), `${JSON.stringify({ schemaVersion: 1, mode: "off", pages: [], status: "not-assessed" }, null, 2)}
|
|
52099
52719
|
`);
|
|
52100
52720
|
return 0;
|
|
52101
52721
|
}
|
|
@@ -52105,14 +52725,14 @@ async function itcExportSeo(args, fs = createNodeFileSystem()) {
|
|
|
52105
52725
|
status: "ready",
|
|
52106
52726
|
draftFile: "3-draft-draft-article.md"
|
|
52107
52727
|
};
|
|
52108
|
-
await fs.writeFile(
|
|
52728
|
+
await fs.writeFile(join22(seoDir, "prepublish-request.json"), `${JSON.stringify(prepublish, null, 2)}
|
|
52109
52729
|
`);
|
|
52110
|
-
const receiptsDir =
|
|
52730
|
+
const receiptsDir = join22(workDir, "4-publish/receipts");
|
|
52111
52731
|
const verifiedPages = [];
|
|
52112
52732
|
if (await fs.exists(receiptsDir)) {
|
|
52113
52733
|
for (const file of (await fs.readDir(receiptsDir)).filter((f) => f.endsWith(".json"))) {
|
|
52114
52734
|
try {
|
|
52115
|
-
const r = JSON.parse(await fs.readFile(
|
|
52735
|
+
const r = JSON.parse(await fs.readFile(join22(receiptsDir, file)));
|
|
52116
52736
|
if (r.mode === "live" && r.effect === "confirmed" && r.publicUrlVerified === true && r.url) {
|
|
52117
52737
|
verifiedPages.push({ plugin: r.plugin, url: r.url });
|
|
52118
52738
|
}
|
|
@@ -52126,7 +52746,7 @@ async function itcExportSeo(args, fs = createNodeFileSystem()) {
|
|
|
52126
52746
|
pages: verifiedPages,
|
|
52127
52747
|
...verifiedPages.length === 0 ? { reason: "no verified public live URLs available" } : {}
|
|
52128
52748
|
};
|
|
52129
|
-
await fs.writeFile(
|
|
52749
|
+
await fs.writeFile(join22(seoDir, "published-pages.json"), `${JSON.stringify(publishedPages, null, 2)}
|
|
52130
52750
|
`);
|
|
52131
52751
|
return 0;
|
|
52132
52752
|
}
|
|
@@ -52136,12 +52756,12 @@ async function itcReportSummary(args, fs = createNodeFileSystem()) {
|
|
|
52136
52756
|
echoError("usage: itc-report-summary <work_dir> [max_revisions]");
|
|
52137
52757
|
return 1;
|
|
52138
52758
|
}
|
|
52139
|
-
const reportDir =
|
|
52759
|
+
const reportDir = join22(workDir, "5-report");
|
|
52140
52760
|
await fs.ensureDir(reportDir);
|
|
52141
52761
|
let verdict = "PASS";
|
|
52142
52762
|
let confidence = null;
|
|
52143
52763
|
let outcome = "done";
|
|
52144
|
-
const verdictPath =
|
|
52764
|
+
const verdictPath = join22(workDir, ".itc-verdict.json");
|
|
52145
52765
|
const hasVerdict = await fs.exists(verdictPath);
|
|
52146
52766
|
if (hasVerdict) {
|
|
52147
52767
|
try {
|
|
@@ -52155,7 +52775,7 @@ async function itcReportSummary(args, fs = createNodeFileSystem()) {
|
|
|
52155
52775
|
}
|
|
52156
52776
|
}
|
|
52157
52777
|
let revisions = 0;
|
|
52158
|
-
const revPath =
|
|
52778
|
+
const revPath = join22(workDir, ".auto-revise-count");
|
|
52159
52779
|
if (await fs.exists(revPath)) {
|
|
52160
52780
|
try {
|
|
52161
52781
|
revisions = Number((await fs.readFile(revPath)).trim()) || 0;
|
|
@@ -52163,12 +52783,12 @@ async function itcReportSummary(args, fs = createNodeFileSystem()) {
|
|
|
52163
52783
|
revisions = 0;
|
|
52164
52784
|
}
|
|
52165
52785
|
}
|
|
52166
|
-
const receiptsDir =
|
|
52786
|
+
const receiptsDir = join22(workDir, "4-publish/receipts");
|
|
52167
52787
|
const receipts = [];
|
|
52168
52788
|
if (await fs.exists(receiptsDir)) {
|
|
52169
52789
|
for (const file of (await fs.readDir(receiptsDir)).filter((f) => f.endsWith(".json"))) {
|
|
52170
52790
|
try {
|
|
52171
|
-
const r = JSON.parse(await fs.readFile(
|
|
52791
|
+
const r = JSON.parse(await fs.readFile(join22(receiptsDir, file)));
|
|
52172
52792
|
receipts.push(r);
|
|
52173
52793
|
} catch {}
|
|
52174
52794
|
}
|
|
@@ -52176,7 +52796,7 @@ async function itcReportSummary(args, fs = createNodeFileSystem()) {
|
|
|
52176
52796
|
if (receipts.some((r) => r.status !== "succeeded")) {
|
|
52177
52797
|
outcome = "failed";
|
|
52178
52798
|
}
|
|
52179
|
-
const planPath =
|
|
52799
|
+
const planPath = join22(workDir, "4-publish/publish-plan.json");
|
|
52180
52800
|
let targetContract;
|
|
52181
52801
|
if (await fs.exists(planPath)) {
|
|
52182
52802
|
const planRaw = await fs.readFile(planPath);
|
|
@@ -52200,7 +52820,7 @@ async function itcReportSummary(args, fs = createNodeFileSystem()) {
|
|
|
52200
52820
|
...targetContract ? { targetContractFailure: targetContract } : {},
|
|
52201
52821
|
maxRevisions: Number(maxRevisions) || 2
|
|
52202
52822
|
};
|
|
52203
|
-
await fs.writeFile(
|
|
52823
|
+
await fs.writeFile(join22(reportDir, "report.json"), `${JSON.stringify(reportJson, null, 2)}
|
|
52204
52824
|
`);
|
|
52205
52825
|
const md = [
|
|
52206
52826
|
"# ITC Publication Run Report",
|
|
@@ -52218,7 +52838,7 @@ async function itcReportSummary(args, fs = createNodeFileSystem()) {
|
|
|
52218
52838
|
...receipts.map((r) => `- ${r.plugin} (${r.profile}): status=${r.status}, effect=${r.effect}`)
|
|
52219
52839
|
].join(`
|
|
52220
52840
|
`);
|
|
52221
|
-
await fs.writeFile(
|
|
52841
|
+
await fs.writeFile(join22(reportDir, "report.md"), `${md}
|
|
52222
52842
|
`);
|
|
52223
52843
|
return 0;
|
|
52224
52844
|
}
|
|
@@ -52231,13 +52851,13 @@ async function itcAuthorVerdict(args, fs = createNodeFileSystem()) {
|
|
|
52231
52851
|
const auto = process.env.KK_ITC_AUTO === "true";
|
|
52232
52852
|
const maxRevisions = Number(process.env.KK_ITC_MAX_REVISIONS ?? "2") || 2;
|
|
52233
52853
|
const readCount = async (name) => {
|
|
52234
|
-
const p =
|
|
52854
|
+
const p = join22(workDir, name);
|
|
52235
52855
|
if (!await fs.exists(p))
|
|
52236
52856
|
return 0;
|
|
52237
52857
|
const n = Number((await fs.readFile(p)).trim());
|
|
52238
52858
|
return Number.isFinite(n) ? n : 0;
|
|
52239
52859
|
};
|
|
52240
|
-
const verdictPath =
|
|
52860
|
+
const verdictPath = join22(workDir, ".itc-verdict.json");
|
|
52241
52861
|
let verdict = "";
|
|
52242
52862
|
let confidence = "";
|
|
52243
52863
|
if (await fs.exists(verdictPath)) {
|
|
@@ -52263,8 +52883,8 @@ async function itcAuthorVerdict(args, fs = createNodeFileSystem()) {
|
|
|
52263
52883
|
} else {
|
|
52264
52884
|
disposition = "fail";
|
|
52265
52885
|
}
|
|
52266
|
-
await fs.ensureDir(
|
|
52267
|
-
await fs.writeFile(
|
|
52886
|
+
await fs.ensureDir(join22(workDir, ".itc"));
|
|
52887
|
+
await fs.writeFile(join22(workDir, ".itc/disposition.txt"), `${disposition}
|
|
52268
52888
|
`);
|
|
52269
52889
|
return 0;
|
|
52270
52890
|
}
|
|
@@ -52361,9 +52981,9 @@ async function runItcPublishPlan(args, fs) {
|
|
|
52361
52981
|
try {
|
|
52362
52982
|
const { plugins } = discoverAllPlugins();
|
|
52363
52983
|
const plan = await buildItcPublishPlan(request, plugins, fs);
|
|
52364
|
-
const outDir =
|
|
52984
|
+
const outDir = join23(request.workDir, "4-publish");
|
|
52365
52985
|
await fs.ensureDir(outDir);
|
|
52366
|
-
await fs.writeFile(
|
|
52986
|
+
await fs.writeFile(join23(outDir, "publish-plan.json"), `${JSON.stringify(plan, null, 4)}
|
|
52367
52987
|
`);
|
|
52368
52988
|
return 0;
|
|
52369
52989
|
} catch (err) {
|
|
@@ -52403,23 +53023,23 @@ async function runSlotDecision(args, fs) {
|
|
|
52403
53023
|
}
|
|
52404
53024
|
try {
|
|
52405
53025
|
if (mode === "--topic") {
|
|
52406
|
-
const candidatesPath =
|
|
53026
|
+
const candidatesPath = join23(dir, "topic-candidates.json");
|
|
52407
53027
|
const artifact = JSON.parse(await fs.readFile(candidatesPath));
|
|
52408
53028
|
const result = await itcDecision({
|
|
52409
53029
|
parentRunId,
|
|
52410
53030
|
decisionId: "kk-itc-topic-choice",
|
|
52411
53031
|
candidates: artifact.candidates.map((c) => ({ id: c.id, title: c.title, description: c.reason })),
|
|
52412
53032
|
evidenceFiles: [candidatesPath],
|
|
52413
|
-
outPath:
|
|
53033
|
+
outPath: join23(dir, "topic-decision.json")
|
|
52414
53034
|
}, fs);
|
|
52415
53035
|
echo(result.value);
|
|
52416
53036
|
return 0;
|
|
52417
53037
|
}
|
|
52418
|
-
const outlineDir =
|
|
53038
|
+
const outlineDir = join23(dir, "2-outline");
|
|
52419
53039
|
const candidates = [];
|
|
52420
53040
|
const evidenceFiles = [];
|
|
52421
53041
|
for (const id of ["a", "b", "c"]) {
|
|
52422
|
-
const file =
|
|
53042
|
+
const file = join23(outlineDir, `outline-option-${id}.md`);
|
|
52423
53043
|
if (!await fs.exists(file))
|
|
52424
53044
|
continue;
|
|
52425
53045
|
const heading = (await fs.readFile(file)).split(`
|
|
@@ -52429,18 +53049,18 @@ async function runSlotDecision(args, fs) {
|
|
|
52429
53049
|
}
|
|
52430
53050
|
if (candidates.length === 0)
|
|
52431
53051
|
throw new Error(`no outline options in ${outlineDir}`);
|
|
52432
|
-
evidenceFiles.push(
|
|
53052
|
+
evidenceFiles.push(join23(outlineDir, "outline-recommendation.json"));
|
|
52433
53053
|
const result = await itcDecision({
|
|
52434
53054
|
parentRunId,
|
|
52435
53055
|
decisionId: "kk-itc-outline-choice",
|
|
52436
53056
|
candidates,
|
|
52437
53057
|
evidenceFiles,
|
|
52438
|
-
outPath:
|
|
53058
|
+
outPath: join23(outlineDir, "outline-decision.json")
|
|
52439
53059
|
}, fs);
|
|
52440
53060
|
const pick = result.value === "defer" ? "" : result.value;
|
|
52441
53061
|
if (pick === "")
|
|
52442
53062
|
echoError(`itc-decision: outline choice deferred to the operator: ${result.reason}`);
|
|
52443
|
-
await fs.writeFile(
|
|
53063
|
+
await fs.writeFile(join23(outlineDir, ".auto-pick"), pick);
|
|
52444
53064
|
return 0;
|
|
52445
53065
|
} catch (err) {
|
|
52446
53066
|
echoError(`itc-decision: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -52453,7 +53073,7 @@ async function runItcAuthorComplete(args, fs) {
|
|
|
52453
53073
|
echoError("usage: itc-author-complete <work_dir> <parentRunId> [reviewMode]");
|
|
52454
53074
|
return 1;
|
|
52455
53075
|
}
|
|
52456
|
-
const draftPath =
|
|
53076
|
+
const draftPath = join23(workDir, "3-draft-draft-article.md");
|
|
52457
53077
|
if (!await fs.exists(draftPath)) {
|
|
52458
53078
|
echoError(`itc-author-complete: draft not found: ${draftPath}`);
|
|
52459
53079
|
return 1;
|
|
@@ -52464,7 +53084,7 @@ async function runItcAuthorComplete(args, fs) {
|
|
|
52464
53084
|
return 1;
|
|
52465
53085
|
}
|
|
52466
53086
|
const draftDigest = createHash7("sha256").update(draftText).digest("hex");
|
|
52467
|
-
const outlinePath =
|
|
53087
|
+
const outlinePath = join23(workDir, "2-outline/outline-approved.md");
|
|
52468
53088
|
if (!await fs.exists(outlinePath)) {
|
|
52469
53089
|
echoError(`itc-author-complete: approved outline not found: ${outlinePath}`);
|
|
52470
53090
|
return 1;
|
|
@@ -52475,7 +53095,7 @@ async function runItcAuthorComplete(args, fs) {
|
|
|
52475
53095
|
return 1;
|
|
52476
53096
|
}
|
|
52477
53097
|
const outlineDigest = createHash7("sha256").update(outlineText).digest("hex");
|
|
52478
|
-
const verdictPath =
|
|
53098
|
+
const verdictPath = join23(workDir, ".itc-verdict.json");
|
|
52479
53099
|
let judgeDigest = null;
|
|
52480
53100
|
let reviewMode = reviewModeArg === "manual" ? "manual" : "judged";
|
|
52481
53101
|
if (await fs.exists(verdictPath)) {
|
|
@@ -52500,7 +53120,7 @@ async function runItcAuthorComplete(args, fs) {
|
|
|
52500
53120
|
} else {
|
|
52501
53121
|
reviewMode = "manual";
|
|
52502
53122
|
}
|
|
52503
|
-
const runPath =
|
|
53123
|
+
const runPath = join23(workDir, ".itc/author-run.json");
|
|
52504
53124
|
let childRunId = `${parentRunId}-author`;
|
|
52505
53125
|
if (await fs.exists(runPath)) {
|
|
52506
53126
|
try {
|
|
@@ -52519,9 +53139,9 @@ async function runItcAuthorComplete(args, fs) {
|
|
|
52519
53139
|
reviewMode,
|
|
52520
53140
|
status: "complete"
|
|
52521
53141
|
};
|
|
52522
|
-
const itcDir =
|
|
53142
|
+
const itcDir = join23(workDir, ".itc");
|
|
52523
53143
|
await fs.ensureDir(itcDir);
|
|
52524
|
-
await fs.writeFile(
|
|
53144
|
+
await fs.writeFile(join23(itcDir, "author-complete.json"), `${JSON.stringify(completeReceipt, null, 2)}
|
|
52525
53145
|
`);
|
|
52526
53146
|
return 0;
|
|
52527
53147
|
}
|
|
@@ -52559,7 +53179,7 @@ async function runItcPrepare(args, fs) {
|
|
|
52559
53179
|
echoError("itc-prepare: --chosen applies only to a --from-collect launch");
|
|
52560
53180
|
return 1;
|
|
52561
53181
|
}
|
|
52562
|
-
const chosenPath =
|
|
53182
|
+
const chosenPath = join23(intakeDir, "chosen-source.json");
|
|
52563
53183
|
if (!await fs.exists(chosenPath)) {
|
|
52564
53184
|
echoError(`itc-prepare: chosen source not found: ${chosenPath}`);
|
|
52565
53185
|
return 1;
|
|
@@ -52595,9 +53215,9 @@ async function runItcPrepare(args, fs) {
|
|
|
52595
53215
|
["topic-pool-manifest.json", "topic-pool-manifest.json"]
|
|
52596
53216
|
];
|
|
52597
53217
|
for (const [from, to] of copies) {
|
|
52598
|
-
const src =
|
|
53218
|
+
const src = join23(intakeDir, from);
|
|
52599
53219
|
if (await fs.exists(src))
|
|
52600
|
-
await fs.writeFile(
|
|
53220
|
+
await fs.writeFile(join23(workDir, to), await fs.readFile(src));
|
|
52601
53221
|
}
|
|
52602
53222
|
}
|
|
52603
53223
|
} catch (err) {
|
|
@@ -52613,7 +53233,7 @@ init_dist3();
|
|
|
52613
53233
|
|
|
52614
53234
|
// src/publish-matrix.ts
|
|
52615
53235
|
import { createHash as createHash8 } from "crypto";
|
|
52616
|
-
import { dirname as dirname13, join as
|
|
53236
|
+
import { dirname as dirname13, join as join24, resolve as resolve22 } from "path";
|
|
52617
53237
|
init_dist3();
|
|
52618
53238
|
init_dist();
|
|
52619
53239
|
init_zod();
|
|
@@ -52750,7 +53370,7 @@ function listValue(args, flag) {
|
|
|
52750
53370
|
}
|
|
52751
53371
|
async function appendEvent(fs, publishDir, event) {
|
|
52752
53372
|
await fs.ensureDir(publishDir);
|
|
52753
|
-
const path =
|
|
53373
|
+
const path = join24(publishDir, "publish-events.jsonl");
|
|
52754
53374
|
const prior = await fs.exists(path) ? await fs.readFile(path) : "";
|
|
52755
53375
|
await fs.writeFile(path, `${prior}${JSON.stringify({ at: new Date().toISOString(), ...event })}
|
|
52756
53376
|
`);
|
|
@@ -52778,7 +53398,7 @@ async function runPublishMatrixValidate(args, deps = {}) {
|
|
|
52778
53398
|
let candidate = raw === undefined || raw.trim() === "" ? undefined : raw;
|
|
52779
53399
|
if (candidate === undefined) {
|
|
52780
53400
|
source = "bundle.json";
|
|
52781
|
-
const bundlePath =
|
|
53401
|
+
const bundlePath = join24(workDir, "bundle.json");
|
|
52782
53402
|
if (!await fs.exists(bundlePath)) {
|
|
52783
53403
|
echoError(`publish-matrix-validate: no --run-date and no bundle at ${bundlePath} to derive one from`);
|
|
52784
53404
|
return 1;
|
|
@@ -52799,8 +53419,8 @@ async function runPublishMatrixValidate(args, deps = {}) {
|
|
|
52799
53419
|
echoError(`publish-matrix-validate: run date '${candidate}' (from ${source}) is not a valid calendar day (YYYY-MM-DD or YYYYMMDD)`);
|
|
52800
53420
|
return 1;
|
|
52801
53421
|
}
|
|
52802
|
-
await fs.ensureDir(
|
|
52803
|
-
await fs.writeFile(
|
|
53422
|
+
await fs.ensureDir(join24(workDir, "4-publish"));
|
|
53423
|
+
await fs.writeFile(join24(workDir, "4-publish", "run-date.txt"), `${normalized}
|
|
52804
53424
|
`);
|
|
52805
53425
|
echo(`run date ${normalized} (from ${source})`);
|
|
52806
53426
|
return 0;
|
|
@@ -52810,7 +53430,7 @@ var LINK_SOURCES = [
|
|
|
52810
53430
|
{ placeholder: "{{podcast_url}}", plugin: "podcast-pub", receipt: "podcast" }
|
|
52811
53431
|
];
|
|
52812
53432
|
async function readPlan(fs, workDir, window) {
|
|
52813
|
-
const planPath =
|
|
53433
|
+
const planPath = join24(workDir, "4-publish", `plan.${window}.json`);
|
|
52814
53434
|
if (!await fs.exists(planPath)) {
|
|
52815
53435
|
throw new Error(`no plan at ${planPath}; run publish-matrix-plan first`);
|
|
52816
53436
|
}
|
|
@@ -52981,8 +53601,8 @@ async function composePlan(fs, input) {
|
|
|
52981
53601
|
variantPath: entry.path,
|
|
52982
53602
|
variantDigest: entry.sha256
|
|
52983
53603
|
});
|
|
52984
|
-
await fs.ensureDir(
|
|
52985
|
-
await fs.writeFile(
|
|
53604
|
+
await fs.ensureDir(join24(input.workDir, "4-publish", "payloads"));
|
|
53605
|
+
await fs.writeFile(join24(input.workDir, payloadRelative), payloadText);
|
|
52986
53606
|
}
|
|
52987
53607
|
return {
|
|
52988
53608
|
plan: {
|
|
@@ -53033,11 +53653,11 @@ async function planPublishMatrix(args, deps = {}) {
|
|
|
53033
53653
|
dummy,
|
|
53034
53654
|
...deps.projectRoot ? { projectRoot: deps.projectRoot } : {}
|
|
53035
53655
|
});
|
|
53036
|
-
const publishDir =
|
|
53656
|
+
const publishDir = join24(workDir, "4-publish");
|
|
53037
53657
|
await fs.ensureDir(publishDir);
|
|
53038
53658
|
const planText = `${JSON.stringify(plan, null, 2)}
|
|
53039
53659
|
`;
|
|
53040
|
-
await fs.writeFile(
|
|
53660
|
+
await fs.writeFile(join24(publishDir, `plan.${plan.window}.json`), planText);
|
|
53041
53661
|
const planDigest = createHash8("sha256").update(planText).digest("hex");
|
|
53042
53662
|
for (const target of omitted) {
|
|
53043
53663
|
const receipt = {
|
|
@@ -53055,8 +53675,8 @@ async function planPublishMatrix(args, deps = {}) {
|
|
|
53055
53675
|
window: plan.window,
|
|
53056
53676
|
...target.mode === "dummy" ? { dummy: true } : {}
|
|
53057
53677
|
};
|
|
53058
|
-
await fs.ensureDir(
|
|
53059
|
-
await fs.writeFile(
|
|
53678
|
+
await fs.ensureDir(join24(publishDir, "receipts"));
|
|
53679
|
+
await fs.writeFile(join24(publishDir, "receipts", `${target.id}.json`), `${JSON.stringify(receipt, null, 2)}
|
|
53060
53680
|
`);
|
|
53061
53681
|
await appendEvent(fs, publishDir, {
|
|
53062
53682
|
event: "omitted",
|
|
@@ -53138,9 +53758,9 @@ async function checkPublishMatrix(args, deps = {}) {
|
|
|
53138
53758
|
echoError(`publish-matrix-check: channel probe for '${name}' refused: ${probe.detail ?? "unknown"}`);
|
|
53139
53759
|
}
|
|
53140
53760
|
}
|
|
53141
|
-
const publishDir =
|
|
53761
|
+
const publishDir = join24(workDir, "4-publish");
|
|
53142
53762
|
await fs.ensureDir(publishDir);
|
|
53143
|
-
await fs.writeFile(
|
|
53763
|
+
await fs.writeFile(join24(publishDir, outFile), `${JSON.stringify({ schemaVersion: 1, window: label, plugins: probes }, null, 2)}
|
|
53144
53764
|
`);
|
|
53145
53765
|
echo(names.length === 0 ? "no probes" : `${probes.length} probe(s) recorded for ${label}`);
|
|
53146
53766
|
return 0;
|
|
@@ -53168,7 +53788,7 @@ async function runPublishMatrixClassify(args, deps = {}) {
|
|
|
53168
53788
|
echoError(`publish-matrix-classify: unknown window '${windowArg}'`);
|
|
53169
53789
|
return 1;
|
|
53170
53790
|
}
|
|
53171
|
-
const publishDir =
|
|
53791
|
+
const publishDir = join24(workDir, "4-publish");
|
|
53172
53792
|
let plan;
|
|
53173
53793
|
try {
|
|
53174
53794
|
plan = await readPlan(fs, workDir, parsedWindow.data);
|
|
@@ -53183,7 +53803,7 @@ async function runPublishMatrixClassify(args, deps = {}) {
|
|
|
53183
53803
|
echoError(`publish-matrix-classify: ${err instanceof Error ? err.message : String(err)}`);
|
|
53184
53804
|
await fs.ensureDir(publishDir);
|
|
53185
53805
|
const noneDate = runDate ?? await (async () => {
|
|
53186
|
-
const stampPath =
|
|
53806
|
+
const stampPath = join24(publishDir, "run-date.txt");
|
|
53187
53807
|
if (!await fs.exists(stampPath))
|
|
53188
53808
|
return;
|
|
53189
53809
|
return normalizeRunDate((await fs.readFile(stampPath)).trim());
|
|
@@ -53198,16 +53818,16 @@ async function runPublishMatrixClassify(args, deps = {}) {
|
|
|
53198
53818
|
excluded: [],
|
|
53199
53819
|
rows: []
|
|
53200
53820
|
};
|
|
53201
|
-
await fs.writeFile(
|
|
53821
|
+
await fs.writeFile(join24(publishDir, `outcome.${parsedWindow.data}.json`), `${JSON.stringify(none, null, 2)}
|
|
53202
53822
|
`);
|
|
53203
|
-
await fs.writeFile(
|
|
53823
|
+
await fs.writeFile(join24(publishDir, `publish-status.${parsedWindow.data}.txt`), `none
|
|
53204
53824
|
`);
|
|
53205
|
-
await fs.writeFile(
|
|
53825
|
+
await fs.writeFile(join24(workDir, "publish-classify.summary.txt"), `${JSON.stringify({ status: "none", published: 0, targets: 0 })}
|
|
53206
53826
|
`);
|
|
53207
53827
|
return 0;
|
|
53208
53828
|
}
|
|
53209
53829
|
if (runDate === undefined) {
|
|
53210
|
-
const stampPath =
|
|
53830
|
+
const stampPath = join24(publishDir, "run-date.txt");
|
|
53211
53831
|
const stamp = await fs.exists(stampPath) ? normalizeRunDate((await fs.readFile(stampPath)).trim()) : undefined;
|
|
53212
53832
|
if (stamp === undefined) {
|
|
53213
53833
|
echoError("publish-matrix-classify: plan has no runDate and no usable --run-date/run-date.txt was provided");
|
|
@@ -53215,27 +53835,27 @@ async function runPublishMatrixClassify(args, deps = {}) {
|
|
|
53215
53835
|
}
|
|
53216
53836
|
runDate = stamp;
|
|
53217
53837
|
}
|
|
53218
|
-
const receiptsDir =
|
|
53838
|
+
const receiptsDir = join24(publishDir, "receipts");
|
|
53219
53839
|
const receipts = [];
|
|
53220
53840
|
if (await fs.exists(receiptsDir)) {
|
|
53221
53841
|
for (const name of await fs.readDir(receiptsDir)) {
|
|
53222
53842
|
if (!name.endsWith(".json"))
|
|
53223
53843
|
continue;
|
|
53224
53844
|
try {
|
|
53225
|
-
receipts.push(JSON.parse(await fs.readFile(
|
|
53845
|
+
receipts.push(JSON.parse(await fs.readFile(join24(receiptsDir, name))));
|
|
53226
53846
|
} catch {}
|
|
53227
53847
|
}
|
|
53228
53848
|
}
|
|
53229
53849
|
const outcome = classifyPublishMatrix(plan, receipts, runDate);
|
|
53230
53850
|
await fs.ensureDir(publishDir);
|
|
53231
|
-
await fs.writeFile(
|
|
53851
|
+
await fs.writeFile(join24(publishDir, `outcome.${outcome.window}.json`), `${JSON.stringify(outcome, null, 2)}
|
|
53232
53852
|
`);
|
|
53233
|
-
await fs.writeFile(
|
|
53853
|
+
await fs.writeFile(join24(publishDir, `publish-status.${outcome.window}.txt`), `${outcome.status}
|
|
53234
53854
|
`);
|
|
53235
|
-
await fs.writeFile(
|
|
53855
|
+
await fs.writeFile(join24(workDir, "publish-classify.summary.txt"), `${JSON.stringify({ status: outcome.status, published: outcome.published, targets: outcome.targets })}
|
|
53236
53856
|
`);
|
|
53237
53857
|
if (outcome.window === "after-generate") {
|
|
53238
|
-
const legacyDir =
|
|
53858
|
+
const legacyDir = join24(workDir, "31-publish");
|
|
53239
53859
|
for (const source of LEGACY_RESULT_FILES) {
|
|
53240
53860
|
const row = plan.rows.find((candidate) => candidate.plugin === source.plugin && candidate.tier === 0);
|
|
53241
53861
|
if (row === undefined || row.mode === "dummy")
|
|
@@ -53247,7 +53867,7 @@ async function runPublishMatrixClassify(args, deps = {}) {
|
|
|
53247
53867
|
if (!await fs.exists(aggregate))
|
|
53248
53868
|
continue;
|
|
53249
53869
|
await fs.ensureDir(legacyDir);
|
|
53250
|
-
await fs.writeFile(
|
|
53870
|
+
await fs.writeFile(join24(legacyDir, `${runDate.replaceAll("-", "")}${source.file}`), await fs.readFile(aggregate));
|
|
53251
53871
|
}
|
|
53252
53872
|
}
|
|
53253
53873
|
echo(outcome.status);
|
|
@@ -53269,7 +53889,7 @@ async function runPublishMatrixSettle(args, deps = {}) {
|
|
|
53269
53889
|
echoError(`publish-matrix-settle: unknown window '${windowArg}'`);
|
|
53270
53890
|
return 1;
|
|
53271
53891
|
}
|
|
53272
|
-
const publishDir =
|
|
53892
|
+
const publishDir = join24(workDir, "4-publish");
|
|
53273
53893
|
let plan;
|
|
53274
53894
|
try {
|
|
53275
53895
|
plan = await readPlan(fs, workDir, parsedWindow.data);
|
|
@@ -53278,9 +53898,9 @@ async function runPublishMatrixSettle(args, deps = {}) {
|
|
|
53278
53898
|
return 1;
|
|
53279
53899
|
}
|
|
53280
53900
|
const parentRunId = plan.parentRunId;
|
|
53281
|
-
const receiptsDir =
|
|
53901
|
+
const receiptsDir = join24(publishDir, "receipts");
|
|
53282
53902
|
const receiptFor = async (id) => {
|
|
53283
|
-
const path =
|
|
53903
|
+
const path = join24(receiptsDir, `${id}.json`);
|
|
53284
53904
|
if (!await fs.exists(path))
|
|
53285
53905
|
return;
|
|
53286
53906
|
try {
|
|
@@ -53332,7 +53952,7 @@ async function runPublishMatrixSettle(args, deps = {}) {
|
|
|
53332
53952
|
unresolved
|
|
53333
53953
|
};
|
|
53334
53954
|
await fs.ensureDir(publishDir);
|
|
53335
|
-
const settlementPath =
|
|
53955
|
+
const settlementPath = join24(publishDir, `settlement.${parsedWindow.data}.json`);
|
|
53336
53956
|
if (unresolved.length > 0) {
|
|
53337
53957
|
await fs.writeFile(settlementPath, `${JSON.stringify(settlement, null, 2)}
|
|
53338
53958
|
`);
|
|
@@ -53398,7 +54018,7 @@ async function writeDummyReceipt(fs, row, context) {
|
|
|
53398
54018
|
};
|
|
53399
54019
|
}
|
|
53400
54020
|
async function readSucceeded(fs, receiptsDir, id) {
|
|
53401
|
-
const path =
|
|
54021
|
+
const path = join24(receiptsDir, `${id}.json`);
|
|
53402
54022
|
if (!await fs.exists(path))
|
|
53403
54023
|
return false;
|
|
53404
54024
|
try {
|
|
@@ -53415,7 +54035,7 @@ async function readLinkMap(fs, publishDir, plan) {
|
|
|
53415
54035
|
const planPaths = (await fs.exists(publishDir) ? await fs.readDir(publishDir) : []).filter((name) => /^plan\..*\.json$/.test(name));
|
|
53416
54036
|
for (const name of planPaths) {
|
|
53417
54037
|
try {
|
|
53418
|
-
const candidate = JSON.parse(await fs.readFile(
|
|
54038
|
+
const candidate = JSON.parse(await fs.readFile(join24(publishDir, name)));
|
|
53419
54039
|
if (candidate.parentRunId !== plan.parentRunId)
|
|
53420
54040
|
continue;
|
|
53421
54041
|
for (const row of candidate.rows) {
|
|
@@ -53428,7 +54048,7 @@ async function readLinkMap(fs, publishDir, plan) {
|
|
|
53428
54048
|
const entry = tierZero.find((candidate) => candidate.plugin === source.plugin);
|
|
53429
54049
|
if (entry === undefined)
|
|
53430
54050
|
continue;
|
|
53431
|
-
const path =
|
|
54051
|
+
const path = join24(publishDir, "receipts", `${entry.rowId}.json`);
|
|
53432
54052
|
if (!await fs.exists(path))
|
|
53433
54053
|
continue;
|
|
53434
54054
|
try {
|
|
@@ -53471,17 +54091,17 @@ async function sendPublishMatrix(args, deps = {}) {
|
|
|
53471
54091
|
}
|
|
53472
54092
|
try {
|
|
53473
54093
|
const plan = await readPlan(fs, workDir, parsedWindow.data);
|
|
53474
|
-
const publishDir =
|
|
53475
|
-
const receiptsDir =
|
|
54094
|
+
const publishDir = join24(workDir, "4-publish");
|
|
54095
|
+
const receiptsDir = join24(publishDir, "receipts");
|
|
53476
54096
|
await fs.ensureDir(receiptsDir);
|
|
53477
|
-
const planDigest = createHash8("sha256").update(await fs.readFile(
|
|
54097
|
+
const planDigest = createHash8("sha256").update(await fs.readFile(join24(publishDir, `plan.${plan.window}.json`))).digest("hex");
|
|
53478
54098
|
const succeededAlready = (id) => readSucceeded(fs, receiptsDir, id);
|
|
53479
54099
|
const tiers = [...new Set(plan.rows.map((row) => row.tier))].sort((a, b) => a - b);
|
|
53480
54100
|
const pending = onlyTier === undefined ? plan.rows : plan.rows.filter((row) => row.tier === onlyTier);
|
|
53481
54101
|
for (const row of pending) {
|
|
53482
54102
|
if (row.mode === "dummy")
|
|
53483
54103
|
continue;
|
|
53484
|
-
const payloadPath =
|
|
54104
|
+
const payloadPath = join24(workDir, row.payloadPath);
|
|
53485
54105
|
const payloadRaw = await fs.exists(payloadPath) ? await fs.readFile(payloadPath) : null;
|
|
53486
54106
|
if (payloadRaw === null) {
|
|
53487
54107
|
throw new Error(`payload missing for '${row.id}': ${payloadPath}; re-run publish-matrix-plan`);
|
|
@@ -53491,7 +54111,7 @@ async function sendPublishMatrix(args, deps = {}) {
|
|
|
53491
54111
|
throw new Error(`payload for '${row.id}' changed after the plan was frozen (reviewed ${row.payloadDigest}, on disk ${payloadActual}); re-run publish-matrix-plan`);
|
|
53492
54112
|
}
|
|
53493
54113
|
if (row.variantPath !== undefined && row.variantDigest !== undefined) {
|
|
53494
|
-
const variantPath =
|
|
54114
|
+
const variantPath = join24(workDir, row.variantPath);
|
|
53495
54115
|
const variantRaw = await fs.exists(variantPath) ? await fs.readFile(variantPath) : null;
|
|
53496
54116
|
if (variantRaw === null) {
|
|
53497
54117
|
throw new Error(`variant source missing for '${row.id}': ${variantPath}; re-run publish-matrix-plan`);
|
|
@@ -53525,7 +54145,7 @@ async function sendPublishMatrix(args, deps = {}) {
|
|
|
53525
54145
|
plugin: row.plugin,
|
|
53526
54146
|
profile: row.profile ?? "article",
|
|
53527
54147
|
mode: row.mode,
|
|
53528
|
-
payloadPath:
|
|
54148
|
+
payloadPath: join24(workDir, row.payloadPath),
|
|
53529
54149
|
transport: "fanout",
|
|
53530
54150
|
receiptExtras: { target: row.id, window: plan.window }
|
|
53531
54151
|
}));
|
|
@@ -53555,7 +54175,7 @@ async function sendPublishMatrix(args, deps = {}) {
|
|
|
53555
54175
|
if (LINK_SOURCES.some((source) => resolved.includes(source.placeholder))) {
|
|
53556
54176
|
return { error: "unresolved-link" };
|
|
53557
54177
|
}
|
|
53558
|
-
const resolvedPath =
|
|
54178
|
+
const resolvedPath = join24(workDir, "4-publish", "payloads", `content.${row.key}.resolved.json`);
|
|
53559
54179
|
await fs.writeFile(resolvedPath, resolved);
|
|
53560
54180
|
return { payloadPath: resolvedPath };
|
|
53561
54181
|
},
|
|
@@ -53627,16 +54247,16 @@ async function runPublishMatrixRssGate(args, deps = {}) {
|
|
|
53627
54247
|
echoError("usage: publish-matrix-rss-gate <work_dir> [--date <YYYY-MM-DD|YYYYMMDD>] [--feed <url>]");
|
|
53628
54248
|
return 1;
|
|
53629
54249
|
}
|
|
53630
|
-
const publishDir =
|
|
54250
|
+
const publishDir = join24(workDir, "4-publish");
|
|
53631
54251
|
let date = dateRaw !== undefined && dateRaw.trim() !== "" ? normalizeRunDate(dateRaw) : undefined;
|
|
53632
54252
|
if (dateRaw !== undefined && dateRaw.trim() !== "" && date === undefined) {
|
|
53633
54253
|
echoError(`publish-matrix-rss-gate: '${dateRaw}' is not a valid calendar day (YYYY-MM-DD or YYYYMMDD)`);
|
|
53634
54254
|
return 1;
|
|
53635
54255
|
}
|
|
53636
54256
|
let guid = "";
|
|
53637
|
-
const receiptsDir =
|
|
54257
|
+
const receiptsDir = join24(publishDir, "receipts");
|
|
53638
54258
|
for (const planWindow of ["after-generate", "cn-peak"]) {
|
|
53639
|
-
const planPath =
|
|
54259
|
+
const planPath = join24(publishDir, `plan.${planWindow}.json`);
|
|
53640
54260
|
if (!await fs.exists(planPath))
|
|
53641
54261
|
continue;
|
|
53642
54262
|
const plan = JSON.parse(await fs.readFile(planPath));
|
|
@@ -53652,7 +54272,7 @@ async function runPublishMatrixRssGate(args, deps = {}) {
|
|
|
53652
54272
|
for (const row of plan.rows) {
|
|
53653
54273
|
if (row.plugin !== "podcast-pub")
|
|
53654
54274
|
continue;
|
|
53655
|
-
const receiptPath =
|
|
54275
|
+
const receiptPath = join24(receiptsDir, `${row.id}.json`);
|
|
53656
54276
|
if (!await fs.exists(receiptPath))
|
|
53657
54277
|
continue;
|
|
53658
54278
|
const receipt = JSON.parse(await fs.readFile(receiptPath));
|
|
@@ -53661,7 +54281,7 @@ async function runPublishMatrixRssGate(args, deps = {}) {
|
|
|
53661
54281
|
}
|
|
53662
54282
|
}
|
|
53663
54283
|
if (date === undefined) {
|
|
53664
|
-
const stampPath =
|
|
54284
|
+
const stampPath = join24(publishDir, "run-date.txt");
|
|
53665
54285
|
const stamp = await fs.exists(stampPath) ? normalizeRunDate((await fs.readFile(stampPath)).trim()) : undefined;
|
|
53666
54286
|
if (stamp === undefined) {
|
|
53667
54287
|
echoError("publish-matrix-rss-gate: no usable --date and no run-date.txt/plan.runDate to derive one from");
|
|
@@ -53671,7 +54291,7 @@ async function runPublishMatrixRssGate(args, deps = {}) {
|
|
|
53671
54291
|
}
|
|
53672
54292
|
const write = async (result) => {
|
|
53673
54293
|
await fs.ensureDir(publishDir);
|
|
53674
|
-
await fs.writeFile(
|
|
54294
|
+
await fs.writeFile(join24(publishDir, "rss-gate.json"), `${JSON.stringify(result, null, 2)}
|
|
53675
54295
|
`);
|
|
53676
54296
|
await appendEvent(fs, publishDir, {
|
|
53677
54297
|
event: result.hit ? "gate-hit" : "gate-miss",
|
|
@@ -53687,9 +54307,9 @@ async function runPublishMatrixRssGate(args, deps = {}) {
|
|
|
53687
54307
|
reason: `feed ${result.feedUrl} has no episode for ${result.date} (${result.reason})`,
|
|
53688
54308
|
evidence: ["4-publish/rss-gate.json", ...result.detail === undefined ? [] : [result.detail]]
|
|
53689
54309
|
};
|
|
53690
|
-
await fs.writeFile(
|
|
54310
|
+
await fs.writeFile(join24(publishDir, "incident.json"), `${JSON.stringify(incident, null, 2)}
|
|
53691
54311
|
`);
|
|
53692
|
-
await fs.writeFile(
|
|
54312
|
+
await fs.writeFile(join24(publishDir, "incident.md"), [
|
|
53693
54313
|
`# Overseas window missed \u2014 ${result.date}`,
|
|
53694
54314
|
"",
|
|
53695
54315
|
`The podcast feed \`${result.feedUrl}\` does not carry the ${result.date} episode (${result.reason}).`,
|
|
@@ -53828,7 +54448,7 @@ async function runPublishMatrixBundle(args, deps = {}) {
|
|
|
53828
54448
|
echoError("usage: publish-matrix-bundle <work_dir> --run-date <YYYY-MM-DD> --run-id <voice3 run id> [--preset <slug>]");
|
|
53829
54449
|
return 1;
|
|
53830
54450
|
}
|
|
53831
|
-
const variantsDir =
|
|
54451
|
+
const variantsDir = join24(workDir, "30-variants");
|
|
53832
54452
|
if (!await fs.exists(variantsDir)) {
|
|
53833
54453
|
echoError(`publish-matrix-bundle: no variants at ${variantsDir}; run daily-bundle-variants first`);
|
|
53834
54454
|
return 1;
|
|
@@ -53840,7 +54460,7 @@ async function runPublishMatrixBundle(args, deps = {}) {
|
|
|
53840
54460
|
const variants = {};
|
|
53841
54461
|
for (const file of entries) {
|
|
53842
54462
|
const key = file.replace(/\.content\.json$/, "").replaceAll("_", ":");
|
|
53843
|
-
const raw = await fs.readFile(
|
|
54463
|
+
const raw = await fs.readFile(join24(variantsDir, file));
|
|
53844
54464
|
const parsed = ContentSchema.safeParse(JSON.parse(raw));
|
|
53845
54465
|
if (!parsed.success) {
|
|
53846
54466
|
throw new Error(`variant '${key}' is not valid Content: ${parsed.error.message}`);
|
|
@@ -53864,7 +54484,7 @@ async function runPublishMatrixBundle(args, deps = {}) {
|
|
|
53864
54484
|
}
|
|
53865
54485
|
}
|
|
53866
54486
|
const articleTitle = await readVariantTitle(fs, workDir, variants);
|
|
53867
|
-
const safetyPath =
|
|
54487
|
+
const safetyPath = join24(workDir, "4-safety-variants", "safety.json");
|
|
53868
54488
|
let safety;
|
|
53869
54489
|
if (await fs.exists(safetyPath)) {
|
|
53870
54490
|
const safetyParsed = BundleSafetySchema.safeParse(JSON.parse(await fs.readFile(safetyPath)));
|
|
@@ -53885,7 +54505,7 @@ async function runPublishMatrixBundle(args, deps = {}) {
|
|
|
53885
54505
|
...safety !== undefined ? { safety } : {}
|
|
53886
54506
|
};
|
|
53887
54507
|
const validated = BundleSchema.parse(bundle);
|
|
53888
|
-
const bundlePath =
|
|
54508
|
+
const bundlePath = join24(workDir, "bundle.json");
|
|
53889
54509
|
if (await fs.exists(bundlePath)) {
|
|
53890
54510
|
try {
|
|
53891
54511
|
const prior = BundleSchema.parse(JSON.parse(await fs.readFile(bundlePath)));
|
|
@@ -53912,7 +54532,7 @@ async function readVariantTitle(fs, workDir, variants) {
|
|
|
53912
54532
|
if (entry === undefined)
|
|
53913
54533
|
continue;
|
|
53914
54534
|
try {
|
|
53915
|
-
const parsed = ContentSchema.parse(JSON.parse(await fs.readFile(
|
|
54535
|
+
const parsed = ContentSchema.parse(JSON.parse(await fs.readFile(join24(workDir, entry.path))));
|
|
53916
54536
|
if (parsed.title !== undefined && parsed.title.trim() !== "")
|
|
53917
54537
|
return parsed.title.trim();
|
|
53918
54538
|
} catch {}
|
|
@@ -53982,7 +54602,7 @@ function registerStageCommand(program) {
|
|
|
53982
54602
|
}
|
|
53983
54603
|
|
|
53984
54604
|
// src/commands/store.ts
|
|
53985
|
-
import { dirname as dirname14, join as
|
|
54605
|
+
import { dirname as dirname14, join as join26, resolve as resolve24 } from "path";
|
|
53986
54606
|
init_dist3();
|
|
53987
54607
|
init_commander();
|
|
53988
54608
|
|
|
@@ -53990,7 +54610,7 @@ init_commander();
|
|
|
53990
54610
|
init_dist3();
|
|
53991
54611
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
53992
54612
|
import { tmpdir as tmpdir3 } from "os";
|
|
53993
|
-
import { join as
|
|
54613
|
+
import { join as join25, resolve as resolve23 } from "path";
|
|
53994
54614
|
var STORE_CATALOG_PATH_REL = "plugins/kk/decisions/kk-items.yaml";
|
|
53995
54615
|
var SPUR_CONFIG_PATH_REL = ".spur/config.yaml";
|
|
53996
54616
|
var EVIDENCE_MAX_CHARS = 2000;
|
|
@@ -54019,7 +54639,7 @@ async function loadInjectedHub() {
|
|
|
54019
54639
|
}
|
|
54020
54640
|
async function readDecisionRouting(projectRoot, fs = createNodeFileSystem()) {
|
|
54021
54641
|
const catalogPath = await resolveCatalogPath(projectRoot, fs);
|
|
54022
|
-
const configPath =
|
|
54642
|
+
const configPath = join25(projectRoot, SPUR_CONFIG_PATH_REL);
|
|
54023
54643
|
if (!await fs.exists(configPath))
|
|
54024
54644
|
return { catalogPath, routing: { makers: {} } };
|
|
54025
54645
|
let parsed;
|
|
@@ -54103,8 +54723,8 @@ function createBufferedDecisionFetch(inner = fetch) {
|
|
|
54103
54723
|
var bufferedDecisionFetch = createBufferedDecisionFetch();
|
|
54104
54724
|
function runDecisionSubprocess(decisionId, input, maker, fs = createNodeFileSystem()) {
|
|
54105
54725
|
const command = process.env.KK_SPUR_BIN ?? "spur";
|
|
54106
|
-
const scratch =
|
|
54107
|
-
const evidencePath =
|
|
54726
|
+
const scratch = join25(tmpdir3(), `kk-decision-${randomUUID2()}`);
|
|
54727
|
+
const evidencePath = join25(scratch, "evidence.md");
|
|
54108
54728
|
try {
|
|
54109
54729
|
fs.writeFile(evidencePath, input.body);
|
|
54110
54730
|
const args = [
|
|
@@ -54340,8 +54960,8 @@ async function readAnnotations(fs, outputPath) {
|
|
|
54340
54960
|
async function heuristicAnnotations(fs, scratch, runId, items) {
|
|
54341
54961
|
if (items.length === 0)
|
|
54342
54962
|
return new Map;
|
|
54343
|
-
const inputPath =
|
|
54344
|
-
const outputPath =
|
|
54963
|
+
const inputPath = join26(scratch, "label-input.json");
|
|
54964
|
+
const outputPath = join26(scratch, "label-out.json");
|
|
54345
54965
|
await fs.ensureDir(scratch);
|
|
54346
54966
|
const docs = items.map((item) => ({
|
|
54347
54967
|
id: item.id,
|
|
@@ -54692,7 +55312,7 @@ async function labelAction(options) {
|
|
|
54692
55312
|
const mode = parseLabelMode(options.mode);
|
|
54693
55313
|
const configPath = resolveKkConfigPath();
|
|
54694
55314
|
const dbPath = await resolveStorePath(configPath, options.db);
|
|
54695
|
-
const scratch =
|
|
55315
|
+
const scratch = join26(dirname14(plan.kind === "new-ids" ? plan.newIds : dbPath), LABEL_SCRATCH_DIR);
|
|
54696
55316
|
const store = await openStore({ path: dbPath });
|
|
54697
55317
|
const catalogVersion = await readCatalogVersion(fs);
|
|
54698
55318
|
const labels = (await readStoreConfig(configPath)).labels;
|