@kody-ade/kody-engine 0.4.145 → 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 +83 -19
- 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",
|
|
@@ -2700,7 +2700,6 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2700
2700
|
|
|
2701
2701
|
// src/kody-cli.ts
|
|
2702
2702
|
import { execFileSync as execFileSync29 } from "child_process";
|
|
2703
|
-
import { createHash } from "crypto";
|
|
2704
2703
|
import * as fs41 from "fs";
|
|
2705
2704
|
import * as path37 from "path";
|
|
2706
2705
|
|
|
@@ -10212,6 +10211,65 @@ function timingEqual(a, b) {
|
|
|
10212
10211
|
return diff === 0;
|
|
10213
10212
|
}
|
|
10214
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
|
+
|
|
10215
10273
|
// src/scripts/poolServe.ts
|
|
10216
10274
|
var PERF_GUEST = {
|
|
10217
10275
|
low: { cpu_kind: "shared", cpus: 2, memory_mb: 2048 },
|
|
@@ -10337,6 +10395,16 @@ var poolServe = async (ctx) => {
|
|
|
10337
10395
|
const tick = setInterval(() => {
|
|
10338
10396
|
registry.resyncAll().catch((err) => log(`resync tick failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
10339
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;
|
|
10340
10408
|
const server = createServer2(async (req, res) => {
|
|
10341
10409
|
try {
|
|
10342
10410
|
if (!req.method || !req.url) return sendJson2(res, 400, { error: "bad request" });
|
|
@@ -10394,6 +10462,7 @@ var poolServe = async (ctx) => {
|
|
|
10394
10462
|
const shutdown = (signal) => {
|
|
10395
10463
|
log(`${signal} \u2014 shutting down`);
|
|
10396
10464
|
clearInterval(tick);
|
|
10465
|
+
if (dutyTick) clearInterval(dutyTick);
|
|
10397
10466
|
server.close(() => process.exit(0));
|
|
10398
10467
|
};
|
|
10399
10468
|
process.once("SIGINT", () => shutdown("SIGINT"));
|
|
@@ -11337,7 +11406,7 @@ function parseJob(body) {
|
|
|
11337
11406
|
if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
|
|
11338
11407
|
const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
|
|
11339
11408
|
if (!githubToken) return { error: "githubToken required" };
|
|
11340
|
-
const mode = b.mode === "interactive" ? "interactive" : "issue";
|
|
11409
|
+
const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
|
|
11341
11410
|
const job = { jobId, repo, githubToken, mode };
|
|
11342
11411
|
if (mode === "issue") {
|
|
11343
11412
|
const issueNumber = Number(b.issueNumber);
|
|
@@ -11345,7 +11414,7 @@ function parseJob(body) {
|
|
|
11345
11414
|
return { error: "issueNumber (positive integer) required for issue mode" };
|
|
11346
11415
|
}
|
|
11347
11416
|
job.issueNumber = issueNumber;
|
|
11348
|
-
} else {
|
|
11417
|
+
} else if (mode === "interactive") {
|
|
11349
11418
|
const sessionId = typeof b.sessionId === "string" ? b.sessionId.trim() : "";
|
|
11350
11419
|
if (!sessionId) return { error: "sessionId required for interactive mode" };
|
|
11351
11420
|
job.sessionId = sessionId;
|
|
@@ -11369,11 +11438,15 @@ async function defaultRunJob(job) {
|
|
|
11369
11438
|
fs37.mkdirSync(workdir, { recursive: true });
|
|
11370
11439
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
11371
11440
|
const interactive = job.mode === "interactive";
|
|
11441
|
+
const scheduled = job.mode === "scheduled";
|
|
11372
11442
|
const childEnv = {
|
|
11373
11443
|
...process.env,
|
|
11374
11444
|
REPO: job.repo,
|
|
11375
11445
|
REF: branch,
|
|
11376
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" } : {},
|
|
11377
11450
|
// GITHUB_REPOSITORY + GH_TOKEN are normally injected by GitHub Actions.
|
|
11378
11451
|
// The engine's interactive mode needs GITHUB_REPOSITORY to persist
|
|
11379
11452
|
// chat.ready / events to .kody/events via the Contents API (the durable
|
|
@@ -11383,7 +11456,7 @@ async function defaultRunJob(job) {
|
|
|
11383
11456
|
GH_TOKEN: job.githubToken,
|
|
11384
11457
|
// Issue mode bakes ISSUE_NUMBER → `kody run --issue N`. Interactive mode
|
|
11385
11458
|
// leaves it empty and sets SESSION_ID so the engine boots a chat session.
|
|
11386
|
-
ISSUE_NUMBER: interactive ? "" : String(job.issueNumber),
|
|
11459
|
+
ISSUE_NUMBER: interactive || scheduled ? "" : String(job.issueNumber),
|
|
11387
11460
|
ALL_SECRETS: allSecrets,
|
|
11388
11461
|
SESSION_ID: job.sessionId ?? "",
|
|
11389
11462
|
DASHBOARD_URL: job.dashboardUrl ?? "",
|
|
@@ -11421,11 +11494,10 @@ async function defaultRunJob(job) {
|
|
|
11421
11494
|
const authorEmail = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
|
|
11422
11495
|
await run("git", ["config", "user.name", authorName], workdir);
|
|
11423
11496
|
await run("git", ["config", "user.email", authorEmail], workdir);
|
|
11424
|
-
const runArgs = interactive ? [] : ["run", "--issue", String(job.issueNumber)];
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
`
|
|
11428
|
-
);
|
|
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
|
+
`);
|
|
11429
11501
|
const runCode = await run("kody", runArgs, workdir);
|
|
11430
11502
|
process.stdout.write(`[runner-serve] job ${job.jobId}: finished (exit ${runCode})
|
|
11431
11503
|
`);
|
|
@@ -13744,15 +13816,7 @@ function resolveAuthToken(env = process.env) {
|
|
|
13744
13816
|
const token = picked?.[1];
|
|
13745
13817
|
if (token && !env.GH_TOKEN) env.GH_TOKEN = token;
|
|
13746
13818
|
if (token) {
|
|
13747
|
-
|
|
13748
|
-
const lenDiff = token.length - trimmed.length;
|
|
13749
|
-
const sha = createHash("sha256").update(token).digest("hex");
|
|
13750
|
-
const shaTrimmed = createHash("sha256").update(trimmed).digest("hex");
|
|
13751
|
-
process.stdout.write(
|
|
13752
|
-
`\u2192 kody: GH_TOKEN sourced from env.${picked[0]} (length=${token.length}, prefix=${token.slice(0, 4)}, trailingWhitespace=${lenDiff > 0 ? "YES " + lenDiff + " chars" : "no"})
|
|
13753
|
-
`
|
|
13754
|
-
);
|
|
13755
|
-
process.stdout.write(`\u2192 kody: token sha256=${sha.slice(0, 16)}\u2026${sha.slice(-8)} (trimmed sha=${shaTrimmed.slice(0, 16)}\u2026${shaTrimmed.slice(-8)})
|
|
13819
|
+
process.stdout.write(`\u2192 kody: GH_TOKEN sourced from env.${picked[0]}
|
|
13756
13820
|
`);
|
|
13757
13821
|
} else {
|
|
13758
13822
|
process.stdout.write("\u2192 kody: WARNING no auth token found (KODY_TOKEN/GH_TOKEN/GITHUB_TOKEN/GH_PAT all empty)\n");
|
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",
|