@kody-ade/kody-engine 0.4.367 → 0.4.369
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/kody.js +276 -62
- package/dist/implementations/types.ts +15 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.369",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -1989,6 +1989,7 @@ function parseWorkflowStep(value) {
|
|
|
1989
1989
|
const target = stringField(raw.target);
|
|
1990
1990
|
const targetFact = stringField(raw.targetFact ?? raw.target_fact);
|
|
1991
1991
|
const cliArgs = raw.cliArgs;
|
|
1992
|
+
const report = parseReportPublication(raw.report);
|
|
1992
1993
|
return {
|
|
1993
1994
|
capability,
|
|
1994
1995
|
...action && isSafeSlug(action) ? { action } : {},
|
|
@@ -2001,7 +2002,35 @@ function parseWorkflowStep(value) {
|
|
|
2001
2002
|
...cliArgs && typeof cliArgs === "object" && !Array.isArray(cliArgs) ? { cliArgs } : {},
|
|
2002
2003
|
...isPlainObject(raw.runWhen) ? { runWhen: raw.runWhen } : {},
|
|
2003
2004
|
...stringList(raw.continueOn ?? raw.continue_on).length > 0 ? { continueOn: stringList(raw.continueOn ?? raw.continue_on) } : {},
|
|
2004
|
-
...raw.saveReport === true ? { saveReport: true } : {}
|
|
2005
|
+
...raw.saveReport === true ? { saveReport: true } : {},
|
|
2006
|
+
...report ? { report } : {}
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
2009
|
+
function parseReportPublication(value) {
|
|
2010
|
+
if (!isPlainObject(value)) return void 0;
|
|
2011
|
+
const type = stringField(value.type);
|
|
2012
|
+
const owner = stringField(value.owner);
|
|
2013
|
+
if (!type || !/^[a-z0-9][a-z0-9_-]{0,79}$/.test(type) || !owner || !isSafeSlug(owner)) return void 0;
|
|
2014
|
+
const version = typeof value.version === "number" && Number.isInteger(value.version) && value.version > 0 ? value.version : void 0;
|
|
2015
|
+
const slug = stringField(value.slug);
|
|
2016
|
+
const slugFact = stringField(value.slugFact);
|
|
2017
|
+
const title = stringField(value.title);
|
|
2018
|
+
const titleFact = stringField(value.titleFact);
|
|
2019
|
+
const publishWhenFact = stringField(value.publishWhenFact);
|
|
2020
|
+
const reviewStatus = stringField(value.reviewStatus);
|
|
2021
|
+
const reviewArea = stringField(value.reviewArea);
|
|
2022
|
+
if (!slug && !slugFact) return void 0;
|
|
2023
|
+
return {
|
|
2024
|
+
type,
|
|
2025
|
+
...version ? { version } : {},
|
|
2026
|
+
owner,
|
|
2027
|
+
...slug ? { slug } : {},
|
|
2028
|
+
...slugFact ? { slugFact } : {},
|
|
2029
|
+
...title ? { title } : {},
|
|
2030
|
+
...titleFact ? { titleFact } : {},
|
|
2031
|
+
...publishWhenFact ? { publishWhenFact } : {},
|
|
2032
|
+
...reviewStatus ? { reviewStatus } : {},
|
|
2033
|
+
...reviewArea ? { reviewArea } : {}
|
|
2005
2034
|
};
|
|
2006
2035
|
}
|
|
2007
2036
|
function isPlainObject(value) {
|
|
@@ -15873,6 +15902,106 @@ var init_parseJobStateFromAgentResult = __esm({
|
|
|
15873
15902
|
}
|
|
15874
15903
|
});
|
|
15875
15904
|
|
|
15905
|
+
// src/scripts/publishReport.ts
|
|
15906
|
+
function buildRuntimeReportMarkdown(input) {
|
|
15907
|
+
return [
|
|
15908
|
+
"---",
|
|
15909
|
+
`generatedAt: ${yamlString(input.generatedAt)}`,
|
|
15910
|
+
`reportType: ${input.reportType}`,
|
|
15911
|
+
`reportTypeVersion: ${input.reportTypeVersion}`,
|
|
15912
|
+
"producer:",
|
|
15913
|
+
` model: ${input.owner}`,
|
|
15914
|
+
` capability: ${input.capability}`,
|
|
15915
|
+
...input.reviewStatus ? [`reviewStatus: ${input.reviewStatus}`] : [],
|
|
15916
|
+
...input.reviewArea ? [`reviewArea: ${input.reviewArea}`] : [],
|
|
15917
|
+
"---",
|
|
15918
|
+
`# ${input.title}`,
|
|
15919
|
+
"",
|
|
15920
|
+
input.summary,
|
|
15921
|
+
"",
|
|
15922
|
+
"## Report data",
|
|
15923
|
+
"```json",
|
|
15924
|
+
JSON.stringify(input.data, null, 2),
|
|
15925
|
+
"```",
|
|
15926
|
+
""
|
|
15927
|
+
].join("\n");
|
|
15928
|
+
}
|
|
15929
|
+
function parsePublication(value) {
|
|
15930
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15931
|
+
const raw = value;
|
|
15932
|
+
if (typeof raw.type !== "string" || !SAFE_SLUG.test(raw.type)) return null;
|
|
15933
|
+
if (typeof raw.owner !== "string" || !SAFE_SLUG.test(raw.owner)) return null;
|
|
15934
|
+
return raw;
|
|
15935
|
+
}
|
|
15936
|
+
function latestResult(raw, agentResult) {
|
|
15937
|
+
const results = [];
|
|
15938
|
+
if (Array.isArray(raw)) {
|
|
15939
|
+
for (const item of raw) {
|
|
15940
|
+
const parsed = parseCapabilityResult(item);
|
|
15941
|
+
if (parsed) results.push(parsed);
|
|
15942
|
+
}
|
|
15943
|
+
}
|
|
15944
|
+
if (agentResult?.finalText) results.push(...parseCapabilityResultsFromText(agentResult.finalText));
|
|
15945
|
+
return results.at(-1) ?? null;
|
|
15946
|
+
}
|
|
15947
|
+
function recordField6(value) {
|
|
15948
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
15949
|
+
}
|
|
15950
|
+
function resolveDotted(root, path51) {
|
|
15951
|
+
if (!path51) return void 0;
|
|
15952
|
+
return path51.split(".").reduce((value, key) => recordField6(value)?.[key], root);
|
|
15953
|
+
}
|
|
15954
|
+
function stringValue4(value) {
|
|
15955
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
15956
|
+
}
|
|
15957
|
+
function humanize(value) {
|
|
15958
|
+
return value.split(/[-_]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
|
|
15959
|
+
}
|
|
15960
|
+
function yamlString(value) {
|
|
15961
|
+
return JSON.stringify(value);
|
|
15962
|
+
}
|
|
15963
|
+
var SAFE_SLUG, publishReport;
|
|
15964
|
+
var init_publishReport = __esm({
|
|
15965
|
+
"src/scripts/publishReport.ts"() {
|
|
15966
|
+
"use strict";
|
|
15967
|
+
init_capabilityResult();
|
|
15968
|
+
init_stateRepo();
|
|
15969
|
+
SAFE_SLUG = /^[a-z0-9][a-z0-9_-]{0,79}$/;
|
|
15970
|
+
publishReport = async (ctx, _profile, agentResult) => {
|
|
15971
|
+
const publication = parsePublication(ctx.data.reportPublication);
|
|
15972
|
+
if (!publication) return;
|
|
15973
|
+
const result = latestResult(ctx.data.capabilityResults, agentResult);
|
|
15974
|
+
const stateData = recordField6(recordField6(ctx.data.nextJobState)?.data) ?? {};
|
|
15975
|
+
const data = { ...stateData, ...result?.facts ?? {} };
|
|
15976
|
+
if (publication.publishWhenFact && resolveDotted(data, publication.publishWhenFact) === void 0) return;
|
|
15977
|
+
const slug = publication.slug ?? stringValue4(resolveDotted(data, publication.slugFact));
|
|
15978
|
+
if (!slug || !SAFE_SLUG.test(slug)) return;
|
|
15979
|
+
const title = publication.title ?? stringValue4(resolveDotted(data, publication.titleFact)) ?? humanize(slug);
|
|
15980
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15981
|
+
const markdown = buildRuntimeReportMarkdown({
|
|
15982
|
+
generatedAt,
|
|
15983
|
+
reportType: publication.type,
|
|
15984
|
+
reportTypeVersion: publication.version ?? 1,
|
|
15985
|
+
owner: publication.owner,
|
|
15986
|
+
capability: stringValue4(ctx.data.jobCapability) ?? stringValue4(ctx.data.capabilitySlug) ?? "unknown",
|
|
15987
|
+
title,
|
|
15988
|
+
summary: result?.summary ?? title,
|
|
15989
|
+
data,
|
|
15990
|
+
...publication.reviewStatus ? { reviewStatus: publication.reviewStatus } : {},
|
|
15991
|
+
...publication.reviewArea ? { reviewArea: publication.reviewArea } : {}
|
|
15992
|
+
});
|
|
15993
|
+
const runId = generatedAt.replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-");
|
|
15994
|
+
writeStateText(
|
|
15995
|
+
ctx.config,
|
|
15996
|
+
ctx.cwd,
|
|
15997
|
+
`reports/${slug}/runs/${runId}.md`,
|
|
15998
|
+
markdown,
|
|
15999
|
+
`chore(reports): add ${slug} run`
|
|
16000
|
+
);
|
|
16001
|
+
};
|
|
16002
|
+
}
|
|
16003
|
+
});
|
|
16004
|
+
|
|
15876
16005
|
// src/scripts/parseReproOutput.ts
|
|
15877
16006
|
function extractTestPath(text) {
|
|
15878
16007
|
const m = text.match(/^[\s>*_#`~-]*TEST_PATH[\s>*_#`~-]*\s*:\s*(.+?)\s*$/im);
|
|
@@ -19292,6 +19421,7 @@ var init_scripts = __esm({
|
|
|
19292
19421
|
init_parseAgentResult();
|
|
19293
19422
|
init_parseIssueStateFromAgentResult();
|
|
19294
19423
|
init_parseJobStateFromAgentResult();
|
|
19424
|
+
init_publishReport();
|
|
19295
19425
|
init_parseReproOutput();
|
|
19296
19426
|
init_persistArtifacts();
|
|
19297
19427
|
init_persistFlowState();
|
|
@@ -19420,6 +19550,7 @@ var init_scripts = __esm({
|
|
|
19420
19550
|
advanceFlow,
|
|
19421
19551
|
persistFlowState,
|
|
19422
19552
|
applyCapabilityReports,
|
|
19553
|
+
publishReport,
|
|
19423
19554
|
recordClassification,
|
|
19424
19555
|
dispatchClassified,
|
|
19425
19556
|
notifyTerminal,
|
|
@@ -20552,6 +20683,7 @@ var init_executor = __esm({
|
|
|
20552
20683
|
"commitAndPush",
|
|
20553
20684
|
"ensurePr",
|
|
20554
20685
|
"applyCapabilityReports",
|
|
20686
|
+
"publishReport",
|
|
20555
20687
|
"openAgentFactoryStatePr"
|
|
20556
20688
|
]);
|
|
20557
20689
|
MAX_CHAIN_HOPS = 60;
|
|
@@ -20608,9 +20740,16 @@ function validateJob(input) {
|
|
|
20608
20740
|
flavor: j.flavor,
|
|
20609
20741
|
force: j.force === true,
|
|
20610
20742
|
saveReport: j.saveReport === true,
|
|
20743
|
+
report: parseReportPublication2(j.report),
|
|
20611
20744
|
resultTarget: parseCapabilityResultTarget(j.resultTarget)
|
|
20612
20745
|
};
|
|
20613
20746
|
}
|
|
20747
|
+
function parseReportPublication2(raw) {
|
|
20748
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
20749
|
+
const report = raw;
|
|
20750
|
+
if (typeof report.type !== "string" || typeof report.owner !== "string") return void 0;
|
|
20751
|
+
return raw;
|
|
20752
|
+
}
|
|
20614
20753
|
function parseCapabilityResultTarget(raw) {
|
|
20615
20754
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
20616
20755
|
const target = raw;
|
|
@@ -20686,6 +20825,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
|
|
|
20686
20825
|
preloadedData.selectedImplementation = profileName;
|
|
20687
20826
|
if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
|
|
20688
20827
|
if (valid.saveReport === true) preloadedData.jobSaveReport = true;
|
|
20828
|
+
if (valid.report) preloadedData.reportPublication = valid.report;
|
|
20689
20829
|
if (valid.force === true) preloadedData.jobForce = true;
|
|
20690
20830
|
if (valid.evidence) preloadedData.capabilityEvidence = { evidence: valid.evidence };
|
|
20691
20831
|
if (valid.resultTarget) preloadedData.capabilityResultTarget = valid.resultTarget;
|
|
@@ -20841,6 +20981,7 @@ function workflowStepToJob(step, parent, chainData) {
|
|
|
20841
20981
|
flavor: parent.flavor,
|
|
20842
20982
|
force: parent.force,
|
|
20843
20983
|
saveReport: step.saveReport === true || parent.saveReport === true,
|
|
20984
|
+
...step.report ? { report: step.report } : {},
|
|
20844
20985
|
...parent.resultTarget ? { resultTarget: parent.resultTarget } : {}
|
|
20845
20986
|
};
|
|
20846
20987
|
}
|
|
@@ -21827,6 +21968,26 @@ async function ghApp(jwt, apiPath2, method = "GET") {
|
|
|
21827
21968
|
}
|
|
21828
21969
|
return await res.json();
|
|
21829
21970
|
}
|
|
21971
|
+
async function ghAppPage(authToken, apiPath2) {
|
|
21972
|
+
const res = await fetch(`${GH_API}${apiPath2}`, {
|
|
21973
|
+
headers: {
|
|
21974
|
+
Authorization: `Bearer ${authToken}`,
|
|
21975
|
+
Accept: "application/vnd.github+json",
|
|
21976
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
21977
|
+
"User-Agent": "kody-engine"
|
|
21978
|
+
}
|
|
21979
|
+
});
|
|
21980
|
+
if (!res.ok) {
|
|
21981
|
+
const body = await res.text().catch(() => "");
|
|
21982
|
+
throw new Error(
|
|
21983
|
+
`GitHub App API GET ${apiPath2} \u2192 ${res.status} ${res.statusText}${body ? `: ${body.slice(0, 200)}` : ""}`
|
|
21984
|
+
);
|
|
21985
|
+
}
|
|
21986
|
+
return {
|
|
21987
|
+
data: await res.json(),
|
|
21988
|
+
hasNext: /rel="next"/.test(res.headers.get("link") ?? "")
|
|
21989
|
+
};
|
|
21990
|
+
}
|
|
21830
21991
|
function readAppCreds(env = process.env) {
|
|
21831
21992
|
const appId = env.KODY_APP_ID?.trim();
|
|
21832
21993
|
const privateKey = env.KODY_APP_PRIVATE_KEY;
|
|
@@ -21851,6 +22012,36 @@ async function mintAppInstallationToken(creds) {
|
|
|
21851
22012
|
const tok = await ghApp(jwt, `/app/installations/${installationId}/access_tokens`, "POST");
|
|
21852
22013
|
return tok.token;
|
|
21853
22014
|
}
|
|
22015
|
+
async function discoverAppRepositories(creds) {
|
|
22016
|
+
const jwt = buildAppJwt(creds.appId, creds.privateKey);
|
|
22017
|
+
const installations = [];
|
|
22018
|
+
for (let page = 1; ; page++) {
|
|
22019
|
+
const result = await ghAppPage(jwt, `/app/installations?per_page=100&page=${page}`);
|
|
22020
|
+
installations.push(...result.data.filter((item) => Number.isInteger(item.id) && item.id > 0));
|
|
22021
|
+
if (!result.hasNext) break;
|
|
22022
|
+
}
|
|
22023
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
22024
|
+
for (const installation of installations) {
|
|
22025
|
+
const token = await mintAppInstallationToken({
|
|
22026
|
+
appId: creds.appId,
|
|
22027
|
+
privateKey: creds.privateKey,
|
|
22028
|
+
installationId: String(installation.id)
|
|
22029
|
+
});
|
|
22030
|
+
for (let page = 1; ; page++) {
|
|
22031
|
+
const result = await ghAppPage(
|
|
22032
|
+
token,
|
|
22033
|
+
`/installation/repositories?per_page=100&page=${page}`
|
|
22034
|
+
);
|
|
22035
|
+
for (const repository of result.data.repositories ?? []) {
|
|
22036
|
+
const repo = repository.full_name?.trim();
|
|
22037
|
+
if (!repo || !/^[^/\s]+\/[^/\s]+$/.test(repo)) continue;
|
|
22038
|
+
byRepo.set(repo.toLowerCase(), { repo, token });
|
|
22039
|
+
}
|
|
22040
|
+
if (!result.hasNext) break;
|
|
22041
|
+
}
|
|
22042
|
+
}
|
|
22043
|
+
return [...byRepo.values()].sort((left, right) => left.repo.localeCompare(right.repo));
|
|
22044
|
+
}
|
|
21854
22045
|
|
|
21855
22046
|
// src/kody-cli.ts
|
|
21856
22047
|
init_companyStore();
|
|
@@ -24667,67 +24858,49 @@ init_registry();
|
|
|
24667
24858
|
// src/servers/pool-serve.ts
|
|
24668
24859
|
import { createServer as createServer5 } from "http";
|
|
24669
24860
|
|
|
24670
|
-
// src/
|
|
24671
|
-
|
|
24672
|
-
|
|
24673
|
-
|
|
24674
|
-
|
|
24675
|
-
|
|
24676
|
-
try {
|
|
24677
|
-
const res = await fetchImpl(STATUS_URL, { headers: { "User-Agent": "kody-engine" } });
|
|
24678
|
-
if (!res.ok) return { degraded: false, label: `http_${res.status}` };
|
|
24679
|
-
const body = await res.json();
|
|
24680
|
-
const actions = (body.components ?? []).find((c) => (c.name ?? "").trim().toLowerCase() === "actions");
|
|
24681
|
-
const label = actions?.status ?? "unknown";
|
|
24682
|
-
const degraded = !!actions && label !== "operational";
|
|
24683
|
-
const probe = { degraded, label };
|
|
24684
|
-
statusCache = { probe, expiresAt: Date.now() + STATUS_CACHE_TTL_MS };
|
|
24685
|
-
return probe;
|
|
24686
|
-
} catch {
|
|
24687
|
-
return { degraded: false, label: "probe_error" };
|
|
24861
|
+
// src/pool/agency-loop-tick.ts
|
|
24862
|
+
function normalizeRepositories(repositories) {
|
|
24863
|
+
const unique = /* @__PURE__ */ new Set();
|
|
24864
|
+
for (const raw of repositories) {
|
|
24865
|
+
const repo = raw.trim().toLowerCase();
|
|
24866
|
+
if (/^[^/\s]+\/[^/\s]+$/.test(repo)) unique.add(repo);
|
|
24688
24867
|
}
|
|
24868
|
+
return [...unique].sort();
|
|
24689
24869
|
}
|
|
24690
|
-
async function
|
|
24691
|
-
|
|
24692
|
-
|
|
24693
|
-
|
|
24694
|
-
|
|
24695
|
-
async function runCapabilityFallbackTick(deps) {
|
|
24696
|
-
if (!await deps.isDegraded()) {
|
|
24697
|
-
return { ran: false, claimed: 0 };
|
|
24698
|
-
}
|
|
24699
|
-
const repos = deps.activeRepos();
|
|
24700
|
-
if (repos.length === 0) {
|
|
24701
|
-
deps.log("GitHub Actions degraded but no active repo pools \u2014 nothing to tick");
|
|
24702
|
-
return { ran: true, claimed: 0 };
|
|
24870
|
+
async function runAgencyLoopTick(deps) {
|
|
24871
|
+
const repositories = normalizeRepositories(await deps.discover());
|
|
24872
|
+
if (repositories.length === 0) {
|
|
24873
|
+
deps.log("no consumer agencies discovered \u2014 nothing to tick");
|
|
24874
|
+
return { discovered: 0, claimed: 0 };
|
|
24703
24875
|
}
|
|
24704
|
-
deps.log(
|
|
24876
|
+
deps.log(
|
|
24877
|
+
`running scheduled fan-out for ${repositories.length} consumer agenc${repositories.length === 1 ? "y" : "ies"}`
|
|
24878
|
+
);
|
|
24705
24879
|
const clock = deps.now ?? Date.now;
|
|
24706
24880
|
let claimed = 0;
|
|
24707
|
-
for (const
|
|
24708
|
-
const [owner, repo] =
|
|
24709
|
-
if (!owner || !repo) continue;
|
|
24881
|
+
for (const repository of repositories) {
|
|
24882
|
+
const [owner, repo] = repository.split("/");
|
|
24710
24883
|
try {
|
|
24711
|
-
const
|
|
24884
|
+
const result = await deps.claim(owner, repo, {
|
|
24712
24885
|
jobId: `sched-${owner}-${repo}-${clock()}`,
|
|
24713
|
-
repo:
|
|
24886
|
+
repo: repository,
|
|
24714
24887
|
runRequest: {
|
|
24715
24888
|
target: { type: "workflow", id: "scheduled-fanout" },
|
|
24716
24889
|
intent: "tick",
|
|
24717
24890
|
source: "schedule"
|
|
24718
24891
|
}
|
|
24719
24892
|
});
|
|
24720
|
-
if (
|
|
24893
|
+
if (result.ok) {
|
|
24721
24894
|
claimed++;
|
|
24722
|
-
deps.log(`[${
|
|
24895
|
+
deps.log(`[${repository}] scheduled fan-out claimed ${result.machineId}`);
|
|
24723
24896
|
} else {
|
|
24724
|
-
deps.log(`[${
|
|
24897
|
+
deps.log(`[${repository}] scheduled fan-out skipped: ${result.reason ?? "runner unavailable"}`);
|
|
24725
24898
|
}
|
|
24726
|
-
} catch (
|
|
24727
|
-
deps.log(`[${
|
|
24899
|
+
} catch (error) {
|
|
24900
|
+
deps.log(`[${repository}] scheduled fan-out error: ${error instanceof Error ? error.message : String(error)}`);
|
|
24728
24901
|
}
|
|
24729
24902
|
}
|
|
24730
|
-
return {
|
|
24903
|
+
return { discovered: repositories.length, claimed };
|
|
24731
24904
|
}
|
|
24732
24905
|
|
|
24733
24906
|
// src/servers/pool-serve.ts
|
|
@@ -25113,8 +25286,9 @@ var PoolRegistry = class {
|
|
|
25113
25286
|
this.cfg = cfg;
|
|
25114
25287
|
this.log = cfg.log ?? (() => {
|
|
25115
25288
|
});
|
|
25116
|
-
this.
|
|
25117
|
-
|
|
25289
|
+
this.resolveGithubToken = cfg.resolveGithubToken ?? (async () => cfg.githubToken);
|
|
25290
|
+
this.resolveFlyToken = cfg.resolveFlyToken ?? (async (owner, repo) => readRepoSecret({
|
|
25291
|
+
githubToken: await this.resolveGithubToken(owner, repo),
|
|
25118
25292
|
masterKey: cfg.masterKey,
|
|
25119
25293
|
owner,
|
|
25120
25294
|
repo,
|
|
@@ -25122,7 +25296,7 @@ var PoolRegistry = class {
|
|
|
25122
25296
|
}));
|
|
25123
25297
|
this.resolvePoolMin = cfg.resolvePoolMin ?? (async (owner, repo) => parsePoolMin(
|
|
25124
25298
|
await readRepoSecret({
|
|
25125
|
-
githubToken:
|
|
25299
|
+
githubToken: await this.resolveGithubToken(owner, repo),
|
|
25126
25300
|
masterKey: cfg.masterKey,
|
|
25127
25301
|
owner,
|
|
25128
25302
|
repo,
|
|
@@ -25134,6 +25308,7 @@ var PoolRegistry = class {
|
|
|
25134
25308
|
cfg;
|
|
25135
25309
|
pools = /* @__PURE__ */ new Map();
|
|
25136
25310
|
poolCreates = /* @__PURE__ */ new Map();
|
|
25311
|
+
resolveGithubToken;
|
|
25137
25312
|
resolveFlyToken;
|
|
25138
25313
|
resolvePoolMin;
|
|
25139
25314
|
log;
|
|
@@ -25186,10 +25361,17 @@ var PoolRegistry = class {
|
|
|
25186
25361
|
async claim(owner, repo, req) {
|
|
25187
25362
|
const pm = await this.getPool(owner, repo);
|
|
25188
25363
|
if (!pm) return { ok: false, reason: "repo has no FLY_API_TOKEN (no pool)" };
|
|
25364
|
+
let githubToken2;
|
|
25365
|
+
try {
|
|
25366
|
+
githubToken2 = await this.resolveGithubToken(owner, repo);
|
|
25367
|
+
} catch (err) {
|
|
25368
|
+
this.log(`[${this.key(owner, repo)}] repository auth failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25369
|
+
return { ok: false, reason: "repository authentication failed" };
|
|
25370
|
+
}
|
|
25189
25371
|
let allSecrets = {};
|
|
25190
25372
|
try {
|
|
25191
25373
|
const vault = await readRepoSecrets({
|
|
25192
|
-
githubToken:
|
|
25374
|
+
githubToken: githubToken2,
|
|
25193
25375
|
masterKey: this.cfg.masterKey,
|
|
25194
25376
|
owner,
|
|
25195
25377
|
repo
|
|
@@ -25206,7 +25388,7 @@ var PoolRegistry = class {
|
|
|
25206
25388
|
const job = {
|
|
25207
25389
|
jobId: req.jobId,
|
|
25208
25390
|
repo: `${owner}/${repo}`,
|
|
25209
|
-
githubToken:
|
|
25391
|
+
githubToken: githubToken2,
|
|
25210
25392
|
runRequest: req.runRequest,
|
|
25211
25393
|
issueNumber: req.issueNumber,
|
|
25212
25394
|
sessionId: req.sessionId,
|
|
@@ -25368,8 +25550,24 @@ function synthesizeLegacyClaimRequest(input) {
|
|
|
25368
25550
|
async function poolServe() {
|
|
25369
25551
|
const masterRaw = process.env.KODY_MASTER_KEY?.trim();
|
|
25370
25552
|
if (!masterRaw) throw new Error("KODY_MASTER_KEY required for pool-serve");
|
|
25371
|
-
const
|
|
25372
|
-
|
|
25553
|
+
const appCreds = readAppCreds();
|
|
25554
|
+
const fallbackGithubToken = process.env.GITHUB_TOKEN?.trim() ?? "";
|
|
25555
|
+
if (!appCreds && !fallbackGithubToken) {
|
|
25556
|
+
throw new Error("GitHub App credentials or GITHUB_TOKEN required for pool-serve");
|
|
25557
|
+
}
|
|
25558
|
+
const repoTokens = /* @__PURE__ */ new Map();
|
|
25559
|
+
const resolveGithubToken = async (owner, repo) => {
|
|
25560
|
+
const key = `${owner}/${repo}`.toLowerCase();
|
|
25561
|
+
const discovered = repoTokens.get(key);
|
|
25562
|
+
if (discovered) return discovered;
|
|
25563
|
+
if (appCreds) {
|
|
25564
|
+
const token = await mintAppInstallationToken({ ...appCreds, repo: `${owner}/${repo}` });
|
|
25565
|
+
repoTokens.set(key, token);
|
|
25566
|
+
return token;
|
|
25567
|
+
}
|
|
25568
|
+
if (fallbackGithubToken) return fallbackGithubToken;
|
|
25569
|
+
throw new Error(`no unattended GitHub token for ${key}`);
|
|
25570
|
+
};
|
|
25373
25571
|
const master = masterKeyBytes(masterRaw);
|
|
25374
25572
|
const poolApiKey = derivePoolApiKey(master);
|
|
25375
25573
|
const runnerApiKey = deriveRunnerApiKey(master);
|
|
@@ -25382,7 +25580,8 @@ async function poolServe() {
|
|
|
25382
25580
|
const apiPort = envInt2("POOL_API_PORT", 4100);
|
|
25383
25581
|
const healthTimeoutMs = envInt2("POOL_HEALTH_TIMEOUT_MS", 12e4);
|
|
25384
25582
|
const registry = new PoolRegistry({
|
|
25385
|
-
githubToken:
|
|
25583
|
+
githubToken: fallbackGithubToken,
|
|
25584
|
+
resolveGithubToken,
|
|
25386
25585
|
masterKey: master,
|
|
25387
25586
|
base: {
|
|
25388
25587
|
min,
|
|
@@ -25400,16 +25599,30 @@ async function poolServe() {
|
|
|
25400
25599
|
const tick = setInterval(() => {
|
|
25401
25600
|
registry.resyncAll().catch((err) => log(`resync tick failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
25402
25601
|
}, refillMs);
|
|
25403
|
-
const
|
|
25404
|
-
|
|
25405
|
-
|
|
25406
|
-
|
|
25407
|
-
|
|
25408
|
-
|
|
25602
|
+
const discoverAgencies = async () => {
|
|
25603
|
+
if (!appCreds) return registry.activeRepos();
|
|
25604
|
+
const repositories = await discoverAppRepositories(appCreds);
|
|
25605
|
+
for (const access of repositories) repoTokens.set(access.repo.toLowerCase(), access.token);
|
|
25606
|
+
return [.../* @__PURE__ */ new Set([...repositories.map((access) => access.repo), ...registry.activeRepos()])];
|
|
25607
|
+
};
|
|
25608
|
+
let agencyTickInFlight = null;
|
|
25609
|
+
const runLoopTick = () => {
|
|
25610
|
+
if (agencyTickInFlight) return agencyTickInFlight;
|
|
25611
|
+
agencyTickInFlight = runAgencyLoopTick({
|
|
25612
|
+
discover: discoverAgencies,
|
|
25409
25613
|
claim: (owner, repo, req) => registry.claim(owner, repo, req),
|
|
25410
25614
|
log
|
|
25411
|
-
}).catch((err) => log(`
|
|
25412
|
-
|
|
25615
|
+
}).catch((err) => log(`agency Loop tick failed: ${err instanceof Error ? err.message : String(err)}`)).finally(() => {
|
|
25616
|
+
agencyTickInFlight = null;
|
|
25617
|
+
});
|
|
25618
|
+
return agencyTickInFlight;
|
|
25619
|
+
};
|
|
25620
|
+
const loopTickEnabled = (process.env.POOL_LOOP_TICK ?? process.env.POOL_CAPABILITY_TICK ?? "1") !== "0";
|
|
25621
|
+
const loopTickMs = envInt2(
|
|
25622
|
+
process.env.POOL_LOOP_TICK_MS ? "POOL_LOOP_TICK_MS" : "POOL_CAPABILITY_TICK_MS",
|
|
25623
|
+
15 * 6e4
|
|
25624
|
+
);
|
|
25625
|
+
const loopTick = loopTickEnabled ? setInterval(() => void runLoopTick(), loopTickMs) : null;
|
|
25413
25626
|
const server = createServer5(async (req, res) => {
|
|
25414
25627
|
try {
|
|
25415
25628
|
if (!req.method || !req.url) return sendJson2(res, 400, { error: "bad request" });
|
|
@@ -25463,10 +25676,11 @@ async function poolServe() {
|
|
|
25463
25676
|
resolve10();
|
|
25464
25677
|
});
|
|
25465
25678
|
});
|
|
25679
|
+
if (loopTickEnabled) void runLoopTick();
|
|
25466
25680
|
const shutdown = (signal) => {
|
|
25467
25681
|
log(`${signal} \u2014 shutting down`);
|
|
25468
25682
|
clearInterval(tick);
|
|
25469
|
-
if (
|
|
25683
|
+
if (loopTick) clearInterval(loopTick);
|
|
25470
25684
|
server.close(() => process.exit(0));
|
|
25471
25685
|
};
|
|
25472
25686
|
process.once("SIGINT", () => shutdown("SIGINT"));
|
|
@@ -500,6 +500,19 @@ export type AnyScript = PreflightScript | PostflightScript
|
|
|
500
500
|
|
|
501
501
|
export type JobFlavor = "instant" | "scheduled"
|
|
502
502
|
|
|
503
|
+
export interface ReportPublicationConfig {
|
|
504
|
+
type: string
|
|
505
|
+
version?: number
|
|
506
|
+
owner: string
|
|
507
|
+
slug?: string
|
|
508
|
+
slugFact?: string
|
|
509
|
+
title?: string
|
|
510
|
+
titleFact?: string
|
|
511
|
+
publishWhenFact?: string
|
|
512
|
+
reviewStatus?: string
|
|
513
|
+
reviewArea?: string
|
|
514
|
+
}
|
|
515
|
+
|
|
503
516
|
export interface Job {
|
|
504
517
|
/** Public action the user/operator invoked. Mirrors the capability action. */
|
|
505
518
|
action?: string
|
|
@@ -530,6 +543,8 @@ export interface Job {
|
|
|
530
543
|
force?: boolean
|
|
531
544
|
/** Ask the owning goal/loop to write a report run after its persisted decision. */
|
|
532
545
|
saveReport?: boolean
|
|
546
|
+
/** Workflow-owned request for the runtime to publish typed capability output. */
|
|
547
|
+
report?: ReportPublicationConfig
|
|
533
548
|
/** Internal parent context used by postflights to attach neutral capability output. */
|
|
534
549
|
resultTarget?: CapabilityResultTarget
|
|
535
550
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.369",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|