@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.
Files changed (76) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/index.js +796 -176
  3. package/package.json +1 -1
  4. package/plugins/generations/content-gen/dist/index.js +2 -2
  5. package/plugins/generations/core-facts-gen/dist/index.js +2 -2
  6. package/plugins/generations/daily-article-gen/dist/index.js +2 -2
  7. package/plugins/generations/dailynews-gen/dist/index.js +72 -9
  8. package/plugins/generations/dailynews-gen/src/index.ts +23 -1
  9. package/plugins/generations/dailynews-gen/src/script-plan.ts +103 -5
  10. package/plugins/generations/episode-plan-gen/dist/index.js +2 -2
  11. package/plugins/generations/gemini-tts/dist/index.js +2 -2
  12. package/plugins/generations/image-gen/dist/index.js +2 -2
  13. package/plugins/generations/news-report-gen/dist/index.js +2 -2
  14. package/plugins/generations/voice-gen/dist/index.js +2 -2
  15. package/plugins/generations/voxcpm-tts/dist/index.js +2 -2
  16. package/plugins/ingestions/aihot-ingest/dist/index.js +2 -2
  17. package/plugins/ingestions/horizon-ingest/dist/index.js +2 -2
  18. package/plugins/ingestions/last30days-ingest/dist/index.js +2 -2
  19. package/plugins/ingestions/web-search/dist/index.js +2 -2
  20. package/plugins/kk/plugin.json +1 -1
  21. package/plugins/kk/scripts/daily-scheduled-run.ts +4 -0
  22. package/plugins/kk/scripts/daily-stages.ts +476 -12
  23. package/plugins/kk/scripts/kk-workflow-stages.ts +20 -4
  24. package/plugins/kk/skills/content-judge/SKILL.md +5 -4
  25. package/plugins/kk/skills/content-judge/references/rubrics.md +14 -0
  26. package/plugins/kk/skills/news-editorial/SKILL.md +122 -0
  27. package/plugins/kk/skills/news-editorial/references/selection.md +47 -0
  28. package/plugins/kk/skills/news-editorial/references/style-contract.zh.md +99 -0
  29. package/plugins/kk/skills/publish/SKILL.md +3 -3
  30. package/plugins/kk/skills/web-automation/SKILL.md +229 -77
  31. package/plugins/kk/skills/web-automation/references/browser-launch.md +127 -36
  32. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +4 -6
  33. package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +183 -222
  34. package/plugins/kk/workflows/kk-daily-publish3.yaml +16 -8
  35. package/plugins/publishings/bilibili-pub/dist/index.js +5 -3
  36. package/plugins/publishings/bilibili-pub/dist/profile-check.js +3 -1
  37. package/plugins/publishings/cnblogs-pub/dist/index.js +5 -3
  38. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +3 -1
  39. package/plugins/publishings/csdn-pub/dist/index.js +5 -3
  40. package/plugins/publishings/csdn-pub/dist/profile-check.js +5 -3
  41. package/plugins/publishings/emdash-pub/dist/index.js +2 -2
  42. package/plugins/publishings/infoq-pub/dist/index.js +5 -3
  43. package/plugins/publishings/infoq-pub/dist/profile-check.js +5 -3
  44. package/plugins/publishings/jike-pub/dist/index.js +5 -3
  45. package/plugins/publishings/jike-pub/dist/profile-check.js +5 -3
  46. package/plugins/publishings/juejin-pub/dist/index.js +5 -3
  47. package/plugins/publishings/juejin-pub/dist/profile-check.js +5 -3
  48. package/plugins/publishings/linkedin-pub/dist/index.js +5 -3
  49. package/plugins/publishings/linkedin-pub/dist/profile-check.js +5 -3
  50. package/plugins/publishings/medium-pub/dist/index.js +2 -2
  51. package/plugins/publishings/podcast-pub/dist/index.js +147 -4
  52. package/plugins/publishings/podcast-pub/src/index.ts +71 -2
  53. package/plugins/publishings/podcast-pub/src/map.ts +3 -0
  54. package/plugins/publishings/podcast-pub/src/transcript.ts +195 -0
  55. package/plugins/publishings/qiita-pub/dist/index.js +2 -2
  56. package/plugins/publishings/shipinhao-pub/dist/index.js +5 -3
  57. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +3 -1
  58. package/plugins/publishings/sspai-pub/dist/index.js +5 -3
  59. package/plugins/publishings/sspai-pub/dist/profile-check.js +3 -1
  60. package/plugins/publishings/substack-pub/dist/index.js +5 -3
  61. package/plugins/publishings/substack-pub/dist/profile-check.js +5 -3
  62. package/plugins/publishings/surfdash-pub/dist/index.js +2 -2
  63. package/plugins/publishings/threads-pub/dist/index.js +2 -2
  64. package/plugins/publishings/wechatmp-pub/dist/index.js +52 -33
  65. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +5 -3
  66. package/plugins/publishings/wechatmp-pub/src/flow.ts +70 -33
  67. package/plugins/publishings/weibo-pub/dist/index.js +5 -3
  68. package/plugins/publishings/weibo-pub/dist/profile-check.js +5 -3
  69. package/plugins/publishings/x-pub/dist/index.js +5 -3
  70. package/plugins/publishings/x-pub/dist/profile-check.js +5 -3
  71. package/plugins/publishings/xhs-pub/dist/index.js +5 -3
  72. package/plugins/publishings/xhs-pub/dist/profile-check.js +5 -3
  73. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +5 -3
  74. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +3 -1
  75. package/plugins/publishings/zenn-pub/dist/index.js +2 -2
  76. 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
