@kody-ade/kody-engine 0.4.496 → 0.4.498
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/README.md +5 -1
- package/dist/bin/kody.js +276 -234
- package/dist/runtime-services/goal-scheduler/scheduler.sh +0 -0
- package/package.json +25 -26
package/README.md
CHANGED
|
@@ -107,7 +107,11 @@ fans out scheduled capabilities at runtime, so capability schedules never
|
|
|
107
107
|
generate additional GitHub workflow files. Idempotent — pass `--force` to
|
|
108
108
|
overwrite.
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
Store model provider keys (for example `MINIMAX_API_KEY`) in the connected
|
|
111
|
+
repository's Kody vault. GitHub Actions uses OIDC to request only the selected
|
|
112
|
+
model key at runtime; user secrets are not copied into Actions. `KODY_TOKEN`
|
|
113
|
+
remains an optional GitHub authentication fallback when Kody must trigger
|
|
114
|
+
downstream workflows or modify `.github/workflows/*`.
|
|
111
115
|
|
|
112
116
|
The consumer workflow listens on `issue_comment` for `@kody ...` dispatch and `workflow_dispatch` for manual runs, chat mode, and scheduled wakeups.
|
|
113
117
|
|
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.498",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -4267,56 +4267,6 @@ var init_agencyBoundaryEval = __esm({
|
|
|
4267
4267
|
}
|
|
4268
4268
|
});
|
|
4269
4269
|
|
|
4270
|
-
// src/scripts/capabilityExecutionEnvironment.ts
|
|
4271
|
-
function capabilityInputEnvironment(input) {
|
|
4272
|
-
const environment = {
|
|
4273
|
-
KODY_CAPABILITY_INPUT: JSON.stringify(input ?? null)
|
|
4274
|
-
};
|
|
4275
|
-
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
4276
|
-
return environment;
|
|
4277
|
-
}
|
|
4278
|
-
for (const [name, value] of Object.entries(input)) {
|
|
4279
|
-
if (value === void 0 || value === null) continue;
|
|
4280
|
-
const key = environmentKey(name);
|
|
4281
|
-
environment[`KODY_ARG_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
|
|
4282
|
-
}
|
|
4283
|
-
return environment;
|
|
4284
|
-
}
|
|
4285
|
-
function capabilityConfigEnvironment(config) {
|
|
4286
|
-
if (!config || typeof config !== "object" || Array.isArray(config)) return {};
|
|
4287
|
-
return Object.fromEntries(
|
|
4288
|
-
flattenConfig(config).map(([key, value]) => [
|
|
4289
|
-
`KODY_CFG_${key}`,
|
|
4290
|
-
value
|
|
4291
|
-
])
|
|
4292
|
-
);
|
|
4293
|
-
}
|
|
4294
|
-
function flattenConfig(config, prefix = "") {
|
|
4295
|
-
const entries = [];
|
|
4296
|
-
for (const [name, value] of Object.entries(config)) {
|
|
4297
|
-
if (value === null || value === void 0) continue;
|
|
4298
|
-
const key = prefix ? `${prefix}_${environmentKey(name)}` : environmentKey(name);
|
|
4299
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4300
|
-
entries.push([key, String(value)]);
|
|
4301
|
-
} else if (Array.isArray(value)) {
|
|
4302
|
-
entries.push([key, JSON.stringify(value)]);
|
|
4303
|
-
} else if (typeof value === "object") {
|
|
4304
|
-
entries.push(
|
|
4305
|
-
...flattenConfig(value, key)
|
|
4306
|
-
);
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
return entries;
|
|
4310
|
-
}
|
|
4311
|
-
function environmentKey(name) {
|
|
4312
|
-
return name.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
|
|
4313
|
-
}
|
|
4314
|
-
var init_capabilityExecutionEnvironment = __esm({
|
|
4315
|
-
"src/scripts/capabilityExecutionEnvironment.ts"() {
|
|
4316
|
-
"use strict";
|
|
4317
|
-
}
|
|
4318
|
-
});
|
|
4319
|
-
|
|
4320
4270
|
// src/agency/capability-contract-validation.ts
|
|
4321
4271
|
import Ajv from "ajv";
|
|
4322
4272
|
function validateCapabilityContractValue(boundary, schema, value) {
|
|
@@ -6949,12 +6899,16 @@ function resolveLitellmCommand() {
|
|
|
6949
6899
|
throw new Error("litellm is importable but its console script was not found next to python3");
|
|
6950
6900
|
}
|
|
6951
6901
|
}
|
|
6952
|
-
async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL) {
|
|
6902
|
+
async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL, runtimeEnvironment = {}) {
|
|
6953
6903
|
if (!needsLitellmProxy(model)) return null;
|
|
6954
6904
|
const cmd = resolveLitellmCommand();
|
|
6955
6905
|
let activeUrl = url.replace(/\/+$/, "");
|
|
6956
6906
|
const modelGroups = litellmModelGroups(model);
|
|
6957
|
-
const childEnv = stripBlockingEnv({
|
|
6907
|
+
const childEnv = stripBlockingEnv({
|
|
6908
|
+
...process.env,
|
|
6909
|
+
...readDotenvApiKeys(projectDir),
|
|
6910
|
+
...runtimeEnvironment
|
|
6911
|
+
});
|
|
6958
6912
|
let child;
|
|
6959
6913
|
let logPath;
|
|
6960
6914
|
const spawnProxy = () => {
|
|
@@ -7332,6 +7286,256 @@ var init_runtimeCleanup = __esm({
|
|
|
7332
7286
|
}
|
|
7333
7287
|
});
|
|
7334
7288
|
|
|
7289
|
+
// src/backendVault.ts
|
|
7290
|
+
import { createDecipheriv, createHash as createHash4 } from "crypto";
|
|
7291
|
+
function cacheKey(owner, repo, masterKey) {
|
|
7292
|
+
const keyHash = createHash4("sha256").update(masterKey).digest("hex").slice(0, 16);
|
|
7293
|
+
return `${owner}/${repo}:${keyHash}`.toLowerCase();
|
|
7294
|
+
}
|
|
7295
|
+
function decryptVault(payload, masterKey) {
|
|
7296
|
+
const parts = payload.split(":");
|
|
7297
|
+
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
7298
|
+
throw new Error("invalid vault payload format");
|
|
7299
|
+
}
|
|
7300
|
+
const [, ivB64, ctB64, tagB64] = parts;
|
|
7301
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
7302
|
+
const ct = Buffer.from(ctB64, "base64");
|
|
7303
|
+
const tag = Buffer.from(tagB64, "base64");
|
|
7304
|
+
const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
|
|
7305
|
+
decipher.setAuthTag(tag);
|
|
7306
|
+
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
7307
|
+
}
|
|
7308
|
+
async function readVaultSecrets(opts) {
|
|
7309
|
+
const key = cacheKey(opts.owner, opts.repo, opts.masterKey);
|
|
7310
|
+
const hit = cache.get(key);
|
|
7311
|
+
if (hit && hit.expiresAt > Date.now()) return hit.secrets;
|
|
7312
|
+
const record2 = await createStateBackendFromEnv().getRepoDoc(`${opts.owner}/${opts.repo}`, VAULT_PATH);
|
|
7313
|
+
const raw = record2?.doc;
|
|
7314
|
+
const ciphertext = raw && typeof raw === "object" && !Array.isArray(raw) && typeof raw.ciphertext === "string" ? raw.ciphertext.trim() : "";
|
|
7315
|
+
if (!ciphertext) {
|
|
7316
|
+
cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
7317
|
+
return {};
|
|
7318
|
+
}
|
|
7319
|
+
const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
|
|
7320
|
+
const flat = {};
|
|
7321
|
+
for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
|
|
7322
|
+
if (entry && typeof entry.value === "string") flat[name] = entry.value;
|
|
7323
|
+
}
|
|
7324
|
+
cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
7325
|
+
return flat;
|
|
7326
|
+
}
|
|
7327
|
+
async function readRepoSecret(opts) {
|
|
7328
|
+
const secrets = await readVaultSecrets(opts);
|
|
7329
|
+
const v = secrets[opts.name];
|
|
7330
|
+
return v?.trim() ? v : null;
|
|
7331
|
+
}
|
|
7332
|
+
async function readRepoSecrets(opts) {
|
|
7333
|
+
return readVaultSecrets(opts);
|
|
7334
|
+
}
|
|
7335
|
+
var VAULT_PATH, CACHE_TTL_MS, cache;
|
|
7336
|
+
var init_backendVault = __esm({
|
|
7337
|
+
"src/backendVault.ts"() {
|
|
7338
|
+
"use strict";
|
|
7339
|
+
init_state_backend();
|
|
7340
|
+
VAULT_PATH = "secrets.enc";
|
|
7341
|
+
CACHE_TTL_MS = 6e4;
|
|
7342
|
+
cache = /* @__PURE__ */ new Map();
|
|
7343
|
+
}
|
|
7344
|
+
});
|
|
7345
|
+
|
|
7346
|
+
// src/pool/keys.ts
|
|
7347
|
+
import { hkdfSync } from "crypto";
|
|
7348
|
+
function masterKeyBytes(raw) {
|
|
7349
|
+
const v = raw.trim();
|
|
7350
|
+
if (!v) throw new Error("KODY_MASTER_KEY is empty");
|
|
7351
|
+
if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
|
|
7352
|
+
return Buffer.from(v, "hex");
|
|
7353
|
+
}
|
|
7354
|
+
return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
7355
|
+
}
|
|
7356
|
+
function deriveKey(master, info, length = 32) {
|
|
7357
|
+
return Buffer.from(hkdfSync("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
|
|
7358
|
+
}
|
|
7359
|
+
function derivePoolApiKey(master) {
|
|
7360
|
+
return deriveKey(master, POOL_API_KEY_INFO);
|
|
7361
|
+
}
|
|
7362
|
+
function deriveRunnerApiKey(master) {
|
|
7363
|
+
return deriveKey(master, RUNNER_API_KEY_INFO);
|
|
7364
|
+
}
|
|
7365
|
+
function bearerOk(headerAuth, xApiKey, expected) {
|
|
7366
|
+
const x = (xApiKey ?? "").trim();
|
|
7367
|
+
if (x && timingEqual(x, expected)) return true;
|
|
7368
|
+
const a = (headerAuth ?? "").trim();
|
|
7369
|
+
if (a.toLowerCase().startsWith("bearer ")) {
|
|
7370
|
+
return timingEqual(a.slice(7).trim(), expected);
|
|
7371
|
+
}
|
|
7372
|
+
return false;
|
|
7373
|
+
}
|
|
7374
|
+
function timingEqual(a, b) {
|
|
7375
|
+
if (a.length !== b.length) return false;
|
|
7376
|
+
let diff = 0;
|
|
7377
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
7378
|
+
return diff === 0;
|
|
7379
|
+
}
|
|
7380
|
+
var POOL_API_KEY_INFO, RUNNER_API_KEY_INFO;
|
|
7381
|
+
var init_keys = __esm({
|
|
7382
|
+
"src/pool/keys.ts"() {
|
|
7383
|
+
"use strict";
|
|
7384
|
+
POOL_API_KEY_INFO = "kody-pool-api:v1";
|
|
7385
|
+
RUNNER_API_KEY_INFO = "kody-runner-api:v1";
|
|
7386
|
+
}
|
|
7387
|
+
});
|
|
7388
|
+
|
|
7389
|
+
// src/scripts/runtimeSecrets.ts
|
|
7390
|
+
function envSecret(name, env) {
|
|
7391
|
+
const value = env[name]?.trim() ? env[name] : "";
|
|
7392
|
+
return value ? { value, source: "env" } : { value: "", source: "missing" };
|
|
7393
|
+
}
|
|
7394
|
+
async function resolveRuntimeSecret(name, ctx, opts = {}) {
|
|
7395
|
+
const env = opts.env ?? process.env;
|
|
7396
|
+
if (hasGitHubActionsIdentity(env)) {
|
|
7397
|
+
try {
|
|
7398
|
+
const value = await readRuntimeSecretFromKody(name, env);
|
|
7399
|
+
if (value) return { value, source: "vault" };
|
|
7400
|
+
return envSecret(name, env);
|
|
7401
|
+
} catch (err) {
|
|
7402
|
+
const fallback = envSecret(name, env);
|
|
7403
|
+
return {
|
|
7404
|
+
...fallback,
|
|
7405
|
+
warning: `Kody secret read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
7406
|
+
};
|
|
7407
|
+
}
|
|
7408
|
+
}
|
|
7409
|
+
const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
|
|
7410
|
+
if (!masterRaw || !env.CONVEX_URL?.trim() || !env.KODY_SERVICE_KEY?.trim()) return envSecret(name, env);
|
|
7411
|
+
try {
|
|
7412
|
+
const masterKey = masterKeyBytes(masterRaw);
|
|
7413
|
+
if (masterKey.length !== 32) {
|
|
7414
|
+
throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
|
|
7415
|
+
}
|
|
7416
|
+
const value = await readRepoSecret({
|
|
7417
|
+
owner: ctx.config.github.owner,
|
|
7418
|
+
repo: ctx.config.github.repo,
|
|
7419
|
+
name,
|
|
7420
|
+
masterKey
|
|
7421
|
+
});
|
|
7422
|
+
if (value) return { value, source: "vault" };
|
|
7423
|
+
} catch (err) {
|
|
7424
|
+
const fallback = envSecret(name, env);
|
|
7425
|
+
return {
|
|
7426
|
+
...fallback,
|
|
7427
|
+
warning: `vault read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
7428
|
+
};
|
|
7429
|
+
}
|
|
7430
|
+
return envSecret(name, env);
|
|
7431
|
+
}
|
|
7432
|
+
async function resolveRuntimeSecrets(names, ctx, opts = {}) {
|
|
7433
|
+
const declared = Array.isArray(names) ? [...new Set(names.filter((name) => typeof name === "string" && /^[A-Z][A-Z0-9_]*$/.test(name)))] : [];
|
|
7434
|
+
const resolved = [];
|
|
7435
|
+
for (const name of declared) {
|
|
7436
|
+
resolved.push({ name, result: await resolveRuntimeSecret(name, ctx, opts) });
|
|
7437
|
+
}
|
|
7438
|
+
const warnings = resolved.flatMap(({ result }) => result.warning ? [result.warning] : []);
|
|
7439
|
+
const fallbackSecrets = Object.fromEntries(
|
|
7440
|
+
resolved.flatMap(({ name, result }) => result.source === "env" && result.value ? [[name, result.value]] : [])
|
|
7441
|
+
);
|
|
7442
|
+
if (hasGitHubActionsIdentity(opts.env ?? process.env) && Object.keys(fallbackSecrets).length > 0) {
|
|
7443
|
+
try {
|
|
7444
|
+
await writeRuntimeSecretsToKody(fallbackSecrets, opts.env ?? process.env);
|
|
7445
|
+
} catch (err) {
|
|
7446
|
+
warnings.push(`Kody secret migration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7447
|
+
}
|
|
7448
|
+
}
|
|
7449
|
+
return {
|
|
7450
|
+
environment: Object.fromEntries(
|
|
7451
|
+
resolved.flatMap(({ name, result }) => result.value ? [[name, result.value]] : [])
|
|
7452
|
+
),
|
|
7453
|
+
warnings
|
|
7454
|
+
};
|
|
7455
|
+
}
|
|
7456
|
+
var init_runtimeSecrets = __esm({
|
|
7457
|
+
"src/scripts/runtimeSecrets.ts"() {
|
|
7458
|
+
"use strict";
|
|
7459
|
+
init_backendVault();
|
|
7460
|
+
init_kody_api_client();
|
|
7461
|
+
init_keys();
|
|
7462
|
+
}
|
|
7463
|
+
});
|
|
7464
|
+
|
|
7465
|
+
// src/runtimeModelEnvironment.ts
|
|
7466
|
+
async function resolveRuntimeModelEnvironment(model, ctx) {
|
|
7467
|
+
const name = model.apiKeyEnvVar ?? providerApiKeyEnvVar(model.provider);
|
|
7468
|
+
const result = await resolveRuntimeSecret(name, ctx);
|
|
7469
|
+
const warnings = result.warning ? [result.warning] : [];
|
|
7470
|
+
if (!result.value) {
|
|
7471
|
+
return {
|
|
7472
|
+
environment: {},
|
|
7473
|
+
warnings: [...warnings, `Model credential ${name} is missing from the Kody vault.`]
|
|
7474
|
+
};
|
|
7475
|
+
}
|
|
7476
|
+
return {
|
|
7477
|
+
environment: { [name]: result.value },
|
|
7478
|
+
warnings
|
|
7479
|
+
};
|
|
7480
|
+
}
|
|
7481
|
+
var init_runtimeModelEnvironment = __esm({
|
|
7482
|
+
"src/runtimeModelEnvironment.ts"() {
|
|
7483
|
+
"use strict";
|
|
7484
|
+
init_config();
|
|
7485
|
+
init_runtimeSecrets();
|
|
7486
|
+
}
|
|
7487
|
+
});
|
|
7488
|
+
|
|
7489
|
+
// src/scripts/capabilityExecutionEnvironment.ts
|
|
7490
|
+
function capabilityInputEnvironment(input) {
|
|
7491
|
+
const environment = {
|
|
7492
|
+
KODY_CAPABILITY_INPUT: JSON.stringify(input ?? null)
|
|
7493
|
+
};
|
|
7494
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
7495
|
+
return environment;
|
|
7496
|
+
}
|
|
7497
|
+
for (const [name, value] of Object.entries(input)) {
|
|
7498
|
+
if (value === void 0 || value === null) continue;
|
|
7499
|
+
const key = environmentKey(name);
|
|
7500
|
+
environment[`KODY_ARG_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
|
|
7501
|
+
}
|
|
7502
|
+
return environment;
|
|
7503
|
+
}
|
|
7504
|
+
function capabilityConfigEnvironment(config) {
|
|
7505
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) return {};
|
|
7506
|
+
return Object.fromEntries(
|
|
7507
|
+
flattenConfig(config).map(([key, value]) => [
|
|
7508
|
+
`KODY_CFG_${key}`,
|
|
7509
|
+
value
|
|
7510
|
+
])
|
|
7511
|
+
);
|
|
7512
|
+
}
|
|
7513
|
+
function flattenConfig(config, prefix = "") {
|
|
7514
|
+
const entries = [];
|
|
7515
|
+
for (const [name, value] of Object.entries(config)) {
|
|
7516
|
+
if (value === null || value === void 0) continue;
|
|
7517
|
+
const key = prefix ? `${prefix}_${environmentKey(name)}` : environmentKey(name);
|
|
7518
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
7519
|
+
entries.push([key, String(value)]);
|
|
7520
|
+
} else if (Array.isArray(value)) {
|
|
7521
|
+
entries.push([key, JSON.stringify(value)]);
|
|
7522
|
+
} else if (typeof value === "object") {
|
|
7523
|
+
entries.push(
|
|
7524
|
+
...flattenConfig(value, key)
|
|
7525
|
+
);
|
|
7526
|
+
}
|
|
7527
|
+
}
|
|
7528
|
+
return entries;
|
|
7529
|
+
}
|
|
7530
|
+
function environmentKey(name) {
|
|
7531
|
+
return name.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
|
|
7532
|
+
}
|
|
7533
|
+
var init_capabilityExecutionEnvironment = __esm({
|
|
7534
|
+
"src/scripts/capabilityExecutionEnvironment.ts"() {
|
|
7535
|
+
"use strict";
|
|
7536
|
+
}
|
|
7537
|
+
});
|
|
7538
|
+
|
|
7335
7539
|
// src/scripts/evaluateAgencyBoundaries.ts
|
|
7336
7540
|
function shouldEvaluateAgencyBoundaries(data, profile) {
|
|
7337
7541
|
return Boolean(agencyBoundaryCapabilityKind(data, profile));
|
|
@@ -11572,12 +11776,12 @@ var init_classifyByLabel = __esm({
|
|
|
11572
11776
|
});
|
|
11573
11777
|
|
|
11574
11778
|
// src/scripts/commitAndPush.ts
|
|
11575
|
-
import { createHash as
|
|
11779
|
+
import { createHash as createHash5 } from "crypto";
|
|
11576
11780
|
import * as fs31 from "fs";
|
|
11577
11781
|
import * as path29 from "path";
|
|
11578
11782
|
function sentinelPathForStage(cwd, profileName, workflowExecutionKey) {
|
|
11579
11783
|
const runId = resolveRunId();
|
|
11580
|
-
const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${
|
|
11784
|
+
const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash5("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
|
|
11581
11785
|
return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}${executionSuffix}.lock`);
|
|
11582
11786
|
}
|
|
11583
11787
|
var DEFAULT_COMMIT_MESSAGE, commitAndPush2;
|
|
@@ -15351,182 +15555,6 @@ var init_kodyVariables = __esm({
|
|
|
15351
15555
|
}
|
|
15352
15556
|
});
|
|
15353
15557
|
|
|
15354
|
-
// src/backendVault.ts
|
|
15355
|
-
import { createDecipheriv, createHash as createHash5 } from "crypto";
|
|
15356
|
-
function cacheKey(owner, repo, masterKey) {
|
|
15357
|
-
const keyHash = createHash5("sha256").update(masterKey).digest("hex").slice(0, 16);
|
|
15358
|
-
return `${owner}/${repo}:${keyHash}`.toLowerCase();
|
|
15359
|
-
}
|
|
15360
|
-
function decryptVault(payload, masterKey) {
|
|
15361
|
-
const parts = payload.split(":");
|
|
15362
|
-
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
15363
|
-
throw new Error("invalid vault payload format");
|
|
15364
|
-
}
|
|
15365
|
-
const [, ivB64, ctB64, tagB64] = parts;
|
|
15366
|
-
const iv = Buffer.from(ivB64, "base64");
|
|
15367
|
-
const ct = Buffer.from(ctB64, "base64");
|
|
15368
|
-
const tag = Buffer.from(tagB64, "base64");
|
|
15369
|
-
const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
|
|
15370
|
-
decipher.setAuthTag(tag);
|
|
15371
|
-
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
15372
|
-
}
|
|
15373
|
-
async function readVaultSecrets(opts) {
|
|
15374
|
-
const key = cacheKey(opts.owner, opts.repo, opts.masterKey);
|
|
15375
|
-
const hit = cache.get(key);
|
|
15376
|
-
if (hit && hit.expiresAt > Date.now()) return hit.secrets;
|
|
15377
|
-
const record2 = await createStateBackendFromEnv().getRepoDoc(`${opts.owner}/${opts.repo}`, VAULT_PATH);
|
|
15378
|
-
const raw = record2?.doc;
|
|
15379
|
-
const ciphertext = raw && typeof raw === "object" && !Array.isArray(raw) && typeof raw.ciphertext === "string" ? raw.ciphertext.trim() : "";
|
|
15380
|
-
if (!ciphertext) {
|
|
15381
|
-
cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
15382
|
-
return {};
|
|
15383
|
-
}
|
|
15384
|
-
const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
|
|
15385
|
-
const flat = {};
|
|
15386
|
-
for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
|
|
15387
|
-
if (entry && typeof entry.value === "string") flat[name] = entry.value;
|
|
15388
|
-
}
|
|
15389
|
-
cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
15390
|
-
return flat;
|
|
15391
|
-
}
|
|
15392
|
-
async function readRepoSecret(opts) {
|
|
15393
|
-
const secrets = await readVaultSecrets(opts);
|
|
15394
|
-
const v = secrets[opts.name];
|
|
15395
|
-
return v?.trim() ? v : null;
|
|
15396
|
-
}
|
|
15397
|
-
async function readRepoSecrets(opts) {
|
|
15398
|
-
return readVaultSecrets(opts);
|
|
15399
|
-
}
|
|
15400
|
-
var VAULT_PATH, CACHE_TTL_MS, cache;
|
|
15401
|
-
var init_backendVault = __esm({
|
|
15402
|
-
"src/backendVault.ts"() {
|
|
15403
|
-
"use strict";
|
|
15404
|
-
init_state_backend();
|
|
15405
|
-
VAULT_PATH = "secrets.enc";
|
|
15406
|
-
CACHE_TTL_MS = 6e4;
|
|
15407
|
-
cache = /* @__PURE__ */ new Map();
|
|
15408
|
-
}
|
|
15409
|
-
});
|
|
15410
|
-
|
|
15411
|
-
// src/pool/keys.ts
|
|
15412
|
-
import { hkdfSync } from "crypto";
|
|
15413
|
-
function masterKeyBytes(raw) {
|
|
15414
|
-
const v = raw.trim();
|
|
15415
|
-
if (!v) throw new Error("KODY_MASTER_KEY is empty");
|
|
15416
|
-
if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
|
|
15417
|
-
return Buffer.from(v, "hex");
|
|
15418
|
-
}
|
|
15419
|
-
return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
15420
|
-
}
|
|
15421
|
-
function deriveKey(master, info, length = 32) {
|
|
15422
|
-
return Buffer.from(hkdfSync("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
|
|
15423
|
-
}
|
|
15424
|
-
function derivePoolApiKey(master) {
|
|
15425
|
-
return deriveKey(master, POOL_API_KEY_INFO);
|
|
15426
|
-
}
|
|
15427
|
-
function deriveRunnerApiKey(master) {
|
|
15428
|
-
return deriveKey(master, RUNNER_API_KEY_INFO);
|
|
15429
|
-
}
|
|
15430
|
-
function bearerOk(headerAuth, xApiKey, expected) {
|
|
15431
|
-
const x = (xApiKey ?? "").trim();
|
|
15432
|
-
if (x && timingEqual(x, expected)) return true;
|
|
15433
|
-
const a = (headerAuth ?? "").trim();
|
|
15434
|
-
if (a.toLowerCase().startsWith("bearer ")) {
|
|
15435
|
-
return timingEqual(a.slice(7).trim(), expected);
|
|
15436
|
-
}
|
|
15437
|
-
return false;
|
|
15438
|
-
}
|
|
15439
|
-
function timingEqual(a, b) {
|
|
15440
|
-
if (a.length !== b.length) return false;
|
|
15441
|
-
let diff = 0;
|
|
15442
|
-
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
15443
|
-
return diff === 0;
|
|
15444
|
-
}
|
|
15445
|
-
var POOL_API_KEY_INFO, RUNNER_API_KEY_INFO;
|
|
15446
|
-
var init_keys = __esm({
|
|
15447
|
-
"src/pool/keys.ts"() {
|
|
15448
|
-
"use strict";
|
|
15449
|
-
POOL_API_KEY_INFO = "kody-pool-api:v1";
|
|
15450
|
-
RUNNER_API_KEY_INFO = "kody-runner-api:v1";
|
|
15451
|
-
}
|
|
15452
|
-
});
|
|
15453
|
-
|
|
15454
|
-
// src/scripts/runtimeSecrets.ts
|
|
15455
|
-
function envSecret(name, env) {
|
|
15456
|
-
const value = env[name]?.trim() ? env[name] : "";
|
|
15457
|
-
return value ? { value, source: "env" } : { value: "", source: "missing" };
|
|
15458
|
-
}
|
|
15459
|
-
async function resolveRuntimeSecret(name, ctx, opts = {}) {
|
|
15460
|
-
const env = opts.env ?? process.env;
|
|
15461
|
-
if (hasGitHubActionsIdentity(env)) {
|
|
15462
|
-
try {
|
|
15463
|
-
const value = await readRuntimeSecretFromKody(name, env);
|
|
15464
|
-
if (value) return { value, source: "vault" };
|
|
15465
|
-
return envSecret(name, env);
|
|
15466
|
-
} catch (err) {
|
|
15467
|
-
const fallback = envSecret(name, env);
|
|
15468
|
-
return {
|
|
15469
|
-
...fallback,
|
|
15470
|
-
warning: `Kody secret read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
15471
|
-
};
|
|
15472
|
-
}
|
|
15473
|
-
}
|
|
15474
|
-
const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
|
|
15475
|
-
if (!masterRaw || !env.CONVEX_URL?.trim() || !env.KODY_SERVICE_KEY?.trim()) return envSecret(name, env);
|
|
15476
|
-
try {
|
|
15477
|
-
const masterKey = masterKeyBytes(masterRaw);
|
|
15478
|
-
if (masterKey.length !== 32) {
|
|
15479
|
-
throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
|
|
15480
|
-
}
|
|
15481
|
-
const value = await readRepoSecret({
|
|
15482
|
-
owner: ctx.config.github.owner,
|
|
15483
|
-
repo: ctx.config.github.repo,
|
|
15484
|
-
name,
|
|
15485
|
-
masterKey
|
|
15486
|
-
});
|
|
15487
|
-
if (value) return { value, source: "vault" };
|
|
15488
|
-
} catch (err) {
|
|
15489
|
-
const fallback = envSecret(name, env);
|
|
15490
|
-
return {
|
|
15491
|
-
...fallback,
|
|
15492
|
-
warning: `vault read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
15493
|
-
};
|
|
15494
|
-
}
|
|
15495
|
-
return envSecret(name, env);
|
|
15496
|
-
}
|
|
15497
|
-
async function resolveRuntimeSecrets(names, ctx, opts = {}) {
|
|
15498
|
-
const declared = Array.isArray(names) ? [...new Set(names.filter((name) => typeof name === "string" && /^[A-Z][A-Z0-9_]*$/.test(name)))] : [];
|
|
15499
|
-
const resolved = [];
|
|
15500
|
-
for (const name of declared) {
|
|
15501
|
-
resolved.push({ name, result: await resolveRuntimeSecret(name, ctx, opts) });
|
|
15502
|
-
}
|
|
15503
|
-
const warnings = resolved.flatMap(({ result }) => result.warning ? [result.warning] : []);
|
|
15504
|
-
const fallbackSecrets = Object.fromEntries(
|
|
15505
|
-
resolved.flatMap(({ name, result }) => result.source === "env" && result.value ? [[name, result.value]] : [])
|
|
15506
|
-
);
|
|
15507
|
-
if (hasGitHubActionsIdentity(opts.env ?? process.env) && Object.keys(fallbackSecrets).length > 0) {
|
|
15508
|
-
try {
|
|
15509
|
-
await writeRuntimeSecretsToKody(fallbackSecrets, opts.env ?? process.env);
|
|
15510
|
-
} catch (err) {
|
|
15511
|
-
warnings.push(`Kody secret migration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15512
|
-
}
|
|
15513
|
-
}
|
|
15514
|
-
return {
|
|
15515
|
-
environment: Object.fromEntries(
|
|
15516
|
-
resolved.flatMap(({ name, result }) => result.value ? [[name, result.value]] : [])
|
|
15517
|
-
),
|
|
15518
|
-
warnings
|
|
15519
|
-
};
|
|
15520
|
-
}
|
|
15521
|
-
var init_runtimeSecrets = __esm({
|
|
15522
|
-
"src/scripts/runtimeSecrets.ts"() {
|
|
15523
|
-
"use strict";
|
|
15524
|
-
init_backendVault();
|
|
15525
|
-
init_kody_api_client();
|
|
15526
|
-
init_keys();
|
|
15527
|
-
}
|
|
15528
|
-
});
|
|
15529
|
-
|
|
15530
15558
|
// src/scripts/loadQaContext.ts
|
|
15531
15559
|
import * as fs42 from "fs";
|
|
15532
15560
|
import * as path39 from "path";
|
|
@@ -21052,6 +21080,7 @@ async function runImplementation(profileName, input) {
|
|
|
21052
21080
|
});
|
|
21053
21081
|
}
|
|
21054
21082
|
let litellm;
|
|
21083
|
+
let runtimeModelEnvironment;
|
|
21055
21084
|
const ctx = {
|
|
21056
21085
|
args,
|
|
21057
21086
|
cwd: input.cwd,
|
|
@@ -21143,9 +21172,18 @@ async function runImplementation(profileName, input) {
|
|
|
21143
21172
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
21144
21173
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
21145
21174
|
const agents = loadSubagents(profile);
|
|
21175
|
+
if (runtimeModelEnvironment === void 0) {
|
|
21176
|
+
runtimeModelEnvironment = {};
|
|
21177
|
+
if (needsLitellmProxy(model)) {
|
|
21178
|
+
const resolved2 = await resolveRuntimeModelEnvironment(model, ctx);
|
|
21179
|
+
runtimeModelEnvironment = resolved2.environment;
|
|
21180
|
+
for (const warning of resolved2.warnings) process.stderr.write(`\u26A0 ${warning}
|
|
21181
|
+
`);
|
|
21182
|
+
}
|
|
21183
|
+
}
|
|
21146
21184
|
if (litellm === void 0) {
|
|
21147
21185
|
try {
|
|
21148
|
-
litellm = await startLitellmIfNeeded(model, input.cwd);
|
|
21186
|
+
litellm = await startLitellmIfNeeded(model, input.cwd, void 0, runtimeModelEnvironment);
|
|
21149
21187
|
} catch (err) {
|
|
21150
21188
|
throw new Error(`litellm startup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21151
21189
|
}
|
|
@@ -21155,7 +21193,10 @@ async function runImplementation(profileName, input) {
|
|
|
21155
21193
|
prompt,
|
|
21156
21194
|
model,
|
|
21157
21195
|
cwd: input.cwd,
|
|
21158
|
-
environment:
|
|
21196
|
+
environment: {
|
|
21197
|
+
...ctx.data.capabilityEnvironment && typeof ctx.data.capabilityEnvironment === "object" && !Array.isArray(ctx.data.capabilityEnvironment) ? ctx.data.capabilityEnvironment : {},
|
|
21198
|
+
...runtimeModelEnvironment
|
|
21199
|
+
},
|
|
21159
21200
|
litellmUrl: lm?.url ?? null,
|
|
21160
21201
|
// On a connection drop mid-run, restart the (possibly crashed) proxy
|
|
21161
21202
|
// before the agent retries. No-op for direct-Anthropic runs (lm null).
|
|
@@ -21847,7 +21888,6 @@ var MUTATING_POSTFLIGHTS, SHELL_MARKER_RE, TASK_ARTIFACT_WRITE_TOOLS, MAX_CHAIN_
|
|
|
21847
21888
|
var init_executor = __esm({
|
|
21848
21889
|
"src/executor.ts"() {
|
|
21849
21890
|
"use strict";
|
|
21850
|
-
init_capabilityExecutionEnvironment();
|
|
21851
21891
|
init_capability_contract_validation();
|
|
21852
21892
|
init_agent();
|
|
21853
21893
|
init_agents();
|
|
@@ -21863,7 +21903,9 @@ var init_executor = __esm({
|
|
|
21863
21903
|
init_registry();
|
|
21864
21904
|
init_runIndex();
|
|
21865
21905
|
init_runtimeCleanup();
|
|
21906
|
+
init_runtimeModelEnvironment();
|
|
21866
21907
|
init_runtimePaths();
|
|
21908
|
+
init_capabilityExecutionEnvironment();
|
|
21867
21909
|
init_evaluateAgencyBoundaries();
|
|
21868
21910
|
init_scripts();
|
|
21869
21911
|
init_stateWorkspace();
|
|
File without changes
|
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.498",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,29 +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:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
|
|
29
|
-
"test:all": "vitest run tests --no-coverage",
|
|
30
|
-
"typecheck": "tsc --noEmit",
|
|
31
|
-
"lint": "biome check",
|
|
32
|
-
"lint:fix": "biome check --write",
|
|
33
|
-
"format": "biome format --write",
|
|
34
|
-
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
35
|
-
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
|
|
36
|
-
"prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm test:runtime-services && pnpm build && pnpm verify:package"
|
|
37
|
-
},
|
|
38
15
|
"dependencies": {
|
|
39
16
|
"@actions/cache": "^6.0.0",
|
|
40
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
@@ -61,5 +38,27 @@
|
|
|
61
38
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
62
39
|
},
|
|
63
40
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
64
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
65
|
-
|
|
41
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"kody:run": "tsx bin/kody.ts",
|
|
44
|
+
"serve": "tsx bin/kody.ts serve",
|
|
45
|
+
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
46
|
+
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
47
|
+
"clean:dist": "node scripts/clean-dist.cjs",
|
|
48
|
+
"build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
|
|
49
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
50
|
+
"pretest": "pnpm check:modularity",
|
|
51
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
52
|
+
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
53
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
54
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
55
|
+
"test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
|
|
56
|
+
"test:all": "vitest run tests --no-coverage",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"lint": "biome check",
|
|
59
|
+
"lint:fix": "biome check --write",
|
|
60
|
+
"format": "biome format --write",
|
|
61
|
+
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
62
|
+
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
|
|
63
|
+
}
|
|
64
|
+
}
|