@kody-ade/kody-engine 0.4.275 → 0.4.277
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 +324 -48
- 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.277",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -352,12 +352,25 @@ var init_issue = __esm({
|
|
|
352
352
|
}
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
+
// src/stateBranch.ts
|
|
356
|
+
var STATE_BRANCH;
|
|
357
|
+
var init_stateBranch = __esm({
|
|
358
|
+
"src/stateBranch.ts"() {
|
|
359
|
+
"use strict";
|
|
360
|
+
STATE_BRANCH = "kody-state";
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
355
364
|
// src/stateRepo.ts
|
|
356
365
|
import path2 from "path";
|
|
357
366
|
function is404(err) {
|
|
358
367
|
const msg = err instanceof Error ? err.message : String(err);
|
|
359
368
|
return /HTTP 404/i.test(msg) || /Not Found/i.test(msg);
|
|
360
369
|
}
|
|
370
|
+
function isAlreadyExists(err) {
|
|
371
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
372
|
+
return /HTTP 422/i.test(msg) || /Reference already exists/i.test(msg);
|
|
373
|
+
}
|
|
361
374
|
function parseStateRepoSlug(slug2, field = "stateRepo") {
|
|
362
375
|
const value = slug2.trim();
|
|
363
376
|
let repoPath = value;
|
|
@@ -428,11 +441,37 @@ function apiPath(config, targetPath) {
|
|
|
428
441
|
const parsed = parseStateRepo(config);
|
|
429
442
|
return `/repos/${parsed.owner}/${parsed.repo}/contents/${targetPath}`;
|
|
430
443
|
}
|
|
444
|
+
function branchApiPath(config, targetPath) {
|
|
445
|
+
return `${apiPath(config, targetPath)}?ref=${encodeURIComponent(STATE_BRANCH)}`;
|
|
446
|
+
}
|
|
447
|
+
function ensureStateBranch(config, cwd) {
|
|
448
|
+
const parsed = parseStateRepo(config);
|
|
449
|
+
try {
|
|
450
|
+
gh(["api", `/repos/${parsed.owner}/${parsed.repo}/git/ref/heads/${STATE_BRANCH}`], { cwd });
|
|
451
|
+
return;
|
|
452
|
+
} catch (err) {
|
|
453
|
+
if (!is404(err)) throw err;
|
|
454
|
+
}
|
|
455
|
+
const repoRaw = gh(["api", `/repos/${parsed.owner}/${parsed.repo}`], { cwd });
|
|
456
|
+
const defaultBranch = String(JSON.parse(repoRaw).default_branch ?? "").trim();
|
|
457
|
+
if (!defaultBranch) throw new Error(`stateRepo: ${parsed.owner}/${parsed.repo} default branch missing`);
|
|
458
|
+
const refRaw = gh(["api", `/repos/${parsed.owner}/${parsed.repo}/git/ref/heads/${defaultBranch}`], { cwd });
|
|
459
|
+
const sha = String(JSON.parse(refRaw).object?.sha ?? "").trim();
|
|
460
|
+
if (!sha) throw new Error(`stateRepo: ${parsed.owner}/${parsed.repo} ${defaultBranch} ref sha missing`);
|
|
461
|
+
try {
|
|
462
|
+
gh(["api", "--method", "POST", `/repos/${parsed.owner}/${parsed.repo}/git/refs`, "--input", "-"], {
|
|
463
|
+
cwd,
|
|
464
|
+
input: JSON.stringify({ ref: `refs/heads/${STATE_BRANCH}`, sha })
|
|
465
|
+
});
|
|
466
|
+
} catch (err) {
|
|
467
|
+
if (!isAlreadyExists(err)) throw err;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
431
470
|
function readStateText(config, cwd, filePath) {
|
|
432
471
|
const targetPath = stateRepoPath(config, filePath);
|
|
433
472
|
let raw = "";
|
|
434
473
|
try {
|
|
435
|
-
raw = gh(["api",
|
|
474
|
+
raw = gh(["api", branchApiPath(config, targetPath)], { cwd });
|
|
436
475
|
} catch (err) {
|
|
437
476
|
if (is404(err)) return null;
|
|
438
477
|
throw err;
|
|
@@ -457,9 +496,11 @@ function readStateText(config, cwd, filePath) {
|
|
|
457
496
|
}
|
|
458
497
|
function writeStateText(config, cwd, filePath, content, message, sha) {
|
|
459
498
|
const targetPath = stateRepoPath(config, filePath);
|
|
499
|
+
ensureStateBranch(config, cwd);
|
|
460
500
|
const payload = {
|
|
461
501
|
message,
|
|
462
|
-
content: Buffer.from(content, "utf-8").toString("base64")
|
|
502
|
+
content: Buffer.from(content, "utf-8").toString("base64"),
|
|
503
|
+
branch: STATE_BRANCH
|
|
463
504
|
};
|
|
464
505
|
if (sha) payload.sha = sha;
|
|
465
506
|
gh(["api", "--method", "PUT", apiPath(config, targetPath), "--input", "-"], {
|
|
@@ -490,7 +531,7 @@ function listStateDirectory(config, cwd, dirPath) {
|
|
|
490
531
|
const targetPath = stateRepoPath(config, dirPath);
|
|
491
532
|
let raw = "";
|
|
492
533
|
try {
|
|
493
|
-
raw = gh(["api",
|
|
534
|
+
raw = gh(["api", branchApiPath(config, targetPath)], { cwd });
|
|
494
535
|
} catch (err) {
|
|
495
536
|
if (is404(err)) return [];
|
|
496
537
|
throw err;
|
|
@@ -502,6 +543,7 @@ var init_stateRepo = __esm({
|
|
|
502
543
|
"src/stateRepo.ts"() {
|
|
503
544
|
"use strict";
|
|
504
545
|
init_issue();
|
|
546
|
+
init_stateBranch();
|
|
505
547
|
}
|
|
506
548
|
});
|
|
507
549
|
|
|
@@ -19579,6 +19621,68 @@ init_job();
|
|
|
19579
19621
|
init_registry();
|
|
19580
19622
|
init_runtimePaths();
|
|
19581
19623
|
init_stateWorkspace();
|
|
19624
|
+
|
|
19625
|
+
// src/run-request.ts
|
|
19626
|
+
var RUN_REQUEST_ENV = "KODY_RUN_REQUEST_JSON";
|
|
19627
|
+
var INTENTS = /* @__PURE__ */ new Set(["continue", "manage", "run", "tick"]);
|
|
19628
|
+
var SOURCES = /* @__PURE__ */ new Set(["dashboard", "github", "schedule"]);
|
|
19629
|
+
var TARGET_TYPES = /* @__PURE__ */ new Set(["chat", "goal", "issue", "workflow"]);
|
|
19630
|
+
function isRecord(value) {
|
|
19631
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19632
|
+
}
|
|
19633
|
+
function readString(value) {
|
|
19634
|
+
return typeof value === "string" ? value.trim() : "";
|
|
19635
|
+
}
|
|
19636
|
+
function parseTarget(input) {
|
|
19637
|
+
if (!isRecord(input)) return { error: "runRequest.target must be an object" };
|
|
19638
|
+
const type = readString(input.type);
|
|
19639
|
+
if (!TARGET_TYPES.has(type)) return { error: "runRequest.target.type is invalid" };
|
|
19640
|
+
if (type === "issue") {
|
|
19641
|
+
const id2 = Number(input.id);
|
|
19642
|
+
if (!Number.isInteger(id2) || id2 <= 0) return { error: "runRequest.target.id must be a positive issue number" };
|
|
19643
|
+
return { target: { type, id: id2 } };
|
|
19644
|
+
}
|
|
19645
|
+
const id = readString(input.id);
|
|
19646
|
+
if (!id) return { error: "runRequest.target.id is required" };
|
|
19647
|
+
return { target: { type, id } };
|
|
19648
|
+
}
|
|
19649
|
+
function parseRunRequest(input) {
|
|
19650
|
+
let body = input;
|
|
19651
|
+
if (typeof input === "string") {
|
|
19652
|
+
const raw = input.trim();
|
|
19653
|
+
if (!raw) return { error: `${RUN_REQUEST_ENV} is empty` };
|
|
19654
|
+
try {
|
|
19655
|
+
body = JSON.parse(raw);
|
|
19656
|
+
} catch {
|
|
19657
|
+
return { error: `${RUN_REQUEST_ENV} must be valid JSON` };
|
|
19658
|
+
}
|
|
19659
|
+
}
|
|
19660
|
+
if (!isRecord(body)) return { error: "runRequest must be an object" };
|
|
19661
|
+
const parsedTarget = parseTarget(body.target);
|
|
19662
|
+
if ("error" in parsedTarget) return parsedTarget;
|
|
19663
|
+
const intent = readString(body.intent);
|
|
19664
|
+
if (!INTENTS.has(intent)) return { error: "runRequest.intent is invalid" };
|
|
19665
|
+
const source = readString(body.source);
|
|
19666
|
+
if (!SOURCES.has(source)) return { error: "runRequest.source is invalid" };
|
|
19667
|
+
if (body.input !== void 0 && !isRecord(body.input)) {
|
|
19668
|
+
return { error: "runRequest.input must be an object when provided" };
|
|
19669
|
+
}
|
|
19670
|
+
return {
|
|
19671
|
+
request: {
|
|
19672
|
+
target: parsedTarget.target,
|
|
19673
|
+
intent,
|
|
19674
|
+
source,
|
|
19675
|
+
...body.input !== void 0 ? { input: body.input } : {}
|
|
19676
|
+
}
|
|
19677
|
+
};
|
|
19678
|
+
}
|
|
19679
|
+
function readRunRequestFromEnv(env = process.env) {
|
|
19680
|
+
const raw = env[RUN_REQUEST_ENV];
|
|
19681
|
+
if (raw == null || raw.trim() === "") return null;
|
|
19682
|
+
return parseRunRequest(raw);
|
|
19683
|
+
}
|
|
19684
|
+
|
|
19685
|
+
// src/kody-cli.ts
|
|
19582
19686
|
var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
|
|
19583
19687
|
|
|
19584
19688
|
Usage:
|
|
@@ -19637,6 +19741,30 @@ function parseCiArgs(argv) {
|
|
|
19637
19741
|
}
|
|
19638
19742
|
return result;
|
|
19639
19743
|
}
|
|
19744
|
+
function routeRunRequest(request) {
|
|
19745
|
+
const { target, intent } = request;
|
|
19746
|
+
if (target.type === "chat" || target.type === "issue") return { kind: "ignore" };
|
|
19747
|
+
if (target.type === "goal") {
|
|
19748
|
+
if (intent !== "manage" && intent !== "run" && intent !== "tick") {
|
|
19749
|
+
return { kind: "error", error: `goal target does not support intent '${intent}'` };
|
|
19750
|
+
}
|
|
19751
|
+
return {
|
|
19752
|
+
kind: "action",
|
|
19753
|
+
action: "goal-manager",
|
|
19754
|
+
cliArgs: { goal: target.id }
|
|
19755
|
+
};
|
|
19756
|
+
}
|
|
19757
|
+
if (target.type === "workflow") {
|
|
19758
|
+
if (target.id === "scheduled-fanout") {
|
|
19759
|
+
return { kind: "fanout", force: intent === "run" };
|
|
19760
|
+
}
|
|
19761
|
+
if (intent !== "run" && intent !== "tick") {
|
|
19762
|
+
return { kind: "error", error: `workflow target does not support intent '${intent}'` };
|
|
19763
|
+
}
|
|
19764
|
+
return { kind: "action", action: target.id, cliArgs: {} };
|
|
19765
|
+
}
|
|
19766
|
+
return { kind: "error", error: "unsupported run request target" };
|
|
19767
|
+
}
|
|
19640
19768
|
function unpackAllSecrets(env = process.env) {
|
|
19641
19769
|
const raw = env.ALL_SECRETS;
|
|
19642
19770
|
if (!raw) return 0;
|
|
@@ -19848,15 +19976,38 @@ async function runCi(argv) {
|
|
|
19848
19976
|
let manualWorkflowDispatch = false;
|
|
19849
19977
|
let forceRunAction = null;
|
|
19850
19978
|
let forceRunCliArgs = {};
|
|
19979
|
+
let runRequestFanOut = false;
|
|
19980
|
+
let runRequestFanOutForce = false;
|
|
19981
|
+
const parsedRunRequest = readRunRequestFromEnv();
|
|
19982
|
+
if (parsedRunRequest && "error" in parsedRunRequest) {
|
|
19983
|
+
process.stderr.write(`[kody] ${parsedRunRequest.error}
|
|
19984
|
+
`);
|
|
19985
|
+
return 64;
|
|
19986
|
+
}
|
|
19987
|
+
if (!args.issueNumber && !autoFallback && parsedRunRequest && "request" in parsedRunRequest) {
|
|
19988
|
+
const route = routeRunRequest(parsedRunRequest.request);
|
|
19989
|
+
if (route.kind === "error") {
|
|
19990
|
+
process.stderr.write(`[kody] ${route.error}
|
|
19991
|
+
`);
|
|
19992
|
+
return 64;
|
|
19993
|
+
}
|
|
19994
|
+
if (route.kind === "fanout") {
|
|
19995
|
+
runRequestFanOut = true;
|
|
19996
|
+
runRequestFanOutForce = route.force;
|
|
19997
|
+
} else if (route.kind === "action") {
|
|
19998
|
+
forceRunAction = route.action;
|
|
19999
|
+
forceRunCliArgs = route.cliArgs;
|
|
20000
|
+
}
|
|
20001
|
+
}
|
|
19851
20002
|
const envForceAction = (process.env.KODY_FORCE_ACTION ?? "").trim();
|
|
19852
20003
|
const envForceMessage = (process.env.KODY_FORCE_MESSAGE ?? "").trim();
|
|
19853
|
-
if (!args.issueNumber && !autoFallback && envForceAction) {
|
|
20004
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && envForceAction) {
|
|
19854
20005
|
forceRunAction = envForceAction;
|
|
19855
20006
|
if (envForceAction === "goal-manager" && envForceMessage) {
|
|
19856
20007
|
forceRunCliArgs = { goal: envForceMessage };
|
|
19857
20008
|
}
|
|
19858
20009
|
}
|
|
19859
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && eventName === "workflow_dispatch" && dispatchEventPath && fs46.existsSync(dispatchEventPath)) {
|
|
20010
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs46.existsSync(dispatchEventPath)) {
|
|
19860
20011
|
try {
|
|
19861
20012
|
const evt = JSON.parse(fs46.readFileSync(dispatchEventPath, "utf-8"));
|
|
19862
20013
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
@@ -19948,6 +20099,9 @@ async function runCi(argv) {
|
|
|
19948
20099
|
const ec = result.exitCode;
|
|
19949
20100
|
return ec === 0 || ec === 1 || ec === 2 ? ec : 99;
|
|
19950
20101
|
}
|
|
20102
|
+
if (!args.issueNumber && !autoFallback && runRequestFanOut) {
|
|
20103
|
+
return runScheduledFanOut(cwd, args, { force: runRequestFanOutForce });
|
|
20104
|
+
}
|
|
19951
20105
|
if (!args.issueNumber && !autoFallback && (eventName === "schedule" || manualWorkflowDispatch)) {
|
|
19952
20106
|
return runScheduledFanOut(cwd, args, { force: manualWorkflowDispatch });
|
|
19953
20107
|
}
|
|
@@ -21571,6 +21725,10 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
21571
21725
|
else if (arg?.startsWith("--")) result.errors.push(`unknown arg: ${arg}`);
|
|
21572
21726
|
else if (arg) result.errors.push(`unexpected positional: ${arg}`);
|
|
21573
21727
|
}
|
|
21728
|
+
const runRequest = readRunRequestFromEnv(env);
|
|
21729
|
+
if (runRequest && "error" in runRequest) result.errors.push(runRequest.error);
|
|
21730
|
+
const chatTarget = runRequest && "request" in runRequest && runRequest.request.target.type === "chat" ? runRequest.request.target.id : void 0;
|
|
21731
|
+
result.sessionId = result.sessionId ?? chatTarget;
|
|
21574
21732
|
result.sessionId = result.sessionId ?? env.SESSION_ID ?? void 0;
|
|
21575
21733
|
result.initMessage = result.initMessage ?? env.INIT_MESSAGE ?? void 0;
|
|
21576
21734
|
result.model = result.model ?? env.MODEL ?? void 0;
|
|
@@ -21764,7 +21922,11 @@ async function runCapabilityFallbackTick(deps) {
|
|
|
21764
21922
|
const res = await deps.claim(owner, repo, {
|
|
21765
21923
|
jobId: `sched-${owner}-${repo}-${clock()}`,
|
|
21766
21924
|
repo: tag,
|
|
21767
|
-
|
|
21925
|
+
runRequest: {
|
|
21926
|
+
target: { type: "workflow", id: "scheduled-fanout" },
|
|
21927
|
+
intent: "tick",
|
|
21928
|
+
source: "schedule"
|
|
21929
|
+
}
|
|
21768
21930
|
});
|
|
21769
21931
|
if (res.ok) {
|
|
21770
21932
|
claimed++;
|
|
@@ -22339,7 +22501,7 @@ var PoolRegistry = class {
|
|
|
22339
22501
|
jobId: req.jobId,
|
|
22340
22502
|
repo: `${owner}/${repo}`,
|
|
22341
22503
|
githubToken: this.cfg.githubToken,
|
|
22342
|
-
|
|
22504
|
+
runRequest: req.runRequest,
|
|
22343
22505
|
issueNumber: req.issueNumber,
|
|
22344
22506
|
sessionId: req.sessionId,
|
|
22345
22507
|
idleExitMs: req.idleExitMs,
|
|
@@ -22425,24 +22587,78 @@ function parseClaimRequest(body) {
|
|
|
22425
22587
|
const repo = typeof b.repo === "string" ? b.repo.trim() : "";
|
|
22426
22588
|
if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
|
|
22427
22589
|
const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
|
|
22428
|
-
const
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22436
|
-
|
|
22590
|
+
const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
|
|
22591
|
+
const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
|
|
22592
|
+
const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyClaimRequest({
|
|
22593
|
+
mode,
|
|
22594
|
+
issueNumber: b.issueNumber,
|
|
22595
|
+
sessionId: b.sessionId,
|
|
22596
|
+
action,
|
|
22597
|
+
message
|
|
22598
|
+
});
|
|
22599
|
+
if ("error" in runRequest) return { error: runRequest.error };
|
|
22600
|
+
const req = { jobId, repo, runRequest: runRequest.request };
|
|
22601
|
+
if (req.runRequest.target.type === "issue") {
|
|
22602
|
+
req.issueNumber = req.runRequest.target.id;
|
|
22603
|
+
} else if (req.runRequest.target.type === "chat") {
|
|
22604
|
+
req.sessionId = req.runRequest.target.id;
|
|
22437
22605
|
if (Number.isFinite(Number(b.idleExitMs))) req.idleExitMs = Number(b.idleExitMs);
|
|
22438
22606
|
if (Number.isFinite(Number(b.hardCapMs))) req.hardCapMs = Number(b.hardCapMs);
|
|
22439
22607
|
}
|
|
22440
22608
|
if (typeof b.ref === "string" && b.ref.trim()) req.ref = b.ref.trim();
|
|
22441
22609
|
if (typeof b.model === "string" && b.model.trim()) req.model = b.model.trim();
|
|
22442
|
-
if (typeof b.sessionId === "string" && b.sessionId.trim()) req.sessionId = b.sessionId.trim();
|
|
22443
22610
|
if (typeof b.dashboardUrl === "string" && b.dashboardUrl.trim()) req.dashboardUrl = b.dashboardUrl.trim();
|
|
22444
22611
|
return { req };
|
|
22445
22612
|
}
|
|
22613
|
+
function synthesizeLegacyClaimRequest(input) {
|
|
22614
|
+
if (input.mode === "issue") {
|
|
22615
|
+
const issueNumber = Number(input.issueNumber);
|
|
22616
|
+
if (!Number.isInteger(issueNumber) || issueNumber <= 0) return { error: "issueNumber required for issue mode" };
|
|
22617
|
+
return {
|
|
22618
|
+
request: {
|
|
22619
|
+
target: { type: "issue", id: issueNumber },
|
|
22620
|
+
intent: "run",
|
|
22621
|
+
source: "dashboard"
|
|
22622
|
+
}
|
|
22623
|
+
};
|
|
22624
|
+
}
|
|
22625
|
+
if (input.mode === "interactive") {
|
|
22626
|
+
const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
|
|
22627
|
+
if (!sessionId) return { error: "sessionId required for interactive mode" };
|
|
22628
|
+
return {
|
|
22629
|
+
request: {
|
|
22630
|
+
target: { type: "chat", id: sessionId },
|
|
22631
|
+
intent: "continue",
|
|
22632
|
+
source: "dashboard"
|
|
22633
|
+
}
|
|
22634
|
+
};
|
|
22635
|
+
}
|
|
22636
|
+
if (input.action === "goal-manager" && input.message) {
|
|
22637
|
+
return {
|
|
22638
|
+
request: {
|
|
22639
|
+
target: { type: "goal", id: input.message },
|
|
22640
|
+
intent: "manage",
|
|
22641
|
+
source: "dashboard"
|
|
22642
|
+
}
|
|
22643
|
+
};
|
|
22644
|
+
}
|
|
22645
|
+
if (input.action) {
|
|
22646
|
+
return {
|
|
22647
|
+
request: {
|
|
22648
|
+
target: { type: "workflow", id: input.action },
|
|
22649
|
+
intent: "run",
|
|
22650
|
+
source: "dashboard"
|
|
22651
|
+
}
|
|
22652
|
+
};
|
|
22653
|
+
}
|
|
22654
|
+
return {
|
|
22655
|
+
request: {
|
|
22656
|
+
target: { type: "workflow", id: "scheduled-fanout" },
|
|
22657
|
+
intent: "tick",
|
|
22658
|
+
source: "schedule"
|
|
22659
|
+
}
|
|
22660
|
+
};
|
|
22661
|
+
}
|
|
22446
22662
|
async function poolServe() {
|
|
22447
22663
|
const masterRaw = process.env.KODY_MASTER_KEY?.trim();
|
|
22448
22664
|
if (!masterRaw) throw new Error("KODY_MASTER_KEY required for pool-serve");
|
|
@@ -22610,33 +22826,85 @@ function parseJob(body) {
|
|
|
22610
22826
|
if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
|
|
22611
22827
|
const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
|
|
22612
22828
|
if (!githubToken) return { error: "githubToken required" };
|
|
22829
|
+
const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
|
|
22830
|
+
const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
|
|
22613
22831
|
const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
|
|
22614
|
-
const
|
|
22615
|
-
|
|
22616
|
-
|
|
22617
|
-
|
|
22618
|
-
|
|
22619
|
-
|
|
22620
|
-
|
|
22621
|
-
|
|
22622
|
-
|
|
22623
|
-
|
|
22624
|
-
job.
|
|
22832
|
+
const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyRunRequest({
|
|
22833
|
+
mode,
|
|
22834
|
+
issueNumber: b.issueNumber,
|
|
22835
|
+
sessionId: b.sessionId,
|
|
22836
|
+
action,
|
|
22837
|
+
message
|
|
22838
|
+
});
|
|
22839
|
+
if ("error" in runRequest) return { error: runRequest.error };
|
|
22840
|
+
const job = { jobId, repo, githubToken, runRequest: runRequest.request };
|
|
22841
|
+
if (job.runRequest.target.type === "issue") {
|
|
22842
|
+
job.issueNumber = job.runRequest.target.id;
|
|
22843
|
+
} else if (job.runRequest.target.type === "chat") {
|
|
22844
|
+
job.sessionId = job.runRequest.target.id;
|
|
22625
22845
|
if (Number.isFinite(Number(b.idleExitMs))) job.idleExitMs = Number(b.idleExitMs);
|
|
22626
22846
|
if (Number.isFinite(Number(b.hardCapMs))) job.hardCapMs = Number(b.hardCapMs);
|
|
22627
22847
|
}
|
|
22628
22848
|
if (typeof b.ref === "string" && b.ref.trim()) job.ref = b.ref.trim();
|
|
22629
22849
|
if (typeof b.model === "string" && b.model.trim()) job.model = b.model.trim();
|
|
22630
22850
|
if (typeof b.reasoningEffort === "string" && b.reasoningEffort.trim()) job.reasoningEffort = b.reasoningEffort.trim();
|
|
22631
|
-
if (typeof b.action === "string" && b.action.trim()) job.action = b.action.trim();
|
|
22632
|
-
if (typeof b.message === "string" && b.message.trim()) job.message = b.message.trim();
|
|
22633
|
-
if (typeof b.sessionId === "string" && b.sessionId.trim()) job.sessionId = b.sessionId.trim();
|
|
22634
22851
|
if (typeof b.dashboardUrl === "string" && b.dashboardUrl.trim()) job.dashboardUrl = b.dashboardUrl.trim();
|
|
22635
22852
|
if (b.allSecrets && (typeof b.allSecrets === "object" || typeof b.allSecrets === "string")) {
|
|
22636
22853
|
job.allSecrets = b.allSecrets;
|
|
22637
22854
|
}
|
|
22638
22855
|
return { job };
|
|
22639
22856
|
}
|
|
22857
|
+
function synthesizeLegacyRunRequest(input) {
|
|
22858
|
+
if (input.mode === "issue") {
|
|
22859
|
+
const issueNumber = Number(input.issueNumber);
|
|
22860
|
+
if (!Number.isInteger(issueNumber) || issueNumber <= 0) {
|
|
22861
|
+
return { error: "issueNumber (positive integer) required for issue mode" };
|
|
22862
|
+
}
|
|
22863
|
+
return {
|
|
22864
|
+
request: {
|
|
22865
|
+
target: { type: "issue", id: issueNumber },
|
|
22866
|
+
intent: "run",
|
|
22867
|
+
source: "dashboard"
|
|
22868
|
+
}
|
|
22869
|
+
};
|
|
22870
|
+
}
|
|
22871
|
+
if (input.mode === "interactive") {
|
|
22872
|
+
const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
|
|
22873
|
+
if (!sessionId) return { error: "sessionId required for interactive mode" };
|
|
22874
|
+
return {
|
|
22875
|
+
request: {
|
|
22876
|
+
target: { type: "chat", id: sessionId },
|
|
22877
|
+
intent: "continue",
|
|
22878
|
+
source: "dashboard"
|
|
22879
|
+
}
|
|
22880
|
+
};
|
|
22881
|
+
}
|
|
22882
|
+
if (input.action === "goal-manager" && input.message) {
|
|
22883
|
+
return {
|
|
22884
|
+
request: {
|
|
22885
|
+
target: { type: "goal", id: input.message },
|
|
22886
|
+
intent: "manage",
|
|
22887
|
+
source: "dashboard"
|
|
22888
|
+
}
|
|
22889
|
+
};
|
|
22890
|
+
}
|
|
22891
|
+
if (input.action) {
|
|
22892
|
+
return {
|
|
22893
|
+
request: {
|
|
22894
|
+
target: { type: "workflow", id: input.action },
|
|
22895
|
+
intent: "run",
|
|
22896
|
+
source: "dashboard"
|
|
22897
|
+
}
|
|
22898
|
+
};
|
|
22899
|
+
}
|
|
22900
|
+
return {
|
|
22901
|
+
request: {
|
|
22902
|
+
target: { type: "workflow", id: "scheduled-fanout" },
|
|
22903
|
+
intent: "tick",
|
|
22904
|
+
source: "schedule"
|
|
22905
|
+
}
|
|
22906
|
+
};
|
|
22907
|
+
}
|
|
22640
22908
|
async function defaultRunJob(job) {
|
|
22641
22909
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
22642
22910
|
const branch = job.ref ?? "main";
|
|
@@ -22644,20 +22912,17 @@ async function defaultRunJob(job) {
|
|
|
22644
22912
|
fs51.rmSync(workdir, { recursive: true, force: true });
|
|
22645
22913
|
fs51.mkdirSync(workdir, { recursive: true });
|
|
22646
22914
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
22647
|
-
const
|
|
22648
|
-
const
|
|
22649
|
-
const
|
|
22915
|
+
const target = job.runRequest.target;
|
|
22916
|
+
const interactive = target.type === "chat";
|
|
22917
|
+
const scheduled = target.type === "goal" || target.type === "workflow";
|
|
22918
|
+
const issueNumber = target.type === "issue" ? target.id : void 0;
|
|
22919
|
+
const sessionId = target.type === "chat" ? target.id : void 0;
|
|
22650
22920
|
const childEnv = {
|
|
22651
22921
|
...process.env,
|
|
22652
22922
|
REPO: job.repo,
|
|
22653
22923
|
REF: branch,
|
|
22654
22924
|
GITHUB_TOKEN: job.githubToken,
|
|
22655
|
-
|
|
22656
|
-
// Scheduled mode drives the engine down the same path GitHub Actions' cron
|
|
22657
|
-
// takes (runScheduledFanOut → due capabilities/goals). Bare `kody` routes on this.
|
|
22658
|
-
...scheduled ? { GITHUB_EVENT_NAME: "schedule" } : {},
|
|
22659
|
-
...job.action ? { KODY_FORCE_ACTION: job.action } : {},
|
|
22660
|
-
...job.message ? { KODY_FORCE_MESSAGE: job.message } : {},
|
|
22925
|
+
[RUN_REQUEST_ENV]: JSON.stringify(job.runRequest),
|
|
22661
22926
|
// GITHUB_REPOSITORY + GH_TOKEN are normally injected by GitHub Actions.
|
|
22662
22927
|
// The engine's interactive mode needs GITHUB_REPOSITORY to resolve the
|
|
22663
22928
|
// configured state repo and persist chat.ready / events (the durable signal
|
|
@@ -22665,11 +22930,9 @@ async function defaultRunJob(job) {
|
|
|
22665
22930
|
// and the session never appears "ready". GH_TOKEN auths the `gh` CLI.
|
|
22666
22931
|
GITHUB_REPOSITORY: job.repo,
|
|
22667
22932
|
GH_TOKEN: job.githubToken,
|
|
22668
|
-
|
|
22669
|
-
// sets SESSION_ID so the engine boots a chat session.
|
|
22670
|
-
ISSUE_NUMBER: interactive || scheduled ? "" : String(job.issueNumber),
|
|
22933
|
+
ISSUE_NUMBER: issueNumber ? String(issueNumber) : "",
|
|
22671
22934
|
ALL_SECRETS: allSecrets,
|
|
22672
|
-
SESSION_ID:
|
|
22935
|
+
SESSION_ID: sessionId ?? "",
|
|
22673
22936
|
DASHBOARD_URL: job.dashboardUrl ?? "",
|
|
22674
22937
|
MODEL: job.model ?? "",
|
|
22675
22938
|
REASONING_EFFORT: job.reasoningEffort ?? "",
|
|
@@ -22698,7 +22961,7 @@ async function defaultRunJob(job) {
|
|
|
22698
22961
|
const authorEmail = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
|
|
22699
22962
|
await run("git", ["config", "user.name", authorName], workdir);
|
|
22700
22963
|
await run("git", ["config", "user.email", authorEmail], workdir);
|
|
22701
|
-
const jobDesc = interactive ? `
|
|
22964
|
+
const jobDesc = interactive ? `chat session ${sessionId}` : scheduled ? `${target.type} ${target.id}` : `issue #${issueNumber}`;
|
|
22702
22965
|
process.stdout.write(`[runner-serve] job ${job.jobId}: ${jobDesc}
|
|
22703
22966
|
`);
|
|
22704
22967
|
const runCode = await run("kody", [], workdir);
|
|
@@ -22781,7 +23044,7 @@ async function runnerServe() {
|
|
|
22781
23044
|
init_config();
|
|
22782
23045
|
init_litellm();
|
|
22783
23046
|
import { spawn as spawn9 } from "child_process";
|
|
22784
|
-
function
|
|
23047
|
+
function parseTarget2(positional) {
|
|
22785
23048
|
if (!Array.isArray(positional) || positional.length === 0) return "none";
|
|
22786
23049
|
const first = String(positional[0]).toLowerCase();
|
|
22787
23050
|
if (first === "vscode" || first === "code") return "vscode";
|
|
@@ -22796,7 +23059,7 @@ function buildProxyEnv(url) {
|
|
|
22796
23059
|
};
|
|
22797
23060
|
}
|
|
22798
23061
|
async function serve(opts) {
|
|
22799
|
-
const target =
|
|
23062
|
+
const target = parseTarget2(opts.args);
|
|
22800
23063
|
const model = parseProviderModel(opts.config.agent.model);
|
|
22801
23064
|
const usesProxy = needsLitellmProxy(model);
|
|
22802
23065
|
let handle = null;
|
|
@@ -23110,6 +23373,17 @@ function envRunMode(env = process.env) {
|
|
|
23110
23373
|
}
|
|
23111
23374
|
return { ...result, errors: [`unknown KODY_RUN_MODE: ${mode}`] };
|
|
23112
23375
|
}
|
|
23376
|
+
function envRunRequest(env = process.env) {
|
|
23377
|
+
const result = { command: "help", errors: [] };
|
|
23378
|
+
const parsed = readRunRequestFromEnv(env);
|
|
23379
|
+
if (!parsed) return null;
|
|
23380
|
+
if ("error" in parsed) return { ...result, errors: [parsed.error] };
|
|
23381
|
+
const { target } = parsed.request;
|
|
23382
|
+
if (target.type === "chat") return { ...result, command: "chat", chatArgv: [] };
|
|
23383
|
+
if (target.type === "issue") return { ...result, command: "ci", ciArgv: ["--issue", String(target.id)] };
|
|
23384
|
+
if (target.type === "goal" || target.type === "workflow") return { ...result, command: "ci", ciArgv: [] };
|
|
23385
|
+
return { ...result, errors: [`unsupported runRequest target: ${target.type ?? "unknown"}`] };
|
|
23386
|
+
}
|
|
23113
23387
|
var HELP_TEXT = `kody-engine \u2014 single-session autonomous engineer
|
|
23114
23388
|
|
|
23115
23389
|
Usage:
|
|
@@ -23146,6 +23420,8 @@ Exit codes:
|
|
|
23146
23420
|
function parseArgs(argv) {
|
|
23147
23421
|
const result = { command: "help", errors: [] };
|
|
23148
23422
|
if (argv.length === 0) {
|
|
23423
|
+
const runRequest = envRunRequest();
|
|
23424
|
+
if (runRequest) return runRequest;
|
|
23149
23425
|
const mode = envRunMode();
|
|
23150
23426
|
if (mode) return mode;
|
|
23151
23427
|
if (process.env.SESSION_ID) return { ...result, command: "chat", chatArgv: [] };
|
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.277",
|
|
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",
|