- constructor(code, message) {
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.resolve().then(() => (init_browser(), exports_browser));
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 join14, resolve as resolve14 } from "path";
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 = join14(tmpdir(), `kk-fanin-${source.plugin}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
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 join15, resolve as resolve15 } from "path";
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 = join15(tmpdir2(), `kk-fanout-${target}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
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 join18 } from "path";
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 join17, resolve as resolve17 } from "path";
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 join16, resolve as resolve16 } from "path";
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(join16(dir, SHIPPED_DIR)))
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 = join17(await resolvePackageRoot(fs), "plugins/kk/decisions/extract", `${topic}.yaml`);
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(join17(request.intakeDir, "topic-pool.json"), `${JSON.stringify(docs, null, 2)}
50566
+ await fs.writeFile(join18(request.intakeDir, "topic-pool.json"), `${JSON.stringify(docs, null, 2)}
49947
50567
  `);
49948
- await fs.writeFile(join17(request.intakeDir, "topic-pool-manifest.json"), `${JSON.stringify(manifest, null, 2)}
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 = join17(request.intakeDir, "topic-pool-manifest.json");
49972
- const poolPath = join17(request.intakeDir, "topic-pool.json");
49973
- const candidatesPath = join17(request.intakeDir, "topic-candidates.json");
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(join17(request.intakeDir, ".topic-candidates-valid"), `${new Date().toISOString()}
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 = join17(request.intakeDir, "topic-pool-manifest.json");
50031
- const poolPath = join17(request.intakeDir, "topic-pool.json");
50032
- const candidatesPath = join17(request.intakeDir, "topic-candidates.json");
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 = join17(request.intakeDir, "selected-docs.json");
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(join17(request.intakeDir, "chosen-source.json"), `${JSON.stringify(chosenSource, null, 2)}
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 = join17(request.intakeDir, "chosen-source.json");
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(join17(request.intakeDir, ".selection-reconcile.json"), `${JSON.stringify(reconcileInfo, null, 2)}
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 ?? join18(process.cwd(), ".itc-intake", options.parentRunId);
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(join18(intakeDir, "topic-pool.json"))));
50225
- const manifest = JSON.parse(await fs.readFile(join18(intakeDir, "topic-pool-manifest.json")));
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(join18(intakeDir, "topic-candidates.json"), `${JSON.stringify({
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: [join18(intakeDir, "topic-candidates.json")],
50243
- outPath: join18(intakeDir, "topic-decision.json")
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 join22 } from "path";
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 join19, resolve as resolve20 } from "path";
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 join19(homeDir, value.slice(2));
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 join19(homeDir, ".config", "kk", "config.yaml");
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 ? join19(slug, options.runDate) : slug;
51128
- return { workDir: join19(options.worksRoot, "kk-itc", leaf), topicName: slug, dated: options.runDate !== null };
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 = join19(workDir, "itc-options.json");
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 = join19(workDir, "itc-options.json");
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 join20, resolve as resolve21 } from "path";
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(join20(dir, "plugins/kk/publish-presets")))
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 = join20(workDir, "4-illustrations");
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 = join20(workDir, "4-publish", "content.json");
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 join21 } from "path";
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 = join21(workDir, "4-illustrations");
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 ? join21(dir, cover) : undefined;
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 = join21(workDir, "3-draft-draft-article.md");
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 = join21(workDir, "2-outline", "outline-approved.md");
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 = join21(workDir, "docs.json");
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, join21(workDir, "brief.md"));
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 = join21(workDir, "4-publish");
52243
+ const publishDir = join22(workDir, "4-publish");
51624
52244
  await fs.ensureDir(publishDir);
