@kody-ade/kody-engine 0.4.117 → 0.4.118
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 +92 -16
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -880,7 +880,7 @@ var init_loadPriorArt = __esm({
|
|
|
880
880
|
// package.json
|
|
881
881
|
var package_default = {
|
|
882
882
|
name: "@kody-ade/kody-engine",
|
|
883
|
-
version: "0.4.
|
|
883
|
+
version: "0.4.118",
|
|
884
884
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
885
885
|
license: "MIT",
|
|
886
886
|
type: "module",
|
|
@@ -1227,9 +1227,43 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
1227
1227
|
classify: parseClassifyConfig(raw.classify),
|
|
1228
1228
|
release: parseReleaseConfig(raw.release),
|
|
1229
1229
|
jobs: parseJobsConfig(raw.jobs),
|
|
1230
|
+
access: parseAccessConfig(raw.access),
|
|
1230
1231
|
qa: parseQaConfig(raw.qa)
|
|
1231
1232
|
};
|
|
1232
1233
|
}
|
|
1234
|
+
var GITHUB_AUTHOR_ASSOCIATIONS = [
|
|
1235
|
+
"OWNER",
|
|
1236
|
+
"MEMBER",
|
|
1237
|
+
"COLLABORATOR",
|
|
1238
|
+
"CONTRIBUTOR",
|
|
1239
|
+
"FIRST_TIME_CONTRIBUTOR",
|
|
1240
|
+
"FIRST_TIMER",
|
|
1241
|
+
"MANNEQUIN",
|
|
1242
|
+
"NONE"
|
|
1243
|
+
];
|
|
1244
|
+
function parseAccessConfig(raw) {
|
|
1245
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
1246
|
+
const r = raw;
|
|
1247
|
+
if (r.allowedAssociations === void 0) return void 0;
|
|
1248
|
+
if (!Array.isArray(r.allowedAssociations)) {
|
|
1249
|
+
throw new Error(`kody.config.json: access.allowedAssociations must be an array of strings`);
|
|
1250
|
+
}
|
|
1251
|
+
const valid = new Set(GITHUB_AUTHOR_ASSOCIATIONS);
|
|
1252
|
+
const out = [];
|
|
1253
|
+
for (const v of r.allowedAssociations) {
|
|
1254
|
+
if (typeof v !== "string") {
|
|
1255
|
+
throw new Error(`kody.config.json: access.allowedAssociations entries must be strings`);
|
|
1256
|
+
}
|
|
1257
|
+
const up = v.trim().toUpperCase();
|
|
1258
|
+
if (!valid.has(up)) {
|
|
1259
|
+
throw new Error(
|
|
1260
|
+
`kody.config.json: access.allowedAssociations contains "${v}" \u2014 must be one of ${GITHUB_AUTHOR_ASSOCIATIONS.join(", ")}`
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
out.push(up);
|
|
1264
|
+
}
|
|
1265
|
+
return out.length > 0 ? { allowedAssociations: out } : void 0;
|
|
1266
|
+
}
|
|
1233
1267
|
function parseQaConfig(raw) {
|
|
1234
1268
|
if (!raw || typeof raw !== "object") return void 0;
|
|
1235
1269
|
const r = raw;
|
|
@@ -2496,6 +2530,7 @@ function autoDispatch(opts) {
|
|
|
2496
2530
|
const authorType = String(event.comment?.user?.type ?? "");
|
|
2497
2531
|
if (!rawBody.toLowerCase().includes("@kody")) return null;
|
|
2498
2532
|
if (authorLogin === "kody-bot" || authorType === "Bot") return null;
|
|
2533
|
+
if (!associationAllowed(event, opts?.config)) return null;
|
|
2499
2534
|
const body = rawBody.toLowerCase();
|
|
2500
2535
|
const targetNum = Number(event.issue?.number ?? 0);
|
|
2501
2536
|
const isPr = !!event.issue?.pull_request;
|
|
@@ -2571,6 +2606,10 @@ function autoDispatchTyped(opts) {
|
|
|
2571
2606
|
if (authorLogin === "kody-bot" || authorType === "Bot") {
|
|
2572
2607
|
return { kind: "silent", reason: `bot-authored comment (${authorLogin || authorType})` };
|
|
2573
2608
|
}
|
|
2609
|
+
if (!associationAllowed(event, opts?.config)) {
|
|
2610
|
+
const assoc = String(event.comment?.author_association ?? "").toUpperCase() || "<none>";
|
|
2611
|
+
return { kind: "silent", reason: `commenter association '${assoc}' not in access.allowedAssociations` };
|
|
2612
|
+
}
|
|
2574
2613
|
const targetNum = Number(event.issue?.number ?? 0);
|
|
2575
2614
|
const isPr = !!event.issue?.pull_request;
|
|
2576
2615
|
if (!targetNum) {
|
|
@@ -2622,6 +2661,12 @@ function dispatchScheduledWatches(opts) {
|
|
|
2622
2661
|
}
|
|
2623
2662
|
return out;
|
|
2624
2663
|
}
|
|
2664
|
+
function associationAllowed(event, config) {
|
|
2665
|
+
const allowed = config?.access?.allowedAssociations;
|
|
2666
|
+
if (!allowed || allowed.length === 0) return true;
|
|
2667
|
+
const assoc = String(event.comment?.author_association ?? "").toUpperCase();
|
|
2668
|
+
return allowed.includes(assoc);
|
|
2669
|
+
}
|
|
2625
2670
|
function extractAfterTag(body) {
|
|
2626
2671
|
const idx = body.indexOf("@kody");
|
|
2627
2672
|
if (idx === -1) return body;
|
|
@@ -4635,13 +4680,23 @@ function parseJob(body) {
|
|
|
4635
4680
|
if (!jobId) return { error: "jobId required" };
|
|
4636
4681
|
const repo = typeof b.repo === "string" ? b.repo.trim() : "";
|
|
4637
4682
|
if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
|
|
4638
|
-
const issueNumber = Number(b.issueNumber);
|
|
4639
|
-
if (!Number.isInteger(issueNumber) || issueNumber <= 0) {
|
|
4640
|
-
return { error: "issueNumber (positive integer) required" };
|
|
4641
|
-
}
|
|
4642
4683
|
const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
|
|
4643
4684
|
if (!githubToken) return { error: "githubToken required" };
|
|
4644
|
-
const
|
|
4685
|
+
const mode = b.mode === "interactive" ? "interactive" : "issue";
|
|
4686
|
+
const job = { jobId, repo, githubToken, mode };
|
|
4687
|
+
if (mode === "issue") {
|
|
4688
|
+
const issueNumber = Number(b.issueNumber);
|
|
4689
|
+
if (!Number.isInteger(issueNumber) || issueNumber <= 0) {
|
|
4690
|
+
return { error: "issueNumber (positive integer) required for issue mode" };
|
|
4691
|
+
}
|
|
4692
|
+
job.issueNumber = issueNumber;
|
|
4693
|
+
} else {
|
|
4694
|
+
const sessionId = typeof b.sessionId === "string" ? b.sessionId.trim() : "";
|
|
4695
|
+
if (!sessionId) return { error: "sessionId required for interactive mode" };
|
|
4696
|
+
job.sessionId = sessionId;
|
|
4697
|
+
if (Number.isFinite(Number(b.idleExitMs))) job.idleExitMs = Number(b.idleExitMs);
|
|
4698
|
+
if (Number.isFinite(Number(b.hardCapMs))) job.hardCapMs = Number(b.hardCapMs);
|
|
4699
|
+
}
|
|
4645
4700
|
if (typeof b.ref === "string" && b.ref.trim()) job.ref = b.ref.trim();
|
|
4646
4701
|
if (typeof b.model === "string" && b.model.trim()) job.model = b.model.trim();
|
|
4647
4702
|
if (typeof b.sessionId === "string" && b.sessionId.trim()) job.sessionId = b.sessionId.trim();
|
|
@@ -4658,16 +4713,21 @@ async function defaultRunJob(job) {
|
|
|
4658
4713
|
fs19.rmSync(workdir, { recursive: true, force: true });
|
|
4659
4714
|
fs19.mkdirSync(workdir, { recursive: true });
|
|
4660
4715
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
4716
|
+
const interactive = job.mode === "interactive";
|
|
4661
4717
|
const childEnv = {
|
|
4662
4718
|
...process.env,
|
|
4663
4719
|
REPO: job.repo,
|
|
4664
4720
|
REF: branch,
|
|
4665
4721
|
GITHUB_TOKEN: job.githubToken,
|
|
4666
|
-
ISSUE_NUMBER
|
|
4722
|
+
// Issue mode bakes ISSUE_NUMBER → `kody run --issue N`. Interactive mode
|
|
4723
|
+
// leaves it empty and sets SESSION_ID so the engine boots a chat session.
|
|
4724
|
+
ISSUE_NUMBER: interactive ? "" : String(job.issueNumber),
|
|
4667
4725
|
ALL_SECRETS: allSecrets,
|
|
4668
4726
|
SESSION_ID: job.sessionId ?? "",
|
|
4669
4727
|
DASHBOARD_URL: job.dashboardUrl ?? "",
|
|
4670
|
-
MODEL: job.model ?? ""
|
|
4728
|
+
MODEL: job.model ?? "",
|
|
4729
|
+
...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
|
|
4730
|
+
...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
|
|
4671
4731
|
};
|
|
4672
4732
|
const run = (cmd, args, cwd) => new Promise((resolve4) => {
|
|
4673
4733
|
const child = spawn3(cmd, args, { stdio: "inherit", env: childEnv, cwd });
|
|
@@ -4699,9 +4759,12 @@ async function defaultRunJob(job) {
|
|
|
4699
4759
|
const authorEmail = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
|
|
4700
4760
|
await run("git", ["config", "user.name", authorName], workdir);
|
|
4701
4761
|
await run("git", ["config", "user.email", authorEmail], workdir);
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4762
|
+
const runArgs = interactive ? [] : ["run", "--issue", String(job.issueNumber)];
|
|
4763
|
+
process.stdout.write(
|
|
4764
|
+
`[runner-serve] job ${job.jobId}: ${interactive ? `interactive session ${job.sessionId}` : `running issue #${job.issueNumber}`}
|
|
4765
|
+
`
|
|
4766
|
+
);
|
|
4767
|
+
const runCode = await run("kody", runArgs, workdir);
|
|
4705
4768
|
process.stdout.write(`[runner-serve] job ${job.jobId}: finished (exit ${runCode})
|
|
4706
4769
|
`);
|
|
4707
4770
|
process.exit(runCode);
|
|
@@ -5215,12 +5278,15 @@ var PoolRegistry = class {
|
|
|
5215
5278
|
const job = {
|
|
5216
5279
|
jobId: req.jobId,
|
|
5217
5280
|
repo: `${owner}/${repo}`,
|
|
5218
|
-
issueNumber: req.issueNumber,
|
|
5219
5281
|
githubToken: this.cfg.githubToken,
|
|
5282
|
+
mode: req.mode ?? "issue",
|
|
5283
|
+
issueNumber: req.issueNumber,
|
|
5284
|
+
sessionId: req.sessionId,
|
|
5285
|
+
idleExitMs: req.idleExitMs,
|
|
5286
|
+
hardCapMs: req.hardCapMs,
|
|
5220
5287
|
ref: req.ref,
|
|
5221
5288
|
allSecrets,
|
|
5222
5289
|
model: req.model,
|
|
5223
|
-
sessionId: req.sessionId,
|
|
5224
5290
|
dashboardUrl: req.dashboardUrl
|
|
5225
5291
|
};
|
|
5226
5292
|
return pm.claim(job);
|
|
@@ -5318,9 +5384,19 @@ function parseClaimRequest(body) {
|
|
|
5318
5384
|
if (!jobId) return { error: "jobId required" };
|
|
5319
5385
|
const repo = typeof b.repo === "string" ? b.repo.trim() : "";
|
|
5320
5386
|
if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
|
|
5321
|
-
const
|
|
5322
|
-
|
|
5323
|
-
|
|
5387
|
+
const mode = b.mode === "interactive" ? "interactive" : "issue";
|
|
5388
|
+
const req = { jobId, repo, mode };
|
|
5389
|
+
if (mode === "issue") {
|
|
5390
|
+
const issueNumber = Number(b.issueNumber);
|
|
5391
|
+
if (!Number.isInteger(issueNumber) || issueNumber <= 0) return { error: "issueNumber required for issue mode" };
|
|
5392
|
+
req.issueNumber = issueNumber;
|
|
5393
|
+
} else {
|
|
5394
|
+
const sessionId = typeof b.sessionId === "string" ? b.sessionId.trim() : "";
|
|
5395
|
+
if (!sessionId) return { error: "sessionId required for interactive mode" };
|
|
5396
|
+
req.sessionId = sessionId;
|
|
5397
|
+
if (Number.isFinite(Number(b.idleExitMs))) req.idleExitMs = Number(b.idleExitMs);
|
|
5398
|
+
if (Number.isFinite(Number(b.hardCapMs))) req.hardCapMs = Number(b.hardCapMs);
|
|
5399
|
+
}
|
|
5324
5400
|
if (typeof b.ref === "string" && b.ref.trim()) req.ref = b.ref.trim();
|
|
5325
5401
|
if (typeof b.model === "string" && b.model.trim()) req.model = b.model.trim();
|
|
5326
5402
|
if (typeof b.sessionId === "string" && b.sessionId.trim()) req.sessionId = b.sessionId.trim();
|
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.118",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|