@kody-ade/kody-engine 0.4.401 → 0.4.402
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 +742 -415
- 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.402",
|
|
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",
|
|
@@ -1088,7 +1088,7 @@ function cmsHeaders(opts) {
|
|
|
1088
1088
|
}
|
|
1089
1089
|
};
|
|
1090
1090
|
}
|
|
1091
|
-
async function callDashboardCms(opts,
|
|
1091
|
+
async function callDashboardCms(opts, path51, init = {}) {
|
|
1092
1092
|
const baseUrl = dashboardBaseUrl(opts);
|
|
1093
1093
|
if (!baseUrl) {
|
|
1094
1094
|
return {
|
|
@@ -1100,7 +1100,7 @@ async function callDashboardCms(opts, path50, init = {}) {
|
|
|
1100
1100
|
const headerResult = cmsHeaders(opts);
|
|
1101
1101
|
if (!headerResult.ok) return headerResult;
|
|
1102
1102
|
try {
|
|
1103
|
-
const res = await fetch(`${baseUrl}${
|
|
1103
|
+
const res = await fetch(`${baseUrl}${path51}`, {
|
|
1104
1104
|
...init,
|
|
1105
1105
|
headers: {
|
|
1106
1106
|
...headerResult.headers,
|
|
@@ -1172,8 +1172,8 @@ function documentArg(value) {
|
|
|
1172
1172
|
function normalizeCmsDocumentIdInput(input) {
|
|
1173
1173
|
const trimmed = stripWrappingQuotes(input.trim());
|
|
1174
1174
|
const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
|
|
1175
|
-
const
|
|
1176
|
-
return
|
|
1175
|
+
const path51 = parseDocumentPath(withoutQuery);
|
|
1176
|
+
return path51 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
|
|
1177
1177
|
}
|
|
1178
1178
|
function stripWrappingQuotes(value) {
|
|
1179
1179
|
let current = value;
|
|
@@ -1184,9 +1184,9 @@ function stripWrappingQuotes(value) {
|
|
|
1184
1184
|
}
|
|
1185
1185
|
}
|
|
1186
1186
|
function parseDocumentPath(value) {
|
|
1187
|
-
const
|
|
1188
|
-
if (!
|
|
1189
|
-
const parts =
|
|
1187
|
+
const path51 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
|
|
1188
|
+
if (!path51?.includes("/content/entries/")) return null;
|
|
1189
|
+
const parts = path51.split("/").filter(Boolean).map(decodePathPart);
|
|
1190
1190
|
const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
|
|
1191
1191
|
const idPart = parts[entriesIndex + 3];
|
|
1192
1192
|
if (!idPart || idPart === "new") return null;
|
|
@@ -3770,7 +3770,7 @@ var init_task_artifacts = __esm({
|
|
|
3770
3770
|
|
|
3771
3771
|
// src/gha.ts
|
|
3772
3772
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
3773
|
-
import * as
|
|
3773
|
+
import * as fs17 from "fs";
|
|
3774
3774
|
function getRunUrl() {
|
|
3775
3775
|
const server = process.env.GITHUB_SERVER_URL;
|
|
3776
3776
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -3781,10 +3781,10 @@ function getRunUrl() {
|
|
|
3781
3781
|
function reactToTriggerComment(cwd) {
|
|
3782
3782
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
3783
3783
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
3784
|
-
if (!eventPath || !
|
|
3784
|
+
if (!eventPath || !fs17.existsSync(eventPath)) return;
|
|
3785
3785
|
let event = null;
|
|
3786
3786
|
try {
|
|
3787
|
-
event = JSON.parse(
|
|
3787
|
+
event = JSON.parse(fs17.readFileSync(eventPath, "utf-8"));
|
|
3788
3788
|
} catch {
|
|
3789
3789
|
return;
|
|
3790
3790
|
}
|
|
@@ -4313,31 +4313,31 @@ var init_lifecycles = __esm({
|
|
|
4313
4313
|
});
|
|
4314
4314
|
|
|
4315
4315
|
// src/scripts/buildSyntheticPlugin.ts
|
|
4316
|
-
import * as
|
|
4316
|
+
import * as fs18 from "fs";
|
|
4317
4317
|
import * as os3 from "os";
|
|
4318
|
-
import * as
|
|
4318
|
+
import * as path17 from "path";
|
|
4319
4319
|
function getPluginsCatalogRoot() {
|
|
4320
|
-
const here =
|
|
4320
|
+
const here = path17.dirname(new URL(import.meta.url).pathname);
|
|
4321
4321
|
const candidates = [
|
|
4322
|
-
|
|
4322
|
+
path17.join(here, "..", "plugins"),
|
|
4323
4323
|
// dev: src/scripts → src/plugins
|
|
4324
|
-
|
|
4324
|
+
path17.join(here, "..", "..", "plugins"),
|
|
4325
4325
|
// built: dist/scripts → dist/plugins
|
|
4326
|
-
|
|
4326
|
+
path17.join(here, "..", "..", "src", "plugins")
|
|
4327
4327
|
// fallback
|
|
4328
4328
|
];
|
|
4329
4329
|
for (const c of candidates) {
|
|
4330
|
-
if (
|
|
4330
|
+
if (fs18.existsSync(c) && fs18.statSync(c).isDirectory()) return c;
|
|
4331
4331
|
}
|
|
4332
4332
|
return candidates[0];
|
|
4333
4333
|
}
|
|
4334
4334
|
function copyDir(src, dst) {
|
|
4335
|
-
|
|
4336
|
-
for (const ent of
|
|
4337
|
-
const s =
|
|
4338
|
-
const d =
|
|
4335
|
+
fs18.mkdirSync(dst, { recursive: true });
|
|
4336
|
+
for (const ent of fs18.readdirSync(src, { withFileTypes: true })) {
|
|
4337
|
+
const s = path17.join(src, ent.name);
|
|
4338
|
+
const d = path17.join(dst, ent.name);
|
|
4339
4339
|
if (ent.isDirectory()) copyDir(s, d);
|
|
4340
|
-
else if (ent.isFile())
|
|
4340
|
+
else if (ent.isFile()) fs18.copyFileSync(s, d);
|
|
4341
4341
|
}
|
|
4342
4342
|
}
|
|
4343
4343
|
var buildSyntheticPlugin;
|
|
@@ -4350,45 +4350,45 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
4350
4350
|
if (!needsSynthetic) return;
|
|
4351
4351
|
const catalog = getPluginsCatalogRoot();
|
|
4352
4352
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4353
|
-
const root =
|
|
4354
|
-
|
|
4353
|
+
const root = path17.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
4354
|
+
fs18.mkdirSync(path17.join(root, ".claude-plugin"), { recursive: true });
|
|
4355
4355
|
const resolvePart = (bucket, entry) => {
|
|
4356
|
-
const local =
|
|
4357
|
-
if (
|
|
4358
|
-
const central =
|
|
4359
|
-
if (
|
|
4356
|
+
const local = path17.join(profile.dir, bucket, entry);
|
|
4357
|
+
if (fs18.existsSync(local)) return local;
|
|
4358
|
+
const central = path17.join(catalog, bucket, entry);
|
|
4359
|
+
if (fs18.existsSync(central)) return central;
|
|
4360
4360
|
throw new Error(
|
|
4361
4361
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4362
4362
|
);
|
|
4363
4363
|
};
|
|
4364
4364
|
if (cc.skills.length > 0) {
|
|
4365
|
-
const dst =
|
|
4366
|
-
|
|
4365
|
+
const dst = path17.join(root, "skills");
|
|
4366
|
+
fs18.mkdirSync(dst, { recursive: true });
|
|
4367
4367
|
for (const name of cc.skills) {
|
|
4368
|
-
copyDir(resolvePart("skills", name),
|
|
4368
|
+
copyDir(resolvePart("skills", name), path17.join(dst, name));
|
|
4369
4369
|
}
|
|
4370
4370
|
}
|
|
4371
4371
|
if (cc.commands.length > 0) {
|
|
4372
|
-
const dst =
|
|
4373
|
-
|
|
4372
|
+
const dst = path17.join(root, "commands");
|
|
4373
|
+
fs18.mkdirSync(dst, { recursive: true });
|
|
4374
4374
|
for (const name of cc.commands) {
|
|
4375
|
-
|
|
4375
|
+
fs18.copyFileSync(resolvePart("commands", `${name}.md`), path17.join(dst, `${name}.md`));
|
|
4376
4376
|
}
|
|
4377
4377
|
}
|
|
4378
4378
|
if (cc.hooks.length > 0) {
|
|
4379
|
-
const dst =
|
|
4380
|
-
|
|
4379
|
+
const dst = path17.join(root, "hooks");
|
|
4380
|
+
fs18.mkdirSync(dst, { recursive: true });
|
|
4381
4381
|
const merged = { hooks: {} };
|
|
4382
4382
|
for (const name of cc.hooks) {
|
|
4383
4383
|
const src = resolvePart("hooks", `${name}.json`);
|
|
4384
|
-
const parsed = JSON.parse(
|
|
4384
|
+
const parsed = JSON.parse(fs18.readFileSync(src, "utf-8"));
|
|
4385
4385
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4386
4386
|
if (!Array.isArray(entries)) continue;
|
|
4387
4387
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4388
4388
|
merged.hooks[event].push(...entries);
|
|
4389
4389
|
}
|
|
4390
4390
|
}
|
|
4391
|
-
|
|
4391
|
+
fs18.writeFileSync(path17.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4392
4392
|
`);
|
|
4393
4393
|
}
|
|
4394
4394
|
const manifest = {
|
|
@@ -4398,7 +4398,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
4398
4398
|
};
|
|
4399
4399
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4400
4400
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4401
|
-
|
|
4401
|
+
fs18.writeFileSync(path17.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4402
4402
|
`);
|
|
4403
4403
|
ctx.data.syntheticPluginPath = root;
|
|
4404
4404
|
};
|
|
@@ -4406,8 +4406,8 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
4406
4406
|
});
|
|
4407
4407
|
|
|
4408
4408
|
// src/subagents.ts
|
|
4409
|
-
import * as
|
|
4410
|
-
import * as
|
|
4409
|
+
import * as fs19 from "fs";
|
|
4410
|
+
import * as path18 from "path";
|
|
4411
4411
|
function splitFrontmatter(raw) {
|
|
4412
4412
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
4413
4413
|
if (!match) return { fm: {}, body: raw.trim() };
|
|
@@ -4420,10 +4420,10 @@ function splitFrontmatter(raw) {
|
|
|
4420
4420
|
return { fm, body: (match[2] ?? "").trim() };
|
|
4421
4421
|
}
|
|
4422
4422
|
function resolveAgentFile2(profileDir, name) {
|
|
4423
|
-
const local =
|
|
4424
|
-
if (
|
|
4425
|
-
const central =
|
|
4426
|
-
if (
|
|
4423
|
+
const local = path18.join(profileDir, "agents", `${name}.md`);
|
|
4424
|
+
if (fs19.existsSync(local)) return local;
|
|
4425
|
+
const central = path18.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
4426
|
+
if (fs19.existsSync(central)) return central;
|
|
4427
4427
|
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
4428
4428
|
}
|
|
4429
4429
|
function captureSubagentTemplates(profile) {
|
|
@@ -4432,7 +4432,7 @@ function captureSubagentTemplates(profile) {
|
|
|
4432
4432
|
const out = {};
|
|
4433
4433
|
for (const name of names) {
|
|
4434
4434
|
try {
|
|
4435
|
-
out[name] =
|
|
4435
|
+
out[name] = fs19.readFileSync(resolveAgentFile2(profile.dir, name), "utf-8");
|
|
4436
4436
|
} catch {
|
|
4437
4437
|
}
|
|
4438
4438
|
}
|
|
@@ -4443,7 +4443,7 @@ function loadSubagents(profile) {
|
|
|
4443
4443
|
if (!names || names.length === 0) return void 0;
|
|
4444
4444
|
const agents = {};
|
|
4445
4445
|
for (const name of names) {
|
|
4446
|
-
const raw = profile.subagentTemplates?.[name] ??
|
|
4446
|
+
const raw = profile.subagentTemplates?.[name] ?? fs19.readFileSync(resolveAgentFile2(profile.dir, name), "utf-8");
|
|
4447
4447
|
const { fm, body } = splitFrontmatter(raw);
|
|
4448
4448
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
4449
4449
|
const def = {
|
|
@@ -4467,15 +4467,15 @@ var init_subagents = __esm({
|
|
|
4467
4467
|
});
|
|
4468
4468
|
|
|
4469
4469
|
// src/profile.ts
|
|
4470
|
-
import * as
|
|
4471
|
-
import * as
|
|
4470
|
+
import * as fs20 from "fs";
|
|
4471
|
+
import * as path19 from "path";
|
|
4472
4472
|
function loadProfile(profilePath) {
|
|
4473
|
-
if (!
|
|
4473
|
+
if (!fs20.existsSync(profilePath)) {
|
|
4474
4474
|
throw new ProfileError(profilePath, "file not found");
|
|
4475
4475
|
}
|
|
4476
4476
|
let raw;
|
|
4477
4477
|
try {
|
|
4478
|
-
raw = JSON.parse(
|
|
4478
|
+
raw = JSON.parse(fs20.readFileSync(profilePath, "utf-8"));
|
|
4479
4479
|
} catch (err) {
|
|
4480
4480
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
4481
4481
|
}
|
|
@@ -4486,7 +4486,7 @@ function loadProfile(profilePath) {
|
|
|
4486
4486
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
4487
4487
|
if (unknownKeys.length > 0) {
|
|
4488
4488
|
process.stderr.write(
|
|
4489
|
-
`[kody profile] ${
|
|
4489
|
+
`[kody profile] ${path19.basename(path19.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
4490
4490
|
`
|
|
4491
4491
|
);
|
|
4492
4492
|
}
|
|
@@ -4496,7 +4496,7 @@ function loadProfile(profilePath) {
|
|
|
4496
4496
|
if (!refPath) {
|
|
4497
4497
|
throw new ProfileError(profilePath, `capability references unknown implementation '${execRef}'`);
|
|
4498
4498
|
}
|
|
4499
|
-
if (
|
|
4499
|
+
if (path19.resolve(refPath) === path19.resolve(profilePath)) {
|
|
4500
4500
|
} else {
|
|
4501
4501
|
const base = loadProfile(refPath);
|
|
4502
4502
|
return {
|
|
@@ -4594,8 +4594,8 @@ function loadProfile(profilePath) {
|
|
|
4594
4594
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
4595
4595
|
// flip to true after end-to-end verification.
|
|
4596
4596
|
preloadContext: r.preloadContext === true,
|
|
4597
|
-
dir:
|
|
4598
|
-
promptTemplates: readPromptTemplates(
|
|
4597
|
+
dir: path19.dirname(profilePath),
|
|
4598
|
+
promptTemplates: readPromptTemplates(path19.dirname(profilePath))
|
|
4599
4599
|
};
|
|
4600
4600
|
if (lifecycle) {
|
|
4601
4601
|
applyLifecycle(profile, profilePath);
|
|
@@ -4638,17 +4638,17 @@ function readPromptTemplates(dir) {
|
|
|
4638
4638
|
const out = {};
|
|
4639
4639
|
const read = (p) => {
|
|
4640
4640
|
try {
|
|
4641
|
-
out[p] =
|
|
4641
|
+
out[p] = fs20.readFileSync(p, "utf-8");
|
|
4642
4642
|
} catch {
|
|
4643
4643
|
}
|
|
4644
4644
|
};
|
|
4645
|
-
read(
|
|
4646
|
-
read(
|
|
4647
|
-
read(
|
|
4645
|
+
read(path19.join(dir, "prompt.md"));
|
|
4646
|
+
read(path19.join(dir, "capability.md"));
|
|
4647
|
+
read(path19.join(dir, "capability.md"));
|
|
4648
4648
|
try {
|
|
4649
|
-
const promptsDir =
|
|
4650
|
-
for (const ent of
|
|
4651
|
-
if (ent.endsWith(".md")) read(
|
|
4649
|
+
const promptsDir = path19.join(dir, "prompts");
|
|
4650
|
+
for (const ent of fs20.readdirSync(promptsDir)) {
|
|
4651
|
+
if (ent.endsWith(".md")) read(path19.join(promptsDir, ent));
|
|
4652
4652
|
}
|
|
4653
4653
|
} catch {
|
|
4654
4654
|
}
|
|
@@ -5422,16 +5422,16 @@ var init_state = __esm({
|
|
|
5422
5422
|
});
|
|
5423
5423
|
|
|
5424
5424
|
// src/prompt.ts
|
|
5425
|
-
import * as
|
|
5426
|
-
import * as
|
|
5425
|
+
import * as fs21 from "fs";
|
|
5426
|
+
import * as path20 from "path";
|
|
5427
5427
|
function loadProjectConventions(projectDir) {
|
|
5428
5428
|
const out = [];
|
|
5429
5429
|
for (const rel of CONVENTION_FILES) {
|
|
5430
|
-
const abs =
|
|
5431
|
-
if (!
|
|
5430
|
+
const abs = path20.join(projectDir, rel);
|
|
5431
|
+
if (!fs21.existsSync(abs)) continue;
|
|
5432
5432
|
let content;
|
|
5433
5433
|
try {
|
|
5434
|
-
content =
|
|
5434
|
+
content = fs21.readFileSync(abs, "utf-8");
|
|
5435
5435
|
} catch {
|
|
5436
5436
|
continue;
|
|
5437
5437
|
}
|
|
@@ -5666,8 +5666,8 @@ var loadMemoryContext_exports = {};
|
|
|
5666
5666
|
__export(loadMemoryContext_exports, {
|
|
5667
5667
|
loadMemoryContext: () => loadMemoryContext
|
|
5668
5668
|
});
|
|
5669
|
-
import * as
|
|
5670
|
-
import * as
|
|
5669
|
+
import * as fs22 from "fs";
|
|
5670
|
+
import * as path21 from "path";
|
|
5671
5671
|
function formatBlockFromBackend(docs) {
|
|
5672
5672
|
const pages = docs.flatMap((record2) => {
|
|
5673
5673
|
if (!record2.doc || typeof record2.doc !== "object") return [];
|
|
@@ -5690,21 +5690,21 @@ function collectPages(memoryAbs) {
|
|
|
5690
5690
|
walkMd(memoryAbs, (file) => {
|
|
5691
5691
|
let stat;
|
|
5692
5692
|
try {
|
|
5693
|
-
stat =
|
|
5693
|
+
stat = fs22.statSync(file);
|
|
5694
5694
|
} catch {
|
|
5695
5695
|
return;
|
|
5696
5696
|
}
|
|
5697
5697
|
let raw;
|
|
5698
5698
|
try {
|
|
5699
|
-
raw =
|
|
5699
|
+
raw = fs22.readFileSync(file, "utf-8");
|
|
5700
5700
|
} catch {
|
|
5701
5701
|
return;
|
|
5702
5702
|
}
|
|
5703
5703
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
5704
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
5704
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path21.basename(file, ".md");
|
|
5705
5705
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
5706
5706
|
out.push({
|
|
5707
|
-
relPath:
|
|
5707
|
+
relPath: path21.relative(memoryAbs, file),
|
|
5708
5708
|
title,
|
|
5709
5709
|
updated,
|
|
5710
5710
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -5772,16 +5772,16 @@ function walkMd(root, visit) {
|
|
|
5772
5772
|
const dir = stack.pop();
|
|
5773
5773
|
let names;
|
|
5774
5774
|
try {
|
|
5775
|
-
names =
|
|
5775
|
+
names = fs22.readdirSync(dir);
|
|
5776
5776
|
} catch {
|
|
5777
5777
|
continue;
|
|
5778
5778
|
}
|
|
5779
5779
|
for (const name of names) {
|
|
5780
5780
|
if (name.startsWith(".")) continue;
|
|
5781
|
-
const full =
|
|
5781
|
+
const full = path21.join(dir, name);
|
|
5782
5782
|
let stat;
|
|
5783
5783
|
try {
|
|
5784
|
-
stat =
|
|
5784
|
+
stat = fs22.statSync(full);
|
|
5785
5785
|
} catch {
|
|
5786
5786
|
continue;
|
|
5787
5787
|
}
|
|
@@ -5816,8 +5816,8 @@ var init_loadMemoryContext = __esm({
|
|
|
5816
5816
|
}
|
|
5817
5817
|
return;
|
|
5818
5818
|
}
|
|
5819
|
-
const memoryAbs =
|
|
5820
|
-
if (!
|
|
5819
|
+
const memoryAbs = path21.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
5820
|
+
if (!fs22.existsSync(memoryAbs)) {
|
|
5821
5821
|
ctx.data.memoryContext = "";
|
|
5822
5822
|
return;
|
|
5823
5823
|
}
|
|
@@ -5861,11 +5861,11 @@ var init_loadCoverageRules = __esm({
|
|
|
5861
5861
|
|
|
5862
5862
|
// src/container.ts
|
|
5863
5863
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
5864
|
-
import * as
|
|
5864
|
+
import * as fs23 from "fs";
|
|
5865
5865
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
5866
5866
|
try {
|
|
5867
5867
|
const profilePath = resolveProfilePath(profileName);
|
|
5868
|
-
if (!
|
|
5868
|
+
if (!fs23.existsSync(profilePath)) return null;
|
|
5869
5869
|
return loadProfile(profilePath).inputs;
|
|
5870
5870
|
} catch {
|
|
5871
5871
|
return null;
|
|
@@ -6328,11 +6328,11 @@ var init_lifecycleLabels = __esm({
|
|
|
6328
6328
|
});
|
|
6329
6329
|
|
|
6330
6330
|
// src/litellm.ts
|
|
6331
|
-
import { execFileSync as execFileSync4, spawn as
|
|
6332
|
-
import * as
|
|
6331
|
+
import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
|
|
6332
|
+
import * as fs24 from "fs";
|
|
6333
6333
|
import * as net from "net";
|
|
6334
6334
|
import * as os4 from "os";
|
|
6335
|
-
import * as
|
|
6335
|
+
import * as path22 from "path";
|
|
6336
6336
|
async function checkLitellmHealth(url) {
|
|
6337
6337
|
try {
|
|
6338
6338
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -6401,6 +6401,8 @@ function locateLitellmScript() {
|
|
|
6401
6401
|
}
|
|
6402
6402
|
}
|
|
6403
6403
|
function resolveLitellmCommand() {
|
|
6404
|
+
const imageScript = "/opt/venv/bin/litellm";
|
|
6405
|
+
if (fs24.existsSync(imageScript)) return imageScript;
|
|
6404
6406
|
try {
|
|
6405
6407
|
execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
6406
6408
|
return "litellm";
|
|
@@ -6436,13 +6438,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
6436
6438
|
const spawnProxy = () => {
|
|
6437
6439
|
const portMatch = activeUrl.match(/:(\d+)/);
|
|
6438
6440
|
const port = portMatch ? portMatch[1] : "4000";
|
|
6439
|
-
const configPath =
|
|
6440
|
-
|
|
6441
|
+
const configPath = path22.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
6442
|
+
fs24.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
6441
6443
|
const args = ["--config", configPath, "--port", port];
|
|
6442
|
-
const nextLogPath =
|
|
6443
|
-
const outFd =
|
|
6444
|
-
child =
|
|
6445
|
-
|
|
6444
|
+
const nextLogPath = path22.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
6445
|
+
const outFd = fs24.openSync(nextLogPath, "w");
|
|
6446
|
+
child = spawn4(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
6447
|
+
fs24.closeSync(outFd);
|
|
6446
6448
|
logPath = nextLogPath;
|
|
6447
6449
|
};
|
|
6448
6450
|
const waitForHealth = async () => {
|
|
@@ -6456,7 +6458,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
6456
6458
|
const readLogTail = () => {
|
|
6457
6459
|
if (!logPath) return "";
|
|
6458
6460
|
try {
|
|
6459
|
-
return
|
|
6461
|
+
return fs24.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
6460
6462
|
} catch {
|
|
6461
6463
|
return "";
|
|
6462
6464
|
}
|
|
@@ -6539,10 +6541,10 @@ function canListen(port, host) {
|
|
|
6539
6541
|
});
|
|
6540
6542
|
}
|
|
6541
6543
|
function readDotenvApiKeys(projectDir) {
|
|
6542
|
-
const dotenvPath =
|
|
6543
|
-
if (!
|
|
6544
|
+
const dotenvPath = path22.join(projectDir, ".env");
|
|
6545
|
+
if (!fs24.existsSync(dotenvPath)) return {};
|
|
6544
6546
|
const result = {};
|
|
6545
|
-
for (const rawLine of
|
|
6547
|
+
for (const rawLine of fs24.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
6546
6548
|
const line = rawLine.trim();
|
|
6547
6549
|
if (!line || line.startsWith("#")) continue;
|
|
6548
6550
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -6939,8 +6941,8 @@ var init_pushWithRetry = __esm({
|
|
|
6939
6941
|
|
|
6940
6942
|
// src/commit.ts
|
|
6941
6943
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
6942
|
-
import * as
|
|
6943
|
-
import * as
|
|
6944
|
+
import * as fs25 from "fs";
|
|
6945
|
+
import * as path23 from "path";
|
|
6944
6946
|
function isGitHubYamlPath(filePath) {
|
|
6945
6947
|
const normalized = filePath.replace(/^\.\/+/, "");
|
|
6946
6948
|
return normalized.startsWith(".github/") && /\.ya?ml$/i.test(normalized);
|
|
@@ -6982,18 +6984,18 @@ function ensureGitIdentity(cwd) {
|
|
|
6982
6984
|
}
|
|
6983
6985
|
function abortUnfinishedGitOps(cwd) {
|
|
6984
6986
|
const aborted = [];
|
|
6985
|
-
const gitDir =
|
|
6986
|
-
if (!
|
|
6987
|
-
if (
|
|
6987
|
+
const gitDir = path23.join(cwd ?? process.cwd(), ".git");
|
|
6988
|
+
if (!fs25.existsSync(gitDir)) return aborted;
|
|
6989
|
+
if (fs25.existsSync(path23.join(gitDir, "MERGE_HEAD"))) {
|
|
6988
6990
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
6989
6991
|
}
|
|
6990
|
-
if (
|
|
6992
|
+
if (fs25.existsSync(path23.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
6991
6993
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
6992
6994
|
}
|
|
6993
|
-
if (
|
|
6995
|
+
if (fs25.existsSync(path23.join(gitDir, "REVERT_HEAD"))) {
|
|
6994
6996
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
6995
6997
|
}
|
|
6996
|
-
if (
|
|
6998
|
+
if (fs25.existsSync(path23.join(gitDir, "rebase-merge")) || fs25.existsSync(path23.join(gitDir, "rebase-apply"))) {
|
|
6997
6999
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
6998
7000
|
}
|
|
6999
7001
|
try {
|
|
@@ -7050,7 +7052,7 @@ function normalizeCommitMessage(raw) {
|
|
|
7050
7052
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
7051
7053
|
const allChanged = listChangedFiles(cwd);
|
|
7052
7054
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
7053
|
-
const mergeHeadExists =
|
|
7055
|
+
const mergeHeadExists = fs25.existsSync(path23.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
7054
7056
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
7055
7057
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
7056
7058
|
}
|
|
@@ -7690,13 +7692,13 @@ var init_state2 = __esm({
|
|
|
7690
7692
|
});
|
|
7691
7693
|
|
|
7692
7694
|
// src/goal/runLog.ts
|
|
7693
|
-
import * as
|
|
7695
|
+
import * as fs26 from "fs";
|
|
7694
7696
|
function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
|
|
7695
7697
|
const logs = goalRunLogs(data);
|
|
7696
7698
|
const existing = logs[goalId];
|
|
7697
|
-
const
|
|
7699
|
+
const path51 = existing?.path ?? goalRunLogPath(goalId, data);
|
|
7698
7700
|
logs[goalId] = {
|
|
7699
|
-
path:
|
|
7701
|
+
path: path51,
|
|
7700
7702
|
events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
|
|
7701
7703
|
};
|
|
7702
7704
|
}
|
|
@@ -8032,8 +8034,8 @@ function readGithubEvent() {
|
|
|
8032
8034
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8033
8035
|
if (!eventPath) return null;
|
|
8034
8036
|
try {
|
|
8035
|
-
if (!
|
|
8036
|
-
const parsed = JSON.parse(
|
|
8037
|
+
if (!fs26.existsSync(eventPath)) return null;
|
|
8038
|
+
const parsed = JSON.parse(fs26.readFileSync(eventPath, "utf-8"));
|
|
8037
8039
|
return recordValue3(parsed);
|
|
8038
8040
|
} catch {
|
|
8039
8041
|
return null;
|
|
@@ -8143,8 +8145,8 @@ var init_stateStore = __esm({
|
|
|
8143
8145
|
});
|
|
8144
8146
|
|
|
8145
8147
|
// src/goal/targetLoopResolution.ts
|
|
8146
|
-
import * as
|
|
8147
|
-
import * as
|
|
8148
|
+
import * as fs27 from "fs";
|
|
8149
|
+
import * as path24 from "path";
|
|
8148
8150
|
async function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
|
|
8149
8151
|
const targetId = loopGoal.loopTarget?.id.trim() ?? "";
|
|
8150
8152
|
assertSafeGoalId(targetId, "loop target");
|
|
@@ -8222,11 +8224,11 @@ function goalInstanceTime(state) {
|
|
|
8222
8224
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
8223
8225
|
}
|
|
8224
8226
|
function loadGoalTemplate(cwd, targetId) {
|
|
8225
|
-
return readJsonObject(
|
|
8227
|
+
return readJsonObject(path24.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
|
|
8226
8228
|
}
|
|
8227
8229
|
function readJsonObject(filePath) {
|
|
8228
|
-
if (!
|
|
8229
|
-
const parsed = JSON.parse(
|
|
8230
|
+
if (!fs27.existsSync(filePath)) return null;
|
|
8231
|
+
const parsed = JSON.parse(fs27.readFileSync(filePath, "utf8"));
|
|
8230
8232
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8231
8233
|
throw new Error(`goal template ${filePath} must be a JSON object`);
|
|
8232
8234
|
}
|
|
@@ -8669,11 +8671,11 @@ function validateWorkflow(value, options = {}) {
|
|
|
8669
8671
|
function formatWorkflowValidationIssues(issues) {
|
|
8670
8672
|
return issues.map((entry) => `${entry.path}: ${entry.message}`);
|
|
8671
8673
|
}
|
|
8672
|
-
function validateDataMatch(value,
|
|
8674
|
+
function validateDataMatch(value, path51, issues, capabilityOutputs) {
|
|
8673
8675
|
if (value === void 0) return;
|
|
8674
8676
|
const match = asRecord2(value);
|
|
8675
8677
|
if (!match || Object.keys(match).length === 0) {
|
|
8676
|
-
issue(issues, "invalid_condition",
|
|
8678
|
+
issue(issues, "invalid_condition", path51, "workflow condition must contain at least one match");
|
|
8677
8679
|
return;
|
|
8678
8680
|
}
|
|
8679
8681
|
for (const [field, expected] of Object.entries(match)) {
|
|
@@ -8681,7 +8683,7 @@ function validateDataMatch(value, path50, issues, capabilityOutputs) {
|
|
|
8681
8683
|
issue(
|
|
8682
8684
|
issues,
|
|
8683
8685
|
"invalid_data_path",
|
|
8684
|
-
`${
|
|
8686
|
+
`${path51}.${field}`,
|
|
8685
8687
|
`workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
|
|
8686
8688
|
);
|
|
8687
8689
|
}
|
|
@@ -8689,12 +8691,12 @@ function validateDataMatch(value, path50, issues, capabilityOutputs) {
|
|
|
8689
8691
|
issue(
|
|
8690
8692
|
issues,
|
|
8691
8693
|
"undeclared_result_path",
|
|
8692
|
-
`${
|
|
8694
|
+
`${path51}.${field}`,
|
|
8693
8695
|
`workflow condition reads ${field}, but the source capability does not declare it`
|
|
8694
8696
|
);
|
|
8695
8697
|
}
|
|
8696
8698
|
if (!isComparable(expected)) {
|
|
8697
|
-
issue(issues, "invalid_condition_value", `${
|
|
8699
|
+
issue(issues, "invalid_condition_value", `${path51}.${field}`, "workflow condition value must be a JSON scalar");
|
|
8698
8700
|
}
|
|
8699
8701
|
}
|
|
8700
8702
|
}
|
|
@@ -8712,8 +8714,8 @@ function isComparable(value) {
|
|
|
8712
8714
|
if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
|
|
8713
8715
|
return Array.isArray(value) && value.length > 0 && value.every((item) => isComparable(item) && !Array.isArray(item));
|
|
8714
8716
|
}
|
|
8715
|
-
function issue(issues, code,
|
|
8716
|
-
issues.push({ code, path:
|
|
8717
|
+
function issue(issues, code, path51, message) {
|
|
8718
|
+
issues.push({ code, path: path51, message });
|
|
8717
8719
|
}
|
|
8718
8720
|
var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
|
|
8719
8721
|
var init_workflowValidation = __esm({
|
|
@@ -8745,8 +8747,8 @@ var init_workflowValidation = __esm({
|
|
|
8745
8747
|
});
|
|
8746
8748
|
|
|
8747
8749
|
// src/workflowDefinitions.ts
|
|
8748
|
-
import * as
|
|
8749
|
-
import * as
|
|
8750
|
+
import * as fs28 from "fs";
|
|
8751
|
+
import * as path25 from "path";
|
|
8750
8752
|
function isWorkflowDefinitionId(value) {
|
|
8751
8753
|
return WORKFLOW_ID_PATTERN.test(value);
|
|
8752
8754
|
}
|
|
@@ -8787,14 +8789,14 @@ function normalizeWorkflowDefinition(value) {
|
|
|
8787
8789
|
};
|
|
8788
8790
|
}
|
|
8789
8791
|
function readWorkflowDefinition(_config, cwd, id) {
|
|
8790
|
-
const filePath =
|
|
8791
|
-
if (!
|
|
8792
|
-
return parseWorkflowDefinition(
|
|
8792
|
+
const filePath = path25.join(cwd ?? process.cwd(), ".kody-engine", "runtime", workflowDefinitionPath(id));
|
|
8793
|
+
if (!fs28.existsSync(filePath)) return null;
|
|
8794
|
+
return parseWorkflowDefinition(fs28.readFileSync(filePath, "utf8"));
|
|
8793
8795
|
}
|
|
8794
8796
|
function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
|
|
8795
8797
|
return {
|
|
8796
8798
|
slug: id,
|
|
8797
|
-
dir:
|
|
8799
|
+
dir: path25.dirname(source),
|
|
8798
8800
|
profilePath: source,
|
|
8799
8801
|
bodyPath: source,
|
|
8800
8802
|
title: workflow.name,
|
|
@@ -8971,15 +8973,15 @@ var init_backendStateBackend = __esm({
|
|
|
8971
8973
|
this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
|
|
8972
8974
|
}
|
|
8973
8975
|
async load(slug) {
|
|
8974
|
-
const
|
|
8976
|
+
const path51 = stateFilePath(this.jobsDir, slug);
|
|
8975
8977
|
const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
|
|
8976
8978
|
if (!loaded) {
|
|
8977
|
-
return { path:
|
|
8979
|
+
return { path: path51, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
8978
8980
|
}
|
|
8979
8981
|
if (!isStateEnvelope(loaded.doc)) {
|
|
8980
8982
|
throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
|
|
8981
8983
|
}
|
|
8982
|
-
return { path:
|
|
8984
|
+
return { path: path51, handle: loaded.updatedAt, state: loaded.doc, created: false };
|
|
8983
8985
|
}
|
|
8984
8986
|
async save(loaded, next) {
|
|
8985
8987
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
|
|
@@ -8999,8 +9001,8 @@ var init_backendStateBackend = __esm({
|
|
|
8999
9001
|
});
|
|
9000
9002
|
|
|
9001
9003
|
// src/scripts/jobState/localFileBackend.ts
|
|
9002
|
-
import * as
|
|
9003
|
-
import * as
|
|
9004
|
+
import * as fs29 from "fs";
|
|
9005
|
+
import * as path26 from "path";
|
|
9004
9006
|
function sanitizeKey(s) {
|
|
9005
9007
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
9006
9008
|
}
|
|
@@ -9056,7 +9058,7 @@ var init_localFileBackend = __esm({
|
|
|
9056
9058
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
9057
9059
|
this.cwd = opts.cwd;
|
|
9058
9060
|
this.jobsDir = opts.jobsDir;
|
|
9059
|
-
this.absDir =
|
|
9061
|
+
this.absDir = path26.resolve(opts.cwd, opts.jobsDir);
|
|
9060
9062
|
this.owner = opts.owner;
|
|
9061
9063
|
this.repo = opts.repo;
|
|
9062
9064
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -9071,7 +9073,7 @@ var init_localFileBackend = __esm({
|
|
|
9071
9073
|
`);
|
|
9072
9074
|
return;
|
|
9073
9075
|
}
|
|
9074
|
-
|
|
9076
|
+
fs29.mkdirSync(this.absDir, { recursive: true });
|
|
9075
9077
|
const prefix = this.cacheKeyPrefix();
|
|
9076
9078
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
9077
9079
|
try {
|
|
@@ -9100,7 +9102,7 @@ var init_localFileBackend = __esm({
|
|
|
9100
9102
|
`);
|
|
9101
9103
|
return;
|
|
9102
9104
|
}
|
|
9103
|
-
if (!
|
|
9105
|
+
if (!fs29.existsSync(this.absDir)) {
|
|
9104
9106
|
return;
|
|
9105
9107
|
}
|
|
9106
9108
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -9116,11 +9118,11 @@ var init_localFileBackend = __esm({
|
|
|
9116
9118
|
}
|
|
9117
9119
|
load(slug) {
|
|
9118
9120
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
9119
|
-
const absPath =
|
|
9120
|
-
if (!
|
|
9121
|
+
const absPath = path26.resolve(this.cwd, relPath);
|
|
9122
|
+
if (!fs29.existsSync(absPath)) {
|
|
9121
9123
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
9122
9124
|
}
|
|
9123
|
-
const raw =
|
|
9125
|
+
const raw = fs29.readFileSync(absPath, "utf-8");
|
|
9124
9126
|
let parsed;
|
|
9125
9127
|
try {
|
|
9126
9128
|
parsed = JSON.parse(raw);
|
|
@@ -9137,13 +9139,13 @@ var init_localFileBackend = __esm({
|
|
|
9137
9139
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
9138
9140
|
return false;
|
|
9139
9141
|
}
|
|
9140
|
-
const absPath =
|
|
9141
|
-
|
|
9142
|
+
const absPath = path26.resolve(this.cwd, loaded.path);
|
|
9143
|
+
fs29.mkdirSync(path26.dirname(absPath), { recursive: true });
|
|
9142
9144
|
const body = `${JSON.stringify(next, null, 2)}
|
|
9143
9145
|
`;
|
|
9144
9146
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
+
fs29.writeFileSync(tmpPath, body, "utf-8");
|
|
9148
|
+
fs29.renameSync(tmpPath, absPath);
|
|
9147
9149
|
return true;
|
|
9148
9150
|
}
|
|
9149
9151
|
cacheKeyPrefix() {
|
|
@@ -9175,7 +9177,7 @@ var init_jobState = __esm({
|
|
|
9175
9177
|
});
|
|
9176
9178
|
|
|
9177
9179
|
// src/scripts/goalCapabilityScheduling.ts
|
|
9178
|
-
import * as
|
|
9180
|
+
import * as path27 from "path";
|
|
9179
9181
|
function isCapabilityCadenceGoal(goal, extra) {
|
|
9180
9182
|
return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
|
|
9181
9183
|
}
|
|
@@ -9231,7 +9233,7 @@ function planTargetLoopSchedule(opts) {
|
|
|
9231
9233
|
}
|
|
9232
9234
|
async function planGoalCapabilitySchedule(opts) {
|
|
9233
9235
|
const jobsDir = opts.jobsDir ?? capabilitiesRoot(opts.cwd);
|
|
9234
|
-
const jobsRoot =
|
|
9236
|
+
const jobsRoot = path27.resolve(opts.cwd, jobsDir);
|
|
9235
9237
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
9236
9238
|
const at = now.toISOString();
|
|
9237
9239
|
const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
|
|
@@ -11046,8 +11048,8 @@ var init_classifyByLabel = __esm({
|
|
|
11046
11048
|
});
|
|
11047
11049
|
|
|
11048
11050
|
// src/scripts/commitAndPush.ts
|
|
11049
|
-
import * as
|
|
11050
|
-
import * as
|
|
11051
|
+
import * as fs30 from "fs";
|
|
11052
|
+
import * as path28 from "path";
|
|
11051
11053
|
function sentinelPathForStage(cwd, profileName) {
|
|
11052
11054
|
const runId = resolveRunId();
|
|
11053
11055
|
return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
|
|
@@ -11068,9 +11070,9 @@ var init_commitAndPush = __esm({
|
|
|
11068
11070
|
}
|
|
11069
11071
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
11070
11072
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
11071
|
-
if (sentinel &&
|
|
11073
|
+
if (sentinel && fs30.existsSync(sentinel)) {
|
|
11072
11074
|
try {
|
|
11073
|
-
const replay = JSON.parse(
|
|
11075
|
+
const replay = JSON.parse(fs30.readFileSync(sentinel, "utf-8"));
|
|
11074
11076
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
11075
11077
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
11076
11078
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -11130,8 +11132,8 @@ var init_commitAndPush = __esm({
|
|
|
11130
11132
|
const result = ctx.data.commitResult;
|
|
11131
11133
|
if (sentinel && result?.committed) {
|
|
11132
11134
|
try {
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
+
fs30.mkdirSync(path28.dirname(sentinel), { recursive: true });
|
|
11136
|
+
fs30.writeFileSync(
|
|
11135
11137
|
sentinel,
|
|
11136
11138
|
JSON.stringify(
|
|
11137
11139
|
{
|
|
@@ -11225,8 +11227,8 @@ var init_commitGoalState = __esm({
|
|
|
11225
11227
|
});
|
|
11226
11228
|
|
|
11227
11229
|
// src/scripts/composePrompt.ts
|
|
11228
|
-
import * as
|
|
11229
|
-
import * as
|
|
11230
|
+
import * as fs31 from "fs";
|
|
11231
|
+
import * as path29 from "path";
|
|
11230
11232
|
function fenceUntrusted(value) {
|
|
11231
11233
|
if (value.trim().length === 0) return value;
|
|
11232
11234
|
const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
@@ -11350,10 +11352,10 @@ var init_composePrompt = __esm({
|
|
|
11350
11352
|
const explicit = ctx.data.promptTemplate;
|
|
11351
11353
|
const mode = ctx.args.mode;
|
|
11352
11354
|
const candidates = [
|
|
11353
|
-
explicit ?
|
|
11354
|
-
mode ?
|
|
11355
|
-
|
|
11356
|
-
|
|
11355
|
+
explicit ? path29.join(profile.dir, explicit) : null,
|
|
11356
|
+
mode ? path29.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
11357
|
+
path29.join(profile.dir, "prompt.md"),
|
|
11358
|
+
path29.join(profile.dir, "capability.md")
|
|
11357
11359
|
].filter(Boolean);
|
|
11358
11360
|
let templatePath = "";
|
|
11359
11361
|
let template = "";
|
|
@@ -11366,7 +11368,7 @@ var init_composePrompt = __esm({
|
|
|
11366
11368
|
break;
|
|
11367
11369
|
}
|
|
11368
11370
|
try {
|
|
11369
|
-
template =
|
|
11371
|
+
template = fs31.readFileSync(c, "utf-8");
|
|
11370
11372
|
templatePath = c;
|
|
11371
11373
|
break;
|
|
11372
11374
|
} catch (err) {
|
|
@@ -11377,7 +11379,7 @@ var init_composePrompt = __esm({
|
|
|
11377
11379
|
if (!templatePath) {
|
|
11378
11380
|
let dirState;
|
|
11379
11381
|
try {
|
|
11380
|
-
dirState = `dir contents: [${
|
|
11382
|
+
dirState = `dir contents: [${fs31.readdirSync(profile.dir).join(", ")}]`;
|
|
11381
11383
|
} catch (err) {
|
|
11382
11384
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
11383
11385
|
}
|
|
@@ -12066,19 +12068,19 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
12066
12068
|
|
|
12067
12069
|
// src/scripts/diagMcp.ts
|
|
12068
12070
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
12069
|
-
import * as
|
|
12071
|
+
import * as fs32 from "fs";
|
|
12070
12072
|
import * as os5 from "os";
|
|
12071
|
-
import * as
|
|
12073
|
+
import * as path30 from "path";
|
|
12072
12074
|
var diagMcp;
|
|
12073
12075
|
var init_diagMcp = __esm({
|
|
12074
12076
|
"src/scripts/diagMcp.ts"() {
|
|
12075
12077
|
"use strict";
|
|
12076
12078
|
diagMcp = async (_ctx) => {
|
|
12077
12079
|
const home = os5.homedir();
|
|
12078
|
-
const cacheDir =
|
|
12080
|
+
const cacheDir = path30.join(home, ".cache", "ms-playwright");
|
|
12079
12081
|
let entries = [];
|
|
12080
12082
|
try {
|
|
12081
|
-
entries =
|
|
12083
|
+
entries = fs32.readdirSync(cacheDir);
|
|
12082
12084
|
} catch {
|
|
12083
12085
|
}
|
|
12084
12086
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -12106,13 +12108,13 @@ var init_diagMcp = __esm({
|
|
|
12106
12108
|
});
|
|
12107
12109
|
|
|
12108
12110
|
// src/scripts/frameworkDetectors.ts
|
|
12109
|
-
import * as
|
|
12110
|
-
import * as
|
|
12111
|
+
import * as fs33 from "fs";
|
|
12112
|
+
import * as path31 from "path";
|
|
12111
12113
|
function detectFrameworks(cwd) {
|
|
12112
12114
|
const out = [];
|
|
12113
12115
|
let deps = {};
|
|
12114
12116
|
try {
|
|
12115
|
-
const pkg = JSON.parse(
|
|
12117
|
+
const pkg = JSON.parse(fs33.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
|
|
12116
12118
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
12117
12119
|
} catch {
|
|
12118
12120
|
return out;
|
|
@@ -12149,25 +12151,25 @@ function detectFrameworks(cwd) {
|
|
|
12149
12151
|
}
|
|
12150
12152
|
function findFile(cwd, candidates) {
|
|
12151
12153
|
for (const c of candidates) {
|
|
12152
|
-
if (
|
|
12154
|
+
if (fs33.existsSync(path31.join(cwd, c))) return c;
|
|
12153
12155
|
}
|
|
12154
12156
|
return null;
|
|
12155
12157
|
}
|
|
12156
12158
|
function discoverPayloadCollections(cwd) {
|
|
12157
12159
|
const out = [];
|
|
12158
12160
|
for (const dir of COLLECTION_DIRS) {
|
|
12159
|
-
const full =
|
|
12160
|
-
if (!
|
|
12161
|
+
const full = path31.join(cwd, dir);
|
|
12162
|
+
if (!fs33.existsSync(full)) continue;
|
|
12161
12163
|
let files;
|
|
12162
12164
|
try {
|
|
12163
|
-
files =
|
|
12165
|
+
files = fs33.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
12164
12166
|
} catch {
|
|
12165
12167
|
continue;
|
|
12166
12168
|
}
|
|
12167
12169
|
for (const file of files) {
|
|
12168
12170
|
try {
|
|
12169
|
-
const filePath =
|
|
12170
|
-
const content =
|
|
12171
|
+
const filePath = path31.join(full, file);
|
|
12172
|
+
const content = fs33.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
12171
12173
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
12172
12174
|
if (!slugMatch) continue;
|
|
12173
12175
|
const slug = slugMatch[1];
|
|
@@ -12181,7 +12183,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
12181
12183
|
out.push({
|
|
12182
12184
|
name,
|
|
12183
12185
|
slug,
|
|
12184
|
-
filePath:
|
|
12186
|
+
filePath: path31.relative(cwd, filePath),
|
|
12185
12187
|
fields: fields.slice(0, 20),
|
|
12186
12188
|
hasAdmin
|
|
12187
12189
|
});
|
|
@@ -12194,28 +12196,28 @@ function discoverPayloadCollections(cwd) {
|
|
|
12194
12196
|
function discoverAdminComponents(cwd, collections) {
|
|
12195
12197
|
const out = [];
|
|
12196
12198
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
12197
|
-
const full =
|
|
12198
|
-
if (!
|
|
12199
|
+
const full = path31.join(cwd, dir);
|
|
12200
|
+
if (!fs33.existsSync(full)) continue;
|
|
12199
12201
|
let entries;
|
|
12200
12202
|
try {
|
|
12201
|
-
entries =
|
|
12203
|
+
entries = fs33.readdirSync(full, { withFileTypes: true });
|
|
12202
12204
|
} catch {
|
|
12203
12205
|
continue;
|
|
12204
12206
|
}
|
|
12205
12207
|
for (const entry of entries) {
|
|
12206
|
-
const entryPath =
|
|
12208
|
+
const entryPath = path31.join(full, entry.name);
|
|
12207
12209
|
let name;
|
|
12208
12210
|
let filePath;
|
|
12209
12211
|
if (entry.isDirectory()) {
|
|
12210
12212
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
12211
|
-
(f) =>
|
|
12213
|
+
(f) => fs33.existsSync(path31.join(entryPath, f))
|
|
12212
12214
|
);
|
|
12213
12215
|
if (!indexFile) continue;
|
|
12214
12216
|
name = entry.name;
|
|
12215
|
-
filePath =
|
|
12217
|
+
filePath = path31.relative(cwd, path31.join(entryPath, indexFile));
|
|
12216
12218
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
12217
12219
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
12218
|
-
filePath =
|
|
12220
|
+
filePath = path31.relative(cwd, entryPath);
|
|
12219
12221
|
} else {
|
|
12220
12222
|
continue;
|
|
12221
12223
|
}
|
|
@@ -12223,7 +12225,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
12223
12225
|
if (collections) {
|
|
12224
12226
|
for (const col of collections) {
|
|
12225
12227
|
try {
|
|
12226
|
-
const colContent =
|
|
12228
|
+
const colContent = fs33.readFileSync(path31.join(cwd, col.filePath), "utf-8");
|
|
12227
12229
|
if (colContent.includes(name)) {
|
|
12228
12230
|
usedInCollection = col.slug;
|
|
12229
12231
|
break;
|
|
@@ -12241,8 +12243,8 @@ function scanApiRoutes(cwd) {
|
|
|
12241
12243
|
const out = [];
|
|
12242
12244
|
const appDirs = ["src/app", "app"];
|
|
12243
12245
|
for (const appDir of appDirs) {
|
|
12244
|
-
const apiDir =
|
|
12245
|
-
if (!
|
|
12246
|
+
const apiDir = path31.join(cwd, appDir, "api");
|
|
12247
|
+
if (!fs33.existsSync(apiDir)) continue;
|
|
12246
12248
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
12247
12249
|
break;
|
|
12248
12250
|
}
|
|
@@ -12251,14 +12253,14 @@ function scanApiRoutes(cwd) {
|
|
|
12251
12253
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
12252
12254
|
let entries;
|
|
12253
12255
|
try {
|
|
12254
|
-
entries =
|
|
12256
|
+
entries = fs33.readdirSync(dir, { withFileTypes: true });
|
|
12255
12257
|
} catch {
|
|
12256
12258
|
return;
|
|
12257
12259
|
}
|
|
12258
12260
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
12259
12261
|
if (routeFile) {
|
|
12260
12262
|
try {
|
|
12261
|
-
const content =
|
|
12263
|
+
const content = fs33.readFileSync(path31.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
12262
12264
|
const methods = HTTP_METHODS.filter(
|
|
12263
12265
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
12264
12266
|
);
|
|
@@ -12266,7 +12268,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
12266
12268
|
out.push({
|
|
12267
12269
|
path: prefix,
|
|
12268
12270
|
methods,
|
|
12269
|
-
filePath:
|
|
12271
|
+
filePath: path31.relative(cwd, path31.join(dir, routeFile.name))
|
|
12270
12272
|
});
|
|
12271
12273
|
}
|
|
12272
12274
|
} catch {
|
|
@@ -12277,7 +12279,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
12277
12279
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
12278
12280
|
let segment = entry.name;
|
|
12279
12281
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
12280
|
-
walkApiRoutes(
|
|
12282
|
+
walkApiRoutes(path31.join(dir, entry.name), prefix, cwd, out);
|
|
12281
12283
|
continue;
|
|
12282
12284
|
}
|
|
12283
12285
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -12285,16 +12287,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
12285
12287
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
12286
12288
|
segment = `:${segment.slice(1, -1)}`;
|
|
12287
12289
|
}
|
|
12288
|
-
walkApiRoutes(
|
|
12290
|
+
walkApiRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
12289
12291
|
}
|
|
12290
12292
|
}
|
|
12291
12293
|
function scanEnvVars(cwd) {
|
|
12292
12294
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
12293
12295
|
for (const envFile of candidates) {
|
|
12294
|
-
const envPath =
|
|
12295
|
-
if (!
|
|
12296
|
+
const envPath = path31.join(cwd, envFile);
|
|
12297
|
+
if (!fs33.existsSync(envPath)) continue;
|
|
12296
12298
|
try {
|
|
12297
|
-
const content =
|
|
12299
|
+
const content = fs33.readFileSync(envPath, "utf-8");
|
|
12298
12300
|
const vars = [];
|
|
12299
12301
|
for (const line of content.split("\n")) {
|
|
12300
12302
|
const trimmed = line.trim();
|
|
@@ -12339,8 +12341,8 @@ var init_frameworkDetectors = __esm({
|
|
|
12339
12341
|
});
|
|
12340
12342
|
|
|
12341
12343
|
// src/scripts/discoverQaContext.ts
|
|
12342
|
-
import * as
|
|
12343
|
-
import * as
|
|
12344
|
+
import * as fs34 from "fs";
|
|
12345
|
+
import * as path32 from "path";
|
|
12344
12346
|
function runQaDiscovery(cwd) {
|
|
12345
12347
|
const out = {
|
|
12346
12348
|
routes: [],
|
|
@@ -12371,9 +12373,9 @@ function runQaDiscovery(cwd) {
|
|
|
12371
12373
|
}
|
|
12372
12374
|
function detectDevServer(cwd, out) {
|
|
12373
12375
|
try {
|
|
12374
|
-
const pkg = JSON.parse(
|
|
12376
|
+
const pkg = JSON.parse(fs34.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
|
|
12375
12377
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
12376
|
-
const pm =
|
|
12378
|
+
const pm = fs34.existsSync(path32.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs34.existsSync(path32.join(cwd, "yarn.lock")) ? "yarn" : fs34.existsSync(path32.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
12377
12379
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
12378
12380
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
12379
12381
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -12383,8 +12385,8 @@ function detectDevServer(cwd, out) {
|
|
|
12383
12385
|
function scanFrontendRoutes(cwd, out) {
|
|
12384
12386
|
const appDirs = ["src/app", "app"];
|
|
12385
12387
|
for (const appDir of appDirs) {
|
|
12386
|
-
const full =
|
|
12387
|
-
if (!
|
|
12388
|
+
const full = path32.join(cwd, appDir);
|
|
12389
|
+
if (!fs34.existsSync(full)) continue;
|
|
12388
12390
|
walkFrontendRoutes(full, "", out);
|
|
12389
12391
|
break;
|
|
12390
12392
|
}
|
|
@@ -12392,7 +12394,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
12392
12394
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
12393
12395
|
let entries;
|
|
12394
12396
|
try {
|
|
12395
|
-
entries =
|
|
12397
|
+
entries = fs34.readdirSync(dir, { withFileTypes: true });
|
|
12396
12398
|
} catch {
|
|
12397
12399
|
return;
|
|
12398
12400
|
}
|
|
@@ -12409,7 +12411,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
12409
12411
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
12410
12412
|
let segment = entry.name;
|
|
12411
12413
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
12412
|
-
walkFrontendRoutes(
|
|
12414
|
+
walkFrontendRoutes(path32.join(dir, entry.name), prefix, out);
|
|
12413
12415
|
continue;
|
|
12414
12416
|
}
|
|
12415
12417
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -12417,7 +12419,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
12417
12419
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
12418
12420
|
segment = `:${segment.slice(1, -1)}`;
|
|
12419
12421
|
}
|
|
12420
|
-
walkFrontendRoutes(
|
|
12422
|
+
walkFrontendRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
12421
12423
|
}
|
|
12422
12424
|
}
|
|
12423
12425
|
function detectAuthFiles(cwd, out) {
|
|
@@ -12434,23 +12436,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
12434
12436
|
"src/app/api/oauth"
|
|
12435
12437
|
];
|
|
12436
12438
|
for (const c of candidates) {
|
|
12437
|
-
if (
|
|
12439
|
+
if (fs34.existsSync(path32.join(cwd, c))) out.authFiles.push(c);
|
|
12438
12440
|
}
|
|
12439
12441
|
}
|
|
12440
12442
|
function detectRoles(cwd, out) {
|
|
12441
12443
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
12442
12444
|
for (const rp of rolePaths) {
|
|
12443
|
-
const dir =
|
|
12444
|
-
if (!
|
|
12445
|
+
const dir = path32.join(cwd, rp);
|
|
12446
|
+
if (!fs34.existsSync(dir)) continue;
|
|
12445
12447
|
let files;
|
|
12446
12448
|
try {
|
|
12447
|
-
files =
|
|
12449
|
+
files = fs34.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
12448
12450
|
} catch {
|
|
12449
12451
|
continue;
|
|
12450
12452
|
}
|
|
12451
12453
|
for (const f of files) {
|
|
12452
12454
|
try {
|
|
12453
|
-
const content =
|
|
12455
|
+
const content = fs34.readFileSync(path32.join(dir, f), "utf-8").slice(0, 5e3);
|
|
12454
12456
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
12455
12457
|
if (roleMatches) {
|
|
12456
12458
|
for (const m of roleMatches) {
|
|
@@ -13731,12 +13733,12 @@ var init_fixFlow = __esm({
|
|
|
13731
13733
|
|
|
13732
13734
|
// src/scripts/initFlow.ts
|
|
13733
13735
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
13734
|
-
import * as
|
|
13735
|
-
import * as
|
|
13736
|
+
import * as fs35 from "fs";
|
|
13737
|
+
import * as path33 from "path";
|
|
13736
13738
|
function detectPackageManager(cwd) {
|
|
13737
|
-
if (
|
|
13738
|
-
if (
|
|
13739
|
-
if (
|
|
13739
|
+
if (fs35.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
13740
|
+
if (fs35.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
|
|
13741
|
+
if (fs35.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
|
|
13740
13742
|
return "npm";
|
|
13741
13743
|
}
|
|
13742
13744
|
function qualityCommandsFor(pm) {
|
|
@@ -13808,22 +13810,22 @@ function performInit(cwd, force) {
|
|
|
13808
13810
|
const pm = detectPackageManager(cwd);
|
|
13809
13811
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
13810
13812
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
13811
|
-
const configPath =
|
|
13812
|
-
if (
|
|
13813
|
+
const configPath = path33.join(cwd, "kody.config.json");
|
|
13814
|
+
if (fs35.existsSync(configPath) && !force) {
|
|
13813
13815
|
skipped.push("kody.config.json");
|
|
13814
13816
|
} else {
|
|
13815
13817
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
13816
|
-
|
|
13818
|
+
fs35.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
13817
13819
|
`);
|
|
13818
13820
|
wrote.push("kody.config.json");
|
|
13819
13821
|
}
|
|
13820
|
-
const workflowDir =
|
|
13821
|
-
const workflowPath =
|
|
13822
|
-
if (
|
|
13822
|
+
const workflowDir = path33.join(cwd, ".github", "workflows");
|
|
13823
|
+
const workflowPath = path33.join(workflowDir, "kody.yml");
|
|
13824
|
+
if (fs35.existsSync(workflowPath) && !force) {
|
|
13823
13825
|
skipped.push(".github/workflows/kody.yml");
|
|
13824
13826
|
} else {
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
+
fs35.mkdirSync(workflowDir, { recursive: true });
|
|
13828
|
+
fs35.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
13827
13829
|
wrote.push(".github/workflows/kody.yml");
|
|
13828
13830
|
}
|
|
13829
13831
|
for (const exe of listImplementations()) {
|
|
@@ -13834,12 +13836,12 @@ function performInit(cwd, force) {
|
|
|
13834
13836
|
continue;
|
|
13835
13837
|
}
|
|
13836
13838
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
13837
|
-
const target =
|
|
13838
|
-
if (
|
|
13839
|
+
const target = path33.join(workflowDir, `kody-${exe.name}.yml`);
|
|
13840
|
+
if (fs35.existsSync(target) && !force) {
|
|
13839
13841
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
13840
13842
|
continue;
|
|
13841
13843
|
}
|
|
13842
|
-
|
|
13844
|
+
fs35.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
13843
13845
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
13844
13846
|
}
|
|
13845
13847
|
let labels;
|
|
@@ -13987,7 +13989,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
|
|
|
13987
13989
|
});
|
|
13988
13990
|
|
|
13989
13991
|
// src/scripts/loadAgentAdhoc.ts
|
|
13990
|
-
import * as
|
|
13992
|
+
import * as fs36 from "fs";
|
|
13991
13993
|
function resolveMessage(messageArg) {
|
|
13992
13994
|
const fromComment = readCommentBody();
|
|
13993
13995
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -13995,9 +13997,9 @@ function resolveMessage(messageArg) {
|
|
|
13995
13997
|
}
|
|
13996
13998
|
function readCommentBody() {
|
|
13997
13999
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
13998
|
-
if (!eventPath || !
|
|
14000
|
+
if (!eventPath || !fs36.existsSync(eventPath)) return "";
|
|
13999
14001
|
try {
|
|
14000
|
-
const event = JSON.parse(
|
|
14002
|
+
const event = JSON.parse(fs36.readFileSync(eventPath, "utf-8"));
|
|
14001
14003
|
return String(event.comment?.body ?? "");
|
|
14002
14004
|
} catch {
|
|
14003
14005
|
return "";
|
|
@@ -14051,10 +14053,10 @@ var init_loadAgentAdhoc = __esm({
|
|
|
14051
14053
|
throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
|
|
14052
14054
|
}
|
|
14053
14055
|
const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
|
|
14054
|
-
if (!
|
|
14056
|
+
if (!fs36.existsSync(agentPath)) {
|
|
14055
14057
|
throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
|
|
14056
14058
|
}
|
|
14057
|
-
const { title, body } = parseAgentFile(
|
|
14059
|
+
const { title, body } = parseAgentFile(fs36.readFileSync(agentPath, "utf-8"), agentSlug);
|
|
14058
14060
|
const message = resolveMessage(ctx.args.message);
|
|
14059
14061
|
if (!message) {
|
|
14060
14062
|
throw new Error(
|
|
@@ -14126,13 +14128,13 @@ var init_loadCapabilityState = __esm({
|
|
|
14126
14128
|
function isCompanyIntentId(value) {
|
|
14127
14129
|
return SLUG_RE.test(value);
|
|
14128
14130
|
}
|
|
14129
|
-
function normalizeCompanyIntent(
|
|
14131
|
+
function normalizeCompanyIntent(path51, raw) {
|
|
14130
14132
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
14131
|
-
throw new Error(`${
|
|
14133
|
+
throw new Error(`${path51}: intent must be JSON object`);
|
|
14132
14134
|
}
|
|
14133
14135
|
const input = raw;
|
|
14134
14136
|
const id = stringField4(input.id);
|
|
14135
|
-
if (!id || !isCompanyIntentId(id)) throw new Error(`${
|
|
14137
|
+
if (!id || !isCompanyIntentId(id)) throw new Error(`${path51}: invalid intent id`);
|
|
14136
14138
|
const createdAt = stringField4(input.createdAt) || nowIso();
|
|
14137
14139
|
const updatedAt = stringField4(input.updatedAt) || createdAt;
|
|
14138
14140
|
const description = stringField4(input.description);
|
|
@@ -14423,8 +14425,8 @@ var init_loadIssueStateComment = __esm({
|
|
|
14423
14425
|
});
|
|
14424
14426
|
|
|
14425
14427
|
// src/scripts/loadJobFromFile.ts
|
|
14426
|
-
import * as
|
|
14427
|
-
import * as
|
|
14428
|
+
import * as fs37 from "fs";
|
|
14429
|
+
import * as path34 from "path";
|
|
14428
14430
|
function parseJobFile(raw, slug) {
|
|
14429
14431
|
let stripped = raw;
|
|
14430
14432
|
if (stripped.startsWith("---\n")) {
|
|
@@ -14463,10 +14465,10 @@ var init_loadJobFromFile = __esm({
|
|
|
14463
14465
|
if (!slug) {
|
|
14464
14466
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
14465
14467
|
}
|
|
14466
|
-
const capability = resolveCapabilityFolder(slug,
|
|
14468
|
+
const capability = resolveCapabilityFolder(slug, path34.resolve(ctx.cwd, jobsDir));
|
|
14467
14469
|
if (!capability) {
|
|
14468
14470
|
throw new Error(
|
|
14469
|
-
`loadJobFromFile: capability folder not found or incomplete: ${
|
|
14471
|
+
`loadJobFromFile: capability folder not found or incomplete: ${path34.resolve(ctx.cwd, jobsDir, slug)}`
|
|
14470
14472
|
);
|
|
14471
14473
|
}
|
|
14472
14474
|
const { title, body, config } = capability;
|
|
@@ -14476,12 +14478,12 @@ var init_loadJobFromFile = __esm({
|
|
|
14476
14478
|
let agentIdentity = "";
|
|
14477
14479
|
if (agentSlug) {
|
|
14478
14480
|
const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
|
|
14479
|
-
if (!
|
|
14481
|
+
if (!fs37.existsSync(agentPath)) {
|
|
14480
14482
|
throw new Error(
|
|
14481
14483
|
`loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
|
|
14482
14484
|
);
|
|
14483
14485
|
}
|
|
14484
|
-
const agentRaw =
|
|
14486
|
+
const agentRaw = fs37.readFileSync(agentPath, "utf-8");
|
|
14485
14487
|
const parsed = parseJobFile(agentRaw, agentSlug);
|
|
14486
14488
|
agentTitle = parsed.title;
|
|
14487
14489
|
agentIdentity = parsed.body;
|
|
@@ -14561,13 +14563,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
14561
14563
|
});
|
|
14562
14564
|
|
|
14563
14565
|
// src/scripts/kodyVariables.ts
|
|
14564
|
-
import * as
|
|
14565
|
-
import * as
|
|
14566
|
+
import * as fs38 from "fs";
|
|
14567
|
+
import * as path35 from "path";
|
|
14566
14568
|
function readKodyVariables(cwd) {
|
|
14567
|
-
const full =
|
|
14569
|
+
const full = path35.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
14568
14570
|
let raw;
|
|
14569
14571
|
try {
|
|
14570
|
-
raw =
|
|
14572
|
+
raw = fs38.readFileSync(full, "utf-8");
|
|
14571
14573
|
} catch {
|
|
14572
14574
|
return {};
|
|
14573
14575
|
}
|
|
@@ -14730,8 +14732,8 @@ var init_runtimeSecrets = __esm({
|
|
|
14730
14732
|
});
|
|
14731
14733
|
|
|
14732
14734
|
// src/scripts/loadQaContext.ts
|
|
14733
|
-
import * as
|
|
14734
|
-
import * as
|
|
14735
|
+
import * as fs39 from "fs";
|
|
14736
|
+
import * as path36 from "path";
|
|
14735
14737
|
function parseSlugList(value) {
|
|
14736
14738
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
14737
14739
|
return inner.split(",").map(
|
|
@@ -14760,18 +14762,18 @@ function readProfileAgents(raw) {
|
|
|
14760
14762
|
return { agent: agent ?? legacy ?? ["kody"], body };
|
|
14761
14763
|
}
|
|
14762
14764
|
function readProfile(cwd) {
|
|
14763
|
-
const dir =
|
|
14764
|
-
if (!
|
|
14765
|
+
const dir = path36.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
14766
|
+
if (!fs39.existsSync(dir)) return "";
|
|
14765
14767
|
let entries;
|
|
14766
14768
|
try {
|
|
14767
|
-
entries =
|
|
14769
|
+
entries = fs39.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
14768
14770
|
} catch {
|
|
14769
14771
|
return "";
|
|
14770
14772
|
}
|
|
14771
14773
|
const blocks = [];
|
|
14772
14774
|
for (const file of entries) {
|
|
14773
14775
|
try {
|
|
14774
|
-
const raw =
|
|
14776
|
+
const raw = fs39.readFileSync(path36.join(dir, file), "utf-8");
|
|
14775
14777
|
const { agent, body } = readProfileAgents(raw);
|
|
14776
14778
|
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
14777
14779
|
blocks.push(`## ${file}
|
|
@@ -14820,8 +14822,8 @@ var init_loadQaContext = __esm({
|
|
|
14820
14822
|
});
|
|
14821
14823
|
|
|
14822
14824
|
// src/taskContext.ts
|
|
14823
|
-
import * as
|
|
14824
|
-
import * as
|
|
14825
|
+
import * as fs40 from "fs";
|
|
14826
|
+
import * as path37 from "path";
|
|
14825
14827
|
function buildTaskContext(args) {
|
|
14826
14828
|
return {
|
|
14827
14829
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -14837,9 +14839,9 @@ function buildTaskContext(args) {
|
|
|
14837
14839
|
function persistTaskContext(cwd, ctx) {
|
|
14838
14840
|
try {
|
|
14839
14841
|
const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
|
|
14840
|
-
|
|
14841
|
-
const file =
|
|
14842
|
-
|
|
14842
|
+
fs40.mkdirSync(dir, { recursive: true });
|
|
14843
|
+
const file = path37.join(dir, "task-context.json");
|
|
14844
|
+
fs40.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
14843
14845
|
`);
|
|
14844
14846
|
return file;
|
|
14845
14847
|
} catch (err) {
|
|
@@ -15266,19 +15268,19 @@ function parseAgencyModelProposal(raw) {
|
|
|
15266
15268
|
function normalizeBundleFiles(bundle) {
|
|
15267
15269
|
const seen = /* @__PURE__ */ new Set();
|
|
15268
15270
|
return bundle.files.map((file, index) => {
|
|
15269
|
-
const
|
|
15270
|
-
const parts =
|
|
15271
|
-
if (!
|
|
15271
|
+
const path51 = file.path.replace(/^\/+/, "");
|
|
15272
|
+
const parts = path51.split("/");
|
|
15273
|
+
if (!path51 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
|
|
15272
15274
|
throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
|
|
15273
15275
|
}
|
|
15274
15276
|
if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
|
|
15275
|
-
|
|
15277
|
+
path51
|
|
15276
15278
|
)) {
|
|
15277
15279
|
throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
|
|
15278
15280
|
}
|
|
15279
|
-
if (seen.has(
|
|
15280
|
-
seen.add(
|
|
15281
|
-
return { path:
|
|
15281
|
+
if (seen.has(path51)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path51}`);
|
|
15282
|
+
seen.add(path51);
|
|
15283
|
+
return { path: path51, content: file.content.replace(/\r\n?/g, "\n") };
|
|
15282
15284
|
});
|
|
15283
15285
|
}
|
|
15284
15286
|
function buildProposalId(issueNumber, bundle, sourceLabel) {
|
|
@@ -16222,9 +16224,9 @@ var init_postResearchComment = __esm({
|
|
|
16222
16224
|
});
|
|
16223
16225
|
|
|
16224
16226
|
// src/scripts/prepareBrowserAuth.ts
|
|
16225
|
-
import * as
|
|
16227
|
+
import * as fs41 from "fs";
|
|
16226
16228
|
import * as os6 from "os";
|
|
16227
|
-
import * as
|
|
16229
|
+
import * as path38 from "path";
|
|
16228
16230
|
function appendAuthMessage(ctx, message) {
|
|
16229
16231
|
const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
|
|
16230
16232
|
ctx.data.qaAuthBlock = current ? `${current}
|
|
@@ -16263,9 +16265,9 @@ async function githubJson(url, token) {
|
|
|
16263
16265
|
return await response.json();
|
|
16264
16266
|
}
|
|
16265
16267
|
function writeKodyStorageState(input) {
|
|
16266
|
-
const directory =
|
|
16267
|
-
|
|
16268
|
-
const file =
|
|
16268
|
+
const directory = fs41.mkdtempSync(path38.join(os6.tmpdir(), "kody-browser-auth-"));
|
|
16269
|
+
fs41.chmodSync(directory, 448);
|
|
16270
|
+
const file = path38.join(directory, "storage-state.json");
|
|
16269
16271
|
const now = Date.now();
|
|
16270
16272
|
const repoEntry = {
|
|
16271
16273
|
repoUrl: input.repoUrl,
|
|
@@ -16295,7 +16297,7 @@ function writeKodyStorageState(input) {
|
|
|
16295
16297
|
}
|
|
16296
16298
|
]
|
|
16297
16299
|
};
|
|
16298
|
-
|
|
16300
|
+
fs41.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
|
|
16299
16301
|
return { directory, file };
|
|
16300
16302
|
}
|
|
16301
16303
|
function configurePlaywright(profile, storageStatePath) {
|
|
@@ -16377,7 +16379,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
16377
16379
|
configurePlaywright(profile, state.file);
|
|
16378
16380
|
const authDirectory = state.directory;
|
|
16379
16381
|
registerRuntimeCleanup(ctx, () => {
|
|
16380
|
-
|
|
16382
|
+
fs41.rmSync(authDirectory, { recursive: true, force: true });
|
|
16381
16383
|
});
|
|
16382
16384
|
appendAuthMessage(
|
|
16383
16385
|
ctx,
|
|
@@ -16385,7 +16387,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
16385
16387
|
);
|
|
16386
16388
|
return true;
|
|
16387
16389
|
} catch (error) {
|
|
16388
|
-
if (state)
|
|
16390
|
+
if (state) fs41.rmSync(state.directory, { recursive: true, force: true });
|
|
16389
16391
|
const reason = error instanceof Error ? error.message : String(error);
|
|
16390
16392
|
appendAuthMessage(
|
|
16391
16393
|
ctx,
|
|
@@ -16498,9 +16500,9 @@ function latestResult(raw, agentResult) {
|
|
|
16498
16500
|
function recordField4(value) {
|
|
16499
16501
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
16500
16502
|
}
|
|
16501
|
-
function resolveDotted(root,
|
|
16502
|
-
if (!
|
|
16503
|
-
return
|
|
16503
|
+
function resolveDotted(root, path51) {
|
|
16504
|
+
if (!path51) return void 0;
|
|
16505
|
+
return path51.split(".").reduce((value, key) => recordField4(value)?.[key], root);
|
|
16504
16506
|
}
|
|
16505
16507
|
function stringValue4(value) {
|
|
16506
16508
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
@@ -17451,10 +17453,10 @@ var init_previewBuildHelpers = __esm({
|
|
|
17451
17453
|
});
|
|
17452
17454
|
|
|
17453
17455
|
// src/scripts/previewBuildRun.ts
|
|
17454
|
-
import { spawn as
|
|
17456
|
+
import { spawn as spawn5 } from "child_process";
|
|
17455
17457
|
async function runCmd(cmd, args, opts = {}) {
|
|
17456
17458
|
await new Promise((resolve16, reject) => {
|
|
17457
|
-
const child =
|
|
17459
|
+
const child = spawn5(cmd, args, {
|
|
17458
17460
|
cwd: opts.cwd,
|
|
17459
17461
|
env: { ...process.env, ...opts.env ?? {} },
|
|
17460
17462
|
stdio: opts.input ? ["pipe", "inherit", "inherit"] : "inherit"
|
|
@@ -17537,12 +17539,12 @@ fi
|
|
|
17537
17539
|
|
|
17538
17540
|
// src/scripts/runPreviewBuild.ts
|
|
17539
17541
|
import { copyFile, writeFile } from "fs/promises";
|
|
17540
|
-
import * as
|
|
17542
|
+
import * as path39 from "path";
|
|
17541
17543
|
import { fileURLToPath } from "url";
|
|
17542
17544
|
function bundledDockerfilePath(mode) {
|
|
17543
|
-
const here =
|
|
17545
|
+
const here = path39.dirname(fileURLToPath(import.meta.url));
|
|
17544
17546
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
17545
|
-
return
|
|
17547
|
+
return path39.join(here, "preview-build-templates", file);
|
|
17546
17548
|
}
|
|
17547
17549
|
function required(name) {
|
|
17548
17550
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -17789,10 +17791,10 @@ var init_runPreviewBuild = __esm({
|
|
|
17789
17791
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
17790
17792
|
if (Object.keys(buildEnv).length > 0) {
|
|
17791
17793
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
17792
|
-
await writeFile(
|
|
17794
|
+
await writeFile(path39.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
17793
17795
|
`, "utf8");
|
|
17794
17796
|
}
|
|
17795
|
-
const consumerDockerfile =
|
|
17797
|
+
const consumerDockerfile = path39.join(ctx.cwd, "Dockerfile.preview");
|
|
17796
17798
|
const { stat } = await import("fs/promises");
|
|
17797
17799
|
let hasConsumerDockerfile = false;
|
|
17798
17800
|
try {
|
|
@@ -17976,8 +17978,8 @@ var init_tickShellRunner = __esm({
|
|
|
17976
17978
|
});
|
|
17977
17979
|
|
|
17978
17980
|
// src/scripts/runScheduledImplementationTick.ts
|
|
17979
|
-
import * as
|
|
17980
|
-
import * as
|
|
17981
|
+
import * as fs42 from "fs";
|
|
17982
|
+
import * as path40 from "path";
|
|
17981
17983
|
var runScheduledImplementationTick;
|
|
17982
17984
|
var init_runScheduledImplementationTick = __esm({
|
|
17983
17985
|
"src/scripts/runScheduledImplementationTick.ts"() {
|
|
@@ -17998,14 +18000,14 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
17998
18000
|
ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
|
|
17999
18001
|
return;
|
|
18000
18002
|
}
|
|
18001
|
-
const capability = resolveCapabilityFolder(slug,
|
|
18003
|
+
const capability = resolveCapabilityFolder(slug, path40.resolve(ctx.cwd, jobsDir));
|
|
18002
18004
|
if (!capability) {
|
|
18003
18005
|
ctx.output.exitCode = 99;
|
|
18004
18006
|
ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
|
|
18005
18007
|
return;
|
|
18006
18008
|
}
|
|
18007
|
-
const shellPath =
|
|
18008
|
-
if (!
|
|
18009
|
+
const shellPath = path40.join(profile.dir, shell);
|
|
18010
|
+
if (!fs42.existsSync(shellPath)) {
|
|
18009
18011
|
ctx.output.exitCode = 99;
|
|
18010
18012
|
ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
18011
18013
|
return;
|
|
@@ -18036,8 +18038,8 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
18036
18038
|
});
|
|
18037
18039
|
|
|
18038
18040
|
// src/scripts/runTickScript.ts
|
|
18039
|
-
import * as
|
|
18040
|
-
import * as
|
|
18041
|
+
import * as fs43 from "fs";
|
|
18042
|
+
import * as path41 from "path";
|
|
18041
18043
|
var runTickScript;
|
|
18042
18044
|
var init_runTickScript = __esm({
|
|
18043
18045
|
"src/scripts/runTickScript.ts"() {
|
|
@@ -18057,10 +18059,10 @@ var init_runTickScript = __esm({
|
|
|
18057
18059
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
18058
18060
|
return;
|
|
18059
18061
|
}
|
|
18060
|
-
const capability = readCapabilityFolder(
|
|
18062
|
+
const capability = readCapabilityFolder(path41.resolve(ctx.cwd, jobsDir), slug);
|
|
18061
18063
|
if (!capability) {
|
|
18062
18064
|
ctx.output.exitCode = 99;
|
|
18063
|
-
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${
|
|
18065
|
+
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path41.resolve(ctx.cwd, jobsDir, slug)}`;
|
|
18064
18066
|
return;
|
|
18065
18067
|
}
|
|
18066
18068
|
const tickScript = capability.config.tickScript;
|
|
@@ -18069,8 +18071,8 @@ var init_runTickScript = __esm({
|
|
|
18069
18071
|
ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
|
|
18070
18072
|
return;
|
|
18071
18073
|
}
|
|
18072
|
-
const scriptPath =
|
|
18073
|
-
if (!
|
|
18074
|
+
const scriptPath = path41.isAbsolute(tickScript) ? tickScript : path41.join(ctx.cwd, tickScript);
|
|
18075
|
+
if (!fs43.existsSync(scriptPath)) {
|
|
18074
18076
|
ctx.output.exitCode = 99;
|
|
18075
18077
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
18076
18078
|
return;
|
|
@@ -18352,7 +18354,7 @@ var init_syncFlow = __esm({
|
|
|
18352
18354
|
});
|
|
18353
18355
|
|
|
18354
18356
|
// src/scripts/validateAgencyModelProposal.ts
|
|
18355
|
-
import * as
|
|
18357
|
+
import * as path42 from "path";
|
|
18356
18358
|
function validateModelBundle(bundle, expectedKind, options = {}) {
|
|
18357
18359
|
const failures = [];
|
|
18358
18360
|
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
|
|
@@ -18678,7 +18680,7 @@ var init_validateAgencyModelProposal = __esm({
|
|
|
18678
18680
|
const bundle = parseAgencyModelProposal(raw);
|
|
18679
18681
|
const expectedKind = readExpectedModelKind(args);
|
|
18680
18682
|
const failures = validateModelBundle(bundle, expectedKind, {
|
|
18681
|
-
capabilityRoot:
|
|
18683
|
+
capabilityRoot: path42.join(ctx.cwd, ".kody", "capabilities")
|
|
18682
18684
|
});
|
|
18683
18685
|
if (failures.length > 0) {
|
|
18684
18686
|
throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
|
|
@@ -18723,7 +18725,7 @@ var init_verify2 = __esm({
|
|
|
18723
18725
|
});
|
|
18724
18726
|
|
|
18725
18727
|
// src/scripts/verifyReproFails.ts
|
|
18726
|
-
import { spawn as
|
|
18728
|
+
import { spawn as spawn6 } from "child_process";
|
|
18727
18729
|
function composeTestCommand(baseCmd, testPath) {
|
|
18728
18730
|
const trimmed = baseCmd.trim();
|
|
18729
18731
|
const lower = trimmed.toLowerCase();
|
|
@@ -18742,7 +18744,7 @@ function stripAnsi2(s) {
|
|
|
18742
18744
|
}
|
|
18743
18745
|
function runCommand2(command, cwd) {
|
|
18744
18746
|
return new Promise((resolve16) => {
|
|
18745
|
-
const child =
|
|
18747
|
+
const child = spawn6(command, {
|
|
18746
18748
|
cwd,
|
|
18747
18749
|
shell: true,
|
|
18748
18750
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" },
|
|
@@ -19072,9 +19074,9 @@ var init_waitForCi = __esm({
|
|
|
19072
19074
|
});
|
|
19073
19075
|
|
|
19074
19076
|
// src/scripts/warmupMcp.ts
|
|
19075
|
-
import { spawn as
|
|
19077
|
+
import { spawn as spawn7 } from "child_process";
|
|
19076
19078
|
async function warmupOne(command, args, env) {
|
|
19077
|
-
const child =
|
|
19079
|
+
const child = spawn7(command, args, {
|
|
19078
19080
|
stdio: ["pipe", "pipe", "pipe"],
|
|
19079
19081
|
env: env ? { ...process.env, ...env } : process.env
|
|
19080
19082
|
});
|
|
@@ -19229,7 +19231,7 @@ var init_warmupMcp = __esm({
|
|
|
19229
19231
|
});
|
|
19230
19232
|
|
|
19231
19233
|
// src/scripts/writeAgentRunSummary.ts
|
|
19232
|
-
import * as
|
|
19234
|
+
import * as fs44 from "fs";
|
|
19233
19235
|
var writeAgentRunSummary;
|
|
19234
19236
|
var init_writeAgentRunSummary = __esm({
|
|
19235
19237
|
"src/scripts/writeAgentRunSummary.ts"() {
|
|
@@ -19255,7 +19257,7 @@ var init_writeAgentRunSummary = __esm({
|
|
|
19255
19257
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
19256
19258
|
lines.push("");
|
|
19257
19259
|
try {
|
|
19258
|
-
|
|
19260
|
+
fs44.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
19259
19261
|
`);
|
|
19260
19262
|
} catch {
|
|
19261
19263
|
}
|
|
@@ -19581,17 +19583,17 @@ var init_scripts = __esm({
|
|
|
19581
19583
|
});
|
|
19582
19584
|
|
|
19583
19585
|
// src/stateWorkspace.ts
|
|
19584
|
-
import * as
|
|
19585
|
-
import * as
|
|
19586
|
+
import * as fs45 from "fs";
|
|
19587
|
+
import * as path43 from "path";
|
|
19586
19588
|
function tenantId(config) {
|
|
19587
19589
|
const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
|
|
19588
19590
|
const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
|
|
19589
19591
|
return owner && repo ? `${owner}/${repo}` : null;
|
|
19590
19592
|
}
|
|
19591
19593
|
function writeRuntimeFile(cwd, relativePath, content) {
|
|
19592
|
-
const target =
|
|
19593
|
-
|
|
19594
|
-
|
|
19594
|
+
const target = path43.join(cwd, RUNTIME_ROOT, relativePath);
|
|
19595
|
+
fs45.mkdirSync(path43.dirname(target), { recursive: true });
|
|
19596
|
+
fs45.writeFileSync(target, content, "utf8");
|
|
19595
19597
|
}
|
|
19596
19598
|
function record(value) {
|
|
19597
19599
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -19656,11 +19658,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
|
|
|
19656
19658
|
throw new Error("Convex backend is required for runtime workspace documents");
|
|
19657
19659
|
return;
|
|
19658
19660
|
}
|
|
19659
|
-
const key = `${
|
|
19661
|
+
const key = `${path43.resolve(cwd)}|${tenant}`;
|
|
19660
19662
|
if (hydratedWorkspaces.has(key)) return;
|
|
19661
19663
|
const backend = backendOverride ?? createStateBackendFromEnv();
|
|
19662
|
-
const root =
|
|
19663
|
-
|
|
19664
|
+
const root = path43.join(cwd, RUNTIME_ROOT);
|
|
19665
|
+
fs45.rmSync(root, { recursive: true, force: true });
|
|
19664
19666
|
await Promise.all([
|
|
19665
19667
|
hydratePrefix(backend, tenant, cwd, "context:"),
|
|
19666
19668
|
hydratePrefix(backend, tenant, cwd, "memory:"),
|
|
@@ -19676,7 +19678,7 @@ var init_stateWorkspace = __esm({
|
|
|
19676
19678
|
"src/stateWorkspace.ts"() {
|
|
19677
19679
|
"use strict";
|
|
19678
19680
|
init_state_backend();
|
|
19679
|
-
RUNTIME_ROOT =
|
|
19681
|
+
RUNTIME_ROOT = path43.join(".kody-engine", "runtime");
|
|
19680
19682
|
hydratedWorkspaces = /* @__PURE__ */ new Set();
|
|
19681
19683
|
}
|
|
19682
19684
|
});
|
|
@@ -19746,10 +19748,10 @@ var init_tools = __esm({
|
|
|
19746
19748
|
});
|
|
19747
19749
|
|
|
19748
19750
|
// src/executor.ts
|
|
19749
|
-
import { spawn as
|
|
19750
|
-
import * as
|
|
19751
|
+
import { spawn as spawn8 } from "child_process";
|
|
19752
|
+
import * as fs46 from "fs";
|
|
19751
19753
|
import * as os7 from "os";
|
|
19752
|
-
import * as
|
|
19754
|
+
import * as path44 from "path";
|
|
19753
19755
|
function isMutatingPostflight(scriptName) {
|
|
19754
19756
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
19755
19757
|
}
|
|
@@ -19977,7 +19979,7 @@ async function runImplementation(profileName, input) {
|
|
|
19977
19979
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
19978
19980
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
19979
19981
|
const invokeAgent = async (prompt) => {
|
|
19980
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
19982
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path44.isAbsolute(p) ? p : path44.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
19981
19983
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
19982
19984
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
19983
19985
|
const agents = loadSubagents(profile);
|
|
@@ -20415,17 +20417,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
20415
20417
|
function resolveProfilePath(profileName) {
|
|
20416
20418
|
const found = resolveImplementation(profileName);
|
|
20417
20419
|
if (found) return found;
|
|
20418
|
-
const here =
|
|
20420
|
+
const here = path44.dirname(new URL(import.meta.url).pathname);
|
|
20419
20421
|
const candidates = [
|
|
20420
|
-
|
|
20422
|
+
path44.join(here, "implementations", profileName, "profile.json"),
|
|
20421
20423
|
// same-dir sibling (dev)
|
|
20422
|
-
|
|
20424
|
+
path44.join(here, "..", "implementations", profileName, "profile.json"),
|
|
20423
20425
|
// up one (prod: dist/bin → dist/implementations)
|
|
20424
|
-
|
|
20426
|
+
path44.join(here, "..", "src", "implementations", profileName, "profile.json")
|
|
20425
20427
|
// fallback
|
|
20426
20428
|
];
|
|
20427
20429
|
for (const c of candidates) {
|
|
20428
|
-
if (
|
|
20430
|
+
if (fs46.existsSync(c)) return c;
|
|
20429
20431
|
}
|
|
20430
20432
|
return candidates[0];
|
|
20431
20433
|
}
|
|
@@ -20540,15 +20542,15 @@ function resolveShellTimeoutMs(entry) {
|
|
|
20540
20542
|
}
|
|
20541
20543
|
async function runShellEntry(entry, ctx, profile) {
|
|
20542
20544
|
const shellName = entry.shell;
|
|
20543
|
-
const shellPath =
|
|
20544
|
-
if (!
|
|
20545
|
+
const shellPath = path44.join(profile.dir, shellName);
|
|
20546
|
+
if (!fs46.existsSync(shellPath)) {
|
|
20545
20547
|
ctx.skipAgent = true;
|
|
20546
20548
|
ctx.output.exitCode = 99;
|
|
20547
20549
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
20548
20550
|
return;
|
|
20549
20551
|
}
|
|
20550
20552
|
const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
|
|
20551
|
-
const outputFile =
|
|
20553
|
+
const outputFile = path44.join(
|
|
20552
20554
|
os7.tmpdir(),
|
|
20553
20555
|
`kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
20554
20556
|
);
|
|
@@ -20561,7 +20563,7 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
20561
20563
|
env[`KODY_CFG_${k}`] = v;
|
|
20562
20564
|
}
|
|
20563
20565
|
const timeoutMs = resolveShellTimeoutMs(entry);
|
|
20564
|
-
const child =
|
|
20566
|
+
const child = spawn8("bash", [shellPath, ...positional], {
|
|
20565
20567
|
cwd: ctx.cwd,
|
|
20566
20568
|
env,
|
|
20567
20569
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -20620,9 +20622,9 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
20620
20622
|
}
|
|
20621
20623
|
let sideChannelText = "";
|
|
20622
20624
|
try {
|
|
20623
|
-
if (
|
|
20624
|
-
sideChannelText =
|
|
20625
|
-
|
|
20625
|
+
if (fs46.existsSync(outputFile)) {
|
|
20626
|
+
sideChannelText = fs46.readFileSync(outputFile, "utf-8");
|
|
20627
|
+
fs46.rmSync(outputFile, { force: true });
|
|
20626
20628
|
}
|
|
20627
20629
|
} catch {
|
|
20628
20630
|
}
|
|
@@ -20792,7 +20794,7 @@ __export(job_exports, {
|
|
|
20792
20794
|
stableJobKey: () => stableJobKey,
|
|
20793
20795
|
validateJob: () => validateJob
|
|
20794
20796
|
});
|
|
20795
|
-
import * as
|
|
20797
|
+
import * as path45 from "path";
|
|
20796
20798
|
function newJobId(flavor) {
|
|
20797
20799
|
localJobSeq += 1;
|
|
20798
20800
|
const runId = process.env.GITHUB_RUN_ID;
|
|
@@ -21255,11 +21257,11 @@ function selectWorkflowTransition(step, data, counts) {
|
|
|
21255
21257
|
}
|
|
21256
21258
|
function workflowResultConditionPaths(transitions) {
|
|
21257
21259
|
return transitions.flatMap(
|
|
21258
|
-
(transition) => Object.keys(transition.when ?? {}).filter((
|
|
21260
|
+
(transition) => Object.keys(transition.when ?? {}).filter((path51) => path51.startsWith("result."))
|
|
21259
21261
|
);
|
|
21260
21262
|
}
|
|
21261
21263
|
function conditionMatches(condition, context) {
|
|
21262
|
-
return Object.entries(condition).every(([
|
|
21264
|
+
return Object.entries(condition).every(([path51, expected]) => valueMatches(resolveDottedPath2(context, path51), expected));
|
|
21263
21265
|
}
|
|
21264
21266
|
function withWorkflowBoundaryEval(capability, result) {
|
|
21265
21267
|
const capabilityKind = capability.config.capabilityKind;
|
|
@@ -21417,7 +21419,7 @@ function loadCapabilityContext(slug, cwd) {
|
|
|
21417
21419
|
return resolveCapabilityFolder(slug, hydratedCapabilitiesRoot(cwd));
|
|
21418
21420
|
}
|
|
21419
21421
|
function hydratedCapabilitiesRoot(cwd) {
|
|
21420
|
-
return
|
|
21422
|
+
return path45.join(cwd, ".kody-engine", "definitions", "capabilities");
|
|
21421
21423
|
}
|
|
21422
21424
|
function loadWorkflowContext(slug, base) {
|
|
21423
21425
|
if (!slug || !base.config || !isWorkflowDefinitionId(slug)) return null;
|
|
@@ -21580,7 +21582,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
21580
21582
|
|
|
21581
21583
|
// src/servers/brain-serve.ts
|
|
21582
21584
|
import { createServer as createServer2 } from "http";
|
|
21583
|
-
import * as
|
|
21585
|
+
import * as path48 from "path";
|
|
21584
21586
|
|
|
21585
21587
|
// src/chat/loop.ts
|
|
21586
21588
|
init_agent();
|
|
@@ -21588,8 +21590,8 @@ init_agents();
|
|
|
21588
21590
|
init_config();
|
|
21589
21591
|
init_registry();
|
|
21590
21592
|
init_task_artifacts();
|
|
21591
|
-
import * as
|
|
21592
|
-
import * as
|
|
21593
|
+
import * as fs15 from "fs";
|
|
21594
|
+
import * as path16 from "path";
|
|
21593
21595
|
|
|
21594
21596
|
// src/chat/attachments.ts
|
|
21595
21597
|
init_runtimePaths();
|
|
@@ -21646,12 +21648,293 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
21646
21648
|
return { turns: rewritten, imagePaths };
|
|
21647
21649
|
}
|
|
21648
21650
|
|
|
21649
|
-
// src/chat/
|
|
21651
|
+
// src/chat/codex-app-server.ts
|
|
21652
|
+
import { spawn as spawn3 } from "child_process";
|
|
21650
21653
|
import * as fs12 from "fs";
|
|
21651
21654
|
import * as path13 from "path";
|
|
21655
|
+
import { createInterface } from "readline";
|
|
21656
|
+
function codexThreadStartParams(args) {
|
|
21657
|
+
return {
|
|
21658
|
+
cwd: args.cwd,
|
|
21659
|
+
developerInstructions: args.developerInstructions
|
|
21660
|
+
};
|
|
21661
|
+
}
|
|
21662
|
+
function codexTurnStartParams(args) {
|
|
21663
|
+
return {
|
|
21664
|
+
threadId: args.threadId,
|
|
21665
|
+
input: [{ type: "text", text: args.message }]
|
|
21666
|
+
};
|
|
21667
|
+
}
|
|
21668
|
+
function translateCodexNotification(notification, chatId) {
|
|
21669
|
+
const params = notification.params ?? {};
|
|
21670
|
+
if (notification.method === "item/agentMessage/delta") {
|
|
21671
|
+
const delta = params.delta;
|
|
21672
|
+
return typeof delta === "string" && delta.length > 0 ? [{ type: "text", text: delta, chatId }] : [];
|
|
21673
|
+
}
|
|
21674
|
+
if (notification.method === "turn/completed") {
|
|
21675
|
+
return [{ type: "done", chatId }];
|
|
21676
|
+
}
|
|
21677
|
+
if (notification.method === "error") {
|
|
21678
|
+
const message = params.message;
|
|
21679
|
+
return [
|
|
21680
|
+
{
|
|
21681
|
+
type: "error",
|
|
21682
|
+
error: typeof message === "string" ? message : "Codex app-server error",
|
|
21683
|
+
chatId
|
|
21684
|
+
}
|
|
21685
|
+
];
|
|
21686
|
+
}
|
|
21687
|
+
if (notification.method === "item/started") {
|
|
21688
|
+
const item = params.item;
|
|
21689
|
+
if (!item || typeof item !== "object") return [];
|
|
21690
|
+
const record2 = item;
|
|
21691
|
+
return [
|
|
21692
|
+
{
|
|
21693
|
+
type: "tool_use",
|
|
21694
|
+
name: typeof record2.type === "string" ? record2.type : "tool",
|
|
21695
|
+
input: record2,
|
|
21696
|
+
chatId
|
|
21697
|
+
}
|
|
21698
|
+
];
|
|
21699
|
+
}
|
|
21700
|
+
return [];
|
|
21701
|
+
}
|
|
21702
|
+
var CodexAppServerClient = class {
|
|
21703
|
+
process;
|
|
21704
|
+
initialized = false;
|
|
21705
|
+
constructor(options) {
|
|
21706
|
+
const child = (options.spawnImpl ?? spawn3)("codex", ["app-server", "--stdio"], {
|
|
21707
|
+
cwd: options.cwd,
|
|
21708
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
21709
|
+
});
|
|
21710
|
+
this.process = {
|
|
21711
|
+
child,
|
|
21712
|
+
nextId: 1,
|
|
21713
|
+
pending: /* @__PURE__ */ new Map(),
|
|
21714
|
+
turnWaiters: /* @__PURE__ */ new Map()
|
|
21715
|
+
};
|
|
21716
|
+
const lines = createInterface({ input: child.stdout });
|
|
21717
|
+
lines.on("line", (line) => this.handleLine(line));
|
|
21718
|
+
child.on("error", (error) => this.fail(error));
|
|
21719
|
+
child.on("exit", (code, signal) => {
|
|
21720
|
+
this.fail(new Error(`codex app-server exited (${code ?? `signal ${signal}`})`));
|
|
21721
|
+
});
|
|
21722
|
+
}
|
|
21723
|
+
async startThread(args) {
|
|
21724
|
+
await this.ensureInitialized();
|
|
21725
|
+
const result = await this.request("thread/start", codexThreadStartParams(args));
|
|
21726
|
+
const thread = result.thread;
|
|
21727
|
+
if (!thread || typeof thread !== "object") {
|
|
21728
|
+
throw new Error("Codex app-server returned no thread");
|
|
21729
|
+
}
|
|
21730
|
+
const id = thread.id;
|
|
21731
|
+
if (typeof id !== "string" || !id) throw new Error("Codex app-server returned an invalid thread id");
|
|
21732
|
+
return id;
|
|
21733
|
+
}
|
|
21734
|
+
async resumeThread(threadId) {
|
|
21735
|
+
await this.ensureInitialized();
|
|
21736
|
+
await this.request("thread/resume", { threadId });
|
|
21737
|
+
}
|
|
21738
|
+
async runTurn(args) {
|
|
21739
|
+
await new Promise((resolve16, reject) => {
|
|
21740
|
+
this.process.turnWaiters.set(args.threadId, {
|
|
21741
|
+
resolve: resolve16,
|
|
21742
|
+
reject,
|
|
21743
|
+
onNotification: args.onNotification,
|
|
21744
|
+
queue: Promise.resolve()
|
|
21745
|
+
});
|
|
21746
|
+
void this.request("turn/start", codexTurnStartParams(args)).catch((error) => {
|
|
21747
|
+
this.process.turnWaiters.delete(args.threadId);
|
|
21748
|
+
reject(error);
|
|
21749
|
+
});
|
|
21750
|
+
});
|
|
21751
|
+
}
|
|
21752
|
+
close() {
|
|
21753
|
+
this.process.child.kill();
|
|
21754
|
+
this.fail(new Error("Codex app-server client closed"));
|
|
21755
|
+
}
|
|
21756
|
+
request(method, params) {
|
|
21757
|
+
const id = this.process.nextId++;
|
|
21758
|
+
return new Promise((resolve16, reject) => {
|
|
21759
|
+
this.process.pending.set(id, { resolve: resolve16, reject });
|
|
21760
|
+
this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
|
|
21761
|
+
`);
|
|
21762
|
+
});
|
|
21763
|
+
}
|
|
21764
|
+
notify(method, params) {
|
|
21765
|
+
this.process.child.stdin.write(`${JSON.stringify({ method, params })}
|
|
21766
|
+
`);
|
|
21767
|
+
}
|
|
21768
|
+
async ensureInitialized() {
|
|
21769
|
+
if (this.initialized) return;
|
|
21770
|
+
await this.request("initialize", {
|
|
21771
|
+
clientInfo: { name: "kody-brain", version: "0.1.0" }
|
|
21772
|
+
});
|
|
21773
|
+
this.notify("initialized", {});
|
|
21774
|
+
this.initialized = true;
|
|
21775
|
+
}
|
|
21776
|
+
handleLine(line) {
|
|
21777
|
+
let message;
|
|
21778
|
+
try {
|
|
21779
|
+
message = JSON.parse(line);
|
|
21780
|
+
} catch {
|
|
21781
|
+
return;
|
|
21782
|
+
}
|
|
21783
|
+
if (typeof message.id === "number") {
|
|
21784
|
+
const pending = this.process.pending.get(message.id);
|
|
21785
|
+
if (!pending) return;
|
|
21786
|
+
this.process.pending.delete(message.id);
|
|
21787
|
+
if (message.error && typeof message.error === "object") {
|
|
21788
|
+
const error = message.error;
|
|
21789
|
+
pending.reject(new Error(typeof error.message === "string" ? error.message : "Codex request failed"));
|
|
21790
|
+
} else {
|
|
21791
|
+
pending.resolve(message.result ?? {});
|
|
21792
|
+
}
|
|
21793
|
+
return;
|
|
21794
|
+
}
|
|
21795
|
+
if (typeof message.method !== "string") return;
|
|
21796
|
+
const notification = {
|
|
21797
|
+
method: message.method,
|
|
21798
|
+
params: typeof message.params === "object" && message.params !== null ? message.params : {}
|
|
21799
|
+
};
|
|
21800
|
+
const threadId = notification.params?.threadId;
|
|
21801
|
+
if (typeof threadId !== "string") return;
|
|
21802
|
+
const waiter = this.process.turnWaiters.get(threadId);
|
|
21803
|
+
if (!waiter) return;
|
|
21804
|
+
waiter.queue = waiter.queue.then(() => waiter.onNotification(notification));
|
|
21805
|
+
if (notification.method === "turn/completed" || notification.method === "error") {
|
|
21806
|
+
this.process.turnWaiters.delete(threadId);
|
|
21807
|
+
void waiter.queue.then(() => {
|
|
21808
|
+
if (notification.method === "error") {
|
|
21809
|
+
waiter.reject(new Error(String(notification.params?.message ?? "Codex turn failed")));
|
|
21810
|
+
} else {
|
|
21811
|
+
waiter.resolve();
|
|
21812
|
+
}
|
|
21813
|
+
}, waiter.reject);
|
|
21814
|
+
}
|
|
21815
|
+
}
|
|
21816
|
+
fail(error) {
|
|
21817
|
+
for (const pending of this.process.pending.values()) pending.reject(error);
|
|
21818
|
+
this.process.pending.clear();
|
|
21819
|
+
for (const waiter of this.process.turnWaiters.values()) waiter.reject(error);
|
|
21820
|
+
this.process.turnWaiters.clear();
|
|
21821
|
+
}
|
|
21822
|
+
};
|
|
21823
|
+
var clients = /* @__PURE__ */ new Map();
|
|
21824
|
+
function threadMapPath(cwd) {
|
|
21825
|
+
return path13.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
|
|
21826
|
+
}
|
|
21827
|
+
function readThreadMap(cwd) {
|
|
21828
|
+
try {
|
|
21829
|
+
const value = JSON.parse(fs12.readFileSync(threadMapPath(cwd), "utf8"));
|
|
21830
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
21831
|
+
return Object.fromEntries(
|
|
21832
|
+
Object.entries(value).filter(
|
|
21833
|
+
(entry) => typeof entry[1] === "string"
|
|
21834
|
+
)
|
|
21835
|
+
);
|
|
21836
|
+
} catch {
|
|
21837
|
+
return {};
|
|
21838
|
+
}
|
|
21839
|
+
}
|
|
21840
|
+
function writeThreadMap(cwd, map) {
|
|
21841
|
+
const file = threadMapPath(cwd);
|
|
21842
|
+
fs12.mkdirSync(path13.dirname(file), { recursive: true });
|
|
21843
|
+
fs12.writeFileSync(file, `${JSON.stringify(map, null, 2)}
|
|
21844
|
+
`);
|
|
21845
|
+
}
|
|
21846
|
+
async function runCodexChatTurn(args) {
|
|
21847
|
+
const { opts, turns, systemPrompt, store } = args;
|
|
21848
|
+
const last = turns.at(-1);
|
|
21849
|
+
if (!last || last.role !== "user") {
|
|
21850
|
+
const error = "last turn is not a user message";
|
|
21851
|
+
await opts.sink.emit({
|
|
21852
|
+
event: "chat.error",
|
|
21853
|
+
payload: { error },
|
|
21854
|
+
runId: opts.sessionId,
|
|
21855
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21856
|
+
});
|
|
21857
|
+
return { exitCode: 64, error };
|
|
21858
|
+
}
|
|
21859
|
+
const client = clients.get(opts.cwd) ?? new CodexAppServerClient({ cwd: opts.cwd });
|
|
21860
|
+
clients.set(opts.cwd, client);
|
|
21861
|
+
const map = readThreadMap(opts.cwd);
|
|
21862
|
+
let threadId = map[opts.sessionId];
|
|
21863
|
+
if (threadId) {
|
|
21864
|
+
try {
|
|
21865
|
+
await client.resumeThread(threadId);
|
|
21866
|
+
} catch {
|
|
21867
|
+
threadId = void 0;
|
|
21868
|
+
}
|
|
21869
|
+
}
|
|
21870
|
+
threadId ??= await client.startThread({ cwd: opts.cwd, developerInstructions: systemPrompt });
|
|
21871
|
+
if (!map[opts.sessionId]) {
|
|
21872
|
+
map[opts.sessionId] = threadId;
|
|
21873
|
+
writeThreadMap(opts.cwd, map);
|
|
21874
|
+
}
|
|
21875
|
+
const replyParts = [];
|
|
21876
|
+
try {
|
|
21877
|
+
await client.runTurn({
|
|
21878
|
+
threadId,
|
|
21879
|
+
message: last.content,
|
|
21880
|
+
onNotification: async (notification) => {
|
|
21881
|
+
for (const event of translateCodexNotification(notification, opts.sessionId)) {
|
|
21882
|
+
if (event.type === "text" && event.text) {
|
|
21883
|
+
replyParts.push(event.text);
|
|
21884
|
+
await opts.sink.emit({
|
|
21885
|
+
event: "chat.message",
|
|
21886
|
+
payload: { sessionId: opts.sessionId, role: "assistant", content: event.text },
|
|
21887
|
+
runId: opts.sessionId,
|
|
21888
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21889
|
+
});
|
|
21890
|
+
} else if (event.type === "tool_use") {
|
|
21891
|
+
await opts.sink.emit({
|
|
21892
|
+
event: "chat.tool",
|
|
21893
|
+
payload: { phase: "use", name: event.name, input: event.input },
|
|
21894
|
+
runId: opts.sessionId,
|
|
21895
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21896
|
+
});
|
|
21897
|
+
}
|
|
21898
|
+
}
|
|
21899
|
+
}
|
|
21900
|
+
});
|
|
21901
|
+
} catch (error) {
|
|
21902
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21903
|
+
await opts.sink.emit({
|
|
21904
|
+
event: "chat.error",
|
|
21905
|
+
payload: { error: message },
|
|
21906
|
+
runId: opts.sessionId,
|
|
21907
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21908
|
+
});
|
|
21909
|
+
return { exitCode: 99, error: message };
|
|
21910
|
+
}
|
|
21911
|
+
const reply = replyParts.join("").trim();
|
|
21912
|
+
if (!reply) {
|
|
21913
|
+
const error = "Codex completed without producing a reply";
|
|
21914
|
+
await opts.sink.emit({
|
|
21915
|
+
event: "chat.error",
|
|
21916
|
+
payload: { error },
|
|
21917
|
+
runId: opts.sessionId,
|
|
21918
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21919
|
+
});
|
|
21920
|
+
return { exitCode: 99, error };
|
|
21921
|
+
}
|
|
21922
|
+
await store.appendTurn({ role: "assistant", content: reply, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
21923
|
+
await opts.sink.emit({
|
|
21924
|
+
event: "chat.done",
|
|
21925
|
+
payload: { sessionId: opts.sessionId },
|
|
21926
|
+
runId: opts.sessionId,
|
|
21927
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21928
|
+
});
|
|
21929
|
+
return { exitCode: 0, reply };
|
|
21930
|
+
}
|
|
21931
|
+
|
|
21932
|
+
// src/chat/events.ts
|
|
21933
|
+
import * as fs13 from "fs";
|
|
21934
|
+
import * as path14 from "path";
|
|
21652
21935
|
import posixPath2 from "path/posix";
|
|
21653
21936
|
function eventsFilePath(cwd, sessionId) {
|
|
21654
|
-
return
|
|
21937
|
+
return path14.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
|
|
21655
21938
|
}
|
|
21656
21939
|
var FileSink = class {
|
|
21657
21940
|
constructor(file) {
|
|
@@ -21659,8 +21942,8 @@ var FileSink = class {
|
|
|
21659
21942
|
}
|
|
21660
21943
|
file;
|
|
21661
21944
|
async emit(event) {
|
|
21662
|
-
|
|
21663
|
-
|
|
21945
|
+
fs13.mkdirSync(path14.dirname(this.file), { recursive: true });
|
|
21946
|
+
fs13.appendFileSync(this.file, `${JSON.stringify(event)}
|
|
21664
21947
|
`);
|
|
21665
21948
|
}
|
|
21666
21949
|
};
|
|
@@ -21718,15 +22001,15 @@ init_convex_client();
|
|
|
21718
22001
|
import { anyApi as anyApi2 } from "convex/server";
|
|
21719
22002
|
|
|
21720
22003
|
// src/chat/session.ts
|
|
21721
|
-
import * as
|
|
21722
|
-
import * as
|
|
22004
|
+
import * as fs14 from "fs";
|
|
22005
|
+
import * as path15 from "path";
|
|
21723
22006
|
import posixPath3 from "path/posix";
|
|
21724
22007
|
function sessionFilePath(cwd, sessionId) {
|
|
21725
|
-
return
|
|
22008
|
+
return path15.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
|
|
21726
22009
|
}
|
|
21727
22010
|
function readMeta(file) {
|
|
21728
|
-
if (!
|
|
21729
|
-
const raw =
|
|
22011
|
+
if (!fs14.existsSync(file)) return null;
|
|
22012
|
+
const raw = fs14.readFileSync(file, "utf-8");
|
|
21730
22013
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
21731
22014
|
if (!firstLine2) return null;
|
|
21732
22015
|
try {
|
|
@@ -21739,8 +22022,8 @@ function readMeta(file) {
|
|
|
21739
22022
|
}
|
|
21740
22023
|
}
|
|
21741
22024
|
function readSession(file) {
|
|
21742
|
-
if (!
|
|
21743
|
-
const raw =
|
|
22025
|
+
if (!fs14.existsSync(file)) return [];
|
|
22026
|
+
const raw = fs14.readFileSync(file, "utf-8").trim();
|
|
21744
22027
|
if (!raw) return [];
|
|
21745
22028
|
const turns = [];
|
|
21746
22029
|
for (const line of raw.split("\n")) {
|
|
@@ -21756,14 +22039,14 @@ function readSession(file) {
|
|
|
21756
22039
|
return turns;
|
|
21757
22040
|
}
|
|
21758
22041
|
function appendTurn(file, turn) {
|
|
21759
|
-
|
|
22042
|
+
fs14.mkdirSync(path15.dirname(file), { recursive: true });
|
|
21760
22043
|
const line = JSON.stringify({
|
|
21761
22044
|
role: turn.role,
|
|
21762
22045
|
content: turn.content,
|
|
21763
22046
|
timestamp: turn.timestamp,
|
|
21764
22047
|
toolCalls: turn.toolCalls ?? []
|
|
21765
22048
|
});
|
|
21766
|
-
|
|
22049
|
+
fs14.appendFileSync(file, `${line}
|
|
21767
22050
|
`);
|
|
21768
22051
|
}
|
|
21769
22052
|
function seedInitialMessage(file, message) {
|
|
@@ -21996,7 +22279,7 @@ function buildImplementationCatalog() {
|
|
|
21996
22279
|
const entries = [];
|
|
21997
22280
|
for (const { name, profilePath } of discovered) {
|
|
21998
22281
|
try {
|
|
21999
|
-
const raw = JSON.parse(
|
|
22282
|
+
const raw = JSON.parse(fs15.readFileSync(profilePath, "utf-8"));
|
|
22000
22283
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
22001
22284
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
22002
22285
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -22018,6 +22301,9 @@ function buildImplementationCatalog() {
|
|
|
22018
22301
|
}
|
|
22019
22302
|
return lines.join("\n");
|
|
22020
22303
|
}
|
|
22304
|
+
function shouldWriteTaskArtifacts(driver) {
|
|
22305
|
+
return driver !== "codex-app-server";
|
|
22306
|
+
}
|
|
22021
22307
|
async function runChatTurn(opts) {
|
|
22022
22308
|
const store = opts.store ?? createSessionStore({ sessionId: opts.sessionId, sessionFile: opts.sessionFile });
|
|
22023
22309
|
const turns = await store.readTurns();
|
|
@@ -22041,11 +22327,11 @@ async function runChatTurn(opts) {
|
|
|
22041
22327
|
taskKey: `sessions/${opts.sessionId}`
|
|
22042
22328
|
};
|
|
22043
22329
|
initializeTaskArtifacts(taskArtifactsPaths, { taskType: "chat", target: opts.sessionId });
|
|
22044
|
-
const artifactAddendum = taskArtifactsPromptAddendum({
|
|
22330
|
+
const artifactAddendum = shouldWriteTaskArtifacts(opts.driver) ? taskArtifactsPromptAddendum({
|
|
22045
22331
|
taskId: taskArtifactsPaths.taskId,
|
|
22046
22332
|
taskType: "chat",
|
|
22047
22333
|
relDir: taskArtifactsPaths.relDir
|
|
22048
|
-
});
|
|
22334
|
+
}) : null;
|
|
22049
22335
|
const contextBlock = readContextBlock(opts.cwd);
|
|
22050
22336
|
const memoryBlock = readMemoryIndexBlock(opts.cwd);
|
|
22051
22337
|
const instructionsBlock = readInstructionsBlock(opts.cwd);
|
|
@@ -22074,6 +22360,14 @@ async function runChatTurn(opts) {
|
|
|
22074
22360
|
artifactAddendum
|
|
22075
22361
|
].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
|
|
22076
22362
|
const prompt = buildPrompt(promptTurns);
|
|
22363
|
+
if (opts.driver === "codex-app-server") {
|
|
22364
|
+
return runCodexChatTurn({
|
|
22365
|
+
opts,
|
|
22366
|
+
turns: promptTurns,
|
|
22367
|
+
systemPrompt,
|
|
22368
|
+
store
|
|
22369
|
+
});
|
|
22370
|
+
}
|
|
22077
22371
|
if (opts.model.protocol === "openai" && opts.litellmUrl) {
|
|
22078
22372
|
return runOpenAIChatTurn({
|
|
22079
22373
|
opts,
|
|
@@ -22092,7 +22386,7 @@ async function runChatTurn(opts) {
|
|
|
22092
22386
|
quiet: opts.quiet,
|
|
22093
22387
|
additionalDirectories: [
|
|
22094
22388
|
taskArtifactsPaths.absDir,
|
|
22095
|
-
...Array.from(new Set(imagePaths.map((p2) =>
|
|
22389
|
+
...Array.from(new Set(imagePaths.map((p2) => path16.dirname(p2))))
|
|
22096
22390
|
],
|
|
22097
22391
|
systemPromptAppend: systemPrompt,
|
|
22098
22392
|
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
|
|
@@ -22275,10 +22569,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
22275
22569
|
var MEMORY_INDEX_REL = ".kody-engine/runtime/memory/INDEX.md";
|
|
22276
22570
|
var MAX_INDEX_BYTES = 8e3;
|
|
22277
22571
|
function readMemoryIndexBlock(cwd) {
|
|
22278
|
-
const indexPath =
|
|
22572
|
+
const indexPath = path16.join(cwd, MEMORY_INDEX_REL);
|
|
22279
22573
|
let raw;
|
|
22280
22574
|
try {
|
|
22281
|
-
raw =
|
|
22575
|
+
raw = fs15.readFileSync(indexPath, "utf-8");
|
|
22282
22576
|
} catch {
|
|
22283
22577
|
return "";
|
|
22284
22578
|
}
|
|
@@ -22298,17 +22592,17 @@ _\u2026 (memory index truncated; use recall_search to read more)_` : trimmed;
|
|
|
22298
22592
|
var CONTEXT_DIR_REL = ".kody-engine/runtime/context";
|
|
22299
22593
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
22300
22594
|
function readContextBlock(cwd) {
|
|
22301
|
-
const dir =
|
|
22595
|
+
const dir = path16.join(cwd, CONTEXT_DIR_REL);
|
|
22302
22596
|
let files;
|
|
22303
22597
|
try {
|
|
22304
|
-
files =
|
|
22598
|
+
files = fs15.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
22305
22599
|
} catch {
|
|
22306
22600
|
return "";
|
|
22307
22601
|
}
|
|
22308
22602
|
const sections = [];
|
|
22309
22603
|
for (const file of files) {
|
|
22310
22604
|
try {
|
|
22311
|
-
const content =
|
|
22605
|
+
const content = fs15.readFileSync(path16.join(dir, file), "utf-8").trim();
|
|
22312
22606
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
22313
22607
|
|
|
22314
22608
|
${content}`);
|
|
@@ -22334,7 +22628,7 @@ var SYSTEM_PROMPT_OVERRIDE_REL = ".kody-engine/runtime/system-prompt.md";
|
|
|
22334
22628
|
function readSystemPromptOverride(cwd) {
|
|
22335
22629
|
let raw;
|
|
22336
22630
|
try {
|
|
22337
|
-
raw =
|
|
22631
|
+
raw = fs15.readFileSync(path16.join(cwd, SYSTEM_PROMPT_OVERRIDE_REL), "utf-8");
|
|
22338
22632
|
} catch {
|
|
22339
22633
|
return null;
|
|
22340
22634
|
}
|
|
@@ -22342,10 +22636,10 @@ function readSystemPromptOverride(cwd) {
|
|
|
22342
22636
|
return trimmed.length > 0 ? trimmed : null;
|
|
22343
22637
|
}
|
|
22344
22638
|
function readInstructionsBlock(cwd) {
|
|
22345
|
-
const instructionsPath =
|
|
22639
|
+
const instructionsPath = path16.join(cwd, INSTRUCTIONS_REL);
|
|
22346
22640
|
let raw;
|
|
22347
22641
|
try {
|
|
22348
|
-
raw =
|
|
22642
|
+
raw = fs15.readFileSync(instructionsPath, "utf-8");
|
|
22349
22643
|
} catch {
|
|
22350
22644
|
return "";
|
|
22351
22645
|
}
|
|
@@ -22363,13 +22657,28 @@ _\u2026 (instructions truncated)_` : trimmed;
|
|
|
22363
22657
|
].join("\n");
|
|
22364
22658
|
}
|
|
22365
22659
|
|
|
22660
|
+
// src/chat/runtime-drivers.ts
|
|
22661
|
+
var RUNTIME_DRIVERS = {
|
|
22662
|
+
native: "native",
|
|
22663
|
+
"codex app-server": "codex-app-server",
|
|
22664
|
+
"codex-app-server": "codex-app-server"
|
|
22665
|
+
};
|
|
22666
|
+
function resolveBrainDriver(runtime) {
|
|
22667
|
+
const normalized = runtime.trim().toLowerCase();
|
|
22668
|
+
const driver = RUNTIME_DRIVERS[normalized];
|
|
22669
|
+
if (!driver) {
|
|
22670
|
+
throw new Error(`Unsupported Brain runtime: ${runtime}`);
|
|
22671
|
+
}
|
|
22672
|
+
return driver;
|
|
22673
|
+
}
|
|
22674
|
+
|
|
22366
22675
|
// src/servers/brain-serve.ts
|
|
22367
22676
|
init_config();
|
|
22368
22677
|
|
|
22369
22678
|
// src/kody-cli.ts
|
|
22370
22679
|
import { execFileSync as execFileSync24 } from "child_process";
|
|
22371
|
-
import * as
|
|
22372
|
-
import * as
|
|
22680
|
+
import * as fs47 from "fs";
|
|
22681
|
+
import * as path46 from "path";
|
|
22373
22682
|
|
|
22374
22683
|
// src/app-auth.ts
|
|
22375
22684
|
import { createSign } from "crypto";
|
|
@@ -22496,7 +22805,7 @@ init_config();
|
|
|
22496
22805
|
|
|
22497
22806
|
// src/dispatch.ts
|
|
22498
22807
|
init_config();
|
|
22499
|
-
import * as
|
|
22808
|
+
import * as fs16 from "fs";
|
|
22500
22809
|
|
|
22501
22810
|
// src/cron-match.ts
|
|
22502
22811
|
var FIELD_BOUNDS = [
|
|
@@ -22598,10 +22907,10 @@ function autoDispatch(opts) {
|
|
|
22598
22907
|
}
|
|
22599
22908
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
22600
22909
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
22601
|
-
if (!eventName || !eventPath || !
|
|
22910
|
+
if (!eventName || !eventPath || !fs16.existsSync(eventPath)) return null;
|
|
22602
22911
|
let event = {};
|
|
22603
22912
|
try {
|
|
22604
|
-
event = JSON.parse(
|
|
22913
|
+
event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
|
|
22605
22914
|
} catch {
|
|
22606
22915
|
return null;
|
|
22607
22916
|
}
|
|
@@ -22725,7 +23034,7 @@ function autoDispatchTyped(opts) {
|
|
|
22725
23034
|
if (legacy) return { kind: "route", ...legacy };
|
|
22726
23035
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
22727
23036
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
22728
|
-
if (!eventName || !eventPath || !
|
|
23037
|
+
if (!eventName || !eventPath || !fs16.existsSync(eventPath)) {
|
|
22729
23038
|
return { kind: "silent", reason: "no GHA event context" };
|
|
22730
23039
|
}
|
|
22731
23040
|
if (eventName !== "issue_comment") {
|
|
@@ -22733,7 +23042,7 @@ function autoDispatchTyped(opts) {
|
|
|
22733
23042
|
}
|
|
22734
23043
|
let event = {};
|
|
22735
23044
|
try {
|
|
22736
|
-
event = JSON.parse(
|
|
23045
|
+
event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
|
|
22737
23046
|
} catch {
|
|
22738
23047
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
22739
23048
|
}
|
|
@@ -22787,7 +23096,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
22787
23096
|
for (const exe of listImplementations()) {
|
|
22788
23097
|
let raw;
|
|
22789
23098
|
try {
|
|
22790
|
-
raw =
|
|
23099
|
+
raw = fs16.readFileSync(exe.profilePath, "utf-8");
|
|
22791
23100
|
} catch {
|
|
22792
23101
|
continue;
|
|
22793
23102
|
}
|
|
@@ -23171,9 +23480,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
23171
23480
|
return void 0;
|
|
23172
23481
|
}
|
|
23173
23482
|
function detectPackageManager2(cwd) {
|
|
23174
|
-
if (
|
|
23175
|
-
if (
|
|
23176
|
-
if (
|
|
23483
|
+
if (fs47.existsSync(path46.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
23484
|
+
if (fs47.existsSync(path46.join(cwd, "yarn.lock"))) return "yarn";
|
|
23485
|
+
if (fs47.existsSync(path46.join(cwd, "bun.lockb"))) return "bun";
|
|
23177
23486
|
return "npm";
|
|
23178
23487
|
}
|
|
23179
23488
|
function shouldChainScheduledWatch(match) {
|
|
@@ -23266,8 +23575,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
23266
23575
|
const logPath = lastRunLogPath(cwd);
|
|
23267
23576
|
let tail = "";
|
|
23268
23577
|
try {
|
|
23269
|
-
if (
|
|
23270
|
-
const content =
|
|
23578
|
+
if (fs47.existsSync(logPath)) {
|
|
23579
|
+
const content = fs47.readFileSync(logPath, "utf-8");
|
|
23271
23580
|
tail = content.slice(-3e3);
|
|
23272
23581
|
}
|
|
23273
23582
|
} catch {
|
|
@@ -23296,7 +23605,7 @@ async function runCi(argv) {
|
|
|
23296
23605
|
return 0;
|
|
23297
23606
|
}
|
|
23298
23607
|
const args = parseCiArgs(argv);
|
|
23299
|
-
const cwd = args.cwd ?
|
|
23608
|
+
const cwd = args.cwd ? path46.resolve(args.cwd) : process.cwd();
|
|
23300
23609
|
try {
|
|
23301
23610
|
const n = unpackAllSecrets();
|
|
23302
23611
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -23355,9 +23664,9 @@ async function runCi(argv) {
|
|
|
23355
23664
|
forceRunCliArgs = { goal: envForceMessage };
|
|
23356
23665
|
}
|
|
23357
23666
|
}
|
|
23358
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
23667
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs47.existsSync(dispatchEventPath)) {
|
|
23359
23668
|
try {
|
|
23360
|
-
const evt = JSON.parse(
|
|
23669
|
+
const evt = JSON.parse(fs47.readFileSync(dispatchEventPath, "utf-8"));
|
|
23361
23670
|
const inputs = objectValue2(evt.inputs);
|
|
23362
23671
|
const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
|
|
23363
23672
|
const sessionInput = String(inputs?.sessionId ?? "");
|
|
@@ -23730,8 +24039,8 @@ init_repoWorkspace();
|
|
|
23730
24039
|
|
|
23731
24040
|
// src/scripts/brainTurnLog.ts
|
|
23732
24041
|
init_runtimePaths();
|
|
23733
|
-
import * as
|
|
23734
|
-
import * as
|
|
24042
|
+
import * as fs48 from "fs";
|
|
24043
|
+
import * as path47 from "path";
|
|
23735
24044
|
import posixPath4 from "path/posix";
|
|
23736
24045
|
var live = /* @__PURE__ */ new Map();
|
|
23737
24046
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -23739,8 +24048,8 @@ function brainEventsFilePath(dir, chatId) {
|
|
|
23739
24048
|
}
|
|
23740
24049
|
function lastPersistedSeq(dir, chatId) {
|
|
23741
24050
|
const p = brainEventsFilePath(dir, chatId);
|
|
23742
|
-
if (!
|
|
23743
|
-
const lines =
|
|
24051
|
+
if (!fs48.existsSync(p)) return 0;
|
|
24052
|
+
const lines = fs48.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
23744
24053
|
if (lines.length === 0) return 0;
|
|
23745
24054
|
try {
|
|
23746
24055
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -23750,9 +24059,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
23750
24059
|
}
|
|
23751
24060
|
function readSince(dir, chatId, since) {
|
|
23752
24061
|
const p = brainEventsFilePath(dir, chatId);
|
|
23753
|
-
if (!
|
|
24062
|
+
if (!fs48.existsSync(p)) return [];
|
|
23754
24063
|
const out = [];
|
|
23755
|
-
for (const line of
|
|
24064
|
+
for (const line of fs48.readFileSync(p, "utf-8").split("\n")) {
|
|
23756
24065
|
if (!line) continue;
|
|
23757
24066
|
try {
|
|
23758
24067
|
const rec = JSON.parse(line);
|
|
@@ -23778,12 +24087,12 @@ function beginTurn(dir, chatId) {
|
|
|
23778
24087
|
};
|
|
23779
24088
|
live.set(chatId, state);
|
|
23780
24089
|
const p = brainEventsFilePath(dir, chatId);
|
|
23781
|
-
|
|
24090
|
+
fs48.mkdirSync(path47.dirname(p), { recursive: true });
|
|
23782
24091
|
return (event) => {
|
|
23783
24092
|
state.seq += 1;
|
|
23784
24093
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
23785
24094
|
try {
|
|
23786
|
-
|
|
24095
|
+
fs48.appendFileSync(p, `${JSON.stringify(rec)}
|
|
23787
24096
|
`);
|
|
23788
24097
|
} catch (err) {
|
|
23789
24098
|
process.stderr.write(
|
|
@@ -23822,7 +24131,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
23822
24131
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
23823
24132
|
};
|
|
23824
24133
|
try {
|
|
23825
|
-
|
|
24134
|
+
fs48.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
|
|
23826
24135
|
`);
|
|
23827
24136
|
} catch {
|
|
23828
24137
|
}
|
|
@@ -24019,9 +24328,14 @@ var BrokerSink = class {
|
|
|
24019
24328
|
const be = translateChatEvent(event, this.chatId);
|
|
24020
24329
|
if (!be) return;
|
|
24021
24330
|
this.emitToLog(be);
|
|
24022
|
-
if (this.tenantId
|
|
24331
|
+
if (this.tenantId && hasStateBackendConfig()) {
|
|
24332
|
+
await createStateBackendFromEnv().appendChatEvent(this.tenantId, this.chatId, be);
|
|
24333
|
+
}
|
|
24023
24334
|
}
|
|
24024
24335
|
};
|
|
24336
|
+
function hasStateBackendConfig(env = process.env) {
|
|
24337
|
+
return Boolean(env.CONVEX_URL?.trim() && env.KODY_SERVICE_KEY?.trim());
|
|
24338
|
+
}
|
|
24025
24339
|
var chatQueues = /* @__PURE__ */ new Map();
|
|
24026
24340
|
function enqueue(chatId, fn) {
|
|
24027
24341
|
const prev = chatQueues.get(chatId) ?? Promise.resolve();
|
|
@@ -24081,8 +24395,18 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24081
24395
|
const dashboardUrl = strField(body, "dashboardUrl");
|
|
24082
24396
|
const storeRepoUrl = strField(body, "storeRepoUrl");
|
|
24083
24397
|
const storeRef = strField(body, "storeRef");
|
|
24398
|
+
const runtime = strField(body, "runtime");
|
|
24084
24399
|
const allowCrossRepo = boolField(body, "allowCrossRepo");
|
|
24085
24400
|
const agentIdentity = agentIdentityField(body);
|
|
24401
|
+
let turnDriver = opts.driver;
|
|
24402
|
+
if (runtime) {
|
|
24403
|
+
try {
|
|
24404
|
+
turnDriver = resolveBrainDriver(runtime);
|
|
24405
|
+
} catch (err) {
|
|
24406
|
+
sendJson(res, 400, { error: err instanceof Error ? err.message : String(err) });
|
|
24407
|
+
return;
|
|
24408
|
+
}
|
|
24409
|
+
}
|
|
24086
24410
|
openSseStream(res, chatId);
|
|
24087
24411
|
if (repo) {
|
|
24088
24412
|
emitSse(res, {
|
|
@@ -24112,7 +24436,6 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24112
24436
|
}
|
|
24113
24437
|
const stateToken = repoToken || envGithubToken();
|
|
24114
24438
|
const sessionFile = sessionFilePath(agentCwd, chatId);
|
|
24115
|
-
const brainEventsFile = brainEventsFilePath(agentCwd, chatId);
|
|
24116
24439
|
const sessionStore = createSessionStore({
|
|
24117
24440
|
sessionId: chatId,
|
|
24118
24441
|
sessionFile,
|
|
@@ -24142,6 +24465,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24142
24465
|
...allowCrossRepo ? { enableFetchRepoTool: true } : {},
|
|
24143
24466
|
repoToken,
|
|
24144
24467
|
...agentIdentity ? { agentIdentity } : {},
|
|
24468
|
+
...turnDriver ? { driver: turnDriver } : {},
|
|
24145
24469
|
...dashboardUrl && repo && stateToken ? {
|
|
24146
24470
|
cmsDashboardUrl: dashboardUrl,
|
|
24147
24471
|
cmsRepoSlug: repo,
|
|
@@ -24168,7 +24492,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24168
24492
|
function buildServer(opts) {
|
|
24169
24493
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
24170
24494
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
24171
|
-
const reposRoot = opts.reposRoot ??
|
|
24495
|
+
const reposRoot = opts.reposRoot ?? path48.join(path48.dirname(path48.resolve(opts.cwd)), "repos");
|
|
24172
24496
|
return createServer2(async (req, res) => {
|
|
24173
24497
|
if (!req.method || !req.url) {
|
|
24174
24498
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -24196,7 +24520,8 @@ function buildServer(opts) {
|
|
|
24196
24520
|
cloneRepo,
|
|
24197
24521
|
model: opts.model,
|
|
24198
24522
|
litellmUrl: opts.litellmUrl,
|
|
24199
|
-
runTurn
|
|
24523
|
+
runTurn,
|
|
24524
|
+
driver: opts.driver
|
|
24200
24525
|
});
|
|
24201
24526
|
return;
|
|
24202
24527
|
}
|
|
@@ -24223,11 +24548,12 @@ async function brainServe(opts) {
|
|
|
24223
24548
|
}
|
|
24224
24549
|
const apiKey = getApiKey();
|
|
24225
24550
|
const port = Number(process.env.PORT ?? DEFAULT_PORT);
|
|
24551
|
+
const driver = process.env.BRAIN_DRIVER?.trim() === "codex-app-server" ? "codex-app-server" : "native";
|
|
24226
24552
|
const model = parseModelRuntimeConfig(
|
|
24227
24553
|
process.env.MODEL?.trim() || "claude/claude-haiku-4-5-20251001",
|
|
24228
24554
|
process.env.KODY_MODEL_CONFIG
|
|
24229
24555
|
);
|
|
24230
|
-
const usesProxy = needsLitellmProxy(model);
|
|
24556
|
+
const usesProxy = driver === "native" && needsLitellmProxy(model);
|
|
24231
24557
|
let handle = null;
|
|
24232
24558
|
if (usesProxy) {
|
|
24233
24559
|
process.stdout.write(`[brain-serve] starting LiteLLM proxy for ${model.provider}/${model.model}...
|
|
@@ -24243,7 +24569,8 @@ async function brainServe(opts) {
|
|
|
24243
24569
|
// Per-repo clones live here; defaults to a `repos` sibling of cwd.
|
|
24244
24570
|
reposRoot: process.env.BRAIN_REPOS_ROOT?.trim() || void 0,
|
|
24245
24571
|
model,
|
|
24246
|
-
litellmUrl
|
|
24572
|
+
litellmUrl,
|
|
24573
|
+
driver
|
|
24247
24574
|
});
|
|
24248
24575
|
await new Promise((resolve16) => {
|
|
24249
24576
|
server.listen(port, "0.0.0.0", () => {
|
|
@@ -24768,8 +25095,8 @@ async function loadConfigSafe() {
|
|
|
24768
25095
|
}
|
|
24769
25096
|
|
|
24770
25097
|
// src/chat-cli.ts
|
|
24771
|
-
import * as
|
|
24772
|
-
import * as
|
|
25098
|
+
import * as fs49 from "fs";
|
|
25099
|
+
import * as path49 from "path";
|
|
24773
25100
|
|
|
24774
25101
|
// src/chat/inbox.ts
|
|
24775
25102
|
import { execFileSync as execFileSync25 } from "child_process";
|
|
@@ -25041,7 +25368,7 @@ async function runChat(argv) {
|
|
|
25041
25368
|
${CHAT_HELP}`);
|
|
25042
25369
|
return 64;
|
|
25043
25370
|
}
|
|
25044
|
-
const cwd = args.cwd ?
|
|
25371
|
+
const cwd = args.cwd ? path49.resolve(args.cwd) : process.cwd();
|
|
25045
25372
|
const sessionId = args.sessionId;
|
|
25046
25373
|
const runRequest = readRunRequestFromEnv();
|
|
25047
25374
|
if (runRequest && "request" in runRequest) {
|
|
@@ -25102,7 +25429,7 @@ ${CHAT_HELP}`);
|
|
|
25102
25429
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
25103
25430
|
const meta = readMeta(sessionFile);
|
|
25104
25431
|
process.stdout.write(
|
|
25105
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
25432
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs49.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
25106
25433
|
`
|
|
25107
25434
|
);
|
|
25108
25435
|
try {
|
|
@@ -25148,8 +25475,8 @@ init_config();
|
|
|
25148
25475
|
// src/definition-hydration.ts
|
|
25149
25476
|
init_state_backend();
|
|
25150
25477
|
import { createHash as createHash5 } from "crypto";
|
|
25151
|
-
import * as
|
|
25152
|
-
import * as
|
|
25478
|
+
import * as fs50 from "fs";
|
|
25479
|
+
import * as path50 from "path";
|
|
25153
25480
|
var SLUG_RE2 = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
25154
25481
|
function assertSafeDefinitionPath(filePath) {
|
|
25155
25482
|
const segments = filePath.split("/");
|
|
@@ -25183,32 +25510,32 @@ function writeDefinition(root, kind, definition) {
|
|
|
25183
25510
|
if (kind === "agent") {
|
|
25184
25511
|
const raw = bundle.files["agent.md"];
|
|
25185
25512
|
if (typeof raw !== "string") throw new Error(`agent definition ${definition.slug} is missing agent.md`);
|
|
25186
|
-
|
|
25513
|
+
fs50.writeFileSync(path50.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
|
|
25187
25514
|
return;
|
|
25188
25515
|
}
|
|
25189
25516
|
if (kind === "goal") {
|
|
25190
|
-
const goalRoot =
|
|
25517
|
+
const goalRoot = path50.join(root, "goals", definition.slug);
|
|
25191
25518
|
for (const [filePath, contents] of Object.entries(bundle.files)) {
|
|
25192
|
-
const target =
|
|
25193
|
-
|
|
25194
|
-
|
|
25519
|
+
const target = path50.join(goalRoot, filePath);
|
|
25520
|
+
fs50.mkdirSync(path50.dirname(target), { recursive: true });
|
|
25521
|
+
fs50.writeFileSync(target, contents, "utf8");
|
|
25195
25522
|
}
|
|
25196
25523
|
return;
|
|
25197
25524
|
}
|
|
25198
|
-
const capabilityRoot =
|
|
25525
|
+
const capabilityRoot = path50.join(root, "capabilities", definition.slug);
|
|
25199
25526
|
for (const [filePath, contents] of Object.entries(bundle.files)) {
|
|
25200
|
-
const target =
|
|
25201
|
-
|
|
25202
|
-
|
|
25527
|
+
const target = path50.join(capabilityRoot, filePath);
|
|
25528
|
+
fs50.mkdirSync(path50.dirname(target), { recursive: true });
|
|
25529
|
+
fs50.writeFileSync(target, contents, "utf8");
|
|
25203
25530
|
}
|
|
25204
25531
|
}
|
|
25205
25532
|
async function hydrateDefinitions(options) {
|
|
25206
|
-
const root =
|
|
25533
|
+
const root = path50.join(options.cwd, ".kody-engine", "definitions");
|
|
25207
25534
|
const staging = `${root}.tmp-${process.pid}-${Date.now()}`;
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25535
|
+
fs50.rmSync(staging, { recursive: true, force: true });
|
|
25536
|
+
fs50.mkdirSync(path50.join(staging, "agents"), { recursive: true });
|
|
25537
|
+
fs50.mkdirSync(path50.join(staging, "capabilities"), { recursive: true });
|
|
25538
|
+
fs50.mkdirSync(path50.join(staging, "goals"), { recursive: true });
|
|
25212
25539
|
try {
|
|
25213
25540
|
const [capabilities, agents, goals] = await Promise.all([
|
|
25214
25541
|
options.backend.listDefinitions(options.tenantId, "capability"),
|
|
@@ -25234,13 +25561,13 @@ async function hydrateDefinitions(options) {
|
|
|
25234
25561
|
hydratedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25235
25562
|
versions: Object.fromEntries(Object.entries(versions).sort(([left], [right]) => left.localeCompare(right)))
|
|
25236
25563
|
};
|
|
25237
|
-
|
|
25564
|
+
fs50.writeFileSync(path50.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
25238
25565
|
`, "utf8");
|
|
25239
|
-
|
|
25240
|
-
|
|
25566
|
+
fs50.rmSync(root, { recursive: true, force: true });
|
|
25567
|
+
fs50.renameSync(staging, root);
|
|
25241
25568
|
return { root, tenantId: options.tenantId, versions: manifest.versions };
|
|
25242
25569
|
} catch (error) {
|
|
25243
|
-
|
|
25570
|
+
fs50.rmSync(staging, { recursive: true, force: true });
|
|
25244
25571
|
throw error;
|
|
25245
25572
|
}
|
|
25246
25573
|
}
|
|
@@ -25334,8 +25661,8 @@ var FlyClient = class {
|
|
|
25334
25661
|
get fetch() {
|
|
25335
25662
|
return this.opts.fetchImpl ?? fetch;
|
|
25336
25663
|
}
|
|
25337
|
-
async call(
|
|
25338
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
25664
|
+
async call(path51, init = {}) {
|
|
25665
|
+
const res = await this.fetch(`${FLY_API_BASE}${path51}`, {
|
|
25339
25666
|
method: init.method ?? "GET",
|
|
25340
25667
|
headers: {
|
|
25341
25668
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -25346,7 +25673,7 @@ var FlyClient = class {
|
|
|
25346
25673
|
if (res.status === 404 && init.allow404) return null;
|
|
25347
25674
|
if (!res.ok) {
|
|
25348
25675
|
const text2 = await res.text().catch(() => "");
|
|
25349
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
25676
|
+
throw new Error(`Fly API ${res.status} on ${path51}: ${text2.slice(0, 200) || res.statusText}`);
|
|
25350
25677
|
}
|
|
25351
25678
|
if (res.status === 204) return null;
|
|
25352
25679
|
const raw = await res.text();
|
|
@@ -26098,8 +26425,8 @@ async function poolServe() {
|
|
|
26098
26425
|
}
|
|
26099
26426
|
|
|
26100
26427
|
// src/servers/runner-serve.ts
|
|
26101
|
-
import { spawn as
|
|
26102
|
-
import * as
|
|
26428
|
+
import { spawn as spawn9 } from "child_process";
|
|
26429
|
+
import * as fs51 from "fs";
|
|
26103
26430
|
import { createServer as createServer6 } from "http";
|
|
26104
26431
|
var DEFAULT_PORT2 = 8080;
|
|
26105
26432
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -26234,8 +26561,8 @@ async function defaultRunJob(job) {
|
|
|
26234
26561
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
26235
26562
|
const branch = job.ref ?? "main";
|
|
26236
26563
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
26237
|
-
|
|
26238
|
-
|
|
26564
|
+
fs51.rmSync(workdir, { recursive: true, force: true });
|
|
26565
|
+
fs51.mkdirSync(workdir, { recursive: true });
|
|
26239
26566
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
26240
26567
|
const target = job.runRequest.target;
|
|
26241
26568
|
const interactive = target.type === "chat";
|
|
@@ -26265,7 +26592,7 @@ async function defaultRunJob(job) {
|
|
|
26265
26592
|
...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
|
|
26266
26593
|
};
|
|
26267
26594
|
const run = (cmd, args, cwd) => new Promise((resolve16) => {
|
|
26268
|
-
const child =
|
|
26595
|
+
const child = spawn9(cmd, args, { stdio: "inherit", env: childEnv, cwd });
|
|
26269
26596
|
child.on("exit", (code) => resolve16(code ?? 0));
|
|
26270
26597
|
child.on("error", (err) => {
|
|
26271
26598
|
process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
|
|
@@ -26368,7 +26695,7 @@ async function runnerServe() {
|
|
|
26368
26695
|
// src/servers/serve.ts
|
|
26369
26696
|
init_config();
|
|
26370
26697
|
init_litellm();
|
|
26371
|
-
import { spawn as
|
|
26698
|
+
import { spawn as spawn10 } from "child_process";
|
|
26372
26699
|
function parseTarget2(positional) {
|
|
26373
26700
|
if (!Array.isArray(positional) || positional.length === 0) return "none";
|
|
26374
26701
|
const first = String(positional[0]).toLowerCase();
|
|
@@ -26418,7 +26745,7 @@ async function serve(opts) {
|
|
|
26418
26745
|
if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
|
|
26419
26746
|
`);
|
|
26420
26747
|
const args = ["--dangerously-skip-permissions", "--model", model.model];
|
|
26421
|
-
const child =
|
|
26748
|
+
const child = spawn10("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
|
|
26422
26749
|
const exitCode = await new Promise((resolve16) => {
|
|
26423
26750
|
child.on("exit", (code) => resolve16(code ?? 0));
|
|
26424
26751
|
child.on("error", (err) => {
|
|
@@ -26438,7 +26765,7 @@ async function serve(opts) {
|
|
|
26438
26765
|
if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
|
|
26439
26766
|
`);
|
|
26440
26767
|
try {
|
|
26441
|
-
const code =
|
|
26768
|
+
const code = spawn10("code", [opts.cwd], { stdio: "inherit", env: editorEnv, detached: true });
|
|
26442
26769
|
code.on("error", (err) => {
|
|
26443
26770
|
process.stderr.write(`[kody serve] failed to launch VS Code: ${err.message}
|
|
26444
26771
|
`);
|