@kody-ade/kody-engine 0.4.146 → 0.4.147
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 +82 -9
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -1061,7 +1061,7 @@ var init_loadPriorArt = __esm({
|
|
|
1061
1061
|
// package.json
|
|
1062
1062
|
var package_default = {
|
|
1063
1063
|
name: "@kody-ade/kody-engine",
|
|
1064
|
-
version: "0.4.
|
|
1064
|
+
version: "0.4.147",
|
|
1065
1065
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1066
1066
|
license: "MIT",
|
|
1067
1067
|
type: "module",
|
|
@@ -10211,6 +10211,65 @@ function timingEqual(a, b) {
|
|
|
10211
10211
|
return diff === 0;
|
|
10212
10212
|
}
|
|
10213
10213
|
|
|
10214
|
+
// src/pool/duty-fallback-tick.ts
|
|
10215
|
+
async function runDutyFallbackTick(deps) {
|
|
10216
|
+
if (!await deps.isDegraded()) {
|
|
10217
|
+
return { ran: false, claimed: 0 };
|
|
10218
|
+
}
|
|
10219
|
+
const repos = deps.activeRepos();
|
|
10220
|
+
if (repos.length === 0) {
|
|
10221
|
+
deps.log("GitHub Actions degraded but no active repo pools \u2014 nothing to tick");
|
|
10222
|
+
return { ran: true, claimed: 0 };
|
|
10223
|
+
}
|
|
10224
|
+
deps.log(`GitHub Actions degraded \u2014 running scheduled fan-out on Fly for ${repos.length} repo(s)`);
|
|
10225
|
+
const clock = deps.now ?? Date.now;
|
|
10226
|
+
let claimed = 0;
|
|
10227
|
+
for (const tag of repos) {
|
|
10228
|
+
const [owner, repo] = tag.split("/");
|
|
10229
|
+
if (!owner || !repo) continue;
|
|
10230
|
+
try {
|
|
10231
|
+
const res = await deps.claim(owner, repo, {
|
|
10232
|
+
jobId: `sched-${owner}-${repo}-${clock()}`,
|
|
10233
|
+
repo: tag,
|
|
10234
|
+
mode: "scheduled"
|
|
10235
|
+
});
|
|
10236
|
+
if (res.ok) {
|
|
10237
|
+
claimed++;
|
|
10238
|
+
deps.log(`[${tag}] scheduled fan-out claimed ${res.machineId}`);
|
|
10239
|
+
} else {
|
|
10240
|
+
deps.log(`[${tag}] scheduled fan-out skipped: ${res.reason ?? "pool unavailable"}`);
|
|
10241
|
+
}
|
|
10242
|
+
} catch (err) {
|
|
10243
|
+
deps.log(`[${tag}] scheduled fan-out error: ${err instanceof Error ? err.message : String(err)}`);
|
|
10244
|
+
}
|
|
10245
|
+
}
|
|
10246
|
+
return { ran: true, claimed };
|
|
10247
|
+
}
|
|
10248
|
+
|
|
10249
|
+
// src/github-health.ts
|
|
10250
|
+
var STATUS_URL = "https://www.githubstatus.com/api/v2/components.json";
|
|
10251
|
+
var STATUS_CACHE_TTL_MS = 3e4;
|
|
10252
|
+
var statusCache = null;
|
|
10253
|
+
async function probeActionsStatus(fetchImpl = fetch) {
|
|
10254
|
+
if (statusCache && statusCache.expiresAt > Date.now()) return statusCache.probe;
|
|
10255
|
+
try {
|
|
10256
|
+
const res = await fetchImpl(STATUS_URL, { headers: { "User-Agent": "kody-engine" } });
|
|
10257
|
+
if (!res.ok) return { degraded: false, label: `http_${res.status}` };
|
|
10258
|
+
const body = await res.json();
|
|
10259
|
+
const actions = (body.components ?? []).find((c) => (c.name ?? "").trim().toLowerCase() === "actions");
|
|
10260
|
+
const label = actions?.status ?? "unknown";
|
|
10261
|
+
const degraded = !!actions && label !== "operational";
|
|
10262
|
+
const probe = { degraded, label };
|
|
10263
|
+
statusCache = { probe, expiresAt: Date.now() + STATUS_CACHE_TTL_MS };
|
|
10264
|
+
return probe;
|
|
10265
|
+
} catch {
|
|
10266
|
+
return { degraded: false, label: "probe_error" };
|
|
10267
|
+
}
|
|
10268
|
+
}
|
|
10269
|
+
async function gitHubActionsDegraded(fetchImpl = fetch) {
|
|
10270
|
+
return (await probeActionsStatus(fetchImpl)).degraded;
|
|
10271
|
+
}
|
|
10272
|
+
|
|
10214
10273
|
// src/scripts/poolServe.ts
|
|
10215
10274
|
var PERF_GUEST = {
|
|
10216
10275
|
low: { cpu_kind: "shared", cpus: 2, memory_mb: 2048 },
|
|
@@ -10336,6 +10395,16 @@ var poolServe = async (ctx) => {
|
|
|
10336
10395
|
const tick = setInterval(() => {
|
|
10337
10396
|
registry.resyncAll().catch((err) => log(`resync tick failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
10338
10397
|
}, refillMs);
|
|
10398
|
+
const dutyTickEnabled = (process.env.POOL_DUTY_TICK ?? "1") !== "0";
|
|
10399
|
+
const dutyTickMs = envInt("POOL_DUTY_TICK_MS", 15 * 6e4);
|
|
10400
|
+
const dutyTick = dutyTickEnabled ? setInterval(() => {
|
|
10401
|
+
runDutyFallbackTick({
|
|
10402
|
+
isDegraded: () => gitHubActionsDegraded(),
|
|
10403
|
+
activeRepos: () => registry.activeRepos(),
|
|
10404
|
+
claim: (owner, repo, req) => registry.claim(owner, repo, req),
|
|
10405
|
+
log
|
|
10406
|
+
}).catch((err) => log(`duty fallback tick failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
10407
|
+
}, dutyTickMs) : null;
|
|
10339
10408
|
const server = createServer2(async (req, res) => {
|
|
10340
10409
|
try {
|
|
10341
10410
|
if (!req.method || !req.url) return sendJson2(res, 400, { error: "bad request" });
|
|
@@ -10393,6 +10462,7 @@ var poolServe = async (ctx) => {
|
|
|
10393
10462
|
const shutdown = (signal) => {
|
|
10394
10463
|
log(`${signal} \u2014 shutting down`);
|
|
10395
10464
|
clearInterval(tick);
|
|
10465
|
+
if (dutyTick) clearInterval(dutyTick);
|
|
10396
10466
|
server.close(() => process.exit(0));
|
|
10397
10467
|
};
|
|
10398
10468
|
process.once("SIGINT", () => shutdown("SIGINT"));
|
|
@@ -11336,7 +11406,7 @@ function parseJob(body) {
|
|
|
11336
11406
|
if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
|
|
11337
11407
|
const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
|
|
11338
11408
|
if (!githubToken) return { error: "githubToken required" };
|
|
11339
|
-
const mode = b.mode === "interactive" ? "interactive" : "issue";
|
|
11409
|
+
const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
|
|
11340
11410
|
const job = { jobId, repo, githubToken, mode };
|
|
11341
11411
|
if (mode === "issue") {
|
|
11342
11412
|
const issueNumber = Number(b.issueNumber);
|
|
@@ -11344,7 +11414,7 @@ function parseJob(body) {
|
|
|
11344
11414
|
return { error: "issueNumber (positive integer) required for issue mode" };
|
|
11345
11415
|
}
|
|
11346
11416
|
job.issueNumber = issueNumber;
|
|
11347
|
-
} else {
|
|
11417
|
+
} else if (mode === "interactive") {
|
|
11348
11418
|
const sessionId = typeof b.sessionId === "string" ? b.sessionId.trim() : "";
|
|
11349
11419
|
if (!sessionId) return { error: "sessionId required for interactive mode" };
|
|
11350
11420
|
job.sessionId = sessionId;
|
|
@@ -11368,11 +11438,15 @@ async function defaultRunJob(job) {
|
|
|
11368
11438
|
fs37.mkdirSync(workdir, { recursive: true });
|
|
11369
11439
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
11370
11440
|
const interactive = job.mode === "interactive";
|
|
11441
|
+
const scheduled = job.mode === "scheduled";
|
|
11371
11442
|
const childEnv = {
|
|
11372
11443
|
...process.env,
|
|
11373
11444
|
REPO: job.repo,
|
|
11374
11445
|
REF: branch,
|
|
11375
11446
|
GITHUB_TOKEN: job.githubToken,
|
|
11447
|
+
// Scheduled mode drives the engine down the same path GitHub Actions' cron
|
|
11448
|
+
// takes (runScheduledFanOut → due duties/goals). Bare `kody` routes on this.
|
|
11449
|
+
...scheduled ? { GITHUB_EVENT_NAME: "schedule" } : {},
|
|
11376
11450
|
// GITHUB_REPOSITORY + GH_TOKEN are normally injected by GitHub Actions.
|
|
11377
11451
|
// The engine's interactive mode needs GITHUB_REPOSITORY to persist
|
|
11378
11452
|
// chat.ready / events to .kody/events via the Contents API (the durable
|
|
@@ -11382,7 +11456,7 @@ async function defaultRunJob(job) {
|
|
|
11382
11456
|
GH_TOKEN: job.githubToken,
|
|
11383
11457
|
// Issue mode bakes ISSUE_NUMBER → `kody run --issue N`. Interactive mode
|
|
11384
11458
|
// leaves it empty and sets SESSION_ID so the engine boots a chat session.
|
|
11385
|
-
ISSUE_NUMBER: interactive ? "" : String(job.issueNumber),
|
|
11459
|
+
ISSUE_NUMBER: interactive || scheduled ? "" : String(job.issueNumber),
|
|
11386
11460
|
ALL_SECRETS: allSecrets,
|
|
11387
11461
|
SESSION_ID: job.sessionId ?? "",
|
|
11388
11462
|
DASHBOARD_URL: job.dashboardUrl ?? "",
|
|
@@ -11420,11 +11494,10 @@ async function defaultRunJob(job) {
|
|
|
11420
11494
|
const authorEmail = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
|
|
11421
11495
|
await run("git", ["config", "user.name", authorName], workdir);
|
|
11422
11496
|
await run("git", ["config", "user.email", authorEmail], workdir);
|
|
11423
|
-
const runArgs = interactive ? [] : ["run", "--issue", String(job.issueNumber)];
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
`
|
|
11427
|
-
);
|
|
11497
|
+
const runArgs = interactive || scheduled ? [] : ["run", "--issue", String(job.issueNumber)];
|
|
11498
|
+
const jobDesc = interactive ? `interactive session ${job.sessionId}` : scheduled ? "scheduled fan-out" : `running issue #${job.issueNumber}`;
|
|
11499
|
+
process.stdout.write(`[runner-serve] job ${job.jobId}: ${jobDesc}
|
|
11500
|
+
`);
|
|
11428
11501
|
const runCode = await run("kody", runArgs, workdir);
|
|
11429
11502
|
process.stdout.write(`[runner-serve] job ${job.jobId}: finished (exit ${runCode})
|
|
11430
11503
|
`);
|
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.147",
|
|
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",
|