51625
- await fs.writeFile(join21(publishDir, "content.json"), `${JSON.stringify(content, null, 2)}
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: join21(workDir, "4-publish", "preset-decision.json")
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(join21(publishDir, "publish-plan.json"), `${JSON.stringify(emptyPlan, null, 2)}
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 = join21(publishDir, "publish-plan.json");
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 = join21(workDir, "itc-options.json");
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 = join21(workDir, "4-publish");
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 = join21(publishDir, baseFile);
52340
+ let basePath = join22(publishDir, baseFile);
51721
52341
  if (!await fs.exists(basePath)) {
51722
- basePath = join21(publishDir, "content.json");
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 = join21(publishDir, targetFileName);
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(join21(publishDir, "publish-plan.json"), `${JSON.stringify(plan, null, 2)}
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 = join21(workDir, "4-publish/publish-plan.json");
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 = join21(receiptsDir, `${row.key}.json`);
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 = join21(publishDir, `fanout-result.${row.key}${retry ? ".retry" : ""}.json`);
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 = join21(workDir, "4-publish");
51999
- const planPath = join21(publishDir, "publish-plan.json");
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: ${join21(workDir, "itc-options.json")} has no parentRunId`);
52627
+ echoError(`itc-send-targets: ${join22(workDir, "itc-options.json")} has no parentRunId`);
52008
52628
  return 1;
52009
52629
  }
52010
- const receiptsDir = join21(publishDir, "receipts");
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: join21(workDir, target.payloadPath ?? `4-publish/content.${target.plugin}.json`),
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 = join21(workDir, "4-publish");
52044
- const planPath = join21(publishDir, "publish-plan.json");
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 = join21(workDir, "chosen-source.json");
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 = join21(publishDir, "receipts");
52685
+ const receiptsDir = join22(publishDir, "receipts");
52066
52686
  const receipts = [];
52067
52687
  for (const target of plan.targets) {
52068
- const rPath = join21(receiptsDir, `${target.plugin}.json`);
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 = join21(workDir, "seo");
52713
+ const seoDir = join22(workDir, "seo");
52094
52714
  await fs.ensureDir(seoDir);
52095
52715
  if (mode === "off") {
52096
- await fs.writeFile(join21(seoDir, "prepublish-request.json"), `${JSON.stringify({ schemaVersion: 1, mode: "off", status: "not-assessed" }, null, 2)}
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(join21(seoDir, "published-pages.json"), `${JSON.stringify({ schemaVersion: 1, mode: "off", pages: [], status: "not-assessed" }, null, 2)}
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(join21(seoDir, "prepublish-request.json"), `${JSON.stringify(prepublish, null, 2)}
52728
+ await fs.writeFile(join22(seoDir, "prepublish-request.json"), `${JSON.stringify(prepublish, null, 2)}
52109
52729
  `);
52110
- const receiptsDir = join21(workDir, "4-publish/receipts");
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(join21(receiptsDir, file)));
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(join21(seoDir, "published-pages.json"), `${JSON.stringify(publishedPages, null, 2)}
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 = join21(workDir, "5-report");
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 = join21(workDir, ".itc-verdict.json");
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 = join21(workDir, ".auto-revise-count");
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 = join21(workDir, "4-publish/receipts");
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(join21(receiptsDir, file)));
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 = join21(workDir, "4-publish/publish-plan.json");
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(join21(reportDir, "report.json"), `${JSON.stringify(reportJson, null, 2)}
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(join21(reportDir, "report.md"), `${md}
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 = join21(workDir, name);
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 = join21(workDir, ".itc-verdict.json");
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(join21(workDir, ".itc"));
52267
- await fs.writeFile(join21(workDir, ".itc/disposition.txt"), `${disposition}
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 = join22(request.workDir, "4-publish");
52984
+ const outDir = join23(request.workDir, "4-publish");
52365
52985
  await fs.ensureDir(outDir);
52366
- await fs.writeFile(join22(outDir, "publish-plan.json"), `${JSON.stringify(plan, null, 4)}
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 = join22(dir, "topic-candidates.json");
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: join22(dir, "topic-decision.json")
53033
+ outPath: join23(dir, "topic-decision.json")
52414
53034
  }, fs);
52415
53035
  echo(result.value);
52416
53036
  return 0;
52417
53037
  }
52418
- const outlineDir = join22(dir, "2-outline");
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 = join22(outlineDir, `outline-option-${id}.md`);
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(join22(outlineDir, "outline-recommendation.json"));
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: join22(outlineDir, "outline-decision.json")
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(join22(outlineDir, ".auto-pick"), pick);
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 = join22(workDir, "3-draft-draft-article.md");
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 = join22(workDir, "2-outline/outline-approved.md");
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 = join22(workDir, ".itc-verdict.json");
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 = join22(workDir, ".itc/author-run.json");
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 = join22(workDir, ".itc");
53142
+ const itcDir = join23(workDir, ".itc");
52523
53143
  await fs.ensureDir(itcDir);
52524
- await fs.writeFile(join22(itcDir, "author-complete.json"), `${JSON.stringify(completeReceipt, null, 2)}
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 = join22(intakeDir, "chosen-source.json");
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 = join22(intakeDir, from);
53218
+ const src = join23(intakeDir, from);
52599
53219
  if (await fs.exists(src))
52600
- await fs.writeFile(join22(workDir, to), await fs.readFile(src));
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 join23, resolve as resolve22 } from "path";
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 = join23(publishDir, "publish-events.jsonl");
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 = join23(workDir, "bundle.json");
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(join23(workDir, "4-publish"));
52803
- await fs.writeFile(join23(workDir, "4-publish", "run-date.txt"), `${normalized}
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 = join23(workDir, "4-publish", `plan.${window}.json`);
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(join23(input.workDir, "4-publish", "payloads"));
52985
- await fs.writeFile(join23(input.workDir, payloadRelative), payloadText);
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 = join23(workDir, "4-publish");
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(join23(publishDir, `plan.${plan.window}.json`), planText);
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(join23(publishDir, "receipts"));
53059
- await fs.writeFile(join23(publishDir, "receipts", `${target.id}.json`), `${JSON.stringify(receipt, null, 2)}
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 = join23(workDir, "4-publish");
53761
+ const publishDir = join24(workDir, "4-publish");
53142
53762
  await fs.ensureDir(publishDir);
53143
- await fs.writeFile(join23(publishDir, outFile), `${JSON.stringify({ schemaVersion: 1, window: label, plugins: probes }, null, 2)}
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 = join23(workDir, "4-publish");
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 = join23(publishDir, "run-date.txt");
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(join23(publishDir, `outcome.${parsedWindow.data}.json`), `${JSON.stringify(none, null, 2)}
53821
+ await fs.writeFile(join24(publishDir, `outcome.${parsedWindow.data}.json`), `${JSON.stringify(none, null, 2)}
53202
53822
  `);
53203
- await fs.writeFile(join23(publishDir, `publish-status.${parsedWindow.data}.txt`), `none
53823
+ await fs.writeFile(join24(publishDir, `publish-status.${parsedWindow.data}.txt`), `none
53204
53824
  `);
53205
- await fs.writeFile(join23(workDir, "publish-classify.summary.txt"), `${JSON.stringify({ status: "none", published: 0, targets: 0 })}
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 = join23(publishDir, "run-date.txt");
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 = join23(publishDir, "receipts");
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(join23(receiptsDir, name))));
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(join23(publishDir, `outcome.${outcome.window}.json`), `${JSON.stringify(outcome, null, 2)}
53851
+ await fs.writeFile(join24(publishDir, `outcome.${outcome.window}.json`), `${JSON.stringify(outcome, null, 2)}
53232
53852
  `);
53233
- await fs.writeFile(join23(publishDir, `publish-status.${outcome.window}.txt`), `${outcome.status}
53853
+ await fs.writeFile(join24(publishDir, `publish-status.${outcome.window}.txt`), `${outcome.status}
53234
53854
  `);
53235
- await fs.writeFile(join23(workDir, "publish-classify.summary.txt"), `${JSON.stringify({ status: outcome.status, published: outcome.published, targets: outcome.targets })}
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 = join23(workDir, "31-publish");
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(join23(legacyDir, `${runDate.replaceAll("-", "")}${source.file}`), await fs.readFile(aggregate));
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 = join23(workDir, "4-publish");
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 = join23(publishDir, "receipts");
53901
+ const receiptsDir = join24(publishDir, "receipts");
53282
53902
  const receiptFor = async (id) => {
53283
- const path = join23(receiptsDir, `${id}.json`);
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 = join23(publishDir, `settlement.${parsedWindow.data}.json`);
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 = join23(receiptsDir, `${id}.json`);
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(join23(publishDir, name)));
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 = join23(publishDir, "receipts", `${entry.rowId}.json`);
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 = join23(workDir, "4-publish");
53475
- const receiptsDir = join23(publishDir, "receipts");
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(join23(publishDir, `plan.${plan.window}.json`))).digest("hex");
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 = join23(workDir, row.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 = join23(workDir, row.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: join23(workDir, row.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 = join23(workDir, "4-publish", "payloads", `content.${row.key}.resolved.json`);
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 = join23(workDir, "4-publish");
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 = join23(publishDir, "receipts");
54257
+ const receiptsDir = join24(publishDir, "receipts");
53638
54258
  for (const planWindow of ["after-generate", "cn-peak"]) {
53639
- const planPath = join23(publishDir, `plan.${planWindow}.json`);
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 = join23(receiptsDir, `${row.id}.json`);
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 = join23(publishDir, "run-date.txt");
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(join23(publishDir, "rss-gate.json"), `${JSON.stringify(result, null, 2)}
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(join23(publishDir, "incident.json"), `${JSON.stringify(incident, null, 2)}
54310
+ await fs.writeFile(join24(publishDir, "incident.json"), `${JSON.stringify(incident, null, 2)}
53691
54311
  `);
53692
- await fs.writeFile(join23(publishDir, "incident.md"), [
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 = join23(workDir, "30-variants");
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(join23(variantsDir, file));
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 = join23(workDir, "4-safety-variants", "safety.json");
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 = join23(workDir, "bundle.json");
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(join23(workDir, entry.path))));
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 join25, resolve as resolve24 } from "path";
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 join24, resolve as resolve23 } from "path";
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 = join24(projectRoot, SPUR_CONFIG_PATH_REL);
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 = join24(tmpdir3(), `kk-decision-${randomUUID2()}`);
54107
- const evidencePath = join24(scratch, "evidence.md");
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 = join25(scratch, "label-input.json");
54344
- const outputPath = join25(scratch, "label-out.json");
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 = join25(dirname14(plan.kind === "new-ids" ? plan.newIds : dbPath), LABEL_SCRATCH_DIR);
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;