@kody-ade/kody-engine 0.4.306 → 0.4.307
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 +406 -337
- package/package.json +24 -25
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.307",
|
|
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",
|
|
@@ -549,11 +549,11 @@ function branchApiPath(config, targetPath) {
|
|
|
549
549
|
}
|
|
550
550
|
function ensureStateBranch(config, cwd) {
|
|
551
551
|
const parsed = parseStateRepo(config);
|
|
552
|
-
const
|
|
553
|
-
if (ensuredStateBranches.has(
|
|
552
|
+
const cacheKey4 = `${parsed.owner}/${parsed.repo}:${parsed.branch}`;
|
|
553
|
+
if (ensuredStateBranches.has(cacheKey4)) return;
|
|
554
554
|
try {
|
|
555
555
|
gh(["api", `/repos/${parsed.owner}/${parsed.repo}/git/ref/heads/${parsed.branch}`], { cwd });
|
|
556
|
-
ensuredStateBranches.add(
|
|
556
|
+
ensuredStateBranches.add(cacheKey4);
|
|
557
557
|
return;
|
|
558
558
|
} catch (err) {
|
|
559
559
|
if (!is404(err)) throw err;
|
|
@@ -572,7 +572,7 @@ function ensureStateBranch(config, cwd) {
|
|
|
572
572
|
} catch (err) {
|
|
573
573
|
if (!isAlreadyExists(err)) throw err;
|
|
574
574
|
}
|
|
575
|
-
ensuredStateBranches.add(
|
|
575
|
+
ensuredStateBranches.add(cacheKey4);
|
|
576
576
|
}
|
|
577
577
|
function readStateText(config, cwd, filePath) {
|
|
578
578
|
const targetPath = stateRepoPath(config, filePath);
|
|
@@ -8828,12 +8828,12 @@ var init_appendCompanyActivity = __esm({
|
|
|
8828
8828
|
}
|
|
8829
8829
|
});
|
|
8830
8830
|
|
|
8831
|
-
// src/
|
|
8832
|
-
function
|
|
8831
|
+
// src/agencyArchitectDecision.ts
|
|
8832
|
+
function parseAgencyArchitectDecisionText(finalText) {
|
|
8833
8833
|
const raw = extractDecisionJson(finalText);
|
|
8834
8834
|
const parsed = JSON.parse(raw);
|
|
8835
8835
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8836
|
-
throw new Error("
|
|
8836
|
+
throw new Error("agency-architect decision must be JSON object");
|
|
8837
8837
|
}
|
|
8838
8838
|
const input = parsed;
|
|
8839
8839
|
const actions = Array.isArray(input.actions) ? input.actions.map(parseAction) : [];
|
|
@@ -8881,15 +8881,15 @@ function buildAgentLoopState(action) {
|
|
|
8881
8881
|
};
|
|
8882
8882
|
}
|
|
8883
8883
|
function extractDecisionJson(finalText) {
|
|
8884
|
-
const fence = finalText.match(/```(?:kody-
|
|
8884
|
+
const fence = finalText.match(/```(?:kody-agency-architect-decision|json)\s*([\s\S]*?)```/i);
|
|
8885
8885
|
if (fence?.[1]) return fence[1].trim();
|
|
8886
|
-
const line = finalText.match(/
|
|
8886
|
+
const line = finalText.match(/KODY_AGENCY_ARCHITECT_DECISION=(\{[\s\S]*\})/);
|
|
8887
8887
|
if (line?.[1]) return line[1].trim();
|
|
8888
|
-
throw new Error("missing kody-
|
|
8888
|
+
throw new Error("missing kody-agency-architect-decision JSON");
|
|
8889
8889
|
}
|
|
8890
8890
|
function parseAction(value) {
|
|
8891
8891
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
8892
|
-
throw new Error("
|
|
8892
|
+
throw new Error("agency-architect action must be object");
|
|
8893
8893
|
}
|
|
8894
8894
|
const input = value;
|
|
8895
8895
|
const kind = input.kind;
|
|
@@ -8898,7 +8898,7 @@ function parseAction(value) {
|
|
|
8898
8898
|
if (kind === "setGoalLifecycle") return parseSetGoalLifecycle(input);
|
|
8899
8899
|
if (kind === "updateIntentPortfolio") return parseUpdateIntentPortfolio(input);
|
|
8900
8900
|
if (kind === "note") return parseNote(input);
|
|
8901
|
-
throw new Error(`unsupported
|
|
8901
|
+
throw new Error(`unsupported agency-architect action kind: ${String(kind)}`);
|
|
8902
8902
|
}
|
|
8903
8903
|
function parseCreateManagedGoal(input) {
|
|
8904
8904
|
const route = Array.isArray(input.route) ? input.route.map(parseRouteStep) : [];
|
|
@@ -8994,8 +8994,8 @@ function oneOf(value, allowed, fallback) {
|
|
|
8994
8994
|
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
8995
8995
|
}
|
|
8996
8996
|
var SLUG_RE;
|
|
8997
|
-
var
|
|
8998
|
-
"src/
|
|
8997
|
+
var init_agencyArchitectDecision = __esm({
|
|
8998
|
+
"src/agencyArchitectDecision.ts"() {
|
|
8999
8999
|
"use strict";
|
|
9000
9000
|
init_state2();
|
|
9001
9001
|
SLUG_RE = /^[a-z][a-z0-9-]{0,63}$/;
|
|
@@ -9019,11 +9019,13 @@ function normalizeCompanyIntent(path50, raw) {
|
|
|
9019
9019
|
if (!id || !isCompanyIntentId(id)) throw new Error(`${path50}: invalid intent id`);
|
|
9020
9020
|
const createdAt = stringField3(input.createdAt) || nowIso();
|
|
9021
9021
|
const updatedAt = stringField3(input.updatedAt) || createdAt;
|
|
9022
|
+
const description = stringField3(input.description);
|
|
9022
9023
|
return {
|
|
9023
9024
|
version: 1,
|
|
9024
9025
|
id,
|
|
9025
9026
|
status: oneOf2(input.status, ["active", "paused", "archived"], "active"),
|
|
9026
9027
|
for: stringField3(input.for),
|
|
9028
|
+
...description ? { description } : {},
|
|
9027
9029
|
priority: numberField(input.priority, 100),
|
|
9028
9030
|
posture: oneOf2(
|
|
9029
9031
|
input.posture,
|
|
@@ -9142,8 +9144,8 @@ function normalizeAutomationPolicy(raw) {
|
|
|
9142
9144
|
function normalizeManager(raw) {
|
|
9143
9145
|
return {
|
|
9144
9146
|
agent: "cto",
|
|
9145
|
-
loop: "
|
|
9146
|
-
capability: "
|
|
9147
|
+
loop: "agency-architect-loop",
|
|
9148
|
+
capability: "agency-architect",
|
|
9147
9149
|
reviewEvery: oneOf2(raw?.reviewEvery, ["1d", "1w"], "1d"),
|
|
9148
9150
|
...typeof raw?.lastReviewedAt === "string" ? { lastReviewedAt: raw.lastReviewedAt } : {}
|
|
9149
9151
|
};
|
|
@@ -9159,7 +9161,7 @@ var init_companyIntent = __esm({
|
|
|
9159
9161
|
}
|
|
9160
9162
|
});
|
|
9161
9163
|
|
|
9162
|
-
// src/scripts/
|
|
9164
|
+
// src/scripts/applyAgencyArchitectDecision.ts
|
|
9163
9165
|
function applyAction(config, cwd, action) {
|
|
9164
9166
|
if (action.kind === "createManagedGoal") {
|
|
9165
9167
|
const existing = fetchGoalState(config, action.id, cwd);
|
|
@@ -9228,7 +9230,7 @@ function applied(action, changed, reason, resource) {
|
|
|
9228
9230
|
function mergeUnique(left, right) {
|
|
9229
9231
|
return [.../* @__PURE__ */ new Set([...left, ...right])].sort();
|
|
9230
9232
|
}
|
|
9231
|
-
function
|
|
9233
|
+
function logAppliedAgencyArchitectActions(config, cwd, appliedActions) {
|
|
9232
9234
|
const at = nowIso();
|
|
9233
9235
|
for (const action of appliedActions) {
|
|
9234
9236
|
if (!action.intentId) continue;
|
|
@@ -9243,24 +9245,24 @@ function logAppliedCompanyManagerActions(config, cwd, appliedActions) {
|
|
|
9243
9245
|
});
|
|
9244
9246
|
}
|
|
9245
9247
|
}
|
|
9246
|
-
var
|
|
9247
|
-
var
|
|
9248
|
-
"src/scripts/
|
|
9248
|
+
var applyAgencyArchitectDecision;
|
|
9249
|
+
var init_applyAgencyArchitectDecision = __esm({
|
|
9250
|
+
"src/scripts/applyAgencyArchitectDecision.ts"() {
|
|
9249
9251
|
"use strict";
|
|
9250
|
-
|
|
9252
|
+
init_agencyArchitectDecision();
|
|
9251
9253
|
init_companyIntent();
|
|
9252
9254
|
init_stateStore();
|
|
9253
9255
|
init_state2();
|
|
9254
|
-
|
|
9255
|
-
const decision = ctx.data.
|
|
9256
|
+
applyAgencyArchitectDecision = async (ctx) => {
|
|
9257
|
+
const decision = ctx.data.agencyArchitectDecision;
|
|
9256
9258
|
if (!decision || !Array.isArray(decision.actions)) return;
|
|
9257
9259
|
if (ctx.output.exitCode !== 0) return;
|
|
9258
9260
|
const applied2 = [];
|
|
9259
9261
|
for (const action of decision.actions) {
|
|
9260
9262
|
applied2.push(applyAction(ctx.config, ctx.cwd, action));
|
|
9261
9263
|
}
|
|
9262
|
-
ctx.data.
|
|
9263
|
-
ctx.data.
|
|
9264
|
+
ctx.data.agencyArchitectApplied = applied2;
|
|
9265
|
+
ctx.data.agencyArchitectApplySummary = `agency-architect applied ${applied2.filter((item) => item.changed).length}/${applied2.length} action(s)`;
|
|
9264
9266
|
};
|
|
9265
9267
|
}
|
|
9266
9268
|
});
|
|
@@ -9270,15 +9272,15 @@ var appendCompanyIntentDecision2;
|
|
|
9270
9272
|
var init_appendCompanyIntentDecision = __esm({
|
|
9271
9273
|
"src/scripts/appendCompanyIntentDecision.ts"() {
|
|
9272
9274
|
"use strict";
|
|
9273
|
-
|
|
9275
|
+
init_applyAgencyArchitectDecision();
|
|
9274
9276
|
appendCompanyIntentDecision2 = async (ctx) => {
|
|
9275
|
-
const applied2 = ctx.data.
|
|
9277
|
+
const applied2 = ctx.data.agencyArchitectApplied;
|
|
9276
9278
|
if (!applied2 || applied2.length === 0) return;
|
|
9277
9279
|
try {
|
|
9278
|
-
|
|
9280
|
+
logAppliedAgencyArchitectActions(ctx.config, ctx.cwd, applied2);
|
|
9279
9281
|
} catch (err) {
|
|
9280
9282
|
process.stderr.write(
|
|
9281
|
-
`[
|
|
9283
|
+
`[agency-architect] failed append intent decision log: ${err instanceof Error ? err.message : String(err)}
|
|
9282
9284
|
`
|
|
9283
9285
|
);
|
|
9284
9286
|
}
|
|
@@ -13471,9 +13473,340 @@ var init_kodyVariables = __esm({
|
|
|
13471
13473
|
}
|
|
13472
13474
|
});
|
|
13473
13475
|
|
|
13474
|
-
// src/
|
|
13476
|
+
// src/pool/keys.ts
|
|
13477
|
+
import { hkdfSync } from "crypto";
|
|
13478
|
+
function masterKeyBytes(raw) {
|
|
13479
|
+
const v = raw.trim();
|
|
13480
|
+
if (!v) throw new Error("KODY_MASTER_KEY is empty");
|
|
13481
|
+
if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
|
|
13482
|
+
return Buffer.from(v, "hex");
|
|
13483
|
+
}
|
|
13484
|
+
return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
13485
|
+
}
|
|
13486
|
+
function deriveKey(master, info, length = 32) {
|
|
13487
|
+
return Buffer.from(hkdfSync("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
|
|
13488
|
+
}
|
|
13489
|
+
function derivePoolApiKey(master) {
|
|
13490
|
+
return deriveKey(master, POOL_API_KEY_INFO);
|
|
13491
|
+
}
|
|
13492
|
+
function deriveRunnerApiKey(master) {
|
|
13493
|
+
return deriveKey(master, RUNNER_API_KEY_INFO);
|
|
13494
|
+
}
|
|
13495
|
+
function bearerOk(headerAuth, xApiKey, expected) {
|
|
13496
|
+
const x = (xApiKey ?? "").trim();
|
|
13497
|
+
if (x && timingEqual(x, expected)) return true;
|
|
13498
|
+
const a = (headerAuth ?? "").trim();
|
|
13499
|
+
if (a.toLowerCase().startsWith("bearer ")) {
|
|
13500
|
+
return timingEqual(a.slice(7).trim(), expected);
|
|
13501
|
+
}
|
|
13502
|
+
return false;
|
|
13503
|
+
}
|
|
13504
|
+
function timingEqual(a, b) {
|
|
13505
|
+
if (a.length !== b.length) return false;
|
|
13506
|
+
let diff = 0;
|
|
13507
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
13508
|
+
return diff === 0;
|
|
13509
|
+
}
|
|
13510
|
+
var POOL_API_KEY_INFO, RUNNER_API_KEY_INFO;
|
|
13511
|
+
var init_keys = __esm({
|
|
13512
|
+
"src/pool/keys.ts"() {
|
|
13513
|
+
"use strict";
|
|
13514
|
+
POOL_API_KEY_INFO = "kody-pool-api:v1";
|
|
13515
|
+
RUNNER_API_KEY_INFO = "kody-runner-api:v1";
|
|
13516
|
+
}
|
|
13517
|
+
});
|
|
13518
|
+
|
|
13519
|
+
// src/stateRepoGithub.ts
|
|
13475
13520
|
import * as fs37 from "fs";
|
|
13476
13521
|
import * as path35 from "path";
|
|
13522
|
+
function recordValue3(value) {
|
|
13523
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
13524
|
+
}
|
|
13525
|
+
function contentsUrl(owner, repo, filePath) {
|
|
13526
|
+
const encodedPath = filePath.split("/").filter(Boolean).map((part) => encodeURIComponent(part)).join("/");
|
|
13527
|
+
return `${GITHUB_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}`;
|
|
13528
|
+
}
|
|
13529
|
+
async function githubContentsFile(opts) {
|
|
13530
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
13531
|
+
const res = await doFetch(contentsUrl(opts.owner, opts.repo, opts.path), {
|
|
13532
|
+
headers: {
|
|
13533
|
+
Authorization: `Bearer ${opts.githubToken}`,
|
|
13534
|
+
Accept: "application/vnd.github+json",
|
|
13535
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
13536
|
+
"User-Agent": "kody-engine"
|
|
13537
|
+
},
|
|
13538
|
+
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
13539
|
+
});
|
|
13540
|
+
if (res.status === 404) return null;
|
|
13541
|
+
if (!res.ok) {
|
|
13542
|
+
throw new Error(
|
|
13543
|
+
`GitHub ${opts.owner}/${opts.repo}:${opts.path}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
13544
|
+
);
|
|
13545
|
+
}
|
|
13546
|
+
return await res.json();
|
|
13547
|
+
}
|
|
13548
|
+
function decodeContentsFile(file, label) {
|
|
13549
|
+
if (file.type !== "file" || file.encoding !== "base64" || typeof file.content !== "string") {
|
|
13550
|
+
throw new Error(`${label} is not a base64 file`);
|
|
13551
|
+
}
|
|
13552
|
+
if (typeof file.sha !== "string") {
|
|
13553
|
+
throw new Error(`${label} response missing sha`);
|
|
13554
|
+
}
|
|
13555
|
+
return {
|
|
13556
|
+
path: label,
|
|
13557
|
+
content: Buffer.from(file.content, "base64").toString("utf-8"),
|
|
13558
|
+
sha: file.sha
|
|
13559
|
+
};
|
|
13560
|
+
}
|
|
13561
|
+
async function loadGithubStateConfig(opts) {
|
|
13562
|
+
const configFile = await githubContentsFile({
|
|
13563
|
+
owner: opts.owner,
|
|
13564
|
+
repo: opts.repo,
|
|
13565
|
+
path: "kody.config.json",
|
|
13566
|
+
githubToken: opts.githubToken,
|
|
13567
|
+
fetchImpl: opts.fetchImpl
|
|
13568
|
+
});
|
|
13569
|
+
let raw = {};
|
|
13570
|
+
if (configFile) {
|
|
13571
|
+
const decoded = decodeContentsFile(configFile, "kody.config.json");
|
|
13572
|
+
try {
|
|
13573
|
+
raw = JSON.parse(decoded.content);
|
|
13574
|
+
} catch (err) {
|
|
13575
|
+
throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
13576
|
+
}
|
|
13577
|
+
}
|
|
13578
|
+
const githubRaw = recordValue3(raw.github) ?? {};
|
|
13579
|
+
const github = {
|
|
13580
|
+
owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
|
|
13581
|
+
repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
|
|
13582
|
+
};
|
|
13583
|
+
const nestedState = recordValue3(raw.state) ?? {};
|
|
13584
|
+
const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
|
|
13585
|
+
const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
|
|
13586
|
+
const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
|
|
13587
|
+
parseStateRepoSlug(stateRepo);
|
|
13588
|
+
const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
|
|
13589
|
+
return {
|
|
13590
|
+
github,
|
|
13591
|
+
state: {
|
|
13592
|
+
repo: stateRepo,
|
|
13593
|
+
path: normalizeStatePath(statePath)
|
|
13594
|
+
}
|
|
13595
|
+
};
|
|
13596
|
+
}
|
|
13597
|
+
async function readGithubStateText(opts) {
|
|
13598
|
+
const config = await loadGithubStateConfig(opts);
|
|
13599
|
+
return readGithubStateTextWithConfig({
|
|
13600
|
+
config,
|
|
13601
|
+
filePath: opts.filePath,
|
|
13602
|
+
githubToken: opts.githubToken,
|
|
13603
|
+
fetchImpl: opts.fetchImpl
|
|
13604
|
+
});
|
|
13605
|
+
}
|
|
13606
|
+
async function readGithubStateTextWithConfig(opts) {
|
|
13607
|
+
const target = stateRepoPath(opts.config, opts.filePath);
|
|
13608
|
+
const parsed = parseStateRepo(opts.config);
|
|
13609
|
+
const file = await githubContentsFile({
|
|
13610
|
+
owner: parsed.owner,
|
|
13611
|
+
repo: parsed.repo,
|
|
13612
|
+
path: target,
|
|
13613
|
+
githubToken: opts.githubToken,
|
|
13614
|
+
fetchImpl: opts.fetchImpl
|
|
13615
|
+
});
|
|
13616
|
+
return file ? decodeContentsFile(file, target) : null;
|
|
13617
|
+
}
|
|
13618
|
+
async function writeGithubStateTextWithConfig(opts) {
|
|
13619
|
+
const target = stateRepoPath(opts.config, opts.filePath);
|
|
13620
|
+
const parsed = parseStateRepo(opts.config);
|
|
13621
|
+
const payload = {
|
|
13622
|
+
message: opts.message,
|
|
13623
|
+
content: Buffer.from(opts.content, "utf-8").toString("base64")
|
|
13624
|
+
};
|
|
13625
|
+
if (opts.sha) payload.sha = opts.sha;
|
|
13626
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
13627
|
+
const res = await doFetch(contentsUrl(parsed.owner, parsed.repo, target), {
|
|
13628
|
+
method: "PUT",
|
|
13629
|
+
headers: {
|
|
13630
|
+
Authorization: `Bearer ${opts.githubToken}`,
|
|
13631
|
+
Accept: "application/vnd.github+json",
|
|
13632
|
+
"Content-Type": "application/json",
|
|
13633
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
13634
|
+
"User-Agent": "kody-engine"
|
|
13635
|
+
},
|
|
13636
|
+
body: JSON.stringify(payload),
|
|
13637
|
+
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
13638
|
+
});
|
|
13639
|
+
if (!res.ok) {
|
|
13640
|
+
throw new Error(
|
|
13641
|
+
`GitHub write ${parsed.owner}/${parsed.repo}:${target}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
13642
|
+
);
|
|
13643
|
+
}
|
|
13644
|
+
}
|
|
13645
|
+
function jsonlLines(text) {
|
|
13646
|
+
return text.split("\n").filter((line) => line.length > 0);
|
|
13647
|
+
}
|
|
13648
|
+
function renderJsonl(lines) {
|
|
13649
|
+
return lines.length > 0 ? `${lines.join("\n")}
|
|
13650
|
+
` : "";
|
|
13651
|
+
}
|
|
13652
|
+
function mergeJsonl(localText, remoteText) {
|
|
13653
|
+
const remoteLines = jsonlLines(remoteText);
|
|
13654
|
+
const seen = new Set(remoteLines);
|
|
13655
|
+
const localOnly = jsonlLines(localText).filter((line) => !seen.has(line));
|
|
13656
|
+
return renderJsonl([...remoteLines, ...localOnly]);
|
|
13657
|
+
}
|
|
13658
|
+
async function syncJsonlFileFromGithubState(opts) {
|
|
13659
|
+
const remote = await readGithubStateTextWithConfig(opts);
|
|
13660
|
+
if (!remote) return;
|
|
13661
|
+
const local = fs37.existsSync(opts.localPath) ? fs37.readFileSync(opts.localPath, "utf-8") : "";
|
|
13662
|
+
const next = mergeJsonl(local, remote.content);
|
|
13663
|
+
if (next === local) return;
|
|
13664
|
+
fs37.mkdirSync(path35.dirname(opts.localPath), { recursive: true });
|
|
13665
|
+
fs37.writeFileSync(opts.localPath, next);
|
|
13666
|
+
}
|
|
13667
|
+
async function persistJsonlFileToGithubState(opts) {
|
|
13668
|
+
if (!fs37.existsSync(opts.localPath)) return;
|
|
13669
|
+
const localText = fs37.readFileSync(opts.localPath, "utf-8");
|
|
13670
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
13671
|
+
const remote = await readGithubStateTextWithConfig(opts);
|
|
13672
|
+
const body = mergeJsonl(localText, remote?.content ?? "");
|
|
13673
|
+
try {
|
|
13674
|
+
await writeGithubStateTextWithConfig({
|
|
13675
|
+
config: opts.config,
|
|
13676
|
+
filePath: opts.filePath,
|
|
13677
|
+
content: body,
|
|
13678
|
+
message: opts.message,
|
|
13679
|
+
githubToken: opts.githubToken,
|
|
13680
|
+
sha: remote?.sha,
|
|
13681
|
+
fetchImpl: opts.fetchImpl
|
|
13682
|
+
});
|
|
13683
|
+
return;
|
|
13684
|
+
} catch (err) {
|
|
13685
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13686
|
+
const conflict = /409|422|does not match|is at|but expected/i.test(msg);
|
|
13687
|
+
if (!conflict || attempt === 3) throw err;
|
|
13688
|
+
}
|
|
13689
|
+
}
|
|
13690
|
+
}
|
|
13691
|
+
var GITHUB_API, REQ_TIMEOUT_MS;
|
|
13692
|
+
var init_stateRepoGithub = __esm({
|
|
13693
|
+
"src/stateRepoGithub.ts"() {
|
|
13694
|
+
"use strict";
|
|
13695
|
+
init_stateRepo();
|
|
13696
|
+
GITHUB_API = "https://api.github.com";
|
|
13697
|
+
REQ_TIMEOUT_MS = 3e4;
|
|
13698
|
+
}
|
|
13699
|
+
});
|
|
13700
|
+
|
|
13701
|
+
// src/stateRepoVault.ts
|
|
13702
|
+
import { createDecipheriv, createHash as createHash3 } from "crypto";
|
|
13703
|
+
function cacheKey2(owner, repo, masterKey) {
|
|
13704
|
+
const keyHash = createHash3("sha256").update(masterKey).digest("hex").slice(0, 16);
|
|
13705
|
+
return `${owner}/${repo}:${keyHash}`.toLowerCase();
|
|
13706
|
+
}
|
|
13707
|
+
function decryptVault(payload, masterKey) {
|
|
13708
|
+
const parts = payload.split(":");
|
|
13709
|
+
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
13710
|
+
throw new Error("invalid vault payload format");
|
|
13711
|
+
}
|
|
13712
|
+
const [, ivB64, ctB64, tagB64] = parts;
|
|
13713
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
13714
|
+
const ct = Buffer.from(ctB64, "base64");
|
|
13715
|
+
const tag = Buffer.from(tagB64, "base64");
|
|
13716
|
+
const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
|
|
13717
|
+
decipher.setAuthTag(tag);
|
|
13718
|
+
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
13719
|
+
}
|
|
13720
|
+
async function readVaultSecrets(opts) {
|
|
13721
|
+
const key = cacheKey2(opts.owner, opts.repo, opts.masterKey);
|
|
13722
|
+
const hit = cache.get(key);
|
|
13723
|
+
if (hit && hit.expiresAt > Date.now()) return hit.secrets;
|
|
13724
|
+
const file = await readGithubStateText({
|
|
13725
|
+
owner: opts.owner,
|
|
13726
|
+
repo: opts.repo,
|
|
13727
|
+
filePath: VAULT_PATH,
|
|
13728
|
+
githubToken: opts.githubToken,
|
|
13729
|
+
fetchImpl: opts.fetchImpl
|
|
13730
|
+
});
|
|
13731
|
+
if (!file) {
|
|
13732
|
+
cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
13733
|
+
return {};
|
|
13734
|
+
}
|
|
13735
|
+
const ciphertext = file.content.trim();
|
|
13736
|
+
const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
|
|
13737
|
+
const flat = {};
|
|
13738
|
+
for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
|
|
13739
|
+
if (entry && typeof entry.value === "string") flat[name] = entry.value;
|
|
13740
|
+
}
|
|
13741
|
+
cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
13742
|
+
return flat;
|
|
13743
|
+
}
|
|
13744
|
+
async function readRepoSecret(opts) {
|
|
13745
|
+
const secrets = await readVaultSecrets(opts);
|
|
13746
|
+
const v = secrets[opts.name];
|
|
13747
|
+
return v?.trim() ? v : null;
|
|
13748
|
+
}
|
|
13749
|
+
async function readRepoSecrets(opts) {
|
|
13750
|
+
return readVaultSecrets(opts);
|
|
13751
|
+
}
|
|
13752
|
+
var VAULT_PATH, CACHE_TTL_MS, cache;
|
|
13753
|
+
var init_stateRepoVault = __esm({
|
|
13754
|
+
"src/stateRepoVault.ts"() {
|
|
13755
|
+
"use strict";
|
|
13756
|
+
init_stateRepoGithub();
|
|
13757
|
+
VAULT_PATH = "secrets.enc";
|
|
13758
|
+
CACHE_TTL_MS = 6e4;
|
|
13759
|
+
cache = /* @__PURE__ */ new Map();
|
|
13760
|
+
}
|
|
13761
|
+
});
|
|
13762
|
+
|
|
13763
|
+
// src/scripts/runtimeSecrets.ts
|
|
13764
|
+
function tokenFromEnv(env) {
|
|
13765
|
+
return (env.KODY_TOKEN ?? env.GH_TOKEN ?? env.GITHUB_TOKEN ?? env.GH_PAT ?? "").trim();
|
|
13766
|
+
}
|
|
13767
|
+
function envSecret(name, env) {
|
|
13768
|
+
const value = env[name]?.trim() ? env[name] : "";
|
|
13769
|
+
return value ? { value, source: "env" } : { value: "", source: "missing" };
|
|
13770
|
+
}
|
|
13771
|
+
async function resolveRuntimeSecret(name, ctx, opts = {}) {
|
|
13772
|
+
const env = opts.env ?? process.env;
|
|
13773
|
+
const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
|
|
13774
|
+
const githubToken2 = tokenFromEnv(env);
|
|
13775
|
+
if (!masterRaw || !githubToken2) return envSecret(name, env);
|
|
13776
|
+
try {
|
|
13777
|
+
const masterKey = masterKeyBytes(masterRaw);
|
|
13778
|
+
if (masterKey.length !== 32) {
|
|
13779
|
+
throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
|
|
13780
|
+
}
|
|
13781
|
+
const value = await readRepoSecret({
|
|
13782
|
+
owner: ctx.config.github.owner,
|
|
13783
|
+
repo: ctx.config.github.repo,
|
|
13784
|
+
name,
|
|
13785
|
+
githubToken: githubToken2,
|
|
13786
|
+
masterKey,
|
|
13787
|
+
fetchImpl: opts.fetchImpl
|
|
13788
|
+
});
|
|
13789
|
+
if (value) return { value, source: "vault" };
|
|
13790
|
+
} catch (err) {
|
|
13791
|
+
const fallback = envSecret(name, env);
|
|
13792
|
+
return {
|
|
13793
|
+
...fallback,
|
|
13794
|
+
warning: `vault read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
13795
|
+
};
|
|
13796
|
+
}
|
|
13797
|
+
return envSecret(name, env);
|
|
13798
|
+
}
|
|
13799
|
+
var init_runtimeSecrets = __esm({
|
|
13800
|
+
"src/scripts/runtimeSecrets.ts"() {
|
|
13801
|
+
"use strict";
|
|
13802
|
+
init_keys();
|
|
13803
|
+
init_stateRepoVault();
|
|
13804
|
+
}
|
|
13805
|
+
});
|
|
13806
|
+
|
|
13807
|
+
// src/scripts/loadQaContext.ts
|
|
13808
|
+
import * as fs38 from "fs";
|
|
13809
|
+
import * as path36 from "path";
|
|
13477
13810
|
function parseSlugList(value) {
|
|
13478
13811
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
13479
13812
|
return inner.split(",").map(
|
|
@@ -13502,18 +13835,18 @@ function readProfileAgents(raw) {
|
|
|
13502
13835
|
return { agent: agent ?? legacy ?? ["kody"], body };
|
|
13503
13836
|
}
|
|
13504
13837
|
function readProfile(cwd) {
|
|
13505
|
-
const dir =
|
|
13506
|
-
if (!
|
|
13838
|
+
const dir = path36.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
13839
|
+
if (!fs38.existsSync(dir)) return "";
|
|
13507
13840
|
let entries;
|
|
13508
13841
|
try {
|
|
13509
|
-
entries =
|
|
13842
|
+
entries = fs38.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
13510
13843
|
} catch {
|
|
13511
13844
|
return "";
|
|
13512
13845
|
}
|
|
13513
13846
|
const blocks = [];
|
|
13514
13847
|
for (const file of entries) {
|
|
13515
13848
|
try {
|
|
13516
|
-
const raw =
|
|
13849
|
+
const raw = fs38.readFileSync(path36.join(dir, file), "utf-8");
|
|
13517
13850
|
const { agent, body } = readProfileAgents(raw);
|
|
13518
13851
|
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
13519
13852
|
blocks.push(`## ${file}
|
|
@@ -13541,6 +13874,7 @@ var init_loadQaContext = __esm({
|
|
|
13541
13874
|
"src/scripts/loadQaContext.ts"() {
|
|
13542
13875
|
"use strict";
|
|
13543
13876
|
init_kodyVariables();
|
|
13877
|
+
init_runtimeSecrets();
|
|
13544
13878
|
CONTEXT_DIR_REL_PATH = ".kody/context";
|
|
13545
13879
|
QA_AGENT = "qa-engineer";
|
|
13546
13880
|
ALL_AGENTS = "*";
|
|
@@ -13549,18 +13883,20 @@ var init_loadQaContext = __esm({
|
|
|
13549
13883
|
loadQaContext = async (ctx) => {
|
|
13550
13884
|
const vars = readKodyVariables(ctx.cwd);
|
|
13551
13885
|
const login = vars.LOGIN_USER ?? "";
|
|
13552
|
-
const password =
|
|
13886
|
+
const password = await resolveRuntimeSecret("LOGIN_PASSWORD", ctx);
|
|
13553
13887
|
const authProfile = ctx.args.authProfile;
|
|
13554
13888
|
ctx.data.qaLogin = login;
|
|
13889
|
+
ctx.data.qaPasswordSource = password.source;
|
|
13890
|
+
if (password.warning) ctx.data.qaPasswordWarning = password.warning;
|
|
13555
13891
|
ctx.data.qaProfile = readProfile(ctx.cwd);
|
|
13556
|
-
ctx.data.qaAuthBlock = composeAuthBlock(authProfile, login, password);
|
|
13892
|
+
ctx.data.qaAuthBlock = composeAuthBlock(authProfile, login, password.value);
|
|
13557
13893
|
};
|
|
13558
13894
|
}
|
|
13559
13895
|
});
|
|
13560
13896
|
|
|
13561
13897
|
// src/taskContext.ts
|
|
13562
|
-
import * as
|
|
13563
|
-
import * as
|
|
13898
|
+
import * as fs39 from "fs";
|
|
13899
|
+
import * as path37 from "path";
|
|
13564
13900
|
function buildTaskContext(args) {
|
|
13565
13901
|
return {
|
|
13566
13902
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -13576,9 +13912,9 @@ function buildTaskContext(args) {
|
|
|
13576
13912
|
function persistTaskContext(cwd, ctx) {
|
|
13577
13913
|
try {
|
|
13578
13914
|
const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
|
|
13579
|
-
|
|
13580
|
-
const file =
|
|
13581
|
-
|
|
13915
|
+
fs39.mkdirSync(dir, { recursive: true });
|
|
13916
|
+
const file = path37.join(dir, "task-context.json");
|
|
13917
|
+
fs39.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
13582
13918
|
`);
|
|
13583
13919
|
return file;
|
|
13584
13920
|
} catch (err) {
|
|
@@ -14349,32 +14685,32 @@ var init_parseAgentResult = __esm({
|
|
|
14349
14685
|
}
|
|
14350
14686
|
});
|
|
14351
14687
|
|
|
14352
|
-
// src/scripts/
|
|
14688
|
+
// src/scripts/parseAgencyArchitectDecision.ts
|
|
14353
14689
|
function makeAction3(type, payload) {
|
|
14354
14690
|
return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
14355
14691
|
}
|
|
14356
|
-
var
|
|
14357
|
-
var
|
|
14358
|
-
"src/scripts/
|
|
14692
|
+
var parseAgencyArchitectDecision;
|
|
14693
|
+
var init_parseAgencyArchitectDecision = __esm({
|
|
14694
|
+
"src/scripts/parseAgencyArchitectDecision.ts"() {
|
|
14359
14695
|
"use strict";
|
|
14360
|
-
|
|
14361
|
-
|
|
14696
|
+
init_agencyArchitectDecision();
|
|
14697
|
+
parseAgencyArchitectDecision = async (ctx, _profile, agentResult) => {
|
|
14362
14698
|
if (!agentResult) {
|
|
14363
|
-
ctx.data.
|
|
14364
|
-
ctx.data.action = makeAction3("
|
|
14699
|
+
ctx.data.agencyArchitectDecision = { summary: "", actions: [] };
|
|
14700
|
+
ctx.data.action = makeAction3("AGENCY_ARCHITECT_NOT_RUN", { reason: "no agent result" });
|
|
14365
14701
|
return;
|
|
14366
14702
|
}
|
|
14367
14703
|
try {
|
|
14368
|
-
const decision =
|
|
14369
|
-
ctx.data.
|
|
14370
|
-
ctx.data.action = makeAction3("
|
|
14704
|
+
const decision = parseAgencyArchitectDecisionText(agentResult.finalText);
|
|
14705
|
+
ctx.data.agencyArchitectDecision = decision;
|
|
14706
|
+
ctx.data.action = makeAction3("AGENCY_ARCHITECT_DECIDED", {
|
|
14371
14707
|
summary: decision.summary,
|
|
14372
14708
|
actionCount: decision.actions.length
|
|
14373
14709
|
});
|
|
14374
14710
|
} catch (err) {
|
|
14375
14711
|
const reason = err instanceof Error ? err.message : String(err);
|
|
14376
|
-
ctx.data.
|
|
14377
|
-
ctx.data.action = makeAction3("
|
|
14712
|
+
ctx.data.agencyArchitectDecisionError = reason;
|
|
14713
|
+
ctx.data.action = makeAction3("AGENCY_ARCHITECT_FAILED", { reason });
|
|
14378
14714
|
ctx.output.exitCode = 1;
|
|
14379
14715
|
ctx.output.reason = reason;
|
|
14380
14716
|
}
|
|
@@ -15873,192 +16209,10 @@ var init_runFlow = __esm({
|
|
|
15873
16209
|
}
|
|
15874
16210
|
});
|
|
15875
16211
|
|
|
15876
|
-
// src/stateRepoGithub.ts
|
|
15877
|
-
import * as fs39 from "fs";
|
|
15878
|
-
import * as path37 from "path";
|
|
15879
|
-
function recordValue3(value) {
|
|
15880
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
15881
|
-
}
|
|
15882
|
-
function contentsUrl(owner, repo, filePath) {
|
|
15883
|
-
const encodedPath = filePath.split("/").filter(Boolean).map((part) => encodeURIComponent(part)).join("/");
|
|
15884
|
-
return `${GITHUB_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}`;
|
|
15885
|
-
}
|
|
15886
|
-
async function githubContentsFile(opts) {
|
|
15887
|
-
const doFetch = opts.fetchImpl ?? fetch;
|
|
15888
|
-
const res = await doFetch(contentsUrl(opts.owner, opts.repo, opts.path), {
|
|
15889
|
-
headers: {
|
|
15890
|
-
Authorization: `Bearer ${opts.githubToken}`,
|
|
15891
|
-
Accept: "application/vnd.github+json",
|
|
15892
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
15893
|
-
"User-Agent": "kody-engine"
|
|
15894
|
-
},
|
|
15895
|
-
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
15896
|
-
});
|
|
15897
|
-
if (res.status === 404) return null;
|
|
15898
|
-
if (!res.ok) {
|
|
15899
|
-
throw new Error(
|
|
15900
|
-
`GitHub ${opts.owner}/${opts.repo}:${opts.path}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
15901
|
-
);
|
|
15902
|
-
}
|
|
15903
|
-
return await res.json();
|
|
15904
|
-
}
|
|
15905
|
-
function decodeContentsFile(file, label) {
|
|
15906
|
-
if (file.type !== "file" || file.encoding !== "base64" || typeof file.content !== "string") {
|
|
15907
|
-
throw new Error(`${label} is not a base64 file`);
|
|
15908
|
-
}
|
|
15909
|
-
if (typeof file.sha !== "string") {
|
|
15910
|
-
throw new Error(`${label} response missing sha`);
|
|
15911
|
-
}
|
|
15912
|
-
return {
|
|
15913
|
-
path: label,
|
|
15914
|
-
content: Buffer.from(file.content, "base64").toString("utf-8"),
|
|
15915
|
-
sha: file.sha
|
|
15916
|
-
};
|
|
15917
|
-
}
|
|
15918
|
-
async function loadGithubStateConfig(opts) {
|
|
15919
|
-
const configFile = await githubContentsFile({
|
|
15920
|
-
owner: opts.owner,
|
|
15921
|
-
repo: opts.repo,
|
|
15922
|
-
path: "kody.config.json",
|
|
15923
|
-
githubToken: opts.githubToken,
|
|
15924
|
-
fetchImpl: opts.fetchImpl
|
|
15925
|
-
});
|
|
15926
|
-
let raw = {};
|
|
15927
|
-
if (configFile) {
|
|
15928
|
-
const decoded = decodeContentsFile(configFile, "kody.config.json");
|
|
15929
|
-
try {
|
|
15930
|
-
raw = JSON.parse(decoded.content);
|
|
15931
|
-
} catch (err) {
|
|
15932
|
-
throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
15933
|
-
}
|
|
15934
|
-
}
|
|
15935
|
-
const githubRaw = recordValue3(raw.github) ?? {};
|
|
15936
|
-
const github = {
|
|
15937
|
-
owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
|
|
15938
|
-
repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
|
|
15939
|
-
};
|
|
15940
|
-
const nestedState = recordValue3(raw.state) ?? {};
|
|
15941
|
-
const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
|
|
15942
|
-
const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
|
|
15943
|
-
const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
|
|
15944
|
-
parseStateRepoSlug(stateRepo);
|
|
15945
|
-
const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
|
|
15946
|
-
return {
|
|
15947
|
-
github,
|
|
15948
|
-
state: {
|
|
15949
|
-
repo: stateRepo,
|
|
15950
|
-
path: normalizeStatePath(statePath)
|
|
15951
|
-
}
|
|
15952
|
-
};
|
|
15953
|
-
}
|
|
15954
|
-
async function readGithubStateText(opts) {
|
|
15955
|
-
const config = await loadGithubStateConfig(opts);
|
|
15956
|
-
return readGithubStateTextWithConfig({
|
|
15957
|
-
config,
|
|
15958
|
-
filePath: opts.filePath,
|
|
15959
|
-
githubToken: opts.githubToken,
|
|
15960
|
-
fetchImpl: opts.fetchImpl
|
|
15961
|
-
});
|
|
15962
|
-
}
|
|
15963
|
-
async function readGithubStateTextWithConfig(opts) {
|
|
15964
|
-
const target = stateRepoPath(opts.config, opts.filePath);
|
|
15965
|
-
const parsed = parseStateRepo(opts.config);
|
|
15966
|
-
const file = await githubContentsFile({
|
|
15967
|
-
owner: parsed.owner,
|
|
15968
|
-
repo: parsed.repo,
|
|
15969
|
-
path: target,
|
|
15970
|
-
githubToken: opts.githubToken,
|
|
15971
|
-
fetchImpl: opts.fetchImpl
|
|
15972
|
-
});
|
|
15973
|
-
return file ? decodeContentsFile(file, target) : null;
|
|
15974
|
-
}
|
|
15975
|
-
async function writeGithubStateTextWithConfig(opts) {
|
|
15976
|
-
const target = stateRepoPath(opts.config, opts.filePath);
|
|
15977
|
-
const parsed = parseStateRepo(opts.config);
|
|
15978
|
-
const payload = {
|
|
15979
|
-
message: opts.message,
|
|
15980
|
-
content: Buffer.from(opts.content, "utf-8").toString("base64")
|
|
15981
|
-
};
|
|
15982
|
-
if (opts.sha) payload.sha = opts.sha;
|
|
15983
|
-
const doFetch = opts.fetchImpl ?? fetch;
|
|
15984
|
-
const res = await doFetch(contentsUrl(parsed.owner, parsed.repo, target), {
|
|
15985
|
-
method: "PUT",
|
|
15986
|
-
headers: {
|
|
15987
|
-
Authorization: `Bearer ${opts.githubToken}`,
|
|
15988
|
-
Accept: "application/vnd.github+json",
|
|
15989
|
-
"Content-Type": "application/json",
|
|
15990
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
15991
|
-
"User-Agent": "kody-engine"
|
|
15992
|
-
},
|
|
15993
|
-
body: JSON.stringify(payload),
|
|
15994
|
-
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
15995
|
-
});
|
|
15996
|
-
if (!res.ok) {
|
|
15997
|
-
throw new Error(
|
|
15998
|
-
`GitHub write ${parsed.owner}/${parsed.repo}:${target}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
15999
|
-
);
|
|
16000
|
-
}
|
|
16001
|
-
}
|
|
16002
|
-
function jsonlLines(text) {
|
|
16003
|
-
return text.split("\n").filter((line) => line.length > 0);
|
|
16004
|
-
}
|
|
16005
|
-
function renderJsonl(lines) {
|
|
16006
|
-
return lines.length > 0 ? `${lines.join("\n")}
|
|
16007
|
-
` : "";
|
|
16008
|
-
}
|
|
16009
|
-
function mergeJsonl(localText, remoteText) {
|
|
16010
|
-
const remoteLines = jsonlLines(remoteText);
|
|
16011
|
-
const seen = new Set(remoteLines);
|
|
16012
|
-
const localOnly = jsonlLines(localText).filter((line) => !seen.has(line));
|
|
16013
|
-
return renderJsonl([...remoteLines, ...localOnly]);
|
|
16014
|
-
}
|
|
16015
|
-
async function syncJsonlFileFromGithubState(opts) {
|
|
16016
|
-
const remote = await readGithubStateTextWithConfig(opts);
|
|
16017
|
-
if (!remote) return;
|
|
16018
|
-
const local = fs39.existsSync(opts.localPath) ? fs39.readFileSync(opts.localPath, "utf-8") : "";
|
|
16019
|
-
const next = mergeJsonl(local, remote.content);
|
|
16020
|
-
if (next === local) return;
|
|
16021
|
-
fs39.mkdirSync(path37.dirname(opts.localPath), { recursive: true });
|
|
16022
|
-
fs39.writeFileSync(opts.localPath, next);
|
|
16023
|
-
}
|
|
16024
|
-
async function persistJsonlFileToGithubState(opts) {
|
|
16025
|
-
if (!fs39.existsSync(opts.localPath)) return;
|
|
16026
|
-
const localText = fs39.readFileSync(opts.localPath, "utf-8");
|
|
16027
|
-
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
16028
|
-
const remote = await readGithubStateTextWithConfig(opts);
|
|
16029
|
-
const body = mergeJsonl(localText, remote?.content ?? "");
|
|
16030
|
-
try {
|
|
16031
|
-
await writeGithubStateTextWithConfig({
|
|
16032
|
-
config: opts.config,
|
|
16033
|
-
filePath: opts.filePath,
|
|
16034
|
-
content: body,
|
|
16035
|
-
message: opts.message,
|
|
16036
|
-
githubToken: opts.githubToken,
|
|
16037
|
-
sha: remote?.sha,
|
|
16038
|
-
fetchImpl: opts.fetchImpl
|
|
16039
|
-
});
|
|
16040
|
-
return;
|
|
16041
|
-
} catch (err) {
|
|
16042
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
16043
|
-
const conflict = /409|422|does not match|is at|but expected/i.test(msg);
|
|
16044
|
-
if (!conflict || attempt === 3) throw err;
|
|
16045
|
-
}
|
|
16046
|
-
}
|
|
16047
|
-
}
|
|
16048
|
-
var GITHUB_API, REQ_TIMEOUT_MS;
|
|
16049
|
-
var init_stateRepoGithub = __esm({
|
|
16050
|
-
"src/stateRepoGithub.ts"() {
|
|
16051
|
-
"use strict";
|
|
16052
|
-
init_stateRepo();
|
|
16053
|
-
GITHUB_API = "https://api.github.com";
|
|
16054
|
-
REQ_TIMEOUT_MS = 3e4;
|
|
16055
|
-
}
|
|
16056
|
-
});
|
|
16057
|
-
|
|
16058
16212
|
// src/scripts/previewBuildHelpers.ts
|
|
16059
|
-
import { createDecipheriv, createHash as
|
|
16213
|
+
import { createDecipheriv as createDecipheriv2, createHash as createHash4, hkdfSync as hkdfSync2 } from "crypto";
|
|
16060
16214
|
function shortHash(s) {
|
|
16061
|
-
return
|
|
16215
|
+
return createHash4("sha256").update(s).digest("hex").slice(0, 6);
|
|
16062
16216
|
}
|
|
16063
16217
|
function previewAppName(repo, pr) {
|
|
16064
16218
|
const [owner, name] = repo.split("/");
|
|
@@ -16087,7 +16241,7 @@ function decryptVaultPayload(payload, keyRaw) {
|
|
|
16087
16241
|
const iv = Buffer.from(ivB64, "base64");
|
|
16088
16242
|
const ct = Buffer.from(ctB64, "base64");
|
|
16089
16243
|
const tag = Buffer.from(tagB64, "base64");
|
|
16090
|
-
const decipher =
|
|
16244
|
+
const decipher = createDecipheriv2("aes-256-gcm", key, iv);
|
|
16091
16245
|
decipher.setAuthTag(tag);
|
|
16092
16246
|
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
16093
16247
|
}
|
|
@@ -16100,7 +16254,7 @@ function derivePreviewVerifyKey(masterKeyRaw) {
|
|
|
16100
16254
|
if (masterKey.length !== 32) {
|
|
16101
16255
|
throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
|
|
16102
16256
|
}
|
|
16103
|
-
return Buffer.from(
|
|
16257
|
+
return Buffer.from(hkdfSync2("sha256", masterKey, Buffer.alloc(0), PREVIEW_KEY_INFO, 32)).toString("hex");
|
|
16104
16258
|
}
|
|
16105
16259
|
function previewRuntimeEnv(args) {
|
|
16106
16260
|
return {
|
|
@@ -16131,7 +16285,7 @@ function formatPreviewComment(args) {
|
|
|
16131
16285
|
].join("\n");
|
|
16132
16286
|
}
|
|
16133
16287
|
function defaultImageTag(repo, ref) {
|
|
16134
|
-
return
|
|
16288
|
+
return createHash4("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
|
|
16135
16289
|
}
|
|
16136
16290
|
var NEVER_PASS_TO_BUILD, PREVIEW_KEY_INFO;
|
|
16137
16291
|
var init_previewBuildHelpers = __esm({
|
|
@@ -17746,7 +17900,7 @@ var init_scripts = __esm({
|
|
|
17746
17900
|
init_appendCompanyActivity();
|
|
17747
17901
|
init_appendCompanyIntentDecision();
|
|
17748
17902
|
init_applyCapabilityReports();
|
|
17749
|
-
|
|
17903
|
+
init_applyAgencyArchitectDecision();
|
|
17750
17904
|
init_buildSyntheticPlugin();
|
|
17751
17905
|
init_checkCoverageWithRetry();
|
|
17752
17906
|
init_classifyByLabel();
|
|
@@ -17793,7 +17947,7 @@ var init_scripts = __esm({
|
|
|
17793
17947
|
init_openAgentFactoryStatePr();
|
|
17794
17948
|
init_openQaIssue();
|
|
17795
17949
|
init_parseAgentResult();
|
|
17796
|
-
|
|
17950
|
+
init_parseAgencyArchitectDecision();
|
|
17797
17951
|
init_parseIssueStateFromAgentResult();
|
|
17798
17952
|
init_parseJobStateFromAgentResult();
|
|
17799
17953
|
init_parseReproOutput();
|
|
@@ -17888,7 +18042,7 @@ var init_scripts = __esm({
|
|
|
17888
18042
|
};
|
|
17889
18043
|
postflightScripts = {
|
|
17890
18044
|
parseAgentResult: parseAgentResult2,
|
|
17891
|
-
|
|
18045
|
+
parseAgencyArchitectDecision,
|
|
17892
18046
|
parseIssueStateFromAgentResult,
|
|
17893
18047
|
parseJobStateFromAgentResult,
|
|
17894
18048
|
parseReproOutput,
|
|
@@ -17896,7 +18050,7 @@ var init_scripts = __esm({
|
|
|
17896
18050
|
writeJobStateFile,
|
|
17897
18051
|
appendCompanyActivity,
|
|
17898
18052
|
appendCompanyIntentDecision: appendCompanyIntentDecision2,
|
|
17899
|
-
|
|
18053
|
+
applyAgencyArchitectDecision,
|
|
17900
18054
|
requireFeedbackActions,
|
|
17901
18055
|
requirePlanDeviations,
|
|
17902
18056
|
verify,
|
|
@@ -17987,7 +18141,7 @@ function hydrateStateWorkspace(config, cwd) {
|
|
|
17987
18141
|
hydratedWorkspaces.add(hydrateKey);
|
|
17988
18142
|
}
|
|
17989
18143
|
function fetchStateSnapshot(parsed) {
|
|
17990
|
-
const cacheDir = path41.join(cacheRoot2(),
|
|
18144
|
+
const cacheDir = path41.join(cacheRoot2(), cacheKey3(parsed));
|
|
17991
18145
|
const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
17992
18146
|
try {
|
|
17993
18147
|
fs43.mkdirSync(path41.dirname(cacheDir), { recursive: true });
|
|
@@ -18012,7 +18166,7 @@ function fetchStateSnapshot(parsed) {
|
|
|
18012
18166
|
function cacheRoot2() {
|
|
18013
18167
|
return process.env[CACHE_ENV2]?.trim() || path41.join(os7.homedir(), ".cache", "kody", "state-repo");
|
|
18014
18168
|
}
|
|
18015
|
-
function
|
|
18169
|
+
function cacheKey3(parsed) {
|
|
18016
18170
|
return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
|
|
18017
18171
|
}
|
|
18018
18172
|
function runGit3(args) {
|
|
@@ -23130,42 +23284,8 @@ async function runCapabilityFallbackTick(deps) {
|
|
|
23130
23284
|
return { ran: true, claimed };
|
|
23131
23285
|
}
|
|
23132
23286
|
|
|
23133
|
-
// src/pool
|
|
23134
|
-
|
|
23135
|
-
var POOL_API_KEY_INFO = "kody-pool-api:v1";
|
|
23136
|
-
var RUNNER_API_KEY_INFO = "kody-runner-api:v1";
|
|
23137
|
-
function masterKeyBytes(raw) {
|
|
23138
|
-
const v = raw.trim();
|
|
23139
|
-
if (!v) throw new Error("KODY_MASTER_KEY is empty");
|
|
23140
|
-
if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
|
|
23141
|
-
return Buffer.from(v, "hex");
|
|
23142
|
-
}
|
|
23143
|
-
return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
23144
|
-
}
|
|
23145
|
-
function deriveKey(master, info, length = 32) {
|
|
23146
|
-
return Buffer.from(hkdfSync2("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
|
|
23147
|
-
}
|
|
23148
|
-
function derivePoolApiKey(master) {
|
|
23149
|
-
return deriveKey(master, POOL_API_KEY_INFO);
|
|
23150
|
-
}
|
|
23151
|
-
function deriveRunnerApiKey(master) {
|
|
23152
|
-
return deriveKey(master, RUNNER_API_KEY_INFO);
|
|
23153
|
-
}
|
|
23154
|
-
function bearerOk(headerAuth, xApiKey, expected) {
|
|
23155
|
-
const x = (xApiKey ?? "").trim();
|
|
23156
|
-
if (x && timingEqual(x, expected)) return true;
|
|
23157
|
-
const a = (headerAuth ?? "").trim();
|
|
23158
|
-
if (a.toLowerCase().startsWith("bearer ")) {
|
|
23159
|
-
return timingEqual(a.slice(7).trim(), expected);
|
|
23160
|
-
}
|
|
23161
|
-
return false;
|
|
23162
|
-
}
|
|
23163
|
-
function timingEqual(a, b) {
|
|
23164
|
-
if (a.length !== b.length) return false;
|
|
23165
|
-
let diff = 0;
|
|
23166
|
-
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
23167
|
-
return diff === 0;
|
|
23168
|
-
}
|
|
23287
|
+
// src/servers/pool-serve.ts
|
|
23288
|
+
init_keys();
|
|
23169
23289
|
|
|
23170
23290
|
// src/pool/fly.ts
|
|
23171
23291
|
var FLY_API_BASE = "https://api.machines.dev/v1";
|
|
@@ -23530,59 +23650,8 @@ function isSuspendedWithIp(m) {
|
|
|
23530
23650
|
return (m.state === "suspended" || m.state === "suspending") && !!m.private_ip;
|
|
23531
23651
|
}
|
|
23532
23652
|
|
|
23533
|
-
// src/pool/vault.ts
|
|
23534
|
-
init_stateRepoGithub();
|
|
23535
|
-
import { createDecipheriv as createDecipheriv2 } from "crypto";
|
|
23536
|
-
var VAULT_PATH = "secrets.enc";
|
|
23537
|
-
var CACHE_TTL_MS = 6e4;
|
|
23538
|
-
var cache = /* @__PURE__ */ new Map();
|
|
23539
|
-
function decryptVault(payload, masterKey) {
|
|
23540
|
-
const parts = payload.split(":");
|
|
23541
|
-
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
23542
|
-
throw new Error("invalid vault payload format");
|
|
23543
|
-
}
|
|
23544
|
-
const [, ivB64, ctB64, tagB64] = parts;
|
|
23545
|
-
const iv = Buffer.from(ivB64, "base64");
|
|
23546
|
-
const ct = Buffer.from(ctB64, "base64");
|
|
23547
|
-
const tag = Buffer.from(tagB64, "base64");
|
|
23548
|
-
const decipher = createDecipheriv2("aes-256-gcm", masterKey, iv);
|
|
23549
|
-
decipher.setAuthTag(tag);
|
|
23550
|
-
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
23551
|
-
}
|
|
23552
|
-
async function readVaultSecrets(opts) {
|
|
23553
|
-
const key = `${opts.owner}/${opts.repo}`.toLowerCase();
|
|
23554
|
-
const hit = cache.get(key);
|
|
23555
|
-
if (hit && hit.expiresAt > Date.now()) return hit.secrets;
|
|
23556
|
-
const file = await readGithubStateText({
|
|
23557
|
-
owner: opts.owner,
|
|
23558
|
-
repo: opts.repo,
|
|
23559
|
-
filePath: VAULT_PATH,
|
|
23560
|
-
githubToken: opts.githubToken,
|
|
23561
|
-
fetchImpl: opts.fetchImpl
|
|
23562
|
-
});
|
|
23563
|
-
if (!file) {
|
|
23564
|
-
cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
23565
|
-
return {};
|
|
23566
|
-
}
|
|
23567
|
-
const ciphertext = file.content.trim();
|
|
23568
|
-
const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
|
|
23569
|
-
const flat = {};
|
|
23570
|
-
for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
|
|
23571
|
-
if (entry && typeof entry.value === "string") flat[name] = entry.value;
|
|
23572
|
-
}
|
|
23573
|
-
cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
23574
|
-
return flat;
|
|
23575
|
-
}
|
|
23576
|
-
async function readRepoSecret(opts) {
|
|
23577
|
-
const secrets = await readVaultSecrets(opts);
|
|
23578
|
-
const v = secrets[opts.name];
|
|
23579
|
-
return v?.trim() ? v : null;
|
|
23580
|
-
}
|
|
23581
|
-
async function readRepoSecrets(opts) {
|
|
23582
|
-
return readVaultSecrets(opts);
|
|
23583
|
-
}
|
|
23584
|
-
|
|
23585
23653
|
// src/pool/registry.ts
|
|
23654
|
+
init_stateRepoVault();
|
|
23586
23655
|
var POOL_MIN_VAULT_KEY = "POOL_MIN";
|
|
23587
23656
|
var POOL_MIN_MAX = 10;
|
|
23588
23657
|
function parsePoolMin(raw, dflt) {
|
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.307",
|
|
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",
|
|
@@ -12,28 +12,6 @@
|
|
|
12
12
|
"templates",
|
|
13
13
|
"kody.config.schema.json"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"kody:run": "tsx bin/kody.ts",
|
|
17
|
-
"serve": "tsx bin/kody.ts serve",
|
|
18
|
-
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
19
|
-
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
20
|
-
"clean:dist": "node scripts/clean-dist.cjs",
|
|
21
|
-
"build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
|
|
22
|
-
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
23
|
-
"pretest": "pnpm check:modularity",
|
|
24
|
-
"test": "vitest run tests/unit tests/int --coverage",
|
|
25
|
-
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
26
|
-
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
27
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
28
|
-
"test:all": "vitest run tests --no-coverage",
|
|
29
|
-
"typecheck": "tsc --noEmit",
|
|
30
|
-
"lint": "biome check",
|
|
31
|
-
"lint:fix": "biome check --write",
|
|
32
|
-
"format": "biome format --write",
|
|
33
|
-
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
34
|
-
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
|
|
35
|
-
"prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build && pnpm verify:package"
|
|
36
|
-
},
|
|
37
15
|
"dependencies": {
|
|
38
16
|
"@actions/cache": "^6.0.0",
|
|
39
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
@@ -57,5 +35,26 @@
|
|
|
57
35
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
58
36
|
},
|
|
59
37
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
60
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
61
|
-
|
|
38
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"kody:run": "tsx bin/kody.ts",
|
|
41
|
+
"serve": "tsx bin/kody.ts serve",
|
|
42
|
+
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
43
|
+
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
44
|
+
"clean:dist": "node scripts/clean-dist.cjs",
|
|
45
|
+
"build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
|
|
46
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
47
|
+
"pretest": "pnpm check:modularity",
|
|
48
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
49
|
+
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
50
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
51
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
52
|
+
"test:all": "vitest run tests --no-coverage",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"lint": "biome check",
|
|
55
|
+
"lint:fix": "biome check --write",
|
|
56
|
+
"format": "biome format --write",
|
|
57
|
+
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
58
|
+
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
|
|
59
|
+
}
|
|
60
|
+
}
|