@kody-ade/kody-engine 0.4.564 → 0.4.566
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 +1318 -674
- package/dist/implementations/run/prompt.md +1 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.566",
|
|
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",
|
|
@@ -669,6 +669,29 @@ var init_format = __esm({
|
|
|
669
669
|
}
|
|
670
670
|
});
|
|
671
671
|
|
|
672
|
+
// src/fileEditGuards.ts
|
|
673
|
+
import * as fs3 from "fs";
|
|
674
|
+
import * as path3 from "path";
|
|
675
|
+
function createMissingParentWriteGuard(cwd) {
|
|
676
|
+
return async (input) => {
|
|
677
|
+
const toolInput = input.tool_input;
|
|
678
|
+
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
|
|
679
|
+
const filePath = toolInput.file_path;
|
|
680
|
+
if (typeof filePath !== "string" || filePath.length === 0) return {};
|
|
681
|
+
const resolvedPath = path3.resolve(cwd, filePath);
|
|
682
|
+
if (fs3.existsSync(path3.dirname(resolvedPath))) return {};
|
|
683
|
+
return {
|
|
684
|
+
decision: "block",
|
|
685
|
+
reason: `Cannot write ${resolvedPath}: its parent directory does not exist. Locate and edit the real repository source path first. If this task genuinely requires a new directory, create that directory explicitly before writing the file.`
|
|
686
|
+
};
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
var init_fileEditGuards = __esm({
|
|
690
|
+
"src/fileEditGuards.ts"() {
|
|
691
|
+
"use strict";
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
|
|
672
695
|
// src/agency/capability-contract-validation.ts
|
|
673
696
|
import Ajv from "ajv";
|
|
674
697
|
function createCapabilityContractValueValidator(compile) {
|
|
@@ -741,12 +764,12 @@ var init_capability_contract_validation = __esm({
|
|
|
741
764
|
});
|
|
742
765
|
|
|
743
766
|
// src/outputContractHooks.ts
|
|
744
|
-
import * as
|
|
745
|
-
import * as
|
|
767
|
+
import * as fs4 from "fs";
|
|
768
|
+
import * as path4 from "path";
|
|
746
769
|
function outputContractError(contract) {
|
|
747
770
|
let value;
|
|
748
771
|
try {
|
|
749
|
-
value = JSON.parse(
|
|
772
|
+
value = JSON.parse(fs4.readFileSync(contract.path, "utf8"));
|
|
750
773
|
} catch (error) {
|
|
751
774
|
return `The required output file is missing or is not valid JSON: ${error instanceof Error ? error.message : String(error)}`;
|
|
752
775
|
}
|
|
@@ -761,12 +784,12 @@ function correctionMessage(contract, error) {
|
|
|
761
784
|
return `The authoritative output does not match its required contract: ${error}. Please overwrite ${contract.path} with only the required JSON shape before finishing.`;
|
|
762
785
|
}
|
|
763
786
|
function createOutputContractPostWriteHook(contract) {
|
|
764
|
-
const expectedPath =
|
|
787
|
+
const expectedPath = path4.resolve(contract.path);
|
|
765
788
|
return async (input) => {
|
|
766
789
|
const toolInput = input.tool_input;
|
|
767
790
|
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
|
|
768
791
|
const filePath = toolInput.file_path;
|
|
769
|
-
if (typeof filePath !== "string" ||
|
|
792
|
+
if (typeof filePath !== "string" || path4.resolve(filePath) !== expectedPath) return {};
|
|
770
793
|
const error = outputContractError(contract);
|
|
771
794
|
if (!error) return {};
|
|
772
795
|
return {
|
|
@@ -779,7 +802,7 @@ function createOutputContractPostWriteHook(contract) {
|
|
|
779
802
|
}
|
|
780
803
|
function createOutputContractStopHook(contract) {
|
|
781
804
|
return async () => {
|
|
782
|
-
if (!
|
|
805
|
+
if (!fs4.existsSync(contract.path)) {
|
|
783
806
|
return {
|
|
784
807
|
decision: "block",
|
|
785
808
|
reason: "Continue the Journey from the current page and complete the next unresolved user outcome. Do not write the result merely because you paused; write it only after the Journey passes, fails, or cannot safely continue."
|
|
@@ -810,21 +833,21 @@ __export(runtimePaths_exports, {
|
|
|
810
833
|
});
|
|
811
834
|
import { createHash } from "crypto";
|
|
812
835
|
import * as os2 from "os";
|
|
813
|
-
import * as
|
|
836
|
+
import * as path5 from "path";
|
|
814
837
|
function runtimeDirForCwd(cwd, ...parts) {
|
|
815
|
-
const key = createHash("sha256").update(
|
|
816
|
-
return
|
|
838
|
+
const key = createHash("sha256").update(path5.resolve(cwd)).digest("hex").slice(0, 16);
|
|
839
|
+
return path5.join(os2.tmpdir(), "kody-engine", key, ...parts);
|
|
817
840
|
}
|
|
818
841
|
function runtimeStatePath(cwd, ...parts) {
|
|
819
842
|
const configuredRoot = process.env.KODY_RUNTIME_DIR?.trim();
|
|
820
|
-
const base = configuredRoot ?
|
|
821
|
-
return
|
|
843
|
+
const base = configuredRoot ? path5.resolve(configuredRoot) : runtimeDirForCwd(cwd);
|
|
844
|
+
return path5.join(base, ...parts);
|
|
822
845
|
}
|
|
823
846
|
function agentRunDir(cwd) {
|
|
824
847
|
return runtimeStatePath(cwd, "agent-runs");
|
|
825
848
|
}
|
|
826
849
|
function lastRunLogPath(cwd) {
|
|
827
|
-
return
|
|
850
|
+
return path5.join(agentRunDir(cwd), "last-run.jsonl");
|
|
828
851
|
}
|
|
829
852
|
var init_runtimePaths = __esm({
|
|
830
853
|
"src/runtimePaths.ts"() {
|
|
@@ -833,31 +856,31 @@ var init_runtimePaths = __esm({
|
|
|
833
856
|
});
|
|
834
857
|
|
|
835
858
|
// src/scripts/buildSyntheticPlugin.ts
|
|
836
|
-
import * as
|
|
859
|
+
import * as fs5 from "fs";
|
|
837
860
|
import * as os3 from "os";
|
|
838
|
-
import * as
|
|
861
|
+
import * as path6 from "path";
|
|
839
862
|
function getPluginsCatalogRoot() {
|
|
840
|
-
const here =
|
|
863
|
+
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
841
864
|
const candidates = [
|
|
842
|
-
|
|
865
|
+
path6.join(here, "..", "plugins"),
|
|
843
866
|
// dev: src/scripts → src/plugins
|
|
844
|
-
|
|
867
|
+
path6.join(here, "..", "..", "plugins"),
|
|
845
868
|
// built: dist/scripts → dist/plugins
|
|
846
|
-
|
|
869
|
+
path6.join(here, "..", "..", "src", "plugins")
|
|
847
870
|
// fallback
|
|
848
871
|
];
|
|
849
872
|
for (const c of candidates) {
|
|
850
|
-
if (
|
|
873
|
+
if (fs5.existsSync(c) && fs5.statSync(c).isDirectory()) return c;
|
|
851
874
|
}
|
|
852
875
|
return candidates[0];
|
|
853
876
|
}
|
|
854
877
|
function copyDir(src, dst) {
|
|
855
|
-
|
|
856
|
-
for (const ent of
|
|
857
|
-
const s =
|
|
858
|
-
const d =
|
|
878
|
+
fs5.mkdirSync(dst, { recursive: true });
|
|
879
|
+
for (const ent of fs5.readdirSync(src, { withFileTypes: true })) {
|
|
880
|
+
const s = path6.join(src, ent.name);
|
|
881
|
+
const d = path6.join(dst, ent.name);
|
|
859
882
|
if (ent.isDirectory()) copyDir(s, d);
|
|
860
|
-
else if (ent.isFile())
|
|
883
|
+
else if (ent.isFile()) fs5.copyFileSync(s, d);
|
|
861
884
|
}
|
|
862
885
|
}
|
|
863
886
|
var buildSyntheticPlugin;
|
|
@@ -870,47 +893,47 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
870
893
|
if (!needsSynthetic) return;
|
|
871
894
|
const catalog = getPluginsCatalogRoot();
|
|
872
895
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
873
|
-
const root =
|
|
874
|
-
|
|
896
|
+
const root = path6.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
897
|
+
fs5.mkdirSync(path6.join(root, ".claude-plugin"), { recursive: true });
|
|
875
898
|
const resolvePart = (bucket, entry) => {
|
|
876
|
-
const local =
|
|
877
|
-
if (
|
|
878
|
-
const shared =
|
|
879
|
-
if (
|
|
880
|
-
const central =
|
|
881
|
-
if (
|
|
899
|
+
const local = path6.join(profile.dir, bucket, entry);
|
|
900
|
+
if (fs5.existsSync(local)) return local;
|
|
901
|
+
const shared = path6.resolve(profile.dir, "..", "..", "shared", bucket, entry);
|
|
902
|
+
if (fs5.existsSync(shared)) return shared;
|
|
903
|
+
const central = path6.join(catalog, bucket, entry);
|
|
904
|
+
if (fs5.existsSync(central)) return central;
|
|
882
905
|
throw new Error(
|
|
883
|
-
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/), Store shared assets (${
|
|
906
|
+
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/), Store shared assets (${path6.dirname(shared)}/), or catalog (${catalog}/${bucket}/)`
|
|
884
907
|
);
|
|
885
908
|
};
|
|
886
909
|
if (cc.skills.length > 0) {
|
|
887
|
-
const dst =
|
|
888
|
-
|
|
910
|
+
const dst = path6.join(root, "skills");
|
|
911
|
+
fs5.mkdirSync(dst, { recursive: true });
|
|
889
912
|
for (const name of cc.skills) {
|
|
890
|
-
copyDir(resolvePart("skills", name),
|
|
913
|
+
copyDir(resolvePart("skills", name), path6.join(dst, name));
|
|
891
914
|
}
|
|
892
915
|
}
|
|
893
916
|
if (cc.commands.length > 0) {
|
|
894
|
-
const dst =
|
|
895
|
-
|
|
917
|
+
const dst = path6.join(root, "commands");
|
|
918
|
+
fs5.mkdirSync(dst, { recursive: true });
|
|
896
919
|
for (const name of cc.commands) {
|
|
897
|
-
|
|
920
|
+
fs5.copyFileSync(resolvePart("commands", `${name}.md`), path6.join(dst, `${name}.md`));
|
|
898
921
|
}
|
|
899
922
|
}
|
|
900
923
|
if (cc.hooks.length > 0) {
|
|
901
|
-
const dst =
|
|
902
|
-
|
|
924
|
+
const dst = path6.join(root, "hooks");
|
|
925
|
+
fs5.mkdirSync(dst, { recursive: true });
|
|
903
926
|
const merged = { hooks: {} };
|
|
904
927
|
for (const name of cc.hooks) {
|
|
905
928
|
const src = resolvePart("hooks", `${name}.json`);
|
|
906
|
-
const parsed = JSON.parse(
|
|
929
|
+
const parsed = JSON.parse(fs5.readFileSync(src, "utf-8"));
|
|
907
930
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
908
931
|
if (!Array.isArray(entries)) continue;
|
|
909
932
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
910
933
|
merged.hooks[event].push(...entries);
|
|
911
934
|
}
|
|
912
935
|
}
|
|
913
|
-
|
|
936
|
+
fs5.writeFileSync(path6.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
914
937
|
`);
|
|
915
938
|
}
|
|
916
939
|
const manifest = {
|
|
@@ -920,7 +943,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
920
943
|
};
|
|
921
944
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
922
945
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
923
|
-
|
|
946
|
+
fs5.writeFileSync(path6.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
924
947
|
`);
|
|
925
948
|
ctx.data.syntheticPluginPath = root;
|
|
926
949
|
};
|
|
@@ -928,8 +951,8 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
928
951
|
});
|
|
929
952
|
|
|
930
953
|
// src/subagents.ts
|
|
931
|
-
import * as
|
|
932
|
-
import * as
|
|
954
|
+
import * as fs6 from "fs";
|
|
955
|
+
import * as path7 from "path";
|
|
933
956
|
async function enforceSubagentModelInheritance(input) {
|
|
934
957
|
const toolInput = input.tool_input;
|
|
935
958
|
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
|
|
@@ -964,12 +987,12 @@ function splitFrontmatter(raw) {
|
|
|
964
987
|
return { fm, body: (match[2] ?? "").trim() };
|
|
965
988
|
}
|
|
966
989
|
function resolveAgentFile(profileDir, name) {
|
|
967
|
-
const local =
|
|
968
|
-
if (
|
|
969
|
-
const shared =
|
|
970
|
-
if (
|
|
971
|
-
const central =
|
|
972
|
-
if (
|
|
990
|
+
const local = path7.join(profileDir, "agents", `${name}.md`);
|
|
991
|
+
if (fs6.existsSync(local)) return local;
|
|
992
|
+
const shared = path7.resolve(profileDir, "..", "..", "shared", "agents", `${name}.md`);
|
|
993
|
+
if (fs6.existsSync(shared)) return shared;
|
|
994
|
+
const central = path7.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
995
|
+
if (fs6.existsSync(central)) return central;
|
|
973
996
|
throw new Error(
|
|
974
997
|
`loadSubagents: agent '${name}' not found in ${profileDir}/agents/, hydrated shared assets, or engine catalog`
|
|
975
998
|
);
|
|
@@ -980,7 +1003,7 @@ function captureSubagentTemplates(profile) {
|
|
|
980
1003
|
const out = {};
|
|
981
1004
|
for (const name of names) {
|
|
982
1005
|
try {
|
|
983
|
-
out[name] =
|
|
1006
|
+
out[name] = fs6.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
984
1007
|
} catch {
|
|
985
1008
|
}
|
|
986
1009
|
}
|
|
@@ -991,7 +1014,7 @@ function loadSubagents(profile) {
|
|
|
991
1014
|
if (!names || names.length === 0) return void 0;
|
|
992
1015
|
const agents = {};
|
|
993
1016
|
for (const name of names) {
|
|
994
|
-
const raw = profile.subagentTemplates?.[name] ??
|
|
1017
|
+
const raw = profile.subagentTemplates?.[name] ?? fs6.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
995
1018
|
const { fm, body } = splitFrontmatter(raw);
|
|
996
1019
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
997
1020
|
const def = {
|
|
@@ -1024,8 +1047,8 @@ __export(events_exports, {
|
|
|
1024
1047
|
resolveRunId: () => resolveRunId
|
|
1025
1048
|
});
|
|
1026
1049
|
import * as crypto from "crypto";
|
|
1027
|
-
import * as
|
|
1028
|
-
import * as
|
|
1050
|
+
import * as fs7 from "fs";
|
|
1051
|
+
import * as path8 from "path";
|
|
1029
1052
|
function resolveRunId() {
|
|
1030
1053
|
if (process.env.KODY_RUN_ID) {
|
|
1031
1054
|
cachedRunId = process.env.KODY_RUN_ID;
|
|
@@ -1058,16 +1081,16 @@ function emitEvent(cwd, ev) {
|
|
|
1058
1081
|
...ev
|
|
1059
1082
|
};
|
|
1060
1083
|
const file = eventsPath(cwd, runId);
|
|
1061
|
-
|
|
1062
|
-
|
|
1084
|
+
fs7.mkdirSync(path8.dirname(file), { recursive: true });
|
|
1085
|
+
fs7.appendFileSync(file, `${JSON.stringify(fullEvent)}
|
|
1063
1086
|
`);
|
|
1064
1087
|
} catch {
|
|
1065
1088
|
}
|
|
1066
1089
|
}
|
|
1067
1090
|
function readEvents(cwd, runId) {
|
|
1068
1091
|
const file = eventsPath(cwd, runId);
|
|
1069
|
-
if (!
|
|
1070
|
-
const lines =
|
|
1092
|
+
if (!fs7.existsSync(file)) return [];
|
|
1093
|
+
const lines = fs7.readFileSync(file, "utf-8").split("\n");
|
|
1071
1094
|
const out = [];
|
|
1072
1095
|
for (const line of lines) {
|
|
1073
1096
|
const trimmed = line.trim();
|
|
@@ -1081,10 +1104,10 @@ function readEvents(cwd, runId) {
|
|
|
1081
1104
|
}
|
|
1082
1105
|
function listRuns(cwd) {
|
|
1083
1106
|
const runsDir = runtimeStatePath(cwd, "agent-runs");
|
|
1084
|
-
if (!
|
|
1085
|
-
return
|
|
1107
|
+
if (!fs7.existsSync(runsDir)) return [];
|
|
1108
|
+
return fs7.readdirSync(runsDir).filter((name) => {
|
|
1086
1109
|
try {
|
|
1087
|
-
return
|
|
1110
|
+
return fs7.statSync(path8.join(runsDir, name)).isDirectory();
|
|
1088
1111
|
} catch {
|
|
1089
1112
|
return false;
|
|
1090
1113
|
}
|
|
@@ -1121,7 +1144,7 @@ function buildVerifyEnv(source = process.env) {
|
|
|
1121
1144
|
return env;
|
|
1122
1145
|
}
|
|
1123
1146
|
function runCommand(command, cwd) {
|
|
1124
|
-
return new Promise((
|
|
1147
|
+
return new Promise((resolve23) => {
|
|
1125
1148
|
const start = Date.now();
|
|
1126
1149
|
const child = spawn(command, {
|
|
1127
1150
|
cwd,
|
|
@@ -1150,11 +1173,11 @@ function runCommand(command, cwd) {
|
|
|
1150
1173
|
child.on("exit", (code) => {
|
|
1151
1174
|
clearTimeout(timer);
|
|
1152
1175
|
const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
|
|
1153
|
-
|
|
1176
|
+
resolve23({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
|
|
1154
1177
|
});
|
|
1155
1178
|
child.on("error", (err) => {
|
|
1156
1179
|
clearTimeout(timer);
|
|
1157
|
-
|
|
1180
|
+
resolve23({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
|
|
1158
1181
|
});
|
|
1159
1182
|
});
|
|
1160
1183
|
}
|
|
@@ -1463,7 +1486,7 @@ function cmsHeaders(opts) {
|
|
|
1463
1486
|
}
|
|
1464
1487
|
};
|
|
1465
1488
|
}
|
|
1466
|
-
async function callDashboardCms(opts,
|
|
1489
|
+
async function callDashboardCms(opts, path58, init = {}) {
|
|
1467
1490
|
const baseUrl = dashboardBaseUrl(opts);
|
|
1468
1491
|
if (!baseUrl) {
|
|
1469
1492
|
return {
|
|
@@ -1475,7 +1498,7 @@ async function callDashboardCms(opts, path55, init = {}) {
|
|
|
1475
1498
|
const headerResult = cmsHeaders(opts);
|
|
1476
1499
|
if (!headerResult.ok) return headerResult;
|
|
1477
1500
|
try {
|
|
1478
|
-
const res = await fetch(`${baseUrl}${
|
|
1501
|
+
const res = await fetch(`${baseUrl}${path58}`, {
|
|
1479
1502
|
...init,
|
|
1480
1503
|
headers: {
|
|
1481
1504
|
...headerResult.headers,
|
|
@@ -1547,8 +1570,8 @@ function documentArg(value) {
|
|
|
1547
1570
|
function normalizeCmsDocumentIdInput(input) {
|
|
1548
1571
|
const trimmed = stripWrappingQuotes(input.trim());
|
|
1549
1572
|
const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
|
|
1550
|
-
const
|
|
1551
|
-
return
|
|
1573
|
+
const path58 = parseDocumentPath(withoutQuery);
|
|
1574
|
+
return path58 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
|
|
1552
1575
|
}
|
|
1553
1576
|
function stripWrappingQuotes(value) {
|
|
1554
1577
|
let current = value;
|
|
@@ -1559,9 +1582,9 @@ function stripWrappingQuotes(value) {
|
|
|
1559
1582
|
}
|
|
1560
1583
|
}
|
|
1561
1584
|
function parseDocumentPath(value) {
|
|
1562
|
-
const
|
|
1563
|
-
if (!
|
|
1564
|
-
const parts =
|
|
1585
|
+
const path58 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
|
|
1586
|
+
if (!path58?.includes("/content/entries/")) return null;
|
|
1587
|
+
const parts = path58.split("/").filter(Boolean).map(decodePathPart);
|
|
1565
1588
|
const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
|
|
1566
1589
|
const idPart = parts[entriesIndex + 3];
|
|
1567
1590
|
if (!idPart || idPart === "new") return null;
|
|
@@ -2006,8 +2029,8 @@ var init_issue = __esm({
|
|
|
2006
2029
|
});
|
|
2007
2030
|
|
|
2008
2031
|
// src/capabilityFolders.ts
|
|
2009
|
-
import * as
|
|
2010
|
-
import * as
|
|
2032
|
+
import * as fs8 from "fs";
|
|
2033
|
+
import * as path9 from "path";
|
|
2011
2034
|
function capabilityOutputConditionPaths(config) {
|
|
2012
2035
|
if (config.outputSchema) {
|
|
2013
2036
|
return new Set(schemaPropertyPaths(config.outputSchema, "result"));
|
|
@@ -2022,43 +2045,43 @@ function capabilityOutputConditionPaths(config) {
|
|
|
2022
2045
|
]);
|
|
2023
2046
|
}
|
|
2024
2047
|
function listCapabilityFolderSlugs(absDir) {
|
|
2025
|
-
if (!
|
|
2048
|
+
if (!fs8.existsSync(absDir)) return [];
|
|
2026
2049
|
let entries;
|
|
2027
2050
|
try {
|
|
2028
|
-
entries =
|
|
2051
|
+
entries = fs8.readdirSync(absDir, { withFileTypes: true });
|
|
2029
2052
|
} catch {
|
|
2030
2053
|
return [];
|
|
2031
2054
|
}
|
|
2032
|
-
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isCapabilityFolder(
|
|
2055
|
+
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isCapabilityFolder(path9.join(absDir, e.name))).map((e) => e.name).sort();
|
|
2033
2056
|
}
|
|
2034
2057
|
function isCapabilityFolder(dir) {
|
|
2035
|
-
const entries =
|
|
2036
|
-
const legacyBody =
|
|
2037
|
-
if (
|
|
2058
|
+
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
2059
|
+
const legacyBody = path9.join(dir, CAPABILITY_BODY_FILE);
|
|
2060
|
+
if (fs8.existsSync(legacyBody)) {
|
|
2038
2061
|
return entries.every(
|
|
2039
2062
|
(entry) => entry.name === CAPABILITY_BODY_FILE || entry.name === CAPABILITY_CONTRACT_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
|
|
2040
2063
|
);
|
|
2041
2064
|
}
|
|
2042
|
-
const canonicalBody =
|
|
2043
|
-
const canonicalDefinition =
|
|
2044
|
-
if (!
|
|
2065
|
+
const canonicalBody = path9.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
|
|
2066
|
+
const canonicalDefinition = path9.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
|
|
2067
|
+
if (!fs8.existsSync(canonicalBody) || !fs8.existsSync(canonicalDefinition)) return false;
|
|
2045
2068
|
return entries.every(
|
|
2046
2069
|
(entry) => entry.name === CANONICAL_CAPABILITY_BODY_FILE || entry.name === CANONICAL_CAPABILITY_DEFINITION_FILE
|
|
2047
2070
|
);
|
|
2048
2071
|
}
|
|
2049
2072
|
function readCapabilityFolder(root, slug) {
|
|
2050
|
-
const dir =
|
|
2051
|
-
const legacyBodyPath =
|
|
2052
|
-
const canonicalBodyPath =
|
|
2053
|
-
const bodyPath =
|
|
2054
|
-
const contractPath =
|
|
2055
|
-
if (!
|
|
2073
|
+
const dir = path9.join(root, slug);
|
|
2074
|
+
const legacyBodyPath = path9.join(dir, CAPABILITY_BODY_FILE);
|
|
2075
|
+
const canonicalBodyPath = path9.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
|
|
2076
|
+
const bodyPath = fs8.existsSync(legacyBodyPath) ? legacyBodyPath : canonicalBodyPath;
|
|
2077
|
+
const contractPath = path9.join(dir, CAPABILITY_CONTRACT_FILE);
|
|
2078
|
+
if (!fs8.existsSync(bodyPath) || !fs8.statSync(bodyPath).isFile()) return null;
|
|
2056
2079
|
if (!isCapabilityFolder(dir)) return null;
|
|
2057
2080
|
try {
|
|
2058
|
-
const rawBody =
|
|
2081
|
+
const rawBody = fs8.readFileSync(bodyPath, "utf-8");
|
|
2059
2082
|
if (bodyPath === canonicalBodyPath) {
|
|
2060
|
-
const definitionPath =
|
|
2061
|
-
const definition = JSON.parse(
|
|
2083
|
+
const definitionPath = path9.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
|
|
2084
|
+
const definition = JSON.parse(fs8.readFileSync(definitionPath, "utf-8"));
|
|
2062
2085
|
if (definition.id !== slug || typeof definition.action !== "string") return null;
|
|
2063
2086
|
const { title: title2, body: body2 } = parseCapabilityBody(rawBody, slug);
|
|
2064
2087
|
return {
|
|
@@ -2078,8 +2101,8 @@ function readCapabilityFolder(root, slug) {
|
|
|
2078
2101
|
rawProfile: definition
|
|
2079
2102
|
};
|
|
2080
2103
|
}
|
|
2081
|
-
const contract =
|
|
2082
|
-
if (contract?.execution === "script" && !isRegularFile(
|
|
2104
|
+
const contract = fs8.existsSync(contractPath) ? parseCapabilityContract(fs8.readFileSync(contractPath, "utf-8")) : void 0;
|
|
2105
|
+
if (contract?.execution === "script" && !isRegularFile(path9.join(dir, "tools", "run.sh"))) {
|
|
2083
2106
|
throw new Error('script-backed Capability requires a regular "tools/run.sh" file');
|
|
2084
2107
|
}
|
|
2085
2108
|
const { title, body } = parseCapabilityBody(rawBody, slug);
|
|
@@ -2192,7 +2215,7 @@ function parseCapabilityRequirements(raw) {
|
|
|
2192
2215
|
}
|
|
2193
2216
|
function isRegularFile(filePath) {
|
|
2194
2217
|
try {
|
|
2195
|
-
const stat =
|
|
2218
|
+
const stat = fs8.lstatSync(filePath);
|
|
2196
2219
|
return stat.isFile() && !stat.isSymbolicLink();
|
|
2197
2220
|
} catch {
|
|
2198
2221
|
return false;
|
|
@@ -2201,8 +2224,8 @@ function isRegularFile(filePath) {
|
|
|
2201
2224
|
function schemaPropertyPaths(schema, prefix) {
|
|
2202
2225
|
const properties = isPlainObject(schema.properties) ? schema.properties : {};
|
|
2203
2226
|
return Object.entries(properties).flatMap(([name, property]) => {
|
|
2204
|
-
const
|
|
2205
|
-
return isPlainObject(property) ? [
|
|
2227
|
+
const path58 = `${prefix}.${name}`;
|
|
2228
|
+
return isPlainObject(property) ? [path58, ...schemaPropertyPaths(property, path58)] : [path58];
|
|
2206
2229
|
});
|
|
2207
2230
|
}
|
|
2208
2231
|
function parseCapabilityBody(raw, slug) {
|
|
@@ -2266,6 +2289,7 @@ function parseWorkflowStep(value) {
|
|
|
2266
2289
|
const target = stringField(raw.target);
|
|
2267
2290
|
const delivery = stringField(raw.delivery);
|
|
2268
2291
|
const targetFact = stringField(raw.targetFact ?? raw.target_fact);
|
|
2292
|
+
const timeoutSeconds = typeof raw.timeoutSeconds === "number" && Number.isInteger(raw.timeoutSeconds) && raw.timeoutSeconds > 0 && raw.timeoutSeconds <= 3600 ? raw.timeoutSeconds : void 0;
|
|
2269
2293
|
const hasInput = Object.hasOwn(raw, "input");
|
|
2270
2294
|
const inputs = parseWorkflowInputBindings(raw.inputs);
|
|
2271
2295
|
const next = parseWorkflowTransitions(raw.next);
|
|
@@ -2281,6 +2305,7 @@ function parseWorkflowStep(value) {
|
|
|
2281
2305
|
...delivery === "pull-request" ? { delivery } : {},
|
|
2282
2306
|
...targetFact ? { targetFact } : {},
|
|
2283
2307
|
...reason ? { reason } : {},
|
|
2308
|
+
...timeoutSeconds ? { timeoutSeconds } : {},
|
|
2284
2309
|
...next ? { next } : {},
|
|
2285
2310
|
...isPlainObject(raw.runWhen) ? { runWhen: raw.runWhen } : {},
|
|
2286
2311
|
...stringList(raw.continueOn ?? raw.continue_on).length > 0 ? { continueOn: stringList(raw.continueOn ?? raw.continue_on) } : {},
|
|
@@ -2366,51 +2391,51 @@ var init_capabilityFolders = __esm({
|
|
|
2366
2391
|
});
|
|
2367
2392
|
|
|
2368
2393
|
// src/definition-paths.ts
|
|
2369
|
-
import * as
|
|
2370
|
-
import * as
|
|
2394
|
+
import * as fs9 from "fs";
|
|
2395
|
+
import * as path10 from "path";
|
|
2371
2396
|
function definitionsRoot(cwd = process.cwd()) {
|
|
2372
2397
|
const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
|
|
2373
2398
|
const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
|
|
2374
|
-
if (override && overrideCwd &&
|
|
2375
|
-
return storeCatalogRoot(
|
|
2399
|
+
if (override && overrideCwd && path10.resolve(cwd) === path10.resolve(overrideCwd)) {
|
|
2400
|
+
return storeCatalogRoot(path10.resolve(override));
|
|
2376
2401
|
}
|
|
2377
|
-
const hydrated =
|
|
2378
|
-
if (
|
|
2379
|
-
return override ? storeCatalogRoot(
|
|
2402
|
+
const hydrated = path10.join(cwd, ".kody-engine", "definitions");
|
|
2403
|
+
if (fs9.existsSync(hydrated)) return hydrated;
|
|
2404
|
+
return override ? storeCatalogRoot(path10.resolve(override)) : hydrated;
|
|
2380
2405
|
}
|
|
2381
2406
|
function hasExplicitDefinitionsRoot(cwd = process.cwd(), env = process.env) {
|
|
2382
2407
|
const root = env.KODY_DEFINITIONS_ROOT?.trim();
|
|
2383
2408
|
const rootCwd = env.KODY_DEFINITIONS_ROOT_CWD?.trim();
|
|
2384
|
-
return Boolean(root && rootCwd &&
|
|
2409
|
+
return Boolean(root && rootCwd && path10.resolve(cwd) === path10.resolve(rootCwd));
|
|
2385
2410
|
}
|
|
2386
2411
|
function capabilitiesRoot(cwd = process.cwd()) {
|
|
2387
|
-
return storeAssetRoot(cwd, "capabilities") ??
|
|
2412
|
+
return storeAssetRoot(cwd, "capabilities") ?? path10.join(definitionsRoot(cwd), "capabilities");
|
|
2388
2413
|
}
|
|
2389
2414
|
function implementationsRoot(cwd = process.cwd()) {
|
|
2390
|
-
return
|
|
2415
|
+
return path10.join(definitionsRoot(cwd), "implementations");
|
|
2391
2416
|
}
|
|
2392
2417
|
function agentsRoot(cwd = process.cwd()) {
|
|
2393
|
-
return storeAssetRoot(cwd, "agent") ??
|
|
2418
|
+
return storeAssetRoot(cwd, "agent") ?? path10.join(definitionsRoot(cwd), "agents");
|
|
2394
2419
|
}
|
|
2395
2420
|
function storeCatalogRoot(root) {
|
|
2396
2421
|
const manifest = readStoreManifest(root);
|
|
2397
|
-
const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) =>
|
|
2398
|
-
return roots.length === 3 && new Set(roots).size === 1 ?
|
|
2422
|
+
const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) => path10.dirname(value));
|
|
2423
|
+
return roots.length === 3 && new Set(roots).size === 1 ? path10.join(root, roots[0]) : root;
|
|
2399
2424
|
}
|
|
2400
2425
|
function storeAssetRoot(cwd, kind) {
|
|
2401
2426
|
const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
|
|
2402
2427
|
if (!override) return null;
|
|
2403
2428
|
const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
|
|
2404
|
-
if (overrideCwd &&
|
|
2405
|
-
const root =
|
|
2429
|
+
if (overrideCwd && path10.resolve(cwd) !== path10.resolve(overrideCwd)) return null;
|
|
2430
|
+
const root = path10.resolve(override);
|
|
2406
2431
|
const configured = readStoreManifest(root)?.assetRoots?.[kind];
|
|
2407
|
-
return typeof configured === "string" && configured.trim() ?
|
|
2432
|
+
return typeof configured === "string" && configured.trim() ? path10.join(root, configured) : null;
|
|
2408
2433
|
}
|
|
2409
2434
|
function readStoreManifest(root) {
|
|
2410
|
-
const file =
|
|
2411
|
-
if (!
|
|
2435
|
+
const file = path10.join(root, "kody-store.json");
|
|
2436
|
+
if (!fs9.existsSync(file)) return null;
|
|
2412
2437
|
try {
|
|
2413
|
-
return JSON.parse(
|
|
2438
|
+
return JSON.parse(fs9.readFileSync(file, "utf8"));
|
|
2414
2439
|
} catch {
|
|
2415
2440
|
return null;
|
|
2416
2441
|
}
|
|
@@ -2422,32 +2447,32 @@ var init_definition_paths = __esm({
|
|
|
2422
2447
|
});
|
|
2423
2448
|
|
|
2424
2449
|
// src/registry.ts
|
|
2425
|
-
import * as
|
|
2426
|
-
import * as
|
|
2450
|
+
import * as fs10 from "fs";
|
|
2451
|
+
import * as path11 from "path";
|
|
2427
2452
|
function getImplementationsRoot() {
|
|
2428
|
-
const here =
|
|
2453
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2429
2454
|
const candidates = [
|
|
2430
|
-
|
|
2455
|
+
path11.join(here, "implementations"),
|
|
2431
2456
|
// dev: src/
|
|
2432
|
-
|
|
2457
|
+
path11.join(here, "..", "implementations"),
|
|
2433
2458
|
// built: dist/bin → dist/implementations
|
|
2434
|
-
|
|
2459
|
+
path11.join(here, "..", "src", "implementations")
|
|
2435
2460
|
// fallback
|
|
2436
2461
|
];
|
|
2437
2462
|
for (const c of candidates) {
|
|
2438
|
-
if (
|
|
2463
|
+
if (fs10.existsSync(c) && fs10.statSync(c).isDirectory()) return c;
|
|
2439
2464
|
}
|
|
2440
2465
|
return candidates[0];
|
|
2441
2466
|
}
|
|
2442
2467
|
function getRuntimeServicesRoot() {
|
|
2443
|
-
const here =
|
|
2468
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2444
2469
|
const candidates = [
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2470
|
+
path11.join(here, "runtime-services"),
|
|
2471
|
+
path11.join(here, "..", "runtime-services"),
|
|
2472
|
+
path11.join(here, "..", "src", "runtime-services")
|
|
2448
2473
|
];
|
|
2449
2474
|
for (const candidate of candidates) {
|
|
2450
|
-
if (
|
|
2475
|
+
if (fs10.existsSync(candidate) && fs10.statSync(candidate).isDirectory()) return candidate;
|
|
2451
2476
|
}
|
|
2452
2477
|
return candidates[0];
|
|
2453
2478
|
}
|
|
@@ -2455,17 +2480,17 @@ function getProjectCapabilitiesRoot() {
|
|
|
2455
2480
|
return capabilitiesRoot();
|
|
2456
2481
|
}
|
|
2457
2482
|
function getBuiltinCapabilitiesRoot() {
|
|
2458
|
-
const here =
|
|
2483
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2459
2484
|
const candidates = [
|
|
2460
|
-
|
|
2485
|
+
path11.join(here, "capabilities"),
|
|
2461
2486
|
// dev: src/
|
|
2462
|
-
|
|
2487
|
+
path11.join(here, "..", "capabilities"),
|
|
2463
2488
|
// built: dist/bin → dist/capabilities
|
|
2464
|
-
|
|
2489
|
+
path11.join(here, "..", "src", "capabilities")
|
|
2465
2490
|
// fallback
|
|
2466
2491
|
];
|
|
2467
2492
|
for (const c of candidates) {
|
|
2468
|
-
if (
|
|
2493
|
+
if (fs10.existsSync(c) && fs10.statSync(c).isDirectory()) return c;
|
|
2469
2494
|
}
|
|
2470
2495
|
return candidates[0];
|
|
2471
2496
|
}
|
|
@@ -2488,14 +2513,14 @@ function listImplementations(roots = getImplementationRoots()) {
|
|
|
2488
2513
|
const seen = /* @__PURE__ */ new Set();
|
|
2489
2514
|
const out = [];
|
|
2490
2515
|
for (const root of rootList) {
|
|
2491
|
-
if (!
|
|
2516
|
+
if (!fs10.existsSync(root)) continue;
|
|
2492
2517
|
const requireImplementationProfile = isCapabilityRoot(root);
|
|
2493
|
-
const entries =
|
|
2518
|
+
const entries = fs10.readdirSync(root, { withFileTypes: true });
|
|
2494
2519
|
for (const ent of entries) {
|
|
2495
2520
|
if (!ent.isDirectory()) continue;
|
|
2496
2521
|
if (seen.has(ent.name)) continue;
|
|
2497
2522
|
const profilePath = implementationRuntimePath(root, ent.name);
|
|
2498
|
-
if (
|
|
2523
|
+
if (fs10.existsSync(profilePath) && fs10.statSync(profilePath).isFile() && isImplementationProfile(profilePath, requireImplementationProfile)) {
|
|
2499
2524
|
out.push({ name: ent.name, profilePath });
|
|
2500
2525
|
seen.add(ent.name);
|
|
2501
2526
|
}
|
|
@@ -2515,7 +2540,7 @@ function resolveImplementationCandidates(name, roots = getRuntimeProfileRootsFor
|
|
|
2515
2540
|
const out = [];
|
|
2516
2541
|
for (const root of rootList) {
|
|
2517
2542
|
const profilePath = implementationRuntimePath(root, name);
|
|
2518
|
-
if (
|
|
2543
|
+
if (fs10.existsSync(profilePath) && fs10.statSync(profilePath).isFile() && isImplementationProfile(profilePath, isCapabilityRoot(root))) {
|
|
2519
2544
|
out.push(profilePath);
|
|
2520
2545
|
}
|
|
2521
2546
|
}
|
|
@@ -2589,7 +2614,7 @@ function implementationDeclaresInput(implementation, inputName, cwd = process.cw
|
|
|
2589
2614
|
const profilePath = resolveImplementation(implementation, getRuntimeProfileRootsForCwd(cwd));
|
|
2590
2615
|
if (!profilePath) return false;
|
|
2591
2616
|
try {
|
|
2592
|
-
const document = JSON.parse(
|
|
2617
|
+
const document = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2593
2618
|
const raw = document.config ?? document;
|
|
2594
2619
|
if (!Array.isArray(raw.inputs)) return false;
|
|
2595
2620
|
return raw.inputs.some((entry) => {
|
|
@@ -2605,29 +2630,29 @@ function isSafeName(name) {
|
|
|
2605
2630
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2606
2631
|
}
|
|
2607
2632
|
function isCapabilityRoot(root) {
|
|
2608
|
-
const normalized =
|
|
2609
|
-
if (
|
|
2633
|
+
const normalized = path11.normalize(root);
|
|
2634
|
+
if (path11.basename(normalized) === "capabilities") return true;
|
|
2610
2635
|
const knownRoots = [getProjectCapabilitiesRoot(), getBuiltinCapabilitiesRoot()];
|
|
2611
|
-
return knownRoots.some((candidate) => candidate &&
|
|
2636
|
+
return knownRoots.some((candidate) => candidate && path11.normalize(candidate) === normalized);
|
|
2612
2637
|
}
|
|
2613
2638
|
function implementationRuntimePath(root, name) {
|
|
2614
|
-
const runtimePath =
|
|
2615
|
-
if (
|
|
2616
|
-
const internalProfilePath =
|
|
2617
|
-
if (
|
|
2618
|
-
return
|
|
2639
|
+
const runtimePath = path11.join(root, name, "runtime.json");
|
|
2640
|
+
if (fs10.existsSync(runtimePath)) return runtimePath;
|
|
2641
|
+
const internalProfilePath = path11.join(root, name, "profile.json");
|
|
2642
|
+
if (fs10.existsSync(internalProfilePath)) return internalProfilePath;
|
|
2643
|
+
return path11.join(root, name, CAPABILITY_PROFILE_FILE);
|
|
2619
2644
|
}
|
|
2620
2645
|
function isImplementationProfile(profilePath, requireImplementationProfile) {
|
|
2621
2646
|
if (!requireImplementationProfile) return true;
|
|
2622
2647
|
try {
|
|
2623
|
-
const raw = JSON.parse(
|
|
2648
|
+
const raw = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2624
2649
|
return typeof raw.role === "string" && PUBLIC_IMPLEMENTATION_ROLES.has(raw.role);
|
|
2625
2650
|
} catch {
|
|
2626
2651
|
return false;
|
|
2627
2652
|
}
|
|
2628
2653
|
}
|
|
2629
2654
|
function listFolderCapabilityActions(root, source) {
|
|
2630
|
-
if (!
|
|
2655
|
+
if (!fs10.existsSync(root) || !fs10.statSync(root).isDirectory()) return [];
|
|
2631
2656
|
const out = [];
|
|
2632
2657
|
for (const slug of listCapabilityFolderSlugs(root)) {
|
|
2633
2658
|
if (!isSafeName(slug)) continue;
|
|
@@ -2659,7 +2684,7 @@ function hasUnresolvedExplicitImplementation(capability, implementation) {
|
|
|
2659
2684
|
return resolveImplementation(implementation) === null;
|
|
2660
2685
|
}
|
|
2661
2686
|
function listBuiltinCapabilityActions(root = getBuiltinCapabilitiesRoot()) {
|
|
2662
|
-
if (!
|
|
2687
|
+
if (!fs10.existsSync(root) || !fs10.statSync(root).isDirectory()) return [];
|
|
2663
2688
|
const out = [];
|
|
2664
2689
|
for (const slug of listCapabilityFolderSlugs(root)) {
|
|
2665
2690
|
if (!isSafeName(slug)) continue;
|
|
@@ -2684,7 +2709,7 @@ function getProfileInputs(name, roots = getImplementationRoots()) {
|
|
|
2684
2709
|
const profilePath = resolveImplementation(name, roots);
|
|
2685
2710
|
if (!profilePath) return null;
|
|
2686
2711
|
try {
|
|
2687
|
-
const document = JSON.parse(
|
|
2712
|
+
const document = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2688
2713
|
if (!document || typeof document !== "object") return [];
|
|
2689
2714
|
const raw = "config" in document && document.config && typeof document.config === "object" ? document.config : document;
|
|
2690
2715
|
if (!Array.isArray(raw.inputs)) return [];
|
|
@@ -3781,8 +3806,8 @@ var init_capabilityMcp = __esm({
|
|
|
3781
3806
|
|
|
3782
3807
|
// src/repoWorkspace.ts
|
|
3783
3808
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
3784
|
-
import * as
|
|
3785
|
-
import * as
|
|
3809
|
+
import * as fs11 from "fs";
|
|
3810
|
+
import * as path12 from "path";
|
|
3786
3811
|
function buildCloneProcess(repo, token, baseEnv = process.env) {
|
|
3787
3812
|
const url = `https://github.com/${repo}.git`;
|
|
3788
3813
|
const env = { ...baseEnv };
|
|
@@ -3797,10 +3822,10 @@ function buildCloneProcess(repo, token, baseEnv = process.env) {
|
|
|
3797
3822
|
async function resolveAndClone(reposRoot, repo, repoToken, cloneRepo) {
|
|
3798
3823
|
const name = repo?.trim();
|
|
3799
3824
|
if (!name || !REPO_RE.test(name)) return null;
|
|
3800
|
-
const root =
|
|
3801
|
-
const dir =
|
|
3802
|
-
if (dir !== root && !dir.startsWith(root +
|
|
3803
|
-
if (
|
|
3825
|
+
const root = path12.resolve(reposRoot);
|
|
3826
|
+
const dir = path12.resolve(root, name);
|
|
3827
|
+
if (dir !== root && !dir.startsWith(root + path12.sep)) return null;
|
|
3828
|
+
if (fs11.existsSync(path12.join(dir, ".git"))) return dir;
|
|
3804
3829
|
const inflight = repoClones.get(dir);
|
|
3805
3830
|
if (inflight) {
|
|
3806
3831
|
await inflight;
|
|
@@ -3832,9 +3857,9 @@ var init_repoWorkspace = __esm({
|
|
|
3832
3857
|
repoClones = /* @__PURE__ */ new Map();
|
|
3833
3858
|
GIT_CREDENTIAL_HELPER = `!f() { if [ "$1" = get ] && [ -n "$GITHUB_TOKEN" ]; then printf '%s\\n' username=x-access-token "password=$GITHUB_TOKEN"; fi; }; f`;
|
|
3834
3859
|
defaultCloneRepo = (repo, token, dir) => {
|
|
3835
|
-
|
|
3860
|
+
fs11.mkdirSync(path12.dirname(dir), { recursive: true });
|
|
3836
3861
|
const clone = buildCloneProcess(repo, token);
|
|
3837
|
-
return new Promise((
|
|
3862
|
+
return new Promise((resolve23, reject) => {
|
|
3838
3863
|
const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
|
|
3839
3864
|
env: clone.env,
|
|
3840
3865
|
stdio: "inherit"
|
|
@@ -3854,7 +3879,7 @@ var init_repoWorkspace = __esm({
|
|
|
3854
3879
|
}
|
|
3855
3880
|
} catch {
|
|
3856
3881
|
}
|
|
3857
|
-
|
|
3882
|
+
resolve23();
|
|
3858
3883
|
});
|
|
3859
3884
|
child.on("error", reject);
|
|
3860
3885
|
});
|
|
@@ -3926,8 +3951,8 @@ var init_fetchRepoMcp = __esm({
|
|
|
3926
3951
|
});
|
|
3927
3952
|
|
|
3928
3953
|
// src/agent.ts
|
|
3929
|
-
import * as
|
|
3930
|
-
import * as
|
|
3954
|
+
import * as fs12 from "fs";
|
|
3955
|
+
import * as path13 from "path";
|
|
3931
3956
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3932
3957
|
function classifySubtype(subtype) {
|
|
3933
3958
|
if (!subtype) return "generic_failed";
|
|
@@ -3996,8 +4021,8 @@ function buildAgentEnvironment(baseEnv, repoToken, requestEnvironment) {
|
|
|
3996
4021
|
}
|
|
3997
4022
|
async function runAgent(opts) {
|
|
3998
4023
|
const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
|
|
3999
|
-
|
|
4000
|
-
const ndjsonPath =
|
|
4024
|
+
fs12.mkdirSync(ndjsonDir, { recursive: true });
|
|
4025
|
+
const ndjsonPath = path13.join(ndjsonDir, "last-run.jsonl");
|
|
4001
4026
|
const env = buildAgentEnvironment(process.env, opts.repoToken, opts.environment);
|
|
4002
4027
|
if (opts.litellmUrl) {
|
|
4003
4028
|
env.ANTHROPIC_BASE_URL = opts.litellmUrl;
|
|
@@ -4015,12 +4040,13 @@ async function runAgent(opts) {
|
|
|
4015
4040
|
let getSubmitted;
|
|
4016
4041
|
const invokedSubagents = /* @__PURE__ */ new Set();
|
|
4017
4042
|
const subagentInvocationHook = createSubagentInvocationHook(invokedSubagents);
|
|
4043
|
+
const missingParentWriteGuard = createMissingParentWriteGuard(opts.cwd);
|
|
4018
4044
|
const outputContractPostWriteHook = opts.outputContract ? createOutputContractPostWriteHook(opts.outputContract) : null;
|
|
4019
4045
|
const outputContractStopHook = opts.outputContract ? createOutputContractStopHook(opts.outputContract) : null;
|
|
4020
4046
|
for (let attempt = 0; ; attempt++) {
|
|
4021
4047
|
let ndjsonWriteFailed = false;
|
|
4022
4048
|
let ndjsonWriteError;
|
|
4023
|
-
const fullLog =
|
|
4049
|
+
const fullLog = fs12.createWriteStream(ndjsonPath, { flags: "w" });
|
|
4024
4050
|
fullLog.on("error", (err) => {
|
|
4025
4051
|
ndjsonWriteFailed = true;
|
|
4026
4052
|
ndjsonWriteError = err instanceof Error ? err.message : String(err);
|
|
@@ -4051,6 +4077,10 @@ async function runAgent(opts) {
|
|
|
4051
4077
|
{
|
|
4052
4078
|
matcher: "Agent",
|
|
4053
4079
|
hooks: [enforceSubagentModelInheritance]
|
|
4080
|
+
},
|
|
4081
|
+
{
|
|
4082
|
+
matcher: "Write",
|
|
4083
|
+
hooks: [missingParentWriteGuard]
|
|
4054
4084
|
}
|
|
4055
4085
|
],
|
|
4056
4086
|
PostToolUse: [
|
|
@@ -4201,10 +4231,10 @@ async function runAgent(opts) {
|
|
|
4201
4231
|
let timer;
|
|
4202
4232
|
let next;
|
|
4203
4233
|
if (turnTimeoutMs > 0) {
|
|
4204
|
-
const timeoutPromise = new Promise((
|
|
4234
|
+
const timeoutPromise = new Promise((resolve23) => {
|
|
4205
4235
|
timer = setTimeout(() => {
|
|
4206
4236
|
timedOut = true;
|
|
4207
|
-
|
|
4237
|
+
resolve23({ done: true, value: void 0 });
|
|
4208
4238
|
}, turnTimeoutMs);
|
|
4209
4239
|
});
|
|
4210
4240
|
next = await Promise.race([nextPromise, timeoutPromise]);
|
|
@@ -4220,7 +4250,7 @@ async function runAgent(opts) {
|
|
|
4220
4250
|
try {
|
|
4221
4251
|
await Promise.race([
|
|
4222
4252
|
iterator.return(void 0).catch(() => void 0),
|
|
4223
|
-
new Promise((
|
|
4253
|
+
new Promise((resolve23) => setTimeout(resolve23, 1e4).unref())
|
|
4224
4254
|
]);
|
|
4225
4255
|
} catch {
|
|
4226
4256
|
}
|
|
@@ -4403,6 +4433,7 @@ var init_agent = __esm({
|
|
|
4403
4433
|
init_claudeBinary();
|
|
4404
4434
|
init_config();
|
|
4405
4435
|
init_format();
|
|
4436
|
+
init_fileEditGuards();
|
|
4406
4437
|
init_outputContractHooks();
|
|
4407
4438
|
init_runtimePaths();
|
|
4408
4439
|
init_subagents();
|
|
@@ -4424,8 +4455,8 @@ var init_agent = __esm({
|
|
|
4424
4455
|
});
|
|
4425
4456
|
|
|
4426
4457
|
// src/agents.ts
|
|
4427
|
-
import * as
|
|
4428
|
-
import * as
|
|
4458
|
+
import * as fs13 from "fs";
|
|
4459
|
+
import * as path14 from "path";
|
|
4429
4460
|
function stripFrontmatter(raw) {
|
|
4430
4461
|
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
4431
4462
|
return (match ? match[1] : raw).trim();
|
|
@@ -4434,8 +4465,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
|
|
|
4434
4465
|
const trimmed = slug.trim();
|
|
4435
4466
|
if (!trimmed) throw new Error("loadAgentIdentity: empty agent slug");
|
|
4436
4467
|
const agentPath = resolveAgentFile2(cwd, trimmed, agentsDir);
|
|
4437
|
-
if (
|
|
4438
|
-
const body = stripFrontmatter(
|
|
4468
|
+
if (fs13.existsSync(agentPath)) {
|
|
4469
|
+
const body = stripFrontmatter(fs13.readFileSync(agentPath, "utf-8"));
|
|
4439
4470
|
if (body) return body;
|
|
4440
4471
|
const builtinForEmpty = BUILTIN_AGENTS[trimmed];
|
|
4441
4472
|
if (builtinForEmpty) return builtinForEmpty;
|
|
@@ -4446,8 +4477,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
|
|
|
4446
4477
|
throw new Error(`loadAgentIdentity: agent '${trimmed}' declared but ${agentPath} does not exist`);
|
|
4447
4478
|
}
|
|
4448
4479
|
function resolveAgentFile2(cwd, slug, agentsDir = agentsRoot(cwd)) {
|
|
4449
|
-
const localPath =
|
|
4450
|
-
if (
|
|
4480
|
+
const localPath = path14.resolve(cwd, agentsDir, `${slug}.md`);
|
|
4481
|
+
if (fs13.existsSync(localPath)) return localPath;
|
|
4451
4482
|
return localPath;
|
|
4452
4483
|
}
|
|
4453
4484
|
function frameAgentIdentity(slug, agent) {
|
|
@@ -4479,14 +4510,14 @@ var init_agents = __esm({
|
|
|
4479
4510
|
});
|
|
4480
4511
|
|
|
4481
4512
|
// src/task-artifacts.ts
|
|
4482
|
-
import
|
|
4483
|
-
import
|
|
4513
|
+
import fs14 from "fs";
|
|
4514
|
+
import path15 from "path";
|
|
4484
4515
|
import posixPath from "path/posix";
|
|
4485
4516
|
function prepareTaskArtifactsDir(cwd, taskId) {
|
|
4486
4517
|
const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4487
4518
|
const absDir = runtimeStatePath(cwd, "task-artifacts", safeId);
|
|
4488
4519
|
const relDir = absDir;
|
|
4489
|
-
|
|
4520
|
+
fs14.mkdirSync(absDir, { recursive: true });
|
|
4490
4521
|
return { taskId: safeId, absDir, relDir };
|
|
4491
4522
|
}
|
|
4492
4523
|
function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
|
|
@@ -4516,16 +4547,16 @@ function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
|
|
|
4516
4547
|
"handoff-notes.md": "Baseline handoff: the agent did not provide additional notes.\n"
|
|
4517
4548
|
};
|
|
4518
4549
|
for (const file of TASK_ARTIFACT_FILES) {
|
|
4519
|
-
const full =
|
|
4520
|
-
if (!
|
|
4550
|
+
const full = path15.join(artifacts.absDir, file);
|
|
4551
|
+
if (!fs14.existsSync(full)) fs14.writeFileSync(full, defaults[file], "utf8");
|
|
4521
4552
|
}
|
|
4522
4553
|
}
|
|
4523
4554
|
function verifyTaskArtifacts(absDir) {
|
|
4524
4555
|
const missing = [];
|
|
4525
4556
|
for (const name of TASK_ARTIFACT_FILES) {
|
|
4526
|
-
const full =
|
|
4557
|
+
const full = path15.join(absDir, name);
|
|
4527
4558
|
try {
|
|
4528
|
-
const stat =
|
|
4559
|
+
const stat = fs14.statSync(full);
|
|
4529
4560
|
if (!stat.isFile() || stat.size === 0) missing.push(name);
|
|
4530
4561
|
} catch {
|
|
4531
4562
|
missing.push(name);
|
|
@@ -4541,11 +4572,11 @@ async function persistTaskArtifactsToState(config, _cwd, artifacts) {
|
|
|
4541
4572
|
if (hasStateBackendConfig() && tenantId2) {
|
|
4542
4573
|
const backend = createStateBackendFromEnv();
|
|
4543
4574
|
for (const file of TASK_ARTIFACT_FILES) {
|
|
4544
|
-
const full =
|
|
4545
|
-
if (!
|
|
4546
|
-
const stat =
|
|
4575
|
+
const full = path15.join(artifacts.absDir, file);
|
|
4576
|
+
if (!fs14.existsSync(full)) continue;
|
|
4577
|
+
const stat = fs14.statSync(full);
|
|
4547
4578
|
if (!stat.isFile() || stat.size === 0) continue;
|
|
4548
|
-
const content =
|
|
4579
|
+
const content = fs14.readFileSync(full, "utf-8");
|
|
4549
4580
|
const kind = file.replace(/\.(json|md)$/, "");
|
|
4550
4581
|
let doc = content;
|
|
4551
4582
|
if (file.endsWith(".json")) {
|
|
@@ -4693,6 +4724,15 @@ function validateWorkflow(value, options = {}) {
|
|
|
4693
4724
|
if (step.input !== void 0 && step.inputs !== void 0) {
|
|
4694
4725
|
issue(issues, "conflicting_inputs", base, "workflow step cannot declare both input and inputs");
|
|
4695
4726
|
}
|
|
4727
|
+
const timeoutSeconds = step.timeoutSeconds;
|
|
4728
|
+
if (timeoutSeconds !== void 0 && (typeof timeoutSeconds !== "number" || !Number.isInteger(timeoutSeconds) || timeoutSeconds < 1 || timeoutSeconds > 3600)) {
|
|
4729
|
+
issue(
|
|
4730
|
+
issues,
|
|
4731
|
+
"invalid_step_timeout",
|
|
4732
|
+
`${base}.timeoutSeconds`,
|
|
4733
|
+
"workflow step timeoutSeconds must be an integer from 1 to 3600"
|
|
4734
|
+
);
|
|
4735
|
+
}
|
|
4696
4736
|
validateInputBindings(
|
|
4697
4737
|
step.inputs,
|
|
4698
4738
|
`${base}.inputs`,
|
|
@@ -4845,15 +4885,15 @@ function validateWorkflow(value, options = {}) {
|
|
|
4845
4885
|
}
|
|
4846
4886
|
return issues;
|
|
4847
4887
|
}
|
|
4848
|
-
function validateInputBindings(value,
|
|
4888
|
+
function validateInputBindings(value, path58, issues, declaredInputs) {
|
|
4849
4889
|
if (value === void 0) return;
|
|
4850
4890
|
const bindings = asRecord(value);
|
|
4851
4891
|
if (!bindings || Object.keys(bindings).length === 0) {
|
|
4852
|
-
issue(issues, "invalid_inputs",
|
|
4892
|
+
issue(issues, "invalid_inputs", path58, "workflow step inputs must contain at least one named mapping");
|
|
4853
4893
|
return;
|
|
4854
4894
|
}
|
|
4855
4895
|
for (const [name, value2] of Object.entries(bindings)) {
|
|
4856
|
-
const bindingPath = `${
|
|
4896
|
+
const bindingPath = `${path58}.${name}`;
|
|
4857
4897
|
if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(name)) {
|
|
4858
4898
|
issue(issues, "invalid_input_name", bindingPath, `workflow input name ${name} is invalid`);
|
|
4859
4899
|
}
|
|
@@ -4872,7 +4912,7 @@ function validateInputBindings(value, path55, issues, declaredInputs) {
|
|
|
4872
4912
|
}
|
|
4873
4913
|
}
|
|
4874
4914
|
}
|
|
4875
|
-
function validateInputBindingSources(value,
|
|
4915
|
+
function validateInputBindingSources(value, path58, issues, capabilitiesByStep, capabilityOutputs) {
|
|
4876
4916
|
const bindings = asRecord(value);
|
|
4877
4917
|
if (!bindings) return;
|
|
4878
4918
|
for (const [name, rawBinding] of Object.entries(bindings)) {
|
|
@@ -4885,7 +4925,7 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
|
|
|
4885
4925
|
issue(
|
|
4886
4926
|
issues,
|
|
4887
4927
|
"missing_input_step",
|
|
4888
|
-
`${
|
|
4928
|
+
`${path58}.${name}.from`,
|
|
4889
4929
|
`workflow input mapping references missing step ${sourceStep ?? "<none>"}`
|
|
4890
4930
|
);
|
|
4891
4931
|
continue;
|
|
@@ -4896,7 +4936,7 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
|
|
|
4896
4936
|
issue(
|
|
4897
4937
|
issues,
|
|
4898
4938
|
"undeclared_step_output",
|
|
4899
|
-
`${
|
|
4939
|
+
`${path58}.${name}.from`,
|
|
4900
4940
|
`workflow input mapping reads ${outputPath}, but step ${sourceStep} does not declare it`
|
|
4901
4941
|
);
|
|
4902
4942
|
}
|
|
@@ -4905,11 +4945,11 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
|
|
|
4905
4945
|
function formatWorkflowValidationIssues(issues) {
|
|
4906
4946
|
return issues.map((entry) => `${entry.path}: ${entry.message}`);
|
|
4907
4947
|
}
|
|
4908
|
-
function validateDataMatch(value,
|
|
4948
|
+
function validateDataMatch(value, path58, issues, capabilityOutputs) {
|
|
4909
4949
|
if (value === void 0) return;
|
|
4910
4950
|
const match = asRecord(value);
|
|
4911
4951
|
if (!match || Object.keys(match).length === 0) {
|
|
4912
|
-
issue(issues, "invalid_condition",
|
|
4952
|
+
issue(issues, "invalid_condition", path58, "workflow condition must contain at least one match");
|
|
4913
4953
|
return;
|
|
4914
4954
|
}
|
|
4915
4955
|
for (const [field, expected] of Object.entries(match)) {
|
|
@@ -4917,7 +4957,7 @@ function validateDataMatch(value, path55, issues, capabilityOutputs) {
|
|
|
4917
4957
|
issue(
|
|
4918
4958
|
issues,
|
|
4919
4959
|
"invalid_data_path",
|
|
4920
|
-
`${
|
|
4960
|
+
`${path58}.${field}`,
|
|
4921
4961
|
`workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
|
|
4922
4962
|
);
|
|
4923
4963
|
}
|
|
@@ -4925,12 +4965,12 @@ function validateDataMatch(value, path55, issues, capabilityOutputs) {
|
|
|
4925
4965
|
issue(
|
|
4926
4966
|
issues,
|
|
4927
4967
|
"undeclared_result_path",
|
|
4928
|
-
`${
|
|
4968
|
+
`${path58}.${field}`,
|
|
4929
4969
|
`workflow condition reads ${field}, but the source capability does not declare it`
|
|
4930
4970
|
);
|
|
4931
4971
|
}
|
|
4932
4972
|
if (!isComparable(expected)) {
|
|
4933
|
-
issue(issues, "invalid_condition_value", `${
|
|
4973
|
+
issue(issues, "invalid_condition_value", `${path58}.${field}`, "workflow condition value must be a JSON scalar");
|
|
4934
4974
|
}
|
|
4935
4975
|
}
|
|
4936
4976
|
}
|
|
@@ -4954,8 +4994,8 @@ function isJsonValue(value) {
|
|
|
4954
4994
|
if (!value || typeof value !== "object") return false;
|
|
4955
4995
|
return Object.values(value).every(isJsonValue);
|
|
4956
4996
|
}
|
|
4957
|
-
function issue(issues, code,
|
|
4958
|
-
issues.push({ code, path:
|
|
4997
|
+
function issue(issues, code, path58, message) {
|
|
4998
|
+
issues.push({ code, path: path58, message });
|
|
4959
4999
|
}
|
|
4960
5000
|
var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SAFE_INPUT_SOURCE, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
|
|
4961
5001
|
var init_workflowValidation = __esm({
|
|
@@ -4976,6 +5016,7 @@ var init_workflowValidation = __esm({
|
|
|
4976
5016
|
"delivery",
|
|
4977
5017
|
"targetFact",
|
|
4978
5018
|
"reason",
|
|
5019
|
+
"timeoutSeconds",
|
|
4979
5020
|
"next",
|
|
4980
5021
|
"runWhen",
|
|
4981
5022
|
"continueOn",
|
|
@@ -4987,8 +5028,8 @@ var init_workflowValidation = __esm({
|
|
|
4987
5028
|
});
|
|
4988
5029
|
|
|
4989
5030
|
// src/workflowDefinitions.ts
|
|
4990
|
-
import * as
|
|
4991
|
-
import * as
|
|
5031
|
+
import * as fs20 from "fs";
|
|
5032
|
+
import * as path21 from "path";
|
|
4992
5033
|
function isWorkflowDefinitionId(value) {
|
|
4993
5034
|
return WORKFLOW_ID_PATTERN.test(value);
|
|
4994
5035
|
}
|
|
@@ -5033,12 +5074,12 @@ function readWorkflowDefinition(_config, cwd, id) {
|
|
|
5033
5074
|
const root = cwd ?? process.cwd();
|
|
5034
5075
|
const relativePath = workflowDefinitionPath(id);
|
|
5035
5076
|
const candidates = [
|
|
5036
|
-
|
|
5037
|
-
|
|
5077
|
+
path21.join(root, ".kody-engine", "runtime", relativePath),
|
|
5078
|
+
path21.join(definitionsRoot(root), relativePath)
|
|
5038
5079
|
];
|
|
5039
5080
|
for (const filePath of candidates) {
|
|
5040
|
-
if (!
|
|
5041
|
-
const workflow = parseWorkflowDefinition(
|
|
5081
|
+
if (!fs20.existsSync(filePath)) continue;
|
|
5082
|
+
const workflow = parseWorkflowDefinition(fs20.readFileSync(filePath, "utf8"));
|
|
5042
5083
|
if (workflow) return workflow;
|
|
5043
5084
|
}
|
|
5044
5085
|
return null;
|
|
@@ -5046,7 +5087,7 @@ function readWorkflowDefinition(_config, cwd, id) {
|
|
|
5046
5087
|
function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
|
|
5047
5088
|
return {
|
|
5048
5089
|
slug: id,
|
|
5049
|
-
dir:
|
|
5090
|
+
dir: path21.dirname(source),
|
|
5050
5091
|
profilePath: source,
|
|
5051
5092
|
bodyPath: source,
|
|
5052
5093
|
title: workflow.name,
|
|
@@ -5102,7 +5143,7 @@ var init_workflowDefinitions = __esm({
|
|
|
5102
5143
|
|
|
5103
5144
|
// src/gha.ts
|
|
5104
5145
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
5105
|
-
import * as
|
|
5146
|
+
import * as fs23 from "fs";
|
|
5106
5147
|
function getRunUrl() {
|
|
5107
5148
|
const server = process.env.GITHUB_SERVER_URL;
|
|
5108
5149
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -5113,10 +5154,10 @@ function getRunUrl() {
|
|
|
5113
5154
|
function reactToTriggerComment(cwd) {
|
|
5114
5155
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5115
5156
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5116
|
-
if (!eventPath || !
|
|
5157
|
+
if (!eventPath || !fs23.existsSync(eventPath)) return;
|
|
5117
5158
|
let event = null;
|
|
5118
5159
|
try {
|
|
5119
|
-
event = JSON.parse(
|
|
5160
|
+
event = JSON.parse(fs23.readFileSync(eventPath, "utf-8"));
|
|
5120
5161
|
} catch {
|
|
5121
5162
|
return;
|
|
5122
5163
|
}
|
|
@@ -5646,15 +5687,15 @@ var init_lifecycles = __esm({
|
|
|
5646
5687
|
|
|
5647
5688
|
// src/profile.ts
|
|
5648
5689
|
import { createHash as createHash3 } from "crypto";
|
|
5649
|
-
import * as
|
|
5650
|
-
import * as
|
|
5690
|
+
import * as fs24 from "fs";
|
|
5691
|
+
import * as path23 from "path";
|
|
5651
5692
|
function loadProfile(profilePath) {
|
|
5652
|
-
if (!
|
|
5693
|
+
if (!fs24.existsSync(profilePath)) {
|
|
5653
5694
|
throw new ProfileError(profilePath, "file not found");
|
|
5654
5695
|
}
|
|
5655
5696
|
let raw;
|
|
5656
5697
|
try {
|
|
5657
|
-
raw = JSON.parse(
|
|
5698
|
+
raw = JSON.parse(fs24.readFileSync(profilePath, "utf-8"));
|
|
5658
5699
|
} catch (err) {
|
|
5659
5700
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
5660
5701
|
}
|
|
@@ -5666,7 +5707,7 @@ function loadProfile(profilePath) {
|
|
|
5666
5707
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
5667
5708
|
if (unknownKeys.length > 0) {
|
|
5668
5709
|
process.stderr.write(
|
|
5669
|
-
`[kody profile] ${
|
|
5710
|
+
`[kody profile] ${path23.basename(path23.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
5670
5711
|
`
|
|
5671
5712
|
);
|
|
5672
5713
|
}
|
|
@@ -5676,7 +5717,7 @@ function loadProfile(profilePath) {
|
|
|
5676
5717
|
if (!refPath) {
|
|
5677
5718
|
throw new ProfileError(profilePath, `capability references unknown implementation '${execRef}'`);
|
|
5678
5719
|
}
|
|
5679
|
-
if (
|
|
5720
|
+
if (path23.resolve(refPath) === path23.resolve(profilePath)) {
|
|
5680
5721
|
} else {
|
|
5681
5722
|
const base = loadProfile(refPath);
|
|
5682
5723
|
return {
|
|
@@ -5774,8 +5815,8 @@ function loadProfile(profilePath) {
|
|
|
5774
5815
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
5775
5816
|
// flip to true after end-to-end verification.
|
|
5776
5817
|
preloadContext: r.preloadContext === true,
|
|
5777
|
-
dir:
|
|
5778
|
-
promptTemplates: readPromptTemplates(
|
|
5818
|
+
dir: path23.dirname(profilePath),
|
|
5819
|
+
promptTemplates: readPromptTemplates(path23.dirname(profilePath))
|
|
5779
5820
|
};
|
|
5780
5821
|
if (lifecycle) {
|
|
5781
5822
|
applyLifecycle(profile, profilePath);
|
|
@@ -5810,19 +5851,19 @@ function loadProfile(profilePath) {
|
|
|
5810
5851
|
return profile;
|
|
5811
5852
|
}
|
|
5812
5853
|
function compileRuntimeDocument(runtimePath, document) {
|
|
5813
|
-
if (
|
|
5854
|
+
if (path23.basename(runtimePath) !== "runtime.json") return document;
|
|
5814
5855
|
if (document.adapter !== "kody-engine-profile") {
|
|
5815
5856
|
throw new ProfileError(runtimePath, "unsupported runtime adapter document");
|
|
5816
5857
|
}
|
|
5817
|
-
const implementationDir =
|
|
5818
|
-
const implementation = readJsonObject(
|
|
5819
|
-
const definitionsRoot2 =
|
|
5858
|
+
const implementationDir = path23.dirname(runtimePath);
|
|
5859
|
+
const implementation = readJsonObject(path23.join(implementationDir, "definition.json"), "Implementation definition");
|
|
5860
|
+
const definitionsRoot2 = path23.dirname(path23.dirname(implementationDir));
|
|
5820
5861
|
const capabilityId = implementation.capabilityRef && typeof implementation.capabilityRef === "object" && !Array.isArray(implementation.capabilityRef) ? implementation.capabilityRef.id : void 0;
|
|
5821
5862
|
if (typeof capabilityId !== "string" || !capabilityId) {
|
|
5822
5863
|
throw new ProfileError(runtimePath, "Implementation capabilityRef is invalid");
|
|
5823
5864
|
}
|
|
5824
5865
|
const capability = readJsonObject(
|
|
5825
|
-
|
|
5866
|
+
path23.join(definitionsRoot2, "capabilities", capabilityId, "definition.json"),
|
|
5826
5867
|
"Capability definition"
|
|
5827
5868
|
);
|
|
5828
5869
|
const {
|
|
@@ -5861,7 +5902,7 @@ function canonical(value) {
|
|
|
5861
5902
|
}
|
|
5862
5903
|
function readJsonObject(filePath, label) {
|
|
5863
5904
|
try {
|
|
5864
|
-
const value = JSON.parse(
|
|
5905
|
+
const value = JSON.parse(fs24.readFileSync(filePath, "utf-8"));
|
|
5865
5906
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5866
5907
|
throw new Error("must be an object");
|
|
5867
5908
|
}
|
|
@@ -5879,17 +5920,17 @@ function readPromptTemplates(dir) {
|
|
|
5879
5920
|
const out = {};
|
|
5880
5921
|
const read = (p) => {
|
|
5881
5922
|
try {
|
|
5882
|
-
out[p] =
|
|
5923
|
+
out[p] = fs24.readFileSync(p, "utf-8");
|
|
5883
5924
|
} catch {
|
|
5884
5925
|
}
|
|
5885
5926
|
};
|
|
5886
|
-
read(
|
|
5887
|
-
read(
|
|
5888
|
-
read(
|
|
5927
|
+
read(path23.join(dir, "prompt.md"));
|
|
5928
|
+
read(path23.join(dir, "capability.md"));
|
|
5929
|
+
read(path23.join(dir, "capability.md"));
|
|
5889
5930
|
try {
|
|
5890
|
-
const promptsDir =
|
|
5891
|
-
for (const ent of
|
|
5892
|
-
if (ent.endsWith(".md")) read(
|
|
5931
|
+
const promptsDir = path23.join(dir, "prompts");
|
|
5932
|
+
for (const ent of fs24.readdirSync(promptsDir)) {
|
|
5933
|
+
if (ent.endsWith(".md")) read(path23.join(promptsDir, ent));
|
|
5893
5934
|
}
|
|
5894
5935
|
} catch {
|
|
5895
5936
|
}
|
|
@@ -6666,16 +6707,16 @@ var init_state = __esm({
|
|
|
6666
6707
|
});
|
|
6667
6708
|
|
|
6668
6709
|
// src/prompt.ts
|
|
6669
|
-
import * as
|
|
6670
|
-
import * as
|
|
6710
|
+
import * as fs25 from "fs";
|
|
6711
|
+
import * as path24 from "path";
|
|
6671
6712
|
function loadProjectConventions(projectDir) {
|
|
6672
6713
|
const out = [];
|
|
6673
6714
|
for (const rel of CONVENTION_FILES) {
|
|
6674
|
-
const abs =
|
|
6675
|
-
if (!
|
|
6715
|
+
const abs = path24.join(projectDir, rel);
|
|
6716
|
+
if (!fs25.existsSync(abs)) continue;
|
|
6676
6717
|
let content;
|
|
6677
6718
|
try {
|
|
6678
|
-
content =
|
|
6719
|
+
content = fs25.readFileSync(abs, "utf-8");
|
|
6679
6720
|
} catch {
|
|
6680
6721
|
continue;
|
|
6681
6722
|
}
|
|
@@ -6910,8 +6951,8 @@ var loadMemoryContext_exports = {};
|
|
|
6910
6951
|
__export(loadMemoryContext_exports, {
|
|
6911
6952
|
loadMemoryContext: () => loadMemoryContext
|
|
6912
6953
|
});
|
|
6913
|
-
import * as
|
|
6914
|
-
import * as
|
|
6954
|
+
import * as fs26 from "fs";
|
|
6955
|
+
import * as path25 from "path";
|
|
6915
6956
|
function formatBlockFromBackend(docs) {
|
|
6916
6957
|
const pages = docs.flatMap((record2) => {
|
|
6917
6958
|
if (!record2.doc || typeof record2.doc !== "object") return [];
|
|
@@ -6934,21 +6975,21 @@ function collectPages(memoryAbs) {
|
|
|
6934
6975
|
walkMd(memoryAbs, (file) => {
|
|
6935
6976
|
let stat;
|
|
6936
6977
|
try {
|
|
6937
|
-
stat =
|
|
6978
|
+
stat = fs26.statSync(file);
|
|
6938
6979
|
} catch {
|
|
6939
6980
|
return;
|
|
6940
6981
|
}
|
|
6941
6982
|
let raw;
|
|
6942
6983
|
try {
|
|
6943
|
-
raw =
|
|
6984
|
+
raw = fs26.readFileSync(file, "utf-8");
|
|
6944
6985
|
} catch {
|
|
6945
6986
|
return;
|
|
6946
6987
|
}
|
|
6947
6988
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
6948
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
6989
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path25.basename(file, ".md");
|
|
6949
6990
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
6950
6991
|
out.push({
|
|
6951
|
-
relPath:
|
|
6992
|
+
relPath: path25.relative(memoryAbs, file),
|
|
6952
6993
|
title,
|
|
6953
6994
|
updated,
|
|
6954
6995
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -7016,16 +7057,16 @@ function walkMd(root, visit) {
|
|
|
7016
7057
|
const dir = stack.pop();
|
|
7017
7058
|
let names;
|
|
7018
7059
|
try {
|
|
7019
|
-
names =
|
|
7060
|
+
names = fs26.readdirSync(dir);
|
|
7020
7061
|
} catch {
|
|
7021
7062
|
continue;
|
|
7022
7063
|
}
|
|
7023
7064
|
for (const name of names) {
|
|
7024
7065
|
if (name.startsWith(".")) continue;
|
|
7025
|
-
const full =
|
|
7066
|
+
const full = path25.join(dir, name);
|
|
7026
7067
|
let stat;
|
|
7027
7068
|
try {
|
|
7028
|
-
stat =
|
|
7069
|
+
stat = fs26.statSync(full);
|
|
7029
7070
|
} catch {
|
|
7030
7071
|
continue;
|
|
7031
7072
|
}
|
|
@@ -7060,8 +7101,8 @@ var init_loadMemoryContext = __esm({
|
|
|
7060
7101
|
}
|
|
7061
7102
|
return;
|
|
7062
7103
|
}
|
|
7063
|
-
const memoryAbs =
|
|
7064
|
-
if (!
|
|
7104
|
+
const memoryAbs = path25.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
7105
|
+
if (!fs26.existsSync(memoryAbs)) {
|
|
7065
7106
|
ctx.data.memoryContext = "";
|
|
7066
7107
|
return;
|
|
7067
7108
|
}
|
|
@@ -7105,11 +7146,11 @@ var init_loadCoverageRules = __esm({
|
|
|
7105
7146
|
|
|
7106
7147
|
// src/container.ts
|
|
7107
7148
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
7108
|
-
import * as
|
|
7149
|
+
import * as fs27 from "fs";
|
|
7109
7150
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
7110
7151
|
try {
|
|
7111
7152
|
const profilePath = resolveProfilePath(profileName);
|
|
7112
|
-
if (!
|
|
7153
|
+
if (!fs27.existsSync(profilePath)) return null;
|
|
7113
7154
|
return loadProfile(profilePath).inputs;
|
|
7114
7155
|
} catch {
|
|
7115
7156
|
return null;
|
|
@@ -7573,10 +7614,10 @@ var init_lifecycleLabels = __esm({
|
|
|
7573
7614
|
|
|
7574
7615
|
// src/litellm.ts
|
|
7575
7616
|
import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
|
|
7576
|
-
import * as
|
|
7617
|
+
import * as fs28 from "fs";
|
|
7577
7618
|
import * as net from "net";
|
|
7578
7619
|
import * as os4 from "os";
|
|
7579
|
-
import * as
|
|
7620
|
+
import * as path26 from "path";
|
|
7580
7621
|
async function checkLitellmHealth(url) {
|
|
7581
7622
|
try {
|
|
7582
7623
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -7646,7 +7687,7 @@ function locateLitellmScript() {
|
|
|
7646
7687
|
}
|
|
7647
7688
|
function resolveLitellmCommand() {
|
|
7648
7689
|
const imageScript = "/opt/venv/bin/litellm";
|
|
7649
|
-
if (
|
|
7690
|
+
if (fs28.existsSync(imageScript)) return imageScript;
|
|
7650
7691
|
try {
|
|
7651
7692
|
execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
7652
7693
|
return "litellm";
|
|
@@ -7689,13 +7730,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
7689
7730
|
const spawnProxy = () => {
|
|
7690
7731
|
const portMatch = activeUrl.match(/:(\d+)/);
|
|
7691
7732
|
const port = portMatch ? portMatch[1] : "4000";
|
|
7692
|
-
const configPath =
|
|
7693
|
-
|
|
7733
|
+
const configPath = path26.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
7734
|
+
fs28.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
7694
7735
|
const args = ["--config", configPath, "--port", port];
|
|
7695
|
-
const nextLogPath =
|
|
7696
|
-
const outFd =
|
|
7736
|
+
const nextLogPath = path26.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
7737
|
+
const outFd = fs28.openSync(nextLogPath, "w");
|
|
7697
7738
|
child = spawn4(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
7698
|
-
|
|
7739
|
+
fs28.closeSync(outFd);
|
|
7699
7740
|
logPath = nextLogPath;
|
|
7700
7741
|
};
|
|
7701
7742
|
const waitForHealth = async () => {
|
|
@@ -7709,7 +7750,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
7709
7750
|
const readLogTail = () => {
|
|
7710
7751
|
if (!logPath) return "";
|
|
7711
7752
|
try {
|
|
7712
|
-
return
|
|
7753
|
+
return fs28.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
7713
7754
|
} catch {
|
|
7714
7755
|
return "";
|
|
7715
7756
|
}
|
|
@@ -7782,20 +7823,20 @@ async function nextAvailableLitellmUrl(url) {
|
|
|
7782
7823
|
throw new Error(`no free LiteLLM port found after ${startPort}`);
|
|
7783
7824
|
}
|
|
7784
7825
|
function canListen(port, host) {
|
|
7785
|
-
return new Promise((
|
|
7826
|
+
return new Promise((resolve23) => {
|
|
7786
7827
|
const server = net.createServer();
|
|
7787
|
-
server.once("error", () =>
|
|
7828
|
+
server.once("error", () => resolve23(false));
|
|
7788
7829
|
server.once("listening", () => {
|
|
7789
|
-
server.close(() =>
|
|
7830
|
+
server.close(() => resolve23(true));
|
|
7790
7831
|
});
|
|
7791
7832
|
server.listen(port, host);
|
|
7792
7833
|
});
|
|
7793
7834
|
}
|
|
7794
7835
|
function readDotenvApiKeys(projectDir) {
|
|
7795
|
-
const dotenvPath =
|
|
7796
|
-
if (!
|
|
7836
|
+
const dotenvPath = path26.join(projectDir, ".env");
|
|
7837
|
+
if (!fs28.existsSync(dotenvPath)) return {};
|
|
7797
7838
|
const result = {};
|
|
7798
|
-
for (const rawLine of
|
|
7839
|
+
for (const rawLine of fs28.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
7799
7840
|
const line = rawLine.trim();
|
|
7800
7841
|
if (!line || line.startsWith("#")) continue;
|
|
7801
7842
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -8454,8 +8495,8 @@ var init_pushWithRetry = __esm({
|
|
|
8454
8495
|
|
|
8455
8496
|
// src/commit.ts
|
|
8456
8497
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
8457
|
-
import * as
|
|
8458
|
-
import * as
|
|
8498
|
+
import * as fs29 from "fs";
|
|
8499
|
+
import * as path27 from "path";
|
|
8459
8500
|
function isGitHubYamlPath(filePath) {
|
|
8460
8501
|
const normalized = filePath.replace(/^\.\/+/, "");
|
|
8461
8502
|
return normalized.startsWith(".github/") && /\.ya?ml$/i.test(normalized);
|
|
@@ -8497,18 +8538,18 @@ function ensureGitIdentity(cwd) {
|
|
|
8497
8538
|
}
|
|
8498
8539
|
function abortUnfinishedGitOps(cwd) {
|
|
8499
8540
|
const aborted = [];
|
|
8500
|
-
const gitDir =
|
|
8501
|
-
if (!
|
|
8502
|
-
if (
|
|
8541
|
+
const gitDir = path27.join(cwd ?? process.cwd(), ".git");
|
|
8542
|
+
if (!fs29.existsSync(gitDir)) return aborted;
|
|
8543
|
+
if (fs29.existsSync(path27.join(gitDir, "MERGE_HEAD"))) {
|
|
8503
8544
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
8504
8545
|
}
|
|
8505
|
-
if (
|
|
8546
|
+
if (fs29.existsSync(path27.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
8506
8547
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
8507
8548
|
}
|
|
8508
|
-
if (
|
|
8549
|
+
if (fs29.existsSync(path27.join(gitDir, "REVERT_HEAD"))) {
|
|
8509
8550
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
8510
8551
|
}
|
|
8511
|
-
if (
|
|
8552
|
+
if (fs29.existsSync(path27.join(gitDir, "rebase-merge")) || fs29.existsSync(path27.join(gitDir, "rebase-apply"))) {
|
|
8512
8553
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
8513
8554
|
}
|
|
8514
8555
|
try {
|
|
@@ -8565,7 +8606,7 @@ function normalizeCommitMessage(raw) {
|
|
|
8565
8606
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
8566
8607
|
const allChanged = listChangedFiles(cwd);
|
|
8567
8608
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
8568
|
-
const mergeHeadExists =
|
|
8609
|
+
const mergeHeadExists = fs29.existsSync(path27.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
8569
8610
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
8570
8611
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
8571
8612
|
}
|
|
@@ -9205,13 +9246,13 @@ var init_state2 = __esm({
|
|
|
9205
9246
|
});
|
|
9206
9247
|
|
|
9207
9248
|
// src/goal/runLog.ts
|
|
9208
|
-
import * as
|
|
9249
|
+
import * as fs30 from "fs";
|
|
9209
9250
|
function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
|
|
9210
9251
|
const logs = goalRunLogs(data);
|
|
9211
9252
|
const existing = logs[goalId];
|
|
9212
|
-
const
|
|
9253
|
+
const path58 = existing?.path ?? goalRunLogPath(goalId, data);
|
|
9213
9254
|
logs[goalId] = {
|
|
9214
|
-
path:
|
|
9255
|
+
path: path58,
|
|
9215
9256
|
events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
|
|
9216
9257
|
};
|
|
9217
9258
|
}
|
|
@@ -9547,8 +9588,8 @@ function readGithubEvent() {
|
|
|
9547
9588
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
9548
9589
|
if (!eventPath) return null;
|
|
9549
9590
|
try {
|
|
9550
|
-
if (!
|
|
9551
|
-
const parsed = JSON.parse(
|
|
9591
|
+
if (!fs30.existsSync(eventPath)) return null;
|
|
9592
|
+
const parsed = JSON.parse(fs30.readFileSync(eventPath, "utf-8"));
|
|
9552
9593
|
return recordValue3(parsed);
|
|
9553
9594
|
} catch {
|
|
9554
9595
|
return null;
|
|
@@ -9658,8 +9699,8 @@ var init_stateStore = __esm({
|
|
|
9658
9699
|
});
|
|
9659
9700
|
|
|
9660
9701
|
// src/goal/targetLoopResolution.ts
|
|
9661
|
-
import * as
|
|
9662
|
-
import * as
|
|
9702
|
+
import * as fs31 from "fs";
|
|
9703
|
+
import * as path28 from "path";
|
|
9663
9704
|
async function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
|
|
9664
9705
|
const targetId = loopGoal.loopTarget?.id.trim() ?? "";
|
|
9665
9706
|
assertSafeGoalId(targetId, "loop target");
|
|
@@ -9737,11 +9778,11 @@ function goalInstanceTime(state) {
|
|
|
9737
9778
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
9738
9779
|
}
|
|
9739
9780
|
function loadGoalTemplate(cwd, targetId) {
|
|
9740
|
-
return readJsonObject2(
|
|
9781
|
+
return readJsonObject2(path28.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
|
|
9741
9782
|
}
|
|
9742
9783
|
function readJsonObject2(filePath) {
|
|
9743
|
-
if (!
|
|
9744
|
-
const parsed = JSON.parse(
|
|
9784
|
+
if (!fs31.existsSync(filePath)) return null;
|
|
9785
|
+
const parsed = JSON.parse(fs31.readFileSync(filePath, "utf8"));
|
|
9745
9786
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9746
9787
|
throw new Error(`goal template ${filePath} must be a JSON object`);
|
|
9747
9788
|
}
|
|
@@ -10088,15 +10129,15 @@ var init_backendStateBackend = __esm({
|
|
|
10088
10129
|
this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
|
|
10089
10130
|
}
|
|
10090
10131
|
async load(slug) {
|
|
10091
|
-
const
|
|
10132
|
+
const path58 = stateFilePath(this.jobsDir, slug);
|
|
10092
10133
|
const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
|
|
10093
10134
|
if (!loaded) {
|
|
10094
|
-
return { path:
|
|
10135
|
+
return { path: path58, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
10095
10136
|
}
|
|
10096
10137
|
if (!isStateEnvelope(loaded.doc)) {
|
|
10097
10138
|
throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
|
|
10098
10139
|
}
|
|
10099
|
-
return { path:
|
|
10140
|
+
return { path: path58, handle: loaded.updatedAt, state: loaded.doc, created: false };
|
|
10100
10141
|
}
|
|
10101
10142
|
async save(loaded, next) {
|
|
10102
10143
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
|
|
@@ -10116,8 +10157,8 @@ var init_backendStateBackend = __esm({
|
|
|
10116
10157
|
});
|
|
10117
10158
|
|
|
10118
10159
|
// src/scripts/jobState/localFileBackend.ts
|
|
10119
|
-
import * as
|
|
10120
|
-
import * as
|
|
10160
|
+
import * as fs32 from "fs";
|
|
10161
|
+
import * as path29 from "path";
|
|
10121
10162
|
function sanitizeKey(s) {
|
|
10122
10163
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
10123
10164
|
}
|
|
@@ -10173,7 +10214,7 @@ var init_localFileBackend = __esm({
|
|
|
10173
10214
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
10174
10215
|
this.cwd = opts.cwd;
|
|
10175
10216
|
this.jobsDir = opts.jobsDir;
|
|
10176
|
-
this.absDir =
|
|
10217
|
+
this.absDir = path29.resolve(opts.cwd, opts.jobsDir);
|
|
10177
10218
|
this.owner = opts.owner;
|
|
10178
10219
|
this.repo = opts.repo;
|
|
10179
10220
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -10188,7 +10229,7 @@ var init_localFileBackend = __esm({
|
|
|
10188
10229
|
`);
|
|
10189
10230
|
return;
|
|
10190
10231
|
}
|
|
10191
|
-
|
|
10232
|
+
fs32.mkdirSync(this.absDir, { recursive: true });
|
|
10192
10233
|
const prefix = this.cacheKeyPrefix();
|
|
10193
10234
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
10194
10235
|
try {
|
|
@@ -10217,7 +10258,7 @@ var init_localFileBackend = __esm({
|
|
|
10217
10258
|
`);
|
|
10218
10259
|
return;
|
|
10219
10260
|
}
|
|
10220
|
-
if (!
|
|
10261
|
+
if (!fs32.existsSync(this.absDir)) {
|
|
10221
10262
|
return;
|
|
10222
10263
|
}
|
|
10223
10264
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -10233,11 +10274,11 @@ var init_localFileBackend = __esm({
|
|
|
10233
10274
|
}
|
|
10234
10275
|
load(slug) {
|
|
10235
10276
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
10236
|
-
const absPath =
|
|
10237
|
-
if (!
|
|
10277
|
+
const absPath = path29.resolve(this.cwd, relPath);
|
|
10278
|
+
if (!fs32.existsSync(absPath)) {
|
|
10238
10279
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
10239
10280
|
}
|
|
10240
|
-
const raw =
|
|
10281
|
+
const raw = fs32.readFileSync(absPath, "utf-8");
|
|
10241
10282
|
let parsed;
|
|
10242
10283
|
try {
|
|
10243
10284
|
parsed = JSON.parse(raw);
|
|
@@ -10254,13 +10295,13 @@ var init_localFileBackend = __esm({
|
|
|
10254
10295
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
10255
10296
|
return false;
|
|
10256
10297
|
}
|
|
10257
|
-
const absPath =
|
|
10258
|
-
|
|
10298
|
+
const absPath = path29.resolve(this.cwd, loaded.path);
|
|
10299
|
+
fs32.mkdirSync(path29.dirname(absPath), { recursive: true });
|
|
10259
10300
|
const body = `${JSON.stringify(next, null, 2)}
|
|
10260
10301
|
`;
|
|
10261
10302
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
10262
|
-
|
|
10263
|
-
|
|
10303
|
+
fs32.writeFileSync(tmpPath, body, "utf-8");
|
|
10304
|
+
fs32.renameSync(tmpPath, absPath);
|
|
10264
10305
|
return true;
|
|
10265
10306
|
}
|
|
10266
10307
|
cacheKeyPrefix() {
|
|
@@ -10292,7 +10333,7 @@ var init_jobState = __esm({
|
|
|
10292
10333
|
});
|
|
10293
10334
|
|
|
10294
10335
|
// src/scripts/goalCapabilityScheduling.ts
|
|
10295
|
-
import * as
|
|
10336
|
+
import * as path30 from "path";
|
|
10296
10337
|
function isCapabilityCadenceGoal(goal, extra) {
|
|
10297
10338
|
return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
|
|
10298
10339
|
}
|
|
@@ -10348,7 +10389,7 @@ function planTargetLoopSchedule(opts) {
|
|
|
10348
10389
|
}
|
|
10349
10390
|
async function planGoalCapabilitySchedule(opts) {
|
|
10350
10391
|
const jobsDir = opts.jobsDir ?? capabilitiesRoot(opts.cwd);
|
|
10351
|
-
const jobsRoot =
|
|
10392
|
+
const jobsRoot = path30.resolve(opts.cwd, jobsDir);
|
|
10352
10393
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
10353
10394
|
const at = now.toISOString();
|
|
10354
10395
|
const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
|
|
@@ -12164,8 +12205,8 @@ var init_classifyByLabel = __esm({
|
|
|
12164
12205
|
|
|
12165
12206
|
// src/scripts/commitAndPush.ts
|
|
12166
12207
|
import { createHash as createHash5 } from "crypto";
|
|
12167
|
-
import * as
|
|
12168
|
-
import * as
|
|
12208
|
+
import * as fs33 from "fs";
|
|
12209
|
+
import * as path31 from "path";
|
|
12169
12210
|
function sentinelPathForStage(cwd, profileName, workflowExecutionKey) {
|
|
12170
12211
|
const runId = resolveRunId();
|
|
12171
12212
|
const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash5("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
|
|
@@ -12187,9 +12228,9 @@ var init_commitAndPush = __esm({
|
|
|
12187
12228
|
}
|
|
12188
12229
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
12189
12230
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name, ctx.data.workflowExecutionKey) : null;
|
|
12190
|
-
if (sentinel &&
|
|
12231
|
+
if (sentinel && fs33.existsSync(sentinel)) {
|
|
12191
12232
|
try {
|
|
12192
|
-
const replay = JSON.parse(
|
|
12233
|
+
const replay = JSON.parse(fs33.readFileSync(sentinel, "utf-8"));
|
|
12193
12234
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
12194
12235
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
12195
12236
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -12249,8 +12290,8 @@ var init_commitAndPush = __esm({
|
|
|
12249
12290
|
const result = ctx.data.commitResult;
|
|
12250
12291
|
if (sentinel && result?.committed) {
|
|
12251
12292
|
try {
|
|
12252
|
-
|
|
12253
|
-
|
|
12293
|
+
fs33.mkdirSync(path31.dirname(sentinel), { recursive: true });
|
|
12294
|
+
fs33.writeFileSync(
|
|
12254
12295
|
sentinel,
|
|
12255
12296
|
JSON.stringify(
|
|
12256
12297
|
{
|
|
@@ -12344,8 +12385,8 @@ var init_commitGoalState = __esm({
|
|
|
12344
12385
|
});
|
|
12345
12386
|
|
|
12346
12387
|
// src/scripts/composePrompt.ts
|
|
12347
|
-
import * as
|
|
12348
|
-
import * as
|
|
12388
|
+
import * as fs34 from "fs";
|
|
12389
|
+
import * as path32 from "path";
|
|
12349
12390
|
function fenceUntrusted(value) {
|
|
12350
12391
|
if (value.trim().length === 0) return value;
|
|
12351
12392
|
const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
@@ -12469,10 +12510,10 @@ var init_composePrompt = __esm({
|
|
|
12469
12510
|
const explicit = ctx.data.promptTemplate;
|
|
12470
12511
|
const mode = ctx.args.mode;
|
|
12471
12512
|
const candidates = [
|
|
12472
|
-
explicit ?
|
|
12473
|
-
mode ?
|
|
12474
|
-
|
|
12475
|
-
|
|
12513
|
+
explicit ? path32.join(profile.dir, explicit) : null,
|
|
12514
|
+
mode ? path32.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
12515
|
+
path32.join(profile.dir, "prompt.md"),
|
|
12516
|
+
path32.join(profile.dir, "capability.md")
|
|
12476
12517
|
].filter(Boolean);
|
|
12477
12518
|
let templatePath = "";
|
|
12478
12519
|
let template = "";
|
|
@@ -12485,7 +12526,7 @@ var init_composePrompt = __esm({
|
|
|
12485
12526
|
break;
|
|
12486
12527
|
}
|
|
12487
12528
|
try {
|
|
12488
|
-
template =
|
|
12529
|
+
template = fs34.readFileSync(c, "utf-8");
|
|
12489
12530
|
templatePath = c;
|
|
12490
12531
|
break;
|
|
12491
12532
|
} catch (err) {
|
|
@@ -12496,7 +12537,7 @@ var init_composePrompt = __esm({
|
|
|
12496
12537
|
if (!templatePath) {
|
|
12497
12538
|
let dirState;
|
|
12498
12539
|
try {
|
|
12499
|
-
dirState = `dir contents: [${
|
|
12540
|
+
dirState = `dir contents: [${fs34.readdirSync(profile.dir).join(", ")}]`;
|
|
12500
12541
|
} catch (err) {
|
|
12501
12542
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
12502
12543
|
}
|
|
@@ -13230,19 +13271,19 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
13230
13271
|
|
|
13231
13272
|
// src/scripts/diagMcp.ts
|
|
13232
13273
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
13233
|
-
import * as
|
|
13274
|
+
import * as fs35 from "fs";
|
|
13234
13275
|
import * as os5 from "os";
|
|
13235
|
-
import * as
|
|
13276
|
+
import * as path33 from "path";
|
|
13236
13277
|
var diagMcp;
|
|
13237
13278
|
var init_diagMcp = __esm({
|
|
13238
13279
|
"src/scripts/diagMcp.ts"() {
|
|
13239
13280
|
"use strict";
|
|
13240
13281
|
diagMcp = async (_ctx) => {
|
|
13241
13282
|
const home = os5.homedir();
|
|
13242
|
-
const cacheDir =
|
|
13283
|
+
const cacheDir = path33.join(home, ".cache", "ms-playwright");
|
|
13243
13284
|
let entries = [];
|
|
13244
13285
|
try {
|
|
13245
|
-
entries =
|
|
13286
|
+
entries = fs35.readdirSync(cacheDir);
|
|
13246
13287
|
} catch {
|
|
13247
13288
|
}
|
|
13248
13289
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -13270,13 +13311,13 @@ var init_diagMcp = __esm({
|
|
|
13270
13311
|
});
|
|
13271
13312
|
|
|
13272
13313
|
// src/scripts/frameworkDetectors.ts
|
|
13273
|
-
import * as
|
|
13274
|
-
import * as
|
|
13314
|
+
import * as fs36 from "fs";
|
|
13315
|
+
import * as path34 from "path";
|
|
13275
13316
|
function detectFrameworks(cwd) {
|
|
13276
13317
|
const out = [];
|
|
13277
13318
|
let deps = {};
|
|
13278
13319
|
try {
|
|
13279
|
-
const pkg = JSON.parse(
|
|
13320
|
+
const pkg = JSON.parse(fs36.readFileSync(path34.join(cwd, "package.json"), "utf-8"));
|
|
13280
13321
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13281
13322
|
} catch {
|
|
13282
13323
|
return out;
|
|
@@ -13313,25 +13354,25 @@ function detectFrameworks(cwd) {
|
|
|
13313
13354
|
}
|
|
13314
13355
|
function findFile(cwd, candidates) {
|
|
13315
13356
|
for (const c of candidates) {
|
|
13316
|
-
if (
|
|
13357
|
+
if (fs36.existsSync(path34.join(cwd, c))) return c;
|
|
13317
13358
|
}
|
|
13318
13359
|
return null;
|
|
13319
13360
|
}
|
|
13320
13361
|
function discoverPayloadCollections(cwd) {
|
|
13321
13362
|
const out = [];
|
|
13322
13363
|
for (const dir of COLLECTION_DIRS) {
|
|
13323
|
-
const full =
|
|
13324
|
-
if (!
|
|
13364
|
+
const full = path34.join(cwd, dir);
|
|
13365
|
+
if (!fs36.existsSync(full)) continue;
|
|
13325
13366
|
let files;
|
|
13326
13367
|
try {
|
|
13327
|
-
files =
|
|
13368
|
+
files = fs36.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
13328
13369
|
} catch {
|
|
13329
13370
|
continue;
|
|
13330
13371
|
}
|
|
13331
13372
|
for (const file of files) {
|
|
13332
13373
|
try {
|
|
13333
|
-
const filePath =
|
|
13334
|
-
const content =
|
|
13374
|
+
const filePath = path34.join(full, file);
|
|
13375
|
+
const content = fs36.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
13335
13376
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
13336
13377
|
if (!slugMatch) continue;
|
|
13337
13378
|
const slug = slugMatch[1];
|
|
@@ -13345,7 +13386,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
13345
13386
|
out.push({
|
|
13346
13387
|
name,
|
|
13347
13388
|
slug,
|
|
13348
|
-
filePath:
|
|
13389
|
+
filePath: path34.relative(cwd, filePath),
|
|
13349
13390
|
fields: fields.slice(0, 20),
|
|
13350
13391
|
hasAdmin
|
|
13351
13392
|
});
|
|
@@ -13358,28 +13399,28 @@ function discoverPayloadCollections(cwd) {
|
|
|
13358
13399
|
function discoverAdminComponents(cwd, collections) {
|
|
13359
13400
|
const out = [];
|
|
13360
13401
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
13361
|
-
const full =
|
|
13362
|
-
if (!
|
|
13402
|
+
const full = path34.join(cwd, dir);
|
|
13403
|
+
if (!fs36.existsSync(full)) continue;
|
|
13363
13404
|
let entries;
|
|
13364
13405
|
try {
|
|
13365
|
-
entries =
|
|
13406
|
+
entries = fs36.readdirSync(full, { withFileTypes: true });
|
|
13366
13407
|
} catch {
|
|
13367
13408
|
continue;
|
|
13368
13409
|
}
|
|
13369
13410
|
for (const entry of entries) {
|
|
13370
|
-
const entryPath =
|
|
13411
|
+
const entryPath = path34.join(full, entry.name);
|
|
13371
13412
|
let name;
|
|
13372
13413
|
let filePath;
|
|
13373
13414
|
if (entry.isDirectory()) {
|
|
13374
13415
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
13375
|
-
(f) =>
|
|
13416
|
+
(f) => fs36.existsSync(path34.join(entryPath, f))
|
|
13376
13417
|
);
|
|
13377
13418
|
if (!indexFile) continue;
|
|
13378
13419
|
name = entry.name;
|
|
13379
|
-
filePath =
|
|
13420
|
+
filePath = path34.relative(cwd, path34.join(entryPath, indexFile));
|
|
13380
13421
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
13381
13422
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
13382
|
-
filePath =
|
|
13423
|
+
filePath = path34.relative(cwd, entryPath);
|
|
13383
13424
|
} else {
|
|
13384
13425
|
continue;
|
|
13385
13426
|
}
|
|
@@ -13387,7 +13428,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
13387
13428
|
if (collections) {
|
|
13388
13429
|
for (const col of collections) {
|
|
13389
13430
|
try {
|
|
13390
|
-
const colContent =
|
|
13431
|
+
const colContent = fs36.readFileSync(path34.join(cwd, col.filePath), "utf-8");
|
|
13391
13432
|
if (colContent.includes(name)) {
|
|
13392
13433
|
usedInCollection = col.slug;
|
|
13393
13434
|
break;
|
|
@@ -13405,8 +13446,8 @@ function scanApiRoutes(cwd) {
|
|
|
13405
13446
|
const out = [];
|
|
13406
13447
|
const appDirs = ["src/app", "app"];
|
|
13407
13448
|
for (const appDir of appDirs) {
|
|
13408
|
-
const apiDir =
|
|
13409
|
-
if (!
|
|
13449
|
+
const apiDir = path34.join(cwd, appDir, "api");
|
|
13450
|
+
if (!fs36.existsSync(apiDir)) continue;
|
|
13410
13451
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
13411
13452
|
break;
|
|
13412
13453
|
}
|
|
@@ -13415,14 +13456,14 @@ function scanApiRoutes(cwd) {
|
|
|
13415
13456
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
13416
13457
|
let entries;
|
|
13417
13458
|
try {
|
|
13418
|
-
entries =
|
|
13459
|
+
entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
13419
13460
|
} catch {
|
|
13420
13461
|
return;
|
|
13421
13462
|
}
|
|
13422
13463
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
13423
13464
|
if (routeFile) {
|
|
13424
13465
|
try {
|
|
13425
|
-
const content =
|
|
13466
|
+
const content = fs36.readFileSync(path34.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
13426
13467
|
const methods = HTTP_METHODS.filter(
|
|
13427
13468
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
13428
13469
|
);
|
|
@@ -13430,7 +13471,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
13430
13471
|
out.push({
|
|
13431
13472
|
path: prefix,
|
|
13432
13473
|
methods,
|
|
13433
|
-
filePath:
|
|
13474
|
+
filePath: path34.relative(cwd, path34.join(dir, routeFile.name))
|
|
13434
13475
|
});
|
|
13435
13476
|
}
|
|
13436
13477
|
} catch {
|
|
@@ -13441,7 +13482,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
13441
13482
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
13442
13483
|
let segment = entry.name;
|
|
13443
13484
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
13444
|
-
walkApiRoutes(
|
|
13485
|
+
walkApiRoutes(path34.join(dir, entry.name), prefix, cwd, out);
|
|
13445
13486
|
continue;
|
|
13446
13487
|
}
|
|
13447
13488
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -13449,16 +13490,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
13449
13490
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
13450
13491
|
segment = `:${segment.slice(1, -1)}`;
|
|
13451
13492
|
}
|
|
13452
|
-
walkApiRoutes(
|
|
13493
|
+
walkApiRoutes(path34.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
13453
13494
|
}
|
|
13454
13495
|
}
|
|
13455
13496
|
function scanEnvVars(cwd) {
|
|
13456
13497
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
13457
13498
|
for (const envFile of candidates) {
|
|
13458
|
-
const envPath =
|
|
13459
|
-
if (!
|
|
13499
|
+
const envPath = path34.join(cwd, envFile);
|
|
13500
|
+
if (!fs36.existsSync(envPath)) continue;
|
|
13460
13501
|
try {
|
|
13461
|
-
const content =
|
|
13502
|
+
const content = fs36.readFileSync(envPath, "utf-8");
|
|
13462
13503
|
const vars = [];
|
|
13463
13504
|
for (const line of content.split("\n")) {
|
|
13464
13505
|
const trimmed = line.trim();
|
|
@@ -13503,8 +13544,8 @@ var init_frameworkDetectors = __esm({
|
|
|
13503
13544
|
});
|
|
13504
13545
|
|
|
13505
13546
|
// src/scripts/discoverQaContext.ts
|
|
13506
|
-
import * as
|
|
13507
|
-
import * as
|
|
13547
|
+
import * as fs37 from "fs";
|
|
13548
|
+
import * as path35 from "path";
|
|
13508
13549
|
function runQaDiscovery(cwd) {
|
|
13509
13550
|
const out = {
|
|
13510
13551
|
routes: [],
|
|
@@ -13535,9 +13576,9 @@ function runQaDiscovery(cwd) {
|
|
|
13535
13576
|
}
|
|
13536
13577
|
function detectDevServer(cwd, out) {
|
|
13537
13578
|
try {
|
|
13538
|
-
const pkg = JSON.parse(
|
|
13579
|
+
const pkg = JSON.parse(fs37.readFileSync(path35.join(cwd, "package.json"), "utf-8"));
|
|
13539
13580
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13540
|
-
const pm =
|
|
13581
|
+
const pm = fs37.existsSync(path35.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs37.existsSync(path35.join(cwd, "yarn.lock")) ? "yarn" : fs37.existsSync(path35.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
13541
13582
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
13542
13583
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
13543
13584
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -13547,8 +13588,8 @@ function detectDevServer(cwd, out) {
|
|
|
13547
13588
|
function scanFrontendRoutes(cwd, out) {
|
|
13548
13589
|
const appDirs = ["src/app", "app"];
|
|
13549
13590
|
for (const appDir of appDirs) {
|
|
13550
|
-
const full =
|
|
13551
|
-
if (!
|
|
13591
|
+
const full = path35.join(cwd, appDir);
|
|
13592
|
+
if (!fs37.existsSync(full)) continue;
|
|
13552
13593
|
walkFrontendRoutes(full, "", out);
|
|
13553
13594
|
break;
|
|
13554
13595
|
}
|
|
@@ -13556,7 +13597,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
13556
13597
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
13557
13598
|
let entries;
|
|
13558
13599
|
try {
|
|
13559
|
-
entries =
|
|
13600
|
+
entries = fs37.readdirSync(dir, { withFileTypes: true });
|
|
13560
13601
|
} catch {
|
|
13561
13602
|
return;
|
|
13562
13603
|
}
|
|
@@ -13573,7 +13614,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
13573
13614
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
13574
13615
|
let segment = entry.name;
|
|
13575
13616
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
13576
|
-
walkFrontendRoutes(
|
|
13617
|
+
walkFrontendRoutes(path35.join(dir, entry.name), prefix, out);
|
|
13577
13618
|
continue;
|
|
13578
13619
|
}
|
|
13579
13620
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -13581,7 +13622,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
13581
13622
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
13582
13623
|
segment = `:${segment.slice(1, -1)}`;
|
|
13583
13624
|
}
|
|
13584
|
-
walkFrontendRoutes(
|
|
13625
|
+
walkFrontendRoutes(path35.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
13585
13626
|
}
|
|
13586
13627
|
}
|
|
13587
13628
|
function detectAuthFiles(cwd, out) {
|
|
@@ -13598,23 +13639,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
13598
13639
|
"src/app/api/oauth"
|
|
13599
13640
|
];
|
|
13600
13641
|
for (const c of candidates) {
|
|
13601
|
-
if (
|
|
13642
|
+
if (fs37.existsSync(path35.join(cwd, c))) out.authFiles.push(c);
|
|
13602
13643
|
}
|
|
13603
13644
|
}
|
|
13604
13645
|
function detectRoles(cwd, out) {
|
|
13605
13646
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
13606
13647
|
for (const rp of rolePaths) {
|
|
13607
|
-
const dir =
|
|
13608
|
-
if (!
|
|
13648
|
+
const dir = path35.join(cwd, rp);
|
|
13649
|
+
if (!fs37.existsSync(dir)) continue;
|
|
13609
13650
|
let files;
|
|
13610
13651
|
try {
|
|
13611
|
-
files =
|
|
13652
|
+
files = fs37.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
13612
13653
|
} catch {
|
|
13613
13654
|
continue;
|
|
13614
13655
|
}
|
|
13615
13656
|
for (const f of files) {
|
|
13616
13657
|
try {
|
|
13617
|
-
const content =
|
|
13658
|
+
const content = fs37.readFileSync(path35.join(dir, f), "utf-8").slice(0, 5e3);
|
|
13618
13659
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
13619
13660
|
if (roleMatches) {
|
|
13620
13661
|
for (const m of roleMatches) {
|
|
@@ -13875,8 +13916,8 @@ var init_dispatchClassified = __esm({
|
|
|
13875
13916
|
});
|
|
13876
13917
|
|
|
13877
13918
|
// src/loopDefinitions.ts
|
|
13878
|
-
import * as
|
|
13879
|
-
import * as
|
|
13919
|
+
import * as fs38 from "fs";
|
|
13920
|
+
import * as path36 from "path";
|
|
13880
13921
|
function normalizeLoopDefinition(value) {
|
|
13881
13922
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
13882
13923
|
const raw = value;
|
|
@@ -13903,10 +13944,10 @@ function readLoopDefinition(cwd, id) {
|
|
|
13903
13944
|
if (!ID.test(id)) return null;
|
|
13904
13945
|
const roots = loopRoots(cwd);
|
|
13905
13946
|
for (const root of roots) {
|
|
13906
|
-
const filePath =
|
|
13907
|
-
if (!
|
|
13947
|
+
const filePath = path36.join(root, "loops", id, "loop.json");
|
|
13948
|
+
if (!fs38.existsSync(filePath)) continue;
|
|
13908
13949
|
try {
|
|
13909
|
-
const loop = normalizeLoopDefinition(JSON.parse(
|
|
13950
|
+
const loop = normalizeLoopDefinition(JSON.parse(fs38.readFileSync(filePath, "utf8")));
|
|
13910
13951
|
if (loop?.id === id) return loop;
|
|
13911
13952
|
process.stderr.write(`[kody] invalid Loop definition: ${filePath}
|
|
13912
13953
|
`);
|
|
@@ -13916,7 +13957,7 @@ function readLoopDefinition(cwd, id) {
|
|
|
13916
13957
|
}
|
|
13917
13958
|
}
|
|
13918
13959
|
process.stderr.write(
|
|
13919
|
-
`[kody] Loop not found: ${id} (${roots.map((root) =>
|
|
13960
|
+
`[kody] Loop not found: ${id} (${roots.map((root) => path36.join(root, "loops", id, "loop.json")).join(", ")})
|
|
13920
13961
|
`
|
|
13921
13962
|
);
|
|
13922
13963
|
return null;
|
|
@@ -13925,14 +13966,14 @@ function listLoopDefinitions(cwd) {
|
|
|
13925
13966
|
const roots = loopRoots(cwd);
|
|
13926
13967
|
const byId = /* @__PURE__ */ new Map();
|
|
13927
13968
|
for (const root of roots.reverse()) {
|
|
13928
|
-
const loopsDir =
|
|
13929
|
-
if (!
|
|
13930
|
-
for (const id of
|
|
13969
|
+
const loopsDir = path36.join(root, "loops");
|
|
13970
|
+
if (!fs38.existsSync(loopsDir)) continue;
|
|
13971
|
+
for (const id of fs38.readdirSync(loopsDir).sort()) {
|
|
13931
13972
|
if (!ID.test(id)) continue;
|
|
13932
|
-
const filePath =
|
|
13933
|
-
if (!
|
|
13973
|
+
const filePath = path36.join(loopsDir, id, "loop.json");
|
|
13974
|
+
if (!fs38.existsSync(filePath)) continue;
|
|
13934
13975
|
try {
|
|
13935
|
-
const loop = normalizeLoopDefinition(JSON.parse(
|
|
13976
|
+
const loop = normalizeLoopDefinition(JSON.parse(fs38.readFileSync(filePath, "utf8")));
|
|
13936
13977
|
if (loop?.id === id) byId.set(id, loop);
|
|
13937
13978
|
} catch {
|
|
13938
13979
|
process.stderr.write(`[kody] unreadable Loop definition: ${filePath}
|
|
@@ -13944,8 +13985,8 @@ function listLoopDefinitions(cwd) {
|
|
|
13944
13985
|
}
|
|
13945
13986
|
function loopRoots(cwd) {
|
|
13946
13987
|
return [
|
|
13947
|
-
|
|
13948
|
-
|
|
13988
|
+
path36.join(cwd, ".kody-engine", "runtime"),
|
|
13989
|
+
path36.join(cwd, ".kody-engine", "definitions"),
|
|
13949
13990
|
definitionsRoot(cwd)
|
|
13950
13991
|
].filter((root, index, roots) => roots.indexOf(root) === index);
|
|
13951
13992
|
}
|
|
@@ -15223,15 +15264,15 @@ var init_fixFlow = __esm({
|
|
|
15223
15264
|
});
|
|
15224
15265
|
|
|
15225
15266
|
// src/workflow-template.ts
|
|
15226
|
-
import * as
|
|
15227
|
-
import * as
|
|
15267
|
+
import * as fs39 from "fs";
|
|
15268
|
+
import * as path37 from "path";
|
|
15228
15269
|
import { fileURLToPath } from "url";
|
|
15229
15270
|
function loadKodyWorkflowTemplate() {
|
|
15230
|
-
const here =
|
|
15231
|
-
const candidates = [
|
|
15232
|
-
const source = candidates.find((candidate) =>
|
|
15271
|
+
const here = path37.dirname(fileURLToPath(import.meta.url));
|
|
15272
|
+
const candidates = [path37.resolve(here, "../templates/kody.yml"), path37.resolve(here, "../../templates/kody.yml")];
|
|
15273
|
+
const source = candidates.find((candidate) => fs39.existsSync(candidate));
|
|
15233
15274
|
if (!source) throw new Error(`Kody workflow template is missing: ${KODY_WORKFLOW_TEMPLATE_PATH}`);
|
|
15234
|
-
return
|
|
15275
|
+
return fs39.readFileSync(source, "utf8");
|
|
15235
15276
|
}
|
|
15236
15277
|
var KODY_WORKFLOW_TEMPLATE_PATH;
|
|
15237
15278
|
var init_workflow_template = __esm({
|
|
@@ -15243,12 +15284,12 @@ var init_workflow_template = __esm({
|
|
|
15243
15284
|
|
|
15244
15285
|
// src/scripts/initFlow.ts
|
|
15245
15286
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
15246
|
-
import * as
|
|
15247
|
-
import * as
|
|
15287
|
+
import * as fs40 from "fs";
|
|
15288
|
+
import * as path38 from "path";
|
|
15248
15289
|
function detectPackageManager(cwd) {
|
|
15249
|
-
if (
|
|
15250
|
-
if (
|
|
15251
|
-
if (
|
|
15290
|
+
if (fs40.existsSync(path38.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
15291
|
+
if (fs40.existsSync(path38.join(cwd, "yarn.lock"))) return "yarn";
|
|
15292
|
+
if (fs40.existsSync(path38.join(cwd, "bun.lockb"))) return "bun";
|
|
15252
15293
|
return "npm";
|
|
15253
15294
|
}
|
|
15254
15295
|
function qualityCommandsFor(pm) {
|
|
@@ -15320,22 +15361,22 @@ function performInit(cwd, force) {
|
|
|
15320
15361
|
const pm = detectPackageManager(cwd);
|
|
15321
15362
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
15322
15363
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
15323
|
-
const configPath =
|
|
15324
|
-
if (
|
|
15364
|
+
const configPath = path38.join(cwd, "kody.config.json");
|
|
15365
|
+
if (fs40.existsSync(configPath) && !force) {
|
|
15325
15366
|
skipped.push("kody.config.json");
|
|
15326
15367
|
} else {
|
|
15327
15368
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
15328
|
-
|
|
15369
|
+
fs40.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
15329
15370
|
`);
|
|
15330
15371
|
wrote.push("kody.config.json");
|
|
15331
15372
|
}
|
|
15332
|
-
const workflowDir =
|
|
15333
|
-
const workflowPath =
|
|
15334
|
-
if (
|
|
15373
|
+
const workflowDir = path38.join(cwd, ".github", "workflows");
|
|
15374
|
+
const workflowPath = path38.join(workflowDir, "kody.yml");
|
|
15375
|
+
if (fs40.existsSync(workflowPath) && !force) {
|
|
15335
15376
|
skipped.push(".github/workflows/kody.yml");
|
|
15336
15377
|
} else {
|
|
15337
|
-
|
|
15338
|
-
|
|
15378
|
+
fs40.mkdirSync(workflowDir, { recursive: true });
|
|
15379
|
+
fs40.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
|
|
15339
15380
|
wrote.push(".github/workflows/kody.yml");
|
|
15340
15381
|
}
|
|
15341
15382
|
let labels;
|
|
@@ -15386,7 +15427,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
|
|
|
15386
15427
|
});
|
|
15387
15428
|
|
|
15388
15429
|
// src/scripts/loadAgentAdhoc.ts
|
|
15389
|
-
import * as
|
|
15430
|
+
import * as fs41 from "fs";
|
|
15390
15431
|
function resolveMessage(messageArg) {
|
|
15391
15432
|
const fromComment = readCommentBody();
|
|
15392
15433
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -15394,9 +15435,9 @@ function resolveMessage(messageArg) {
|
|
|
15394
15435
|
}
|
|
15395
15436
|
function readCommentBody() {
|
|
15396
15437
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15397
|
-
if (!eventPath || !
|
|
15438
|
+
if (!eventPath || !fs41.existsSync(eventPath)) return "";
|
|
15398
15439
|
try {
|
|
15399
|
-
const event = JSON.parse(
|
|
15440
|
+
const event = JSON.parse(fs41.readFileSync(eventPath, "utf-8"));
|
|
15400
15441
|
return String(event.comment?.body ?? "");
|
|
15401
15442
|
} catch {
|
|
15402
15443
|
return "";
|
|
@@ -15450,10 +15491,10 @@ var init_loadAgentAdhoc = __esm({
|
|
|
15450
15491
|
throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
|
|
15451
15492
|
}
|
|
15452
15493
|
const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
|
|
15453
|
-
if (!
|
|
15494
|
+
if (!fs41.existsSync(agentPath)) {
|
|
15454
15495
|
throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
|
|
15455
15496
|
}
|
|
15456
|
-
const { title, body } = parseAgentFile(
|
|
15497
|
+
const { title, body } = parseAgentFile(fs41.readFileSync(agentPath, "utf-8"), agentSlug);
|
|
15457
15498
|
const message = resolveMessage(ctx.args.message);
|
|
15458
15499
|
if (!message) {
|
|
15459
15500
|
throw new Error(
|
|
@@ -15525,13 +15566,13 @@ var init_loadCapabilityState = __esm({
|
|
|
15525
15566
|
function isCompanyIntentId(value) {
|
|
15526
15567
|
return SLUG_RE2.test(value);
|
|
15527
15568
|
}
|
|
15528
|
-
function normalizeCompanyIntent(
|
|
15569
|
+
function normalizeCompanyIntent(path58, raw) {
|
|
15529
15570
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15530
|
-
throw new Error(`${
|
|
15571
|
+
throw new Error(`${path58}: intent must be JSON object`);
|
|
15531
15572
|
}
|
|
15532
15573
|
const input = raw;
|
|
15533
15574
|
const id = stringField4(input.id);
|
|
15534
|
-
if (!id || !isCompanyIntentId(id)) throw new Error(`${
|
|
15575
|
+
if (!id || !isCompanyIntentId(id)) throw new Error(`${path58}: invalid intent id`);
|
|
15535
15576
|
const createdAt = stringField4(input.createdAt) || nowIso();
|
|
15536
15577
|
const updatedAt = stringField4(input.updatedAt) || createdAt;
|
|
15537
15578
|
const description = stringField4(input.description);
|
|
@@ -15693,7 +15734,7 @@ function retryDelaysMs() {
|
|
|
15693
15734
|
}
|
|
15694
15735
|
function sleep(ms) {
|
|
15695
15736
|
if (ms <= 0) return Promise.resolve();
|
|
15696
|
-
return new Promise((
|
|
15737
|
+
return new Promise((resolve23) => setTimeout(resolve23, ms));
|
|
15697
15738
|
}
|
|
15698
15739
|
async function fetchGoalStateWithRetry(config, goalId, cwd) {
|
|
15699
15740
|
let state = await fetchGoalStateAsync(config, goalId, cwd);
|
|
@@ -15822,8 +15863,8 @@ var init_loadIssueStateComment = __esm({
|
|
|
15822
15863
|
});
|
|
15823
15864
|
|
|
15824
15865
|
// src/scripts/loadJobFromFile.ts
|
|
15825
|
-
import * as
|
|
15826
|
-
import * as
|
|
15866
|
+
import * as fs42 from "fs";
|
|
15867
|
+
import * as path39 from "path";
|
|
15827
15868
|
function parseJobFile(raw, slug) {
|
|
15828
15869
|
let stripped = raw;
|
|
15829
15870
|
if (stripped.startsWith("---\n")) {
|
|
@@ -15862,10 +15903,10 @@ var init_loadJobFromFile = __esm({
|
|
|
15862
15903
|
if (!slug) {
|
|
15863
15904
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
15864
15905
|
}
|
|
15865
|
-
const capability = resolveCapabilityFolder(slug,
|
|
15906
|
+
const capability = resolveCapabilityFolder(slug, path39.resolve(ctx.cwd, jobsDir));
|
|
15866
15907
|
if (!capability) {
|
|
15867
15908
|
throw new Error(
|
|
15868
|
-
`loadJobFromFile: capability folder not found or incomplete: ${
|
|
15909
|
+
`loadJobFromFile: capability folder not found or incomplete: ${path39.resolve(ctx.cwd, jobsDir, slug)}`
|
|
15869
15910
|
);
|
|
15870
15911
|
}
|
|
15871
15912
|
const { title, body, config } = capability;
|
|
@@ -15875,12 +15916,12 @@ var init_loadJobFromFile = __esm({
|
|
|
15875
15916
|
let agentIdentity = "";
|
|
15876
15917
|
if (agentSlug) {
|
|
15877
15918
|
const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
|
|
15878
|
-
if (!
|
|
15919
|
+
if (!fs42.existsSync(agentPath)) {
|
|
15879
15920
|
throw new Error(
|
|
15880
15921
|
`loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
|
|
15881
15922
|
);
|
|
15882
15923
|
}
|
|
15883
|
-
const agentRaw =
|
|
15924
|
+
const agentRaw = fs42.readFileSync(agentPath, "utf-8");
|
|
15884
15925
|
const parsed = parseJobFile(agentRaw, agentSlug);
|
|
15885
15926
|
agentTitle = parsed.title;
|
|
15886
15927
|
agentIdentity = parsed.body;
|
|
@@ -15960,13 +16001,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
15960
16001
|
});
|
|
15961
16002
|
|
|
15962
16003
|
// src/scripts/kodyVariables.ts
|
|
15963
|
-
import * as
|
|
15964
|
-
import * as
|
|
16004
|
+
import * as fs43 from "fs";
|
|
16005
|
+
import * as path40 from "path";
|
|
15965
16006
|
function readKodyVariables(cwd) {
|
|
15966
|
-
const full =
|
|
16007
|
+
const full = path40.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
15967
16008
|
let raw;
|
|
15968
16009
|
try {
|
|
15969
|
-
raw =
|
|
16010
|
+
raw = fs43.readFileSync(full, "utf-8");
|
|
15970
16011
|
} catch {
|
|
15971
16012
|
return {};
|
|
15972
16013
|
}
|
|
@@ -15991,8 +16032,8 @@ var init_kodyVariables = __esm({
|
|
|
15991
16032
|
});
|
|
15992
16033
|
|
|
15993
16034
|
// src/scripts/loadQaContext.ts
|
|
15994
|
-
import * as
|
|
15995
|
-
import * as
|
|
16035
|
+
import * as fs44 from "fs";
|
|
16036
|
+
import * as path41 from "path";
|
|
15996
16037
|
function parseSlugList(value) {
|
|
15997
16038
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
15998
16039
|
return inner.split(",").map(
|
|
@@ -16021,18 +16062,18 @@ function readProfileAgents(raw) {
|
|
|
16021
16062
|
return { agent: agent ?? legacy ?? ["kody"], body };
|
|
16022
16063
|
}
|
|
16023
16064
|
function readProfile(cwd) {
|
|
16024
|
-
const dir =
|
|
16025
|
-
if (!
|
|
16065
|
+
const dir = path41.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
16066
|
+
if (!fs44.existsSync(dir)) return "";
|
|
16026
16067
|
let entries;
|
|
16027
16068
|
try {
|
|
16028
|
-
entries =
|
|
16069
|
+
entries = fs44.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
16029
16070
|
} catch {
|
|
16030
16071
|
return "";
|
|
16031
16072
|
}
|
|
16032
16073
|
const blocks = [];
|
|
16033
16074
|
for (const file of entries) {
|
|
16034
16075
|
try {
|
|
16035
|
-
const raw =
|
|
16076
|
+
const raw = fs44.readFileSync(path41.join(dir, file), "utf-8");
|
|
16036
16077
|
const { agent, body } = readProfileAgents(raw);
|
|
16037
16078
|
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
16038
16079
|
blocks.push(`## ${file}
|
|
@@ -16082,9 +16123,9 @@ var init_loadQaContext = __esm({
|
|
|
16082
16123
|
|
|
16083
16124
|
// src/scripts/loadSimpleCapability.ts
|
|
16084
16125
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
16085
|
-
import * as
|
|
16126
|
+
import * as fs45 from "fs";
|
|
16086
16127
|
import * as os6 from "os";
|
|
16087
|
-
import * as
|
|
16128
|
+
import * as path42 from "path";
|
|
16088
16129
|
function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
|
|
16089
16130
|
const subagentFiles = toolFiles.flatMap((file) => {
|
|
16090
16131
|
const match = /^agents\/([a-z][a-z0-9-]{0,63})\.md$/.exec(file);
|
|
@@ -16097,7 +16138,7 @@ function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
|
|
|
16097
16138
|
profile.subagentTemplates = {
|
|
16098
16139
|
...profile.subagentTemplates ?? {},
|
|
16099
16140
|
...Object.fromEntries(
|
|
16100
|
-
subagentFiles.map(({ name, file }) => [name,
|
|
16141
|
+
subagentFiles.map(({ name, file }) => [name, fs45.readFileSync(path42.join(toolRoot, file), "utf-8")])
|
|
16101
16142
|
)
|
|
16102
16143
|
};
|
|
16103
16144
|
if (!profile.claudeCode.tools.includes("Agent")) {
|
|
@@ -16138,14 +16179,14 @@ function scalar(value) {
|
|
|
16138
16179
|
return value;
|
|
16139
16180
|
}
|
|
16140
16181
|
function listFiles(root) {
|
|
16141
|
-
if (!
|
|
16182
|
+
if (!fs45.existsSync(root)) return [];
|
|
16142
16183
|
const files = [];
|
|
16143
16184
|
const visit = (dir) => {
|
|
16144
|
-
for (const entry of
|
|
16145
|
-
const absolute =
|
|
16185
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
16186
|
+
const absolute = path42.join(dir, entry.name);
|
|
16146
16187
|
if (entry.isSymbolicLink()) continue;
|
|
16147
16188
|
if (entry.isDirectory()) visit(absolute);
|
|
16148
|
-
else if (entry.isFile()) files.push(
|
|
16189
|
+
else if (entry.isFile()) files.push(path42.relative(root, absolute));
|
|
16149
16190
|
}
|
|
16150
16191
|
};
|
|
16151
16192
|
visit(root);
|
|
@@ -16168,8 +16209,8 @@ var init_loadSimpleCapability = __esm({
|
|
|
16168
16209
|
if (!capability) {
|
|
16169
16210
|
throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
|
|
16170
16211
|
}
|
|
16171
|
-
const toolRoot =
|
|
16172
|
-
const skillRoot =
|
|
16212
|
+
const toolRoot = path42.join(capability.dir, "tools");
|
|
16213
|
+
const skillRoot = path42.join(capability.dir, "skills");
|
|
16173
16214
|
const toolFiles = listFiles(toolRoot);
|
|
16174
16215
|
const skillFiles = listFiles(skillRoot);
|
|
16175
16216
|
const parsedInput = parseInput(ctx.args.input);
|
|
@@ -16194,14 +16235,14 @@ var init_loadSimpleCapability = __esm({
|
|
|
16194
16235
|
}
|
|
16195
16236
|
if (requiredSubagents.length > 0) ctx.data.requiredSubagents = requiredSubagents;
|
|
16196
16237
|
if (capability.contract?.execution === "script") {
|
|
16197
|
-
ctx.data.capabilityScriptPath =
|
|
16238
|
+
ctx.data.capabilityScriptPath = path42.join(capability.dir, "tools", "run.sh");
|
|
16198
16239
|
ctx.data.capabilitySecretNames = capability.contract.secrets ?? [];
|
|
16199
16240
|
ctx.data.capabilityScriptTimeoutMs = capability.contract.timeoutMs;
|
|
16200
16241
|
}
|
|
16201
16242
|
if (capability.config.outputSchema) {
|
|
16202
16243
|
ctx.data.capabilityOutputSchema = capability.config.outputSchema;
|
|
16203
16244
|
}
|
|
16204
|
-
const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ?
|
|
16245
|
+
const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ? path42.join(os6.tmpdir(), `kody-capability-output-${randomUUID2()}.json`) : void 0;
|
|
16205
16246
|
if (outputPath) ctx.data.capabilityOutputPath = outputPath;
|
|
16206
16247
|
ctx.data.capabilityEnvironment = {
|
|
16207
16248
|
...capabilityInputEnvironment(input),
|
|
@@ -16224,7 +16265,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
16224
16265
|
...skillFiles.flatMap((file) => [
|
|
16225
16266
|
`### ${file}`,
|
|
16226
16267
|
"",
|
|
16227
|
-
|
|
16268
|
+
fs45.readFileSync(path42.join(skillRoot, file), "utf-8"),
|
|
16228
16269
|
""
|
|
16229
16270
|
])
|
|
16230
16271
|
] : [],
|
|
@@ -16233,7 +16274,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
16233
16274
|
"## Tools",
|
|
16234
16275
|
"",
|
|
16235
16276
|
"Inspect or run these capability-owned files when needed:",
|
|
16236
|
-
...toolFiles.map((file) => `- ${
|
|
16277
|
+
...toolFiles.map((file) => `- ${path42.join(toolRoot, file)}`)
|
|
16237
16278
|
] : [],
|
|
16238
16279
|
"",
|
|
16239
16280
|
...capability.config.outputSchema ? [
|
|
@@ -16264,8 +16305,8 @@ var init_loadSimpleCapability = __esm({
|
|
|
16264
16305
|
});
|
|
16265
16306
|
|
|
16266
16307
|
// src/taskContext.ts
|
|
16267
|
-
import * as
|
|
16268
|
-
import * as
|
|
16308
|
+
import * as fs46 from "fs";
|
|
16309
|
+
import * as path43 from "path";
|
|
16269
16310
|
function buildTaskContext(args) {
|
|
16270
16311
|
return {
|
|
16271
16312
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -16281,9 +16322,9 @@ function buildTaskContext(args) {
|
|
|
16281
16322
|
function persistTaskContext(cwd, ctx) {
|
|
16282
16323
|
try {
|
|
16283
16324
|
const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
|
|
16284
|
-
|
|
16285
|
-
const file =
|
|
16286
|
-
|
|
16325
|
+
fs46.mkdirSync(dir, { recursive: true });
|
|
16326
|
+
const file = path43.join(dir, "task-context.json");
|
|
16327
|
+
fs46.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
16287
16328
|
`);
|
|
16288
16329
|
return file;
|
|
16289
16330
|
} catch (err) {
|
|
@@ -16710,19 +16751,19 @@ function parseAgencyModelProposal(raw) {
|
|
|
16710
16751
|
function normalizeBundleFiles(bundle) {
|
|
16711
16752
|
const seen = /* @__PURE__ */ new Set();
|
|
16712
16753
|
return bundle.files.map((file, index) => {
|
|
16713
|
-
const
|
|
16714
|
-
const parts =
|
|
16715
|
-
if (!
|
|
16754
|
+
const path58 = file.path.replace(/^\/+/, "");
|
|
16755
|
+
const parts = path58.split("/");
|
|
16756
|
+
if (!path58 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
|
|
16716
16757
|
throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
|
|
16717
16758
|
}
|
|
16718
16759
|
if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
|
|
16719
|
-
|
|
16760
|
+
path58
|
|
16720
16761
|
)) {
|
|
16721
16762
|
throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
|
|
16722
16763
|
}
|
|
16723
|
-
if (seen.has(
|
|
16724
|
-
seen.add(
|
|
16725
|
-
return { path:
|
|
16764
|
+
if (seen.has(path58)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path58}`);
|
|
16765
|
+
seen.add(path58);
|
|
16766
|
+
return { path: path58, content: file.content.replace(/\r\n?/g, "\n") };
|
|
16726
16767
|
});
|
|
16727
16768
|
}
|
|
16728
16769
|
function buildProposalId(issueNumber, bundle, sourceLabel) {
|
|
@@ -17204,16 +17245,16 @@ var init_parseReproOutput = __esm({
|
|
|
17204
17245
|
});
|
|
17205
17246
|
|
|
17206
17247
|
// src/scripts/parseSimpleCapabilityOutput.ts
|
|
17207
|
-
import * as
|
|
17248
|
+
import * as fs47 from "fs";
|
|
17208
17249
|
function stringList2(value) {
|
|
17209
17250
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17210
17251
|
}
|
|
17211
17252
|
function readOutputFile(outputPath) {
|
|
17212
|
-
if (!outputPath || !
|
|
17253
|
+
if (!outputPath || !fs47.existsSync(outputPath)) return { found: false };
|
|
17213
17254
|
try {
|
|
17214
|
-
return { found: true, value: JSON.parse(
|
|
17255
|
+
return { found: true, value: JSON.parse(fs47.readFileSync(outputPath, "utf-8")) };
|
|
17215
17256
|
} finally {
|
|
17216
|
-
|
|
17257
|
+
fs47.rmSync(outputPath, { force: true });
|
|
17217
17258
|
}
|
|
17218
17259
|
}
|
|
17219
17260
|
function parseOutput(text2) {
|
|
@@ -17832,9 +17873,9 @@ var init_postResearchComment = __esm({
|
|
|
17832
17873
|
});
|
|
17833
17874
|
|
|
17834
17875
|
// src/scripts/prepareBrowserAuth.ts
|
|
17835
|
-
import * as
|
|
17876
|
+
import * as fs48 from "fs";
|
|
17836
17877
|
import * as os7 from "os";
|
|
17837
|
-
import * as
|
|
17878
|
+
import * as path44 from "path";
|
|
17838
17879
|
function appendAuthMessage(ctx, message) {
|
|
17839
17880
|
const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
|
|
17840
17881
|
ctx.data.qaAuthBlock = current ? `${current}
|
|
@@ -17873,9 +17914,9 @@ async function githubJson(url, token) {
|
|
|
17873
17914
|
return await response.json();
|
|
17874
17915
|
}
|
|
17875
17916
|
function writeKodyStorageState(input) {
|
|
17876
|
-
const directory =
|
|
17877
|
-
|
|
17878
|
-
const file =
|
|
17917
|
+
const directory = fs48.mkdtempSync(path44.join(os7.tmpdir(), "kody-browser-auth-"));
|
|
17918
|
+
fs48.chmodSync(directory, 448);
|
|
17919
|
+
const file = path44.join(directory, "storage-state.json");
|
|
17879
17920
|
const now = Date.now();
|
|
17880
17921
|
const repoEntry = {
|
|
17881
17922
|
repoUrl: input.repoUrl,
|
|
@@ -17905,7 +17946,7 @@ function writeKodyStorageState(input) {
|
|
|
17905
17946
|
}
|
|
17906
17947
|
]
|
|
17907
17948
|
};
|
|
17908
|
-
|
|
17949
|
+
fs48.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
|
|
17909
17950
|
return { directory, file };
|
|
17910
17951
|
}
|
|
17911
17952
|
function configurePlaywright(profile, storageStatePath) {
|
|
@@ -17987,7 +18028,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
17987
18028
|
configurePlaywright(profile, state.file);
|
|
17988
18029
|
const authDirectory = state.directory;
|
|
17989
18030
|
registerRuntimeCleanup(ctx, () => {
|
|
17990
|
-
|
|
18031
|
+
fs48.rmSync(authDirectory, { recursive: true, force: true });
|
|
17991
18032
|
});
|
|
17992
18033
|
appendAuthMessage(
|
|
17993
18034
|
ctx,
|
|
@@ -17995,7 +18036,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
17995
18036
|
);
|
|
17996
18037
|
return true;
|
|
17997
18038
|
} catch (error) {
|
|
17998
|
-
if (state)
|
|
18039
|
+
if (state) fs48.rmSync(state.directory, { recursive: true, force: true });
|
|
17999
18040
|
const reason = error instanceof Error ? error.message : String(error);
|
|
18000
18041
|
appendAuthMessage(
|
|
18001
18042
|
ctx,
|
|
@@ -18134,7 +18175,7 @@ var init_prepareCapabilityDelivery = __esm({
|
|
|
18134
18175
|
|
|
18135
18176
|
// src/scripts/prepareSimpleCapabilityRuntime.ts
|
|
18136
18177
|
import { isIP } from "net";
|
|
18137
|
-
import * as
|
|
18178
|
+
import * as path45 from "path";
|
|
18138
18179
|
function requirementsFrom(ctx) {
|
|
18139
18180
|
const raw = ctx.data.capabilityRequirements;
|
|
18140
18181
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
@@ -18178,7 +18219,7 @@ function browserRuntime(ctx, requirements) {
|
|
|
18178
18219
|
"--allowed-origins",
|
|
18179
18220
|
origin,
|
|
18180
18221
|
"--output-dir",
|
|
18181
|
-
|
|
18222
|
+
path45.resolve(ctx.cwd, "test-results", "quality-runs", qualityRunId)
|
|
18182
18223
|
]
|
|
18183
18224
|
};
|
|
18184
18225
|
}
|
|
@@ -18543,9 +18584,9 @@ function latestResult(raw, agentResult) {
|
|
|
18543
18584
|
function recordField4(value) {
|
|
18544
18585
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
18545
18586
|
}
|
|
18546
|
-
function resolveDotted(root,
|
|
18547
|
-
if (!
|
|
18548
|
-
return
|
|
18587
|
+
function resolveDotted(root, path58) {
|
|
18588
|
+
if (!path58) return void 0;
|
|
18589
|
+
return path58.split(".").reduce((value, key) => recordField4(value)?.[key], root);
|
|
18549
18590
|
}
|
|
18550
18591
|
function stringValue5(value) {
|
|
18551
18592
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
@@ -19387,7 +19428,7 @@ var init_previewBuildHelpers = __esm({
|
|
|
19387
19428
|
// src/scripts/previewBuildRun.ts
|
|
19388
19429
|
import { spawn as spawn5 } from "child_process";
|
|
19389
19430
|
async function runCmd(cmd, args, opts = {}) {
|
|
19390
|
-
await new Promise((
|
|
19431
|
+
await new Promise((resolve23, reject) => {
|
|
19391
19432
|
const child = spawn5(cmd, args, {
|
|
19392
19433
|
cwd: opts.cwd,
|
|
19393
19434
|
env: { ...process.env, ...opts.env ?? {} },
|
|
@@ -19399,7 +19440,7 @@ async function runCmd(cmd, args, opts = {}) {
|
|
|
19399
19440
|
}
|
|
19400
19441
|
child.on("error", reject);
|
|
19401
19442
|
child.on("close", (code) => {
|
|
19402
|
-
if (code === 0)
|
|
19443
|
+
if (code === 0) resolve23();
|
|
19403
19444
|
else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}`));
|
|
19404
19445
|
});
|
|
19405
19446
|
});
|
|
@@ -19471,12 +19512,12 @@ fi
|
|
|
19471
19512
|
|
|
19472
19513
|
// src/scripts/runPreviewBuild.ts
|
|
19473
19514
|
import { copyFile, writeFile } from "fs/promises";
|
|
19474
|
-
import * as
|
|
19515
|
+
import * as path46 from "path";
|
|
19475
19516
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19476
19517
|
function bundledDockerfilePath(mode) {
|
|
19477
|
-
const here =
|
|
19518
|
+
const here = path46.dirname(fileURLToPath2(import.meta.url));
|
|
19478
19519
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
19479
|
-
return
|
|
19520
|
+
return path46.join(here, "preview-build-templates", file);
|
|
19480
19521
|
}
|
|
19481
19522
|
function required(name) {
|
|
19482
19523
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -19711,10 +19752,10 @@ var init_runPreviewBuild = __esm({
|
|
|
19711
19752
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
19712
19753
|
if (Object.keys(buildEnv).length > 0) {
|
|
19713
19754
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
19714
|
-
await writeFile(
|
|
19755
|
+
await writeFile(path46.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
19715
19756
|
`, "utf8");
|
|
19716
19757
|
}
|
|
19717
|
-
const consumerDockerfile =
|
|
19758
|
+
const consumerDockerfile = path46.join(ctx.cwd, "Dockerfile.preview");
|
|
19718
19759
|
const { stat } = await import("fs/promises");
|
|
19719
19760
|
let hasConsumerDockerfile = false;
|
|
19720
19761
|
try {
|
|
@@ -19898,8 +19939,8 @@ var init_tickShellRunner = __esm({
|
|
|
19898
19939
|
});
|
|
19899
19940
|
|
|
19900
19941
|
// src/scripts/runScheduledImplementationTick.ts
|
|
19901
|
-
import * as
|
|
19902
|
-
import * as
|
|
19942
|
+
import * as fs49 from "fs";
|
|
19943
|
+
import * as path47 from "path";
|
|
19903
19944
|
var runScheduledImplementationTick;
|
|
19904
19945
|
var init_runScheduledImplementationTick = __esm({
|
|
19905
19946
|
"src/scripts/runScheduledImplementationTick.ts"() {
|
|
@@ -19920,14 +19961,14 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
19920
19961
|
ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
|
|
19921
19962
|
return;
|
|
19922
19963
|
}
|
|
19923
|
-
const capability = resolveCapabilityFolder(slug,
|
|
19964
|
+
const capability = resolveCapabilityFolder(slug, path47.resolve(ctx.cwd, jobsDir));
|
|
19924
19965
|
if (!capability) {
|
|
19925
19966
|
ctx.output.exitCode = 99;
|
|
19926
19967
|
ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
|
|
19927
19968
|
return;
|
|
19928
19969
|
}
|
|
19929
|
-
const shellPath =
|
|
19930
|
-
if (!
|
|
19970
|
+
const shellPath = path47.join(profile.dir, shell);
|
|
19971
|
+
if (!fs49.existsSync(shellPath)) {
|
|
19931
19972
|
ctx.output.exitCode = 99;
|
|
19932
19973
|
ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
19933
19974
|
return;
|
|
@@ -19959,13 +20000,13 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
19959
20000
|
|
|
19960
20001
|
// src/scripts/runSimpleCapabilityScript.ts
|
|
19961
20002
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
19962
|
-
import * as
|
|
20003
|
+
import * as fs50 from "fs";
|
|
19963
20004
|
function formatDuration2(timeoutMs) {
|
|
19964
20005
|
return timeoutMs % 6e4 === 0 ? `${timeoutMs / 6e4} minutes` : `${timeoutMs}ms`;
|
|
19965
20006
|
}
|
|
19966
20007
|
function isRegularFile2(filePath) {
|
|
19967
20008
|
try {
|
|
19968
|
-
const stat =
|
|
20009
|
+
const stat = fs50.lstatSync(filePath);
|
|
19969
20010
|
return stat.isFile() && !stat.isSymbolicLink();
|
|
19970
20011
|
} catch {
|
|
19971
20012
|
return false;
|
|
@@ -20044,8 +20085,8 @@ var init_runSimpleCapabilityScript = __esm({
|
|
|
20044
20085
|
});
|
|
20045
20086
|
|
|
20046
20087
|
// src/scripts/runTickScript.ts
|
|
20047
|
-
import * as
|
|
20048
|
-
import * as
|
|
20088
|
+
import * as fs51 from "fs";
|
|
20089
|
+
import * as path48 from "path";
|
|
20049
20090
|
var runTickScript;
|
|
20050
20091
|
var init_runTickScript = __esm({
|
|
20051
20092
|
"src/scripts/runTickScript.ts"() {
|
|
@@ -20065,10 +20106,10 @@ var init_runTickScript = __esm({
|
|
|
20065
20106
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
20066
20107
|
return;
|
|
20067
20108
|
}
|
|
20068
|
-
const capability = readCapabilityFolder(
|
|
20109
|
+
const capability = readCapabilityFolder(path48.resolve(ctx.cwd, jobsDir), slug);
|
|
20069
20110
|
if (!capability) {
|
|
20070
20111
|
ctx.output.exitCode = 99;
|
|
20071
|
-
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${
|
|
20112
|
+
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path48.resolve(ctx.cwd, jobsDir, slug)}`;
|
|
20072
20113
|
return;
|
|
20073
20114
|
}
|
|
20074
20115
|
const tickScript = capability.config.tickScript;
|
|
@@ -20077,8 +20118,8 @@ var init_runTickScript = __esm({
|
|
|
20077
20118
|
ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
|
|
20078
20119
|
return;
|
|
20079
20120
|
}
|
|
20080
|
-
const scriptPath =
|
|
20081
|
-
if (!
|
|
20121
|
+
const scriptPath = path48.isAbsolute(tickScript) ? tickScript : path48.join(ctx.cwd, tickScript);
|
|
20122
|
+
if (!fs51.existsSync(scriptPath)) {
|
|
20082
20123
|
ctx.output.exitCode = 99;
|
|
20083
20124
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
20084
20125
|
return;
|
|
@@ -20360,7 +20401,7 @@ var init_syncFlow = __esm({
|
|
|
20360
20401
|
});
|
|
20361
20402
|
|
|
20362
20403
|
// src/scripts/validateAgencyModelProposal.ts
|
|
20363
|
-
import * as
|
|
20404
|
+
import * as path49 from "path";
|
|
20364
20405
|
function validateModelBundle(bundle, expectedKind, options = {}) {
|
|
20365
20406
|
const failures = [];
|
|
20366
20407
|
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
|
|
@@ -20678,7 +20719,7 @@ var init_validateAgencyModelProposal = __esm({
|
|
|
20678
20719
|
const bundle = parseAgencyModelProposal(raw);
|
|
20679
20720
|
const expectedKind = readExpectedModelKind(args);
|
|
20680
20721
|
const failures = validateModelBundle(bundle, expectedKind, {
|
|
20681
|
-
capabilityRoot:
|
|
20722
|
+
capabilityRoot: path49.join(ctx.cwd, ".kody", "capabilities")
|
|
20682
20723
|
});
|
|
20683
20724
|
if (failures.length > 0) {
|
|
20684
20725
|
throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
|
|
@@ -20741,7 +20782,7 @@ function stripAnsi2(s) {
|
|
|
20741
20782
|
return s.replace(ANSI_RE2, "");
|
|
20742
20783
|
}
|
|
20743
20784
|
function runCommand2(command, cwd) {
|
|
20744
|
-
return new Promise((
|
|
20785
|
+
return new Promise((resolve23) => {
|
|
20745
20786
|
const child = spawn6(command, {
|
|
20746
20787
|
cwd,
|
|
20747
20788
|
shell: true,
|
|
@@ -20768,11 +20809,11 @@ function runCommand2(command, cwd) {
|
|
|
20768
20809
|
}, TEST_TIMEOUT_MS);
|
|
20769
20810
|
child.on("exit", (code) => {
|
|
20770
20811
|
clearTimeout(timer);
|
|
20771
|
-
|
|
20812
|
+
resolve23({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
|
|
20772
20813
|
});
|
|
20773
20814
|
child.on("error", (err) => {
|
|
20774
20815
|
clearTimeout(timer);
|
|
20775
|
-
|
|
20816
|
+
resolve23({ exitCode: -1, output: err.message });
|
|
20776
20817
|
});
|
|
20777
20818
|
});
|
|
20778
20819
|
}
|
|
@@ -21178,21 +21219,21 @@ function lineStream(stream) {
|
|
|
21178
21219
|
tryDeliver();
|
|
21179
21220
|
});
|
|
21180
21221
|
return {
|
|
21181
|
-
next: (timeoutMs) => new Promise((
|
|
21222
|
+
next: (timeoutMs) => new Promise((resolve23) => {
|
|
21182
21223
|
if (queue.length > 0) {
|
|
21183
|
-
|
|
21224
|
+
resolve23(queue.shift());
|
|
21184
21225
|
return;
|
|
21185
21226
|
}
|
|
21186
21227
|
if (ended) {
|
|
21187
|
-
|
|
21228
|
+
resolve23(null);
|
|
21188
21229
|
return;
|
|
21189
21230
|
}
|
|
21190
|
-
waiter =
|
|
21231
|
+
waiter = resolve23;
|
|
21191
21232
|
const t = setTimeout(
|
|
21192
21233
|
() => {
|
|
21193
|
-
if (waiter ===
|
|
21234
|
+
if (waiter === resolve23) {
|
|
21194
21235
|
waiter = null;
|
|
21195
|
-
|
|
21236
|
+
resolve23(null);
|
|
21196
21237
|
}
|
|
21197
21238
|
},
|
|
21198
21239
|
Math.max(0, timeoutMs)
|
|
@@ -21229,7 +21270,7 @@ var init_warmupMcp = __esm({
|
|
|
21229
21270
|
});
|
|
21230
21271
|
|
|
21231
21272
|
// src/scripts/writeAgentRunSummary.ts
|
|
21232
|
-
import * as
|
|
21273
|
+
import * as fs52 from "fs";
|
|
21233
21274
|
var writeAgentRunSummary;
|
|
21234
21275
|
var init_writeAgentRunSummary = __esm({
|
|
21235
21276
|
"src/scripts/writeAgentRunSummary.ts"() {
|
|
@@ -21255,7 +21296,7 @@ var init_writeAgentRunSummary = __esm({
|
|
|
21255
21296
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
21256
21297
|
lines.push("");
|
|
21257
21298
|
try {
|
|
21258
|
-
|
|
21299
|
+
fs52.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
21259
21300
|
`);
|
|
21260
21301
|
} catch {
|
|
21261
21302
|
}
|
|
@@ -21593,17 +21634,17 @@ var init_scripts = __esm({
|
|
|
21593
21634
|
});
|
|
21594
21635
|
|
|
21595
21636
|
// src/stateWorkspace.ts
|
|
21596
|
-
import * as
|
|
21597
|
-
import * as
|
|
21637
|
+
import * as fs53 from "fs";
|
|
21638
|
+
import * as path50 from "path";
|
|
21598
21639
|
function tenantId(config) {
|
|
21599
21640
|
const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
|
|
21600
21641
|
const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
|
|
21601
21642
|
return owner && repo ? `${owner}/${repo}` : null;
|
|
21602
21643
|
}
|
|
21603
21644
|
function writeRuntimeFile(cwd, relativePath, content) {
|
|
21604
|
-
const target =
|
|
21605
|
-
|
|
21606
|
-
|
|
21645
|
+
const target = path50.join(cwd, RUNTIME_ROOT, relativePath);
|
|
21646
|
+
fs53.mkdirSync(path50.dirname(target), { recursive: true });
|
|
21647
|
+
fs53.writeFileSync(target, content, "utf8");
|
|
21607
21648
|
}
|
|
21608
21649
|
function record(value) {
|
|
21609
21650
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -21668,11 +21709,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
|
|
|
21668
21709
|
throw new Error("Kody backend access is required for runtime workspace documents");
|
|
21669
21710
|
return;
|
|
21670
21711
|
}
|
|
21671
|
-
const key = `${
|
|
21712
|
+
const key = `${path50.resolve(cwd)}|${tenant}`;
|
|
21672
21713
|
if (hydratedWorkspaces.has(key)) return;
|
|
21673
21714
|
const backend = backendOverride ?? createStateBackendFromEnv();
|
|
21674
|
-
const root =
|
|
21675
|
-
|
|
21715
|
+
const root = path50.join(cwd, RUNTIME_ROOT);
|
|
21716
|
+
fs53.rmSync(root, { recursive: true, force: true });
|
|
21676
21717
|
await Promise.all([
|
|
21677
21718
|
hydratePrefix(backend, tenant, cwd, "context:"),
|
|
21678
21719
|
hydratePrefix(backend, tenant, cwd, "memory:"),
|
|
@@ -21688,7 +21729,7 @@ var init_stateWorkspace = __esm({
|
|
|
21688
21729
|
"src/stateWorkspace.ts"() {
|
|
21689
21730
|
"use strict";
|
|
21690
21731
|
init_state_backend();
|
|
21691
|
-
RUNTIME_ROOT =
|
|
21732
|
+
RUNTIME_ROOT = path50.join(".kody-engine", "runtime");
|
|
21692
21733
|
hydratedWorkspaces = /* @__PURE__ */ new Set();
|
|
21693
21734
|
}
|
|
21694
21735
|
});
|
|
@@ -21759,9 +21800,9 @@ var init_tools = __esm({
|
|
|
21759
21800
|
|
|
21760
21801
|
// src/executor.ts
|
|
21761
21802
|
import { spawn as spawn8 } from "child_process";
|
|
21762
|
-
import * as
|
|
21803
|
+
import * as fs54 from "fs";
|
|
21763
21804
|
import * as os8 from "os";
|
|
21764
|
-
import * as
|
|
21805
|
+
import * as path51 from "path";
|
|
21765
21806
|
function isMutatingPostflight(scriptName) {
|
|
21766
21807
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
21767
21808
|
}
|
|
@@ -22013,7 +22054,7 @@ async function runImplementation(profileName, input) {
|
|
|
22013
22054
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
22014
22055
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
22015
22056
|
const invokeAgent = async (prompt) => {
|
|
22016
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
22057
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path51.isAbsolute(p) ? p : path51.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
22017
22058
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
22018
22059
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
22019
22060
|
const agents = loadSubagents(profile);
|
|
@@ -22493,17 +22534,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
22493
22534
|
function resolveProfilePath(profileName, cwd = process.cwd()) {
|
|
22494
22535
|
const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
|
|
22495
22536
|
if (found) return found;
|
|
22496
|
-
const here =
|
|
22537
|
+
const here = path51.dirname(new URL(import.meta.url).pathname);
|
|
22497
22538
|
const candidates = [
|
|
22498
|
-
|
|
22539
|
+
path51.join(here, "implementations", profileName, "profile.json"),
|
|
22499
22540
|
// same-dir sibling (dev)
|
|
22500
|
-
|
|
22541
|
+
path51.join(here, "..", "implementations", profileName, "profile.json"),
|
|
22501
22542
|
// up one (prod: dist/bin → dist/implementations)
|
|
22502
|
-
|
|
22543
|
+
path51.join(here, "..", "src", "implementations", profileName, "profile.json")
|
|
22503
22544
|
// fallback
|
|
22504
22545
|
];
|
|
22505
22546
|
for (const c of candidates) {
|
|
22506
|
-
if (
|
|
22547
|
+
if (fs54.existsSync(c)) return c;
|
|
22507
22548
|
}
|
|
22508
22549
|
return candidates[0];
|
|
22509
22550
|
}
|
|
@@ -22618,15 +22659,15 @@ function resolveShellTimeoutMs(entry) {
|
|
|
22618
22659
|
}
|
|
22619
22660
|
async function runShellEntry(entry, ctx, profile) {
|
|
22620
22661
|
const shellName = entry.shell;
|
|
22621
|
-
const shellPath =
|
|
22622
|
-
if (!
|
|
22662
|
+
const shellPath = path51.join(profile.dir, shellName);
|
|
22663
|
+
if (!fs54.existsSync(shellPath)) {
|
|
22623
22664
|
ctx.skipAgent = true;
|
|
22624
22665
|
ctx.output.exitCode = 99;
|
|
22625
22666
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
22626
22667
|
return;
|
|
22627
22668
|
}
|
|
22628
22669
|
const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
|
|
22629
|
-
const outputFile =
|
|
22670
|
+
const outputFile = path51.join(
|
|
22630
22671
|
os8.tmpdir(),
|
|
22631
22672
|
`kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
22632
22673
|
);
|
|
@@ -22656,14 +22697,14 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
22656
22697
|
let killTimer;
|
|
22657
22698
|
let escalateTimer;
|
|
22658
22699
|
const result = await new Promise(
|
|
22659
|
-
(
|
|
22700
|
+
(resolve23) => {
|
|
22660
22701
|
let settled = false;
|
|
22661
22702
|
const settle = (code, signal, spawnErr) => {
|
|
22662
22703
|
if (settled) return;
|
|
22663
22704
|
settled = true;
|
|
22664
22705
|
if (killTimer) clearTimeout(killTimer);
|
|
22665
22706
|
if (escalateTimer) clearTimeout(escalateTimer);
|
|
22666
|
-
|
|
22707
|
+
resolve23({ code, signal, spawnErr });
|
|
22667
22708
|
};
|
|
22668
22709
|
child.on("error", (err) => settle(null, null, err));
|
|
22669
22710
|
child.on("close", (code, signal) => settle(code, signal));
|
|
@@ -22693,9 +22734,9 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
22693
22734
|
}
|
|
22694
22735
|
let sideChannelText = "";
|
|
22695
22736
|
try {
|
|
22696
|
-
if (
|
|
22697
|
-
sideChannelText =
|
|
22698
|
-
|
|
22737
|
+
if (fs54.existsSync(outputFile)) {
|
|
22738
|
+
sideChannelText = fs54.readFileSync(outputFile, "utf-8");
|
|
22739
|
+
fs54.rmSync(outputFile, { force: true });
|
|
22699
22740
|
}
|
|
22700
22741
|
} catch {
|
|
22701
22742
|
}
|
|
@@ -23545,25 +23586,31 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
|
|
|
23545
23586
|
|
|
23546
23587
|
`
|
|
23547
23588
|
);
|
|
23548
|
-
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
...
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
|
|
23558
|
-
|
|
23559
|
-
|
|
23560
|
-
|
|
23561
|
-
|
|
23562
|
-
|
|
23563
|
-
|
|
23564
|
-
|
|
23565
|
-
|
|
23566
|
-
|
|
23589
|
+
const stepAbort = workflowStepAbortController(base.abortController, step.timeoutSeconds);
|
|
23590
|
+
try {
|
|
23591
|
+
result = await runJob(child, {
|
|
23592
|
+
...base,
|
|
23593
|
+
abortController: stepAbort.controller,
|
|
23594
|
+
preloadedData: {
|
|
23595
|
+
...chainData,
|
|
23596
|
+
runSubjectType: "capability",
|
|
23597
|
+
runSubjectId: step.capability,
|
|
23598
|
+
runSubjectLabel: step.id,
|
|
23599
|
+
workflowStep: step.id,
|
|
23600
|
+
workflowStepIndex: index + 1,
|
|
23601
|
+
workflowExecutionKey: graphWorkflowExecutionKey(
|
|
23602
|
+
base.preloadedData?.workflowExecutionKey,
|
|
23603
|
+
capability.slug,
|
|
23604
|
+
step.id,
|
|
23605
|
+
state.transitionCounts
|
|
23606
|
+
),
|
|
23607
|
+
workflowStepReason: step.reason,
|
|
23608
|
+
workflowContinueOn: step.continueOn ?? []
|
|
23609
|
+
}
|
|
23610
|
+
});
|
|
23611
|
+
} finally {
|
|
23612
|
+
stepAbort.cleanup();
|
|
23613
|
+
}
|
|
23567
23614
|
finishWorkflowStep(state, step, result);
|
|
23568
23615
|
mergeWorkflowResults(state, result.capabilityResults);
|
|
23569
23616
|
if (result.capabilityOutput && typeof result.capabilityOutput === "object" && !Array.isArray(result.capabilityOutput)) {
|
|
@@ -23699,11 +23746,11 @@ function exhaustedWorkflowTransitions(step, data, counts) {
|
|
|
23699
23746
|
}
|
|
23700
23747
|
function workflowResultConditionPaths(transitions) {
|
|
23701
23748
|
return transitions.flatMap(
|
|
23702
|
-
(transition) => Object.keys(transition.when ?? {}).filter((
|
|
23749
|
+
(transition) => Object.keys(transition.when ?? {}).filter((path58) => path58.startsWith("result."))
|
|
23703
23750
|
);
|
|
23704
23751
|
}
|
|
23705
23752
|
function conditionMatches(condition, context) {
|
|
23706
|
-
return Object.entries(condition).every(([
|
|
23753
|
+
return Object.entries(condition).every(([path58, expected]) => valueMatches(resolveDottedPath2(context, path58), expected));
|
|
23707
23754
|
}
|
|
23708
23755
|
function withWorkflowBoundaryEval(capability, result) {
|
|
23709
23756
|
const capabilityKind = capability.config.capabilityKind;
|
|
@@ -23871,6 +23918,24 @@ function canContinueWorkflow(step, outcome) {
|
|
|
23871
23918
|
if (!outcome || !step.continueOn || step.continueOn.length === 0) return false;
|
|
23872
23919
|
return step.continueOn.includes(outcome.type);
|
|
23873
23920
|
}
|
|
23921
|
+
function workflowStepAbortController(parent, timeoutSeconds) {
|
|
23922
|
+
if (!timeoutSeconds) return { controller: parent, cleanup: () => void 0 };
|
|
23923
|
+
const controller = new AbortController();
|
|
23924
|
+
const forwardParentAbort = () => controller.abort(parent?.signal.reason);
|
|
23925
|
+
if (parent?.signal.aborted) forwardParentAbort();
|
|
23926
|
+
else parent?.signal.addEventListener("abort", forwardParentAbort, { once: true });
|
|
23927
|
+
const timer = setTimeout(() => {
|
|
23928
|
+
controller.abort(new Error(`workflow step timed out after ${timeoutSeconds}s`));
|
|
23929
|
+
}, timeoutSeconds * 1e3);
|
|
23930
|
+
timer.unref?.();
|
|
23931
|
+
return {
|
|
23932
|
+
controller,
|
|
23933
|
+
cleanup: () => {
|
|
23934
|
+
clearTimeout(timer);
|
|
23935
|
+
parent?.signal.removeEventListener("abort", forwardParentAbort);
|
|
23936
|
+
}
|
|
23937
|
+
};
|
|
23938
|
+
}
|
|
23874
23939
|
function workflowOutcome(result) {
|
|
23875
23940
|
return result.taskState?.core.lastOutcome ?? null;
|
|
23876
23941
|
}
|
|
@@ -24133,7 +24198,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
24133
24198
|
|
|
24134
24199
|
// src/servers/brain-serve.ts
|
|
24135
24200
|
import { createServer as createServer2 } from "http";
|
|
24136
|
-
import * as
|
|
24201
|
+
import * as path54 from "path";
|
|
24137
24202
|
|
|
24138
24203
|
// src/chat/loop.ts
|
|
24139
24204
|
init_agent();
|
|
@@ -24141,13 +24206,13 @@ init_agents();
|
|
|
24141
24206
|
init_config();
|
|
24142
24207
|
init_registry();
|
|
24143
24208
|
init_task_artifacts();
|
|
24144
|
-
import * as
|
|
24145
|
-
import * as
|
|
24209
|
+
import * as fs18 from "fs";
|
|
24210
|
+
import * as path19 from "path";
|
|
24146
24211
|
|
|
24147
24212
|
// src/chat/attachments.ts
|
|
24148
24213
|
init_runtimePaths();
|
|
24149
|
-
import * as
|
|
24150
|
-
import * as
|
|
24214
|
+
import * as fs15 from "fs";
|
|
24215
|
+
import * as path16 from "path";
|
|
24151
24216
|
var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
24152
24217
|
var EXT_BY_MIME = {
|
|
24153
24218
|
"image/png": "png",
|
|
@@ -24180,11 +24245,11 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
24180
24245
|
if (!isImage) return `[File: ${name}]`;
|
|
24181
24246
|
try {
|
|
24182
24247
|
if (!dirEnsured) {
|
|
24183
|
-
|
|
24248
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
24184
24249
|
dirEnsured = true;
|
|
24185
24250
|
}
|
|
24186
|
-
const filePath =
|
|
24187
|
-
|
|
24251
|
+
const filePath = path16.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
24252
|
+
fs15.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
24188
24253
|
imageCounter += 1;
|
|
24189
24254
|
imagePaths.push(filePath);
|
|
24190
24255
|
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
|
|
@@ -24201,8 +24266,8 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
24201
24266
|
|
|
24202
24267
|
// src/chat/codex-app-server.ts
|
|
24203
24268
|
import { spawn as spawn3 } from "child_process";
|
|
24204
|
-
import * as
|
|
24205
|
-
import * as
|
|
24269
|
+
import * as fs16 from "fs";
|
|
24270
|
+
import * as path17 from "path";
|
|
24206
24271
|
import { createInterface } from "readline";
|
|
24207
24272
|
function codexThreadStartParams(args) {
|
|
24208
24273
|
return {
|
|
@@ -24287,9 +24352,9 @@ var CodexAppServerClient = class {
|
|
|
24287
24352
|
await this.request("thread/resume", { threadId });
|
|
24288
24353
|
}
|
|
24289
24354
|
async runTurn(args) {
|
|
24290
|
-
await new Promise((
|
|
24355
|
+
await new Promise((resolve23, reject) => {
|
|
24291
24356
|
this.process.turnWaiters.set(args.threadId, {
|
|
24292
|
-
resolve:
|
|
24357
|
+
resolve: resolve23,
|
|
24293
24358
|
reject,
|
|
24294
24359
|
onNotification: args.onNotification,
|
|
24295
24360
|
queue: Promise.resolve()
|
|
@@ -24306,8 +24371,8 @@ var CodexAppServerClient = class {
|
|
|
24306
24371
|
}
|
|
24307
24372
|
request(method, params) {
|
|
24308
24373
|
const id = this.process.nextId++;
|
|
24309
|
-
return new Promise((
|
|
24310
|
-
this.process.pending.set(id, { resolve:
|
|
24374
|
+
return new Promise((resolve23, reject) => {
|
|
24375
|
+
this.process.pending.set(id, { resolve: resolve23, reject });
|
|
24311
24376
|
this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
|
|
24312
24377
|
`);
|
|
24313
24378
|
});
|
|
@@ -24373,11 +24438,11 @@ var CodexAppServerClient = class {
|
|
|
24373
24438
|
};
|
|
24374
24439
|
var clients = /* @__PURE__ */ new Map();
|
|
24375
24440
|
function threadMapPath(cwd) {
|
|
24376
|
-
return
|
|
24441
|
+
return path17.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
|
|
24377
24442
|
}
|
|
24378
24443
|
function readThreadMap(cwd) {
|
|
24379
24444
|
try {
|
|
24380
|
-
const value = JSON.parse(
|
|
24445
|
+
const value = JSON.parse(fs16.readFileSync(threadMapPath(cwd), "utf8"));
|
|
24381
24446
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
24382
24447
|
return Object.fromEntries(
|
|
24383
24448
|
Object.entries(value).filter(
|
|
@@ -24390,8 +24455,8 @@ function readThreadMap(cwd) {
|
|
|
24390
24455
|
}
|
|
24391
24456
|
function writeThreadMap(cwd, map) {
|
|
24392
24457
|
const file = threadMapPath(cwd);
|
|
24393
|
-
|
|
24394
|
-
|
|
24458
|
+
fs16.mkdirSync(path17.dirname(file), { recursive: true });
|
|
24459
|
+
fs16.writeFileSync(file, `${JSON.stringify(map, null, 2)}
|
|
24395
24460
|
`);
|
|
24396
24461
|
}
|
|
24397
24462
|
async function runCodexChatTurn(args) {
|
|
@@ -24481,8 +24546,8 @@ async function runCodexChatTurn(args) {
|
|
|
24481
24546
|
}
|
|
24482
24547
|
|
|
24483
24548
|
// src/chat/events.ts
|
|
24484
|
-
import * as
|
|
24485
|
-
import * as
|
|
24549
|
+
import * as fs17 from "fs";
|
|
24550
|
+
import * as path18 from "path";
|
|
24486
24551
|
import posixPath2 from "path/posix";
|
|
24487
24552
|
var BackendEventSink = class {
|
|
24488
24553
|
constructor(append, tenantId2, sessionId) {
|
|
@@ -24498,7 +24563,7 @@ var BackendEventSink = class {
|
|
|
24498
24563
|
}
|
|
24499
24564
|
};
|
|
24500
24565
|
function eventsFilePath(cwd, sessionId) {
|
|
24501
|
-
return
|
|
24566
|
+
return path18.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
|
|
24502
24567
|
}
|
|
24503
24568
|
var FileSink = class {
|
|
24504
24569
|
constructor(file) {
|
|
@@ -24506,8 +24571,8 @@ var FileSink = class {
|
|
|
24506
24571
|
}
|
|
24507
24572
|
file;
|
|
24508
24573
|
async emit(event) {
|
|
24509
|
-
|
|
24510
|
-
|
|
24574
|
+
fs17.mkdirSync(path18.dirname(this.file), { recursive: true });
|
|
24575
|
+
fs17.appendFileSync(this.file, `${JSON.stringify(event)}
|
|
24511
24576
|
`);
|
|
24512
24577
|
}
|
|
24513
24578
|
};
|
|
@@ -24772,7 +24837,7 @@ function buildImplementationCatalog() {
|
|
|
24772
24837
|
const entries = [];
|
|
24773
24838
|
for (const { name, profilePath } of discovered) {
|
|
24774
24839
|
try {
|
|
24775
|
-
const raw = JSON.parse(
|
|
24840
|
+
const raw = JSON.parse(fs18.readFileSync(profilePath, "utf-8"));
|
|
24776
24841
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
24777
24842
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
24778
24843
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -24894,7 +24959,7 @@ async function runChatTurn(opts) {
|
|
|
24894
24959
|
quiet: opts.quiet,
|
|
24895
24960
|
additionalDirectories: [
|
|
24896
24961
|
taskArtifactsPaths.absDir,
|
|
24897
|
-
...Array.from(new Set(imagePaths.map((p2) =>
|
|
24962
|
+
...Array.from(new Set(imagePaths.map((p2) => path19.dirname(p2))))
|
|
24898
24963
|
],
|
|
24899
24964
|
systemPromptAppend: systemPrompt,
|
|
24900
24965
|
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
|
|
@@ -25082,10 +25147,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
25082
25147
|
var MEMORY_INDEX_REL = ".kody-engine/runtime/memory/INDEX.md";
|
|
25083
25148
|
var MAX_INDEX_BYTES = 8e3;
|
|
25084
25149
|
function readMemoryIndexBlock(cwd) {
|
|
25085
|
-
const indexPath =
|
|
25150
|
+
const indexPath = path19.join(cwd, MEMORY_INDEX_REL);
|
|
25086
25151
|
let raw;
|
|
25087
25152
|
try {
|
|
25088
|
-
raw =
|
|
25153
|
+
raw = fs18.readFileSync(indexPath, "utf-8");
|
|
25089
25154
|
} catch {
|
|
25090
25155
|
return "";
|
|
25091
25156
|
}
|
|
@@ -25105,17 +25170,17 @@ _\u2026 (memory index truncated; use recall_search to read more)_` : trimmed;
|
|
|
25105
25170
|
var CONTEXT_DIR_REL = ".kody-engine/runtime/context";
|
|
25106
25171
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
25107
25172
|
function readContextBlock(cwd) {
|
|
25108
|
-
const dir =
|
|
25173
|
+
const dir = path19.join(cwd, CONTEXT_DIR_REL);
|
|
25109
25174
|
let files;
|
|
25110
25175
|
try {
|
|
25111
|
-
files =
|
|
25176
|
+
files = fs18.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
25112
25177
|
} catch {
|
|
25113
25178
|
return "";
|
|
25114
25179
|
}
|
|
25115
25180
|
const sections = [];
|
|
25116
25181
|
for (const file of files) {
|
|
25117
25182
|
try {
|
|
25118
|
-
const content =
|
|
25183
|
+
const content = fs18.readFileSync(path19.join(dir, file), "utf-8").trim();
|
|
25119
25184
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
25120
25185
|
|
|
25121
25186
|
${content}`);
|
|
@@ -25141,7 +25206,7 @@ var SYSTEM_PROMPT_OVERRIDE_REL = ".kody-engine/runtime/system-prompt.md";
|
|
|
25141
25206
|
function readSystemPromptOverride(cwd) {
|
|
25142
25207
|
let raw;
|
|
25143
25208
|
try {
|
|
25144
|
-
raw =
|
|
25209
|
+
raw = fs18.readFileSync(path19.join(cwd, SYSTEM_PROMPT_OVERRIDE_REL), "utf-8");
|
|
25145
25210
|
} catch {
|
|
25146
25211
|
return null;
|
|
25147
25212
|
}
|
|
@@ -25149,10 +25214,10 @@ function readSystemPromptOverride(cwd) {
|
|
|
25149
25214
|
return trimmed.length > 0 ? trimmed : null;
|
|
25150
25215
|
}
|
|
25151
25216
|
function readInstructionsBlock(cwd) {
|
|
25152
|
-
const instructionsPath =
|
|
25217
|
+
const instructionsPath = path19.join(cwd, INSTRUCTIONS_REL);
|
|
25153
25218
|
let raw;
|
|
25154
25219
|
try {
|
|
25155
|
-
raw =
|
|
25220
|
+
raw = fs18.readFileSync(instructionsPath, "utf-8");
|
|
25156
25221
|
} catch {
|
|
25157
25222
|
return "";
|
|
25158
25223
|
}
|
|
@@ -25186,15 +25251,15 @@ function resolveBrainDriver(runtime) {
|
|
|
25186
25251
|
}
|
|
25187
25252
|
|
|
25188
25253
|
// src/chat/session.ts
|
|
25189
|
-
import * as
|
|
25190
|
-
import * as
|
|
25254
|
+
import * as fs19 from "fs";
|
|
25255
|
+
import * as path20 from "path";
|
|
25191
25256
|
import posixPath3 from "path/posix";
|
|
25192
25257
|
function sessionFilePath(cwd, sessionId) {
|
|
25193
|
-
return
|
|
25258
|
+
return path20.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
|
|
25194
25259
|
}
|
|
25195
25260
|
function readSession(file) {
|
|
25196
|
-
if (!
|
|
25197
|
-
const raw =
|
|
25261
|
+
if (!fs19.existsSync(file)) return [];
|
|
25262
|
+
const raw = fs19.readFileSync(file, "utf-8").trim();
|
|
25198
25263
|
if (!raw) return [];
|
|
25199
25264
|
const turns = [];
|
|
25200
25265
|
for (const line of raw.split("\n")) {
|
|
@@ -25217,8 +25282,8 @@ init_config();
|
|
|
25217
25282
|
init_state_backend();
|
|
25218
25283
|
init_workflowDefinitions();
|
|
25219
25284
|
import { createHash as createHash2 } from "crypto";
|
|
25220
|
-
import * as
|
|
25221
|
-
import * as
|
|
25285
|
+
import * as fs21 from "fs";
|
|
25286
|
+
import * as path22 from "path";
|
|
25222
25287
|
var SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,127}$/;
|
|
25223
25288
|
var REPOSITORY_OWNED_NAMESPACES = ["loops"];
|
|
25224
25289
|
function assertSafeDefinitionPath(filePath) {
|
|
@@ -25250,9 +25315,9 @@ function verifyDefinition(definition) {
|
|
|
25250
25315
|
}
|
|
25251
25316
|
function writeBundle(root, bundle) {
|
|
25252
25317
|
for (const [filePath, contents] of Object.entries(bundle.files)) {
|
|
25253
|
-
const target =
|
|
25254
|
-
|
|
25255
|
-
|
|
25318
|
+
const target = path22.join(root, filePath);
|
|
25319
|
+
fs21.mkdirSync(path22.dirname(target), { recursive: true });
|
|
25320
|
+
fs21.writeFileSync(target, contents, "utf8");
|
|
25256
25321
|
}
|
|
25257
25322
|
}
|
|
25258
25323
|
function writeDefinition(root, kind, definition) {
|
|
@@ -25260,22 +25325,22 @@ function writeDefinition(root, kind, definition) {
|
|
|
25260
25325
|
if (kind === "agent") {
|
|
25261
25326
|
const raw = bundle.files["agent.md"];
|
|
25262
25327
|
if (typeof raw !== "string") throw new Error(`agent definition ${definition.slug} is missing agent.md`);
|
|
25263
|
-
|
|
25328
|
+
fs21.writeFileSync(path22.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
|
|
25264
25329
|
return;
|
|
25265
25330
|
}
|
|
25266
25331
|
if (kind === "goal") {
|
|
25267
|
-
writeBundle(
|
|
25332
|
+
writeBundle(path22.join(root, "goals", definition.slug), bundle);
|
|
25268
25333
|
return;
|
|
25269
25334
|
}
|
|
25270
25335
|
if (kind === "implementation") {
|
|
25271
|
-
writeBundle(
|
|
25336
|
+
writeBundle(path22.join(root, "implementations", definition.slug), bundle);
|
|
25272
25337
|
return;
|
|
25273
25338
|
}
|
|
25274
25339
|
if (kind === "asset") {
|
|
25275
|
-
writeBundle(
|
|
25340
|
+
writeBundle(path22.join(root, "shared"), bundle);
|
|
25276
25341
|
return;
|
|
25277
25342
|
}
|
|
25278
|
-
writeBundle(
|
|
25343
|
+
writeBundle(path22.join(root, "capabilities", definition.slug), bundle);
|
|
25279
25344
|
}
|
|
25280
25345
|
function writeWorkflow(root, document) {
|
|
25281
25346
|
const workflow = normalizeWorkflowDefinition(document.definition);
|
|
@@ -25283,28 +25348,28 @@ function writeWorkflow(root, document) {
|
|
|
25283
25348
|
const contents = `${JSON.stringify(workflow, null, 2)}
|
|
25284
25349
|
`;
|
|
25285
25350
|
const bundle = { schemaVersion: 1, files: { "workflow.json": contents } };
|
|
25286
|
-
const target =
|
|
25287
|
-
|
|
25288
|
-
|
|
25351
|
+
const target = path22.join(root, workflowDefinitionPath(document.workflowId));
|
|
25352
|
+
fs21.mkdirSync(path22.dirname(target), { recursive: true });
|
|
25353
|
+
fs21.writeFileSync(target, contents, "utf8");
|
|
25289
25354
|
return definitionVersion(bundle);
|
|
25290
25355
|
}
|
|
25291
25356
|
function preserveRepositoryDefinitions(root, staging) {
|
|
25292
25357
|
for (const namespace of REPOSITORY_OWNED_NAMESPACES) {
|
|
25293
|
-
const source =
|
|
25294
|
-
if (!
|
|
25295
|
-
|
|
25358
|
+
const source = path22.join(root, namespace);
|
|
25359
|
+
if (!fs21.existsSync(source)) continue;
|
|
25360
|
+
fs21.cpSync(source, path22.join(staging, namespace), { recursive: true });
|
|
25296
25361
|
}
|
|
25297
25362
|
}
|
|
25298
25363
|
async function hydrateDefinitions(options) {
|
|
25299
|
-
const root =
|
|
25364
|
+
const root = path22.join(options.cwd, ".kody-engine", "definitions");
|
|
25300
25365
|
const staging = `${root}.tmp-${process.pid}-${Date.now()}`;
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25366
|
+
fs21.rmSync(staging, { recursive: true, force: true });
|
|
25367
|
+
fs21.mkdirSync(path22.join(staging, "agents"), { recursive: true });
|
|
25368
|
+
fs21.mkdirSync(path22.join(staging, "capabilities"), { recursive: true });
|
|
25369
|
+
fs21.mkdirSync(path22.join(staging, "goals"), { recursive: true });
|
|
25370
|
+
fs21.mkdirSync(path22.join(staging, "implementations"), { recursive: true });
|
|
25371
|
+
fs21.mkdirSync(path22.join(staging, "shared"), { recursive: true });
|
|
25372
|
+
fs21.mkdirSync(path22.join(staging, "workflows"), { recursive: true });
|
|
25308
25373
|
try {
|
|
25309
25374
|
const [capabilities, agents, goals, implementations, assets, workflows] = await Promise.all([
|
|
25310
25375
|
options.backend.listDefinitions(options.tenantId, "capability"),
|
|
@@ -25345,13 +25410,13 @@ async function hydrateDefinitions(options) {
|
|
|
25345
25410
|
hydratedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25346
25411
|
versions: Object.fromEntries(Object.entries(versions).sort(([left], [right]) => left.localeCompare(right)))
|
|
25347
25412
|
};
|
|
25348
|
-
|
|
25413
|
+
fs21.writeFileSync(path22.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
25349
25414
|
`, "utf8");
|
|
25350
|
-
|
|
25351
|
-
|
|
25415
|
+
fs21.rmSync(root, { recursive: true, force: true });
|
|
25416
|
+
fs21.renameSync(staging, root);
|
|
25352
25417
|
return { root, tenantId: options.tenantId, versions: manifest.versions };
|
|
25353
25418
|
} catch (error) {
|
|
25354
|
-
|
|
25419
|
+
fs21.rmSync(staging, { recursive: true, force: true });
|
|
25355
25420
|
throw error;
|
|
25356
25421
|
}
|
|
25357
25422
|
}
|
|
@@ -25373,8 +25438,8 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
|
|
|
25373
25438
|
|
|
25374
25439
|
// src/kody-cli.ts
|
|
25375
25440
|
import { execFileSync as execFileSync24 } from "child_process";
|
|
25376
|
-
import * as
|
|
25377
|
-
import * as
|
|
25441
|
+
import * as fs55 from "fs";
|
|
25442
|
+
import * as path52 from "path";
|
|
25378
25443
|
|
|
25379
25444
|
// src/app-auth.ts
|
|
25380
25445
|
import { createSign } from "crypto";
|
|
@@ -25503,7 +25568,7 @@ init_definition_paths();
|
|
|
25503
25568
|
|
|
25504
25569
|
// src/dispatch.ts
|
|
25505
25570
|
init_config();
|
|
25506
|
-
import * as
|
|
25571
|
+
import * as fs22 from "fs";
|
|
25507
25572
|
|
|
25508
25573
|
// src/cron-match.ts
|
|
25509
25574
|
var FIELD_BOUNDS = [
|
|
@@ -25610,10 +25675,10 @@ function autoDispatch(opts) {
|
|
|
25610
25675
|
}
|
|
25611
25676
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
25612
25677
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
25613
|
-
if (!eventName || !eventPath || !
|
|
25678
|
+
if (!eventName || !eventPath || !fs22.existsSync(eventPath)) return null;
|
|
25614
25679
|
let event = {};
|
|
25615
25680
|
try {
|
|
25616
|
-
event = JSON.parse(
|
|
25681
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
25617
25682
|
} catch {
|
|
25618
25683
|
return null;
|
|
25619
25684
|
}
|
|
@@ -25737,7 +25802,7 @@ function autoDispatchTyped(opts) {
|
|
|
25737
25802
|
if (legacy) return { kind: "route", ...legacy };
|
|
25738
25803
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
25739
25804
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
25740
|
-
if (!eventName || !eventPath || !
|
|
25805
|
+
if (!eventName || !eventPath || !fs22.existsSync(eventPath)) {
|
|
25741
25806
|
return { kind: "silent", reason: "no GHA event context" };
|
|
25742
25807
|
}
|
|
25743
25808
|
if (eventName !== "issue_comment") {
|
|
@@ -25745,7 +25810,7 @@ function autoDispatchTyped(opts) {
|
|
|
25745
25810
|
}
|
|
25746
25811
|
let event = {};
|
|
25747
25812
|
try {
|
|
25748
|
-
event = JSON.parse(
|
|
25813
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
25749
25814
|
} catch {
|
|
25750
25815
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
25751
25816
|
}
|
|
@@ -25799,7 +25864,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
25799
25864
|
for (const exe of listRuntimeProfilesForCwd(opts?.cwd ?? process.cwd())) {
|
|
25800
25865
|
let raw;
|
|
25801
25866
|
try {
|
|
25802
|
-
raw =
|
|
25867
|
+
raw = fs22.readFileSync(exe.profilePath, "utf-8");
|
|
25803
25868
|
} catch {
|
|
25804
25869
|
continue;
|
|
25805
25870
|
}
|
|
@@ -26176,9 +26241,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
26176
26241
|
return void 0;
|
|
26177
26242
|
}
|
|
26178
26243
|
function detectPackageManager2(cwd) {
|
|
26179
|
-
if (
|
|
26180
|
-
if (
|
|
26181
|
-
if (
|
|
26244
|
+
if (fs55.existsSync(path52.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
26245
|
+
if (fs55.existsSync(path52.join(cwd, "yarn.lock"))) return "yarn";
|
|
26246
|
+
if (fs55.existsSync(path52.join(cwd, "bun.lockb"))) return "bun";
|
|
26182
26247
|
return "npm";
|
|
26183
26248
|
}
|
|
26184
26249
|
function shouldChainScheduledWatch(match) {
|
|
@@ -26281,8 +26346,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
26281
26346
|
const logPath = lastRunLogPath(cwd);
|
|
26282
26347
|
let tail = "";
|
|
26283
26348
|
try {
|
|
26284
|
-
if (
|
|
26285
|
-
const content =
|
|
26349
|
+
if (fs55.existsSync(logPath)) {
|
|
26350
|
+
const content = fs55.readFileSync(logPath, "utf-8");
|
|
26286
26351
|
tail = content.slice(-3e3);
|
|
26287
26352
|
}
|
|
26288
26353
|
} catch {
|
|
@@ -26311,7 +26376,7 @@ async function runCi(argv) {
|
|
|
26311
26376
|
return 0;
|
|
26312
26377
|
}
|
|
26313
26378
|
const args = parseCiArgs(argv);
|
|
26314
|
-
const cwd = args.cwd ?
|
|
26379
|
+
const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
|
|
26315
26380
|
try {
|
|
26316
26381
|
const n = unpackAllSecrets();
|
|
26317
26382
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -26377,9 +26442,9 @@ async function runCi(argv) {
|
|
|
26377
26442
|
forceRunCliArgs = { goal: envForceMessage };
|
|
26378
26443
|
}
|
|
26379
26444
|
}
|
|
26380
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
26445
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs55.existsSync(dispatchEventPath)) {
|
|
26381
26446
|
try {
|
|
26382
|
-
const evt = JSON.parse(
|
|
26447
|
+
const evt = JSON.parse(fs55.readFileSync(dispatchEventPath, "utf-8"));
|
|
26383
26448
|
const inputs = objectValue2(evt.inputs);
|
|
26384
26449
|
const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
|
|
26385
26450
|
const sessionInput = String(inputs?.sessionId ?? "");
|
|
@@ -26794,8 +26859,8 @@ init_repoWorkspace();
|
|
|
26794
26859
|
|
|
26795
26860
|
// src/scripts/brainTurnLog.ts
|
|
26796
26861
|
init_runtimePaths();
|
|
26797
|
-
import * as
|
|
26798
|
-
import * as
|
|
26862
|
+
import * as fs56 from "fs";
|
|
26863
|
+
import * as path53 from "path";
|
|
26799
26864
|
import posixPath4 from "path/posix";
|
|
26800
26865
|
var live = /* @__PURE__ */ new Map();
|
|
26801
26866
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -26803,8 +26868,8 @@ function brainEventsFilePath(dir, chatId) {
|
|
|
26803
26868
|
}
|
|
26804
26869
|
function lastPersistedSeq(dir, chatId) {
|
|
26805
26870
|
const p = brainEventsFilePath(dir, chatId);
|
|
26806
|
-
if (!
|
|
26807
|
-
const lines =
|
|
26871
|
+
if (!fs56.existsSync(p)) return 0;
|
|
26872
|
+
const lines = fs56.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
26808
26873
|
if (lines.length === 0) return 0;
|
|
26809
26874
|
try {
|
|
26810
26875
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -26814,9 +26879,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
26814
26879
|
}
|
|
26815
26880
|
function readSince(dir, chatId, since) {
|
|
26816
26881
|
const p = brainEventsFilePath(dir, chatId);
|
|
26817
|
-
if (!
|
|
26882
|
+
if (!fs56.existsSync(p)) return [];
|
|
26818
26883
|
const out = [];
|
|
26819
|
-
for (const line of
|
|
26884
|
+
for (const line of fs56.readFileSync(p, "utf-8").split("\n")) {
|
|
26820
26885
|
if (!line) continue;
|
|
26821
26886
|
try {
|
|
26822
26887
|
const rec = JSON.parse(line);
|
|
@@ -26842,12 +26907,12 @@ function beginTurn(dir, chatId) {
|
|
|
26842
26907
|
};
|
|
26843
26908
|
live.set(chatId, state);
|
|
26844
26909
|
const p = brainEventsFilePath(dir, chatId);
|
|
26845
|
-
|
|
26910
|
+
fs56.mkdirSync(path53.dirname(p), { recursive: true });
|
|
26846
26911
|
return (event) => {
|
|
26847
26912
|
state.seq += 1;
|
|
26848
26913
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
26849
26914
|
try {
|
|
26850
|
-
|
|
26915
|
+
fs56.appendFileSync(p, `${JSON.stringify(rec)}
|
|
26851
26916
|
`);
|
|
26852
26917
|
} catch (err) {
|
|
26853
26918
|
process.stderr.write(
|
|
@@ -26886,7 +26951,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
26886
26951
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
26887
26952
|
};
|
|
26888
26953
|
try {
|
|
26889
|
-
|
|
26954
|
+
fs56.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
|
|
26890
26955
|
`);
|
|
26891
26956
|
} catch {
|
|
26892
26957
|
}
|
|
@@ -26980,17 +27045,17 @@ function authOk(req, expected) {
|
|
|
26980
27045
|
return false;
|
|
26981
27046
|
}
|
|
26982
27047
|
function readJsonBody(req) {
|
|
26983
|
-
return new Promise((
|
|
27048
|
+
return new Promise((resolve23, reject) => {
|
|
26984
27049
|
const chunks = [];
|
|
26985
27050
|
req.on("data", (c) => chunks.push(c));
|
|
26986
27051
|
req.on("end", () => {
|
|
26987
27052
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
26988
27053
|
if (!raw.trim()) {
|
|
26989
|
-
|
|
27054
|
+
resolve23({});
|
|
26990
27055
|
return;
|
|
26991
27056
|
}
|
|
26992
27057
|
try {
|
|
26993
|
-
|
|
27058
|
+
resolve23(JSON.parse(raw));
|
|
26994
27059
|
} catch (err) {
|
|
26995
27060
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
26996
27061
|
}
|
|
@@ -27282,7 +27347,7 @@ function buildServer(opts) {
|
|
|
27282
27347
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
27283
27348
|
const createStore = opts.createStore ?? createSessionStore;
|
|
27284
27349
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
27285
|
-
const reposRoot = opts.reposRoot ??
|
|
27350
|
+
const reposRoot = opts.reposRoot ?? path54.join(path54.dirname(path54.resolve(opts.cwd)), "repos");
|
|
27286
27351
|
return createServer2(async (req, res) => {
|
|
27287
27352
|
if (!req.method || !req.url) {
|
|
27288
27353
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -27363,11 +27428,11 @@ async function brainServe(opts) {
|
|
|
27363
27428
|
litellmUrl,
|
|
27364
27429
|
driver
|
|
27365
27430
|
});
|
|
27366
|
-
await new Promise((
|
|
27431
|
+
await new Promise((resolve23) => {
|
|
27367
27432
|
server.listen(port, "0.0.0.0", () => {
|
|
27368
27433
|
process.stdout.write(`[brain-serve] listening on 0.0.0.0:${port} (cwd=${opts.cwd})
|
|
27369
27434
|
`);
|
|
27370
|
-
|
|
27435
|
+
resolve23();
|
|
27371
27436
|
});
|
|
27372
27437
|
});
|
|
27373
27438
|
const shutdown = (signal) => {
|
|
@@ -27622,14 +27687,14 @@ async function startBrainProxy(opts) {
|
|
|
27622
27687
|
const { httpServer, handler } = buildBrainProxy(opts);
|
|
27623
27688
|
const port = opts.port ?? 0;
|
|
27624
27689
|
const host = opts.host ?? "127.0.0.1";
|
|
27625
|
-
await new Promise((
|
|
27690
|
+
await new Promise((resolve23) => httpServer.listen(port, host, () => resolve23()));
|
|
27626
27691
|
const addr = httpServer.address();
|
|
27627
27692
|
return {
|
|
27628
27693
|
httpServer,
|
|
27629
27694
|
port: addr.port,
|
|
27630
27695
|
url: `http://${host}:${addr.port}`,
|
|
27631
|
-
stop: () => new Promise((
|
|
27632
|
-
httpServer.close(() =>
|
|
27696
|
+
stop: () => new Promise((resolve23) => {
|
|
27697
|
+
httpServer.close(() => resolve23());
|
|
27633
27698
|
}),
|
|
27634
27699
|
handler
|
|
27635
27700
|
};
|
|
@@ -27779,23 +27844,23 @@ function buildMcpHttpServer(opts) {
|
|
|
27779
27844
|
httpServer,
|
|
27780
27845
|
routes,
|
|
27781
27846
|
port,
|
|
27782
|
-
stop: () => new Promise((
|
|
27847
|
+
stop: () => new Promise((resolve23) => {
|
|
27783
27848
|
let pending = transports.size;
|
|
27784
27849
|
if (pending === 0) {
|
|
27785
|
-
httpServer.close(() =>
|
|
27850
|
+
httpServer.close(() => resolve23());
|
|
27786
27851
|
return;
|
|
27787
27852
|
}
|
|
27788
27853
|
for (const transport of transports.values()) {
|
|
27789
27854
|
void transport.close().finally(() => {
|
|
27790
27855
|
pending--;
|
|
27791
|
-
if (pending === 0) httpServer.close(() =>
|
|
27856
|
+
if (pending === 0) httpServer.close(() => resolve23());
|
|
27792
27857
|
});
|
|
27793
27858
|
}
|
|
27794
27859
|
})
|
|
27795
27860
|
};
|
|
27796
27861
|
}
|
|
27797
27862
|
function listenMcpHttpServer(server, host = "127.0.0.1") {
|
|
27798
|
-
return new Promise((
|
|
27863
|
+
return new Promise((resolve23, reject) => {
|
|
27799
27864
|
server.httpServer.once("error", reject);
|
|
27800
27865
|
server.httpServer.listen(server.port, host, () => {
|
|
27801
27866
|
server.httpServer.off("error", reject);
|
|
@@ -27803,7 +27868,7 @@ function listenMcpHttpServer(server, host = "127.0.0.1") {
|
|
|
27803
27868
|
if (addr && typeof addr === "object") {
|
|
27804
27869
|
server.port = addr.port;
|
|
27805
27870
|
}
|
|
27806
|
-
|
|
27871
|
+
resolve23();
|
|
27807
27872
|
});
|
|
27808
27873
|
});
|
|
27809
27874
|
}
|
|
@@ -27886,7 +27951,7 @@ async function loadConfigSafe() {
|
|
|
27886
27951
|
}
|
|
27887
27952
|
|
|
27888
27953
|
// src/chat-cli.ts
|
|
27889
|
-
import * as
|
|
27954
|
+
import * as path55 from "path";
|
|
27890
27955
|
|
|
27891
27956
|
// src/chat/inbox.ts
|
|
27892
27957
|
import { execFileSync as execFileSync25 } from "child_process";
|
|
@@ -27953,7 +28018,7 @@ async function waitForNextUserMessage(opts) {
|
|
|
27953
28018
|
}
|
|
27954
28019
|
}
|
|
27955
28020
|
function sleep3(ms) {
|
|
27956
|
-
return new Promise((
|
|
28021
|
+
return new Promise((resolve23) => setTimeout(resolve23, ms));
|
|
27957
28022
|
}
|
|
27958
28023
|
function currentBranch(cwd) {
|
|
27959
28024
|
try {
|
|
@@ -28177,7 +28242,7 @@ async function runChat(argv) {
|
|
|
28177
28242
|
${CHAT_HELP}`);
|
|
28178
28243
|
return 64;
|
|
28179
28244
|
}
|
|
28180
|
-
const cwd = args.cwd ?
|
|
28245
|
+
const cwd = args.cwd ? path55.resolve(args.cwd) : process.cwd();
|
|
28181
28246
|
const sessionId = args.sessionId;
|
|
28182
28247
|
const runRequest = readRunRequestFromEnv();
|
|
28183
28248
|
if (runRequest && "request" in runRequest) {
|
|
@@ -28302,6 +28367,575 @@ init_definition_paths();
|
|
|
28302
28367
|
init_job();
|
|
28303
28368
|
init_registry();
|
|
28304
28369
|
|
|
28370
|
+
// src/servers/brain-terminal-agent.ts
|
|
28371
|
+
init_repoWorkspace();
|
|
28372
|
+
import * as path57 from "path";
|
|
28373
|
+
import { createInterface as createInterface2 } from "readline";
|
|
28374
|
+
|
|
28375
|
+
// src/terminal/brain-terminal-session.ts
|
|
28376
|
+
import { createHash as createHash9 } from "crypto";
|
|
28377
|
+
var MAX_CAPTURE_CHARS = 2e5;
|
|
28378
|
+
function requiredIdentifier(value, name, max = 240) {
|
|
28379
|
+
if (typeof value !== "string" || !value.trim() || value.length > max) {
|
|
28380
|
+
throw new Error(`${name} must be a non-empty string of at most ${max} characters`);
|
|
28381
|
+
}
|
|
28382
|
+
return value.trim();
|
|
28383
|
+
}
|
|
28384
|
+
function terminalSize(value, name) {
|
|
28385
|
+
if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > 1e3) {
|
|
28386
|
+
throw new Error(`${name} must be an integer between 1 and 1000`);
|
|
28387
|
+
}
|
|
28388
|
+
return Number(value);
|
|
28389
|
+
}
|
|
28390
|
+
function revision(value) {
|
|
28391
|
+
if (value === void 0) return void 0;
|
|
28392
|
+
if (!Number.isInteger(value) || Number(value) < 0) {
|
|
28393
|
+
throw new Error("afterRevision must be a non-negative integer");
|
|
28394
|
+
}
|
|
28395
|
+
return Number(value);
|
|
28396
|
+
}
|
|
28397
|
+
function parseBrainTerminalOpenRequest(value) {
|
|
28398
|
+
if (!value || typeof value !== "object") throw new Error("open request must be an object");
|
|
28399
|
+
const request = value;
|
|
28400
|
+
if (request.type !== "open") throw new Error("first terminal message must be open");
|
|
28401
|
+
if (!request.session || typeof request.session !== "object") throw new Error("session is required");
|
|
28402
|
+
const session = request.session;
|
|
28403
|
+
if (!session.scope || typeof session.scope !== "object") throw new Error("session scope is required");
|
|
28404
|
+
const scope = session.scope;
|
|
28405
|
+
return {
|
|
28406
|
+
type: "open",
|
|
28407
|
+
session: {
|
|
28408
|
+
id: requiredIdentifier(session.id, "session.id"),
|
|
28409
|
+
scope: {
|
|
28410
|
+
owner: requiredIdentifier(scope.owner, "scope.owner", 100),
|
|
28411
|
+
repo: requiredIdentifier(scope.repo, "scope.repo", 100),
|
|
28412
|
+
conversationId: requiredIdentifier(scope.conversationId, "scope.conversationId")
|
|
28413
|
+
}
|
|
28414
|
+
},
|
|
28415
|
+
cwd: requiredIdentifier(request.cwd, "cwd", 1e3),
|
|
28416
|
+
afterRevision: revision(request.afterRevision),
|
|
28417
|
+
cols: terminalSize(request.cols, "cols"),
|
|
28418
|
+
rows: terminalSize(request.rows, "rows")
|
|
28419
|
+
};
|
|
28420
|
+
}
|
|
28421
|
+
function parseBrainTerminalStatusRequest(value) {
|
|
28422
|
+
if (!value || typeof value !== "object") throw new Error("status request must be an object");
|
|
28423
|
+
const request = value;
|
|
28424
|
+
if (request.type !== "status") throw new Error("terminal request must be status");
|
|
28425
|
+
return { type: "status", sessionId: requiredIdentifier(request.sessionId, "sessionId") };
|
|
28426
|
+
}
|
|
28427
|
+
function parseBrainTerminalCommand(value) {
|
|
28428
|
+
if (!value || typeof value !== "object") throw new Error("terminal command must be an object");
|
|
28429
|
+
const command = value;
|
|
28430
|
+
const sessionId = requiredIdentifier(command.sessionId, "sessionId");
|
|
28431
|
+
switch (command.type) {
|
|
28432
|
+
case "attach":
|
|
28433
|
+
return { type: "attach", sessionId, afterRevision: revision(command.afterRevision) };
|
|
28434
|
+
case "input": {
|
|
28435
|
+
const inputId = requiredIdentifier(command.inputId, "inputId");
|
|
28436
|
+
if (typeof command.data !== "string" || command.data.length === 0) {
|
|
28437
|
+
throw new Error("data must be a non-empty string");
|
|
28438
|
+
}
|
|
28439
|
+
return { type: "input", sessionId, inputId, data: command.data };
|
|
28440
|
+
}
|
|
28441
|
+
case "resize":
|
|
28442
|
+
return {
|
|
28443
|
+
type: "resize",
|
|
28444
|
+
sessionId,
|
|
28445
|
+
cols: terminalSize(command.cols, "cols"),
|
|
28446
|
+
rows: terminalSize(command.rows, "rows")
|
|
28447
|
+
};
|
|
28448
|
+
case "detach":
|
|
28449
|
+
return { type: "detach", sessionId };
|
|
28450
|
+
case "restart":
|
|
28451
|
+
return { type: "restart", sessionId };
|
|
28452
|
+
default:
|
|
28453
|
+
throw new Error("unknown terminal command");
|
|
28454
|
+
}
|
|
28455
|
+
}
|
|
28456
|
+
function sessionName(id) {
|
|
28457
|
+
return `kody_${createHash9("sha256").update(id).digest("hex").slice(0, 32)}`;
|
|
28458
|
+
}
|
|
28459
|
+
function stateEvent(session) {
|
|
28460
|
+
return {
|
|
28461
|
+
type: "state",
|
|
28462
|
+
sessionId: session.id,
|
|
28463
|
+
generation: session.generation,
|
|
28464
|
+
state: session.state,
|
|
28465
|
+
processId: session.processId
|
|
28466
|
+
};
|
|
28467
|
+
}
|
|
28468
|
+
function failedEvent(session, code, cause) {
|
|
28469
|
+
return {
|
|
28470
|
+
type: "failed",
|
|
28471
|
+
sessionId: session.id,
|
|
28472
|
+
generation: session.generation,
|
|
28473
|
+
code,
|
|
28474
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
28475
|
+
};
|
|
28476
|
+
}
|
|
28477
|
+
var BrainTerminalSessionAgent = class {
|
|
28478
|
+
constructor(dependencies) {
|
|
28479
|
+
this.dependencies = dependencies;
|
|
28480
|
+
}
|
|
28481
|
+
dependencies;
|
|
28482
|
+
session = null;
|
|
28483
|
+
now() {
|
|
28484
|
+
return (this.dependencies.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
28485
|
+
}
|
|
28486
|
+
requireSession() {
|
|
28487
|
+
if (!this.session) throw new Error("terminal session is not open");
|
|
28488
|
+
return this.session;
|
|
28489
|
+
}
|
|
28490
|
+
async persist(session) {
|
|
28491
|
+
this.session = session;
|
|
28492
|
+
await this.dependencies.store.write(session);
|
|
28493
|
+
}
|
|
28494
|
+
async open(rawRequest) {
|
|
28495
|
+
const request = parseBrainTerminalOpenRequest(rawRequest);
|
|
28496
|
+
let session = await this.dependencies.store.read(request.session.id);
|
|
28497
|
+
if (session && (session.scope.owner !== request.session.scope.owner || session.scope.repo !== request.session.scope.repo || session.scope.conversationId !== request.session.scope.conversationId)) {
|
|
28498
|
+
throw new Error("terminal session scope does not match stored identity");
|
|
28499
|
+
}
|
|
28500
|
+
if (!session) {
|
|
28501
|
+
session = {
|
|
28502
|
+
version: 1,
|
|
28503
|
+
id: request.session.id,
|
|
28504
|
+
scope: request.session.scope,
|
|
28505
|
+
sessionName: sessionName(request.session.id),
|
|
28506
|
+
cwd: request.cwd,
|
|
28507
|
+
generation: 1,
|
|
28508
|
+
state: "starting",
|
|
28509
|
+
revision: 0,
|
|
28510
|
+
output: "",
|
|
28511
|
+
processId: null,
|
|
28512
|
+
cols: request.cols,
|
|
28513
|
+
rows: request.rows,
|
|
28514
|
+
updatedAt: this.now()
|
|
28515
|
+
};
|
|
28516
|
+
await this.persist(session);
|
|
28517
|
+
let started;
|
|
28518
|
+
try {
|
|
28519
|
+
started = await this.dependencies.runtime.start(
|
|
28520
|
+
session.sessionName,
|
|
28521
|
+
session.cwd,
|
|
28522
|
+
session.cols,
|
|
28523
|
+
session.rows
|
|
28524
|
+
);
|
|
28525
|
+
} catch (cause) {
|
|
28526
|
+
session = { ...session, state: "failed", updatedAt: this.now() };
|
|
28527
|
+
await this.persist(session);
|
|
28528
|
+
return [failedEvent(session, "runtime_start_failed", cause)];
|
|
28529
|
+
}
|
|
28530
|
+
session = { ...session, state: "ready", processId: started.processId, updatedAt: this.now() };
|
|
28531
|
+
await this.persist(session);
|
|
28532
|
+
} else {
|
|
28533
|
+
const runtime = await this.dependencies.runtime.inspect(session.sessionName);
|
|
28534
|
+
session = {
|
|
28535
|
+
...session,
|
|
28536
|
+
state: runtime.alive ? "ready" : session.state === "failed" ? "failed" : "exited",
|
|
28537
|
+
processId: runtime.processId,
|
|
28538
|
+
cols: request.cols,
|
|
28539
|
+
rows: request.rows,
|
|
28540
|
+
updatedAt: this.now()
|
|
28541
|
+
};
|
|
28542
|
+
if (runtime.alive) {
|
|
28543
|
+
await this.dependencies.runtime.resize(session.sessionName, request.cols, request.rows);
|
|
28544
|
+
}
|
|
28545
|
+
await this.persist(session);
|
|
28546
|
+
}
|
|
28547
|
+
const events = [stateEvent(session)];
|
|
28548
|
+
if (session.output && (request.afterRevision === void 0 || request.afterRevision < session.revision)) {
|
|
28549
|
+
events.push({
|
|
28550
|
+
type: "output",
|
|
28551
|
+
sessionId: session.id,
|
|
28552
|
+
generation: session.generation,
|
|
28553
|
+
revision: session.revision,
|
|
28554
|
+
data: `\x1B[2J\x1B[H${session.output}`
|
|
28555
|
+
});
|
|
28556
|
+
}
|
|
28557
|
+
return events;
|
|
28558
|
+
}
|
|
28559
|
+
async inspectStored(sessionId) {
|
|
28560
|
+
const id = requiredIdentifier(sessionId, "sessionId");
|
|
28561
|
+
const session = await this.dependencies.store.read(id);
|
|
28562
|
+
if (!session) return null;
|
|
28563
|
+
const runtime = await this.dependencies.runtime.inspect(session.sessionName);
|
|
28564
|
+
const next = {
|
|
28565
|
+
...session,
|
|
28566
|
+
state: runtime.alive ? session.state : "exited",
|
|
28567
|
+
processId: runtime.processId,
|
|
28568
|
+
updatedAt: this.now()
|
|
28569
|
+
};
|
|
28570
|
+
await this.persist(next);
|
|
28571
|
+
return {
|
|
28572
|
+
id: next.id,
|
|
28573
|
+
generation: next.generation,
|
|
28574
|
+
state: next.state,
|
|
28575
|
+
revision: next.revision,
|
|
28576
|
+
processId: next.processId
|
|
28577
|
+
};
|
|
28578
|
+
}
|
|
28579
|
+
async status() {
|
|
28580
|
+
const session = this.requireSession();
|
|
28581
|
+
const runtime = await this.dependencies.runtime.inspect(session.sessionName);
|
|
28582
|
+
const nextState = runtime.alive ? session.state : session.state === "failed" ? "failed" : "exited";
|
|
28583
|
+
if (nextState !== session.state || runtime.processId !== session.processId) {
|
|
28584
|
+
await this.persist({
|
|
28585
|
+
...session,
|
|
28586
|
+
state: nextState,
|
|
28587
|
+
processId: runtime.processId,
|
|
28588
|
+
updatedAt: this.now()
|
|
28589
|
+
});
|
|
28590
|
+
}
|
|
28591
|
+
const current = this.requireSession();
|
|
28592
|
+
return {
|
|
28593
|
+
id: current.id,
|
|
28594
|
+
generation: current.generation,
|
|
28595
|
+
state: current.state,
|
|
28596
|
+
revision: current.revision,
|
|
28597
|
+
processId: current.processId
|
|
28598
|
+
};
|
|
28599
|
+
}
|
|
28600
|
+
async captureOutput() {
|
|
28601
|
+
const session = this.requireSession();
|
|
28602
|
+
if (session.state !== "ready" && session.state !== "detached") return null;
|
|
28603
|
+
const output = (await this.dependencies.runtime.capture(session.sessionName)).slice(-MAX_CAPTURE_CHARS);
|
|
28604
|
+
if (output === session.output) return null;
|
|
28605
|
+
const next = {
|
|
28606
|
+
...session,
|
|
28607
|
+
output,
|
|
28608
|
+
revision: session.revision + 1,
|
|
28609
|
+
updatedAt: this.now()
|
|
28610
|
+
};
|
|
28611
|
+
await this.persist(next);
|
|
28612
|
+
return {
|
|
28613
|
+
type: "output",
|
|
28614
|
+
sessionId: next.id,
|
|
28615
|
+
generation: next.generation,
|
|
28616
|
+
revision: next.revision,
|
|
28617
|
+
data: `\x1B[2J\x1B[H${output}`
|
|
28618
|
+
};
|
|
28619
|
+
}
|
|
28620
|
+
async detach() {
|
|
28621
|
+
const session = this.requireSession();
|
|
28622
|
+
const next = { ...session, state: "detached", updatedAt: this.now() };
|
|
28623
|
+
await this.persist(next);
|
|
28624
|
+
return stateEvent(next);
|
|
28625
|
+
}
|
|
28626
|
+
async command(rawCommand) {
|
|
28627
|
+
const command = parseBrainTerminalCommand(rawCommand);
|
|
28628
|
+
const session = this.requireSession();
|
|
28629
|
+
if (command.sessionId !== session.id) throw new Error("terminal command session identity mismatch");
|
|
28630
|
+
switch (command.type) {
|
|
28631
|
+
case "attach":
|
|
28632
|
+
return stateEvent(session);
|
|
28633
|
+
case "input":
|
|
28634
|
+
if (session.state !== "ready") throw new Error(`input is not allowed while terminal is ${session.state}`);
|
|
28635
|
+
await this.dependencies.runtime.input(session.sessionName, command.data);
|
|
28636
|
+
return {
|
|
28637
|
+
type: "input-accepted",
|
|
28638
|
+
sessionId: session.id,
|
|
28639
|
+
generation: session.generation,
|
|
28640
|
+
inputId: command.inputId
|
|
28641
|
+
};
|
|
28642
|
+
case "resize":
|
|
28643
|
+
if (session.state !== "ready") throw new Error(`resize is not allowed while terminal is ${session.state}`);
|
|
28644
|
+
await this.dependencies.runtime.resize(session.sessionName, command.cols, command.rows);
|
|
28645
|
+
await this.persist({ ...session, cols: command.cols, rows: command.rows, updatedAt: this.now() });
|
|
28646
|
+
return null;
|
|
28647
|
+
case "detach":
|
|
28648
|
+
return this.detach();
|
|
28649
|
+
case "restart": {
|
|
28650
|
+
if (session.state === "starting") throw new Error("restart is not allowed while terminal is starting");
|
|
28651
|
+
await this.dependencies.runtime.stop(session.sessionName);
|
|
28652
|
+
const starting = {
|
|
28653
|
+
...session,
|
|
28654
|
+
generation: session.generation + 1,
|
|
28655
|
+
state: "starting",
|
|
28656
|
+
revision: 0,
|
|
28657
|
+
output: "",
|
|
28658
|
+
processId: null,
|
|
28659
|
+
updatedAt: this.now()
|
|
28660
|
+
};
|
|
28661
|
+
await this.persist(starting);
|
|
28662
|
+
let started;
|
|
28663
|
+
try {
|
|
28664
|
+
started = await this.dependencies.runtime.start(
|
|
28665
|
+
starting.sessionName,
|
|
28666
|
+
starting.cwd,
|
|
28667
|
+
starting.cols,
|
|
28668
|
+
starting.rows
|
|
28669
|
+
);
|
|
28670
|
+
} catch (cause) {
|
|
28671
|
+
const failed = { ...starting, state: "failed", updatedAt: this.now() };
|
|
28672
|
+
await this.persist(failed);
|
|
28673
|
+
return failedEvent(failed, "runtime_start_failed", cause);
|
|
28674
|
+
}
|
|
28675
|
+
const ready = { ...starting, state: "ready", processId: started.processId, updatedAt: this.now() };
|
|
28676
|
+
await this.persist(ready);
|
|
28677
|
+
return stateEvent(ready);
|
|
28678
|
+
}
|
|
28679
|
+
}
|
|
28680
|
+
}
|
|
28681
|
+
};
|
|
28682
|
+
|
|
28683
|
+
// src/terminal/brain-terminal-adapters.ts
|
|
28684
|
+
import { createHash as createHash10, randomBytes as randomBytes2 } from "crypto";
|
|
28685
|
+
import { mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
|
|
28686
|
+
import * as path56 from "path";
|
|
28687
|
+
import { spawn as spawn9 } from "child_process";
|
|
28688
|
+
function runTerminalCommand(command, args, input) {
|
|
28689
|
+
return new Promise((resolve23, reject) => {
|
|
28690
|
+
const child = spawn9(command, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
28691
|
+
const stdout = [];
|
|
28692
|
+
const stderr = [];
|
|
28693
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
28694
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
28695
|
+
child.on("error", reject);
|
|
28696
|
+
child.on("close", (code) => {
|
|
28697
|
+
resolve23({
|
|
28698
|
+
code: code ?? 1,
|
|
28699
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
28700
|
+
stderr: Buffer.concat(stderr).toString("utf8")
|
|
28701
|
+
});
|
|
28702
|
+
});
|
|
28703
|
+
if (input !== void 0) child.stdin.end(input);
|
|
28704
|
+
else child.stdin.end();
|
|
28705
|
+
});
|
|
28706
|
+
}
|
|
28707
|
+
function storeKey(id) {
|
|
28708
|
+
return createHash10("sha256").update(id).digest("hex");
|
|
28709
|
+
}
|
|
28710
|
+
function isStoredSession(value) {
|
|
28711
|
+
if (!value || typeof value !== "object") return false;
|
|
28712
|
+
const session = value;
|
|
28713
|
+
return session.version === 1 && typeof session.id === "string" && typeof session.sessionName === "string" && typeof session.cwd === "string" && Number.isInteger(session.generation) && Number.isInteger(session.revision) && typeof session.output === "string" && typeof session.scope?.owner === "string" && typeof session.scope?.repo === "string" && typeof session.scope?.conversationId === "string";
|
|
28714
|
+
}
|
|
28715
|
+
var FileBrainTerminalMetadataStore = class {
|
|
28716
|
+
constructor(root) {
|
|
28717
|
+
this.root = root;
|
|
28718
|
+
}
|
|
28719
|
+
root;
|
|
28720
|
+
file(id) {
|
|
28721
|
+
return path56.join(this.root, `${storeKey(id)}.json`);
|
|
28722
|
+
}
|
|
28723
|
+
async read(id) {
|
|
28724
|
+
try {
|
|
28725
|
+
const parsed = JSON.parse(await readFile(this.file(id), "utf8"));
|
|
28726
|
+
if (!isStoredSession(parsed) || parsed.id !== id) {
|
|
28727
|
+
throw new Error("stored terminal session is invalid");
|
|
28728
|
+
}
|
|
28729
|
+
return parsed;
|
|
28730
|
+
} catch (error) {
|
|
28731
|
+
if (error.code === "ENOENT") return null;
|
|
28732
|
+
throw error;
|
|
28733
|
+
}
|
|
28734
|
+
}
|
|
28735
|
+
async write(session) {
|
|
28736
|
+
await mkdir(this.root, { recursive: true, mode: 448 });
|
|
28737
|
+
const target = this.file(session.id);
|
|
28738
|
+
const temporary = `${target}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
|
|
28739
|
+
await writeFile2(temporary, `${JSON.stringify(session)}
|
|
28740
|
+
`, { mode: 384 });
|
|
28741
|
+
await rename(temporary, target);
|
|
28742
|
+
}
|
|
28743
|
+
};
|
|
28744
|
+
function commandError(action, result) {
|
|
28745
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
|
|
28746
|
+
return new Error(`tmux ${action} failed: ${detail.slice(0, 500)}`);
|
|
28747
|
+
}
|
|
28748
|
+
var TmuxBrainTerminalRuntime = class {
|
|
28749
|
+
constructor(run = runTerminalCommand) {
|
|
28750
|
+
this.run = run;
|
|
28751
|
+
}
|
|
28752
|
+
run;
|
|
28753
|
+
async tmux(action, args, input) {
|
|
28754
|
+
const result = await this.run("tmux", args, input);
|
|
28755
|
+
if (result.code !== 0) throw commandError(action, result);
|
|
28756
|
+
return result;
|
|
28757
|
+
}
|
|
28758
|
+
async start(sessionName2, cwd, cols, rows) {
|
|
28759
|
+
await this.tmux("start", [
|
|
28760
|
+
"new-session",
|
|
28761
|
+
"-d",
|
|
28762
|
+
"-s",
|
|
28763
|
+
sessionName2,
|
|
28764
|
+
"-x",
|
|
28765
|
+
String(cols),
|
|
28766
|
+
"-y",
|
|
28767
|
+
String(rows),
|
|
28768
|
+
"-c",
|
|
28769
|
+
cwd,
|
|
28770
|
+
"/bin/bash",
|
|
28771
|
+
"-l"
|
|
28772
|
+
]);
|
|
28773
|
+
await this.tmux("configure", ["set-option", "-t", sessionName2, "status", "off"]);
|
|
28774
|
+
await this.tmux("configure", ["set-option", "-t", sessionName2, "history-limit", "50000"]);
|
|
28775
|
+
await this.tmux("configure", ["set-option", "-w", "-t", sessionName2, "remain-on-exit", "on"]);
|
|
28776
|
+
const inspected = await this.inspect(sessionName2);
|
|
28777
|
+
if (!inspected.alive || inspected.processId === null) throw new Error("tmux terminal did not start");
|
|
28778
|
+
return { processId: inspected.processId };
|
|
28779
|
+
}
|
|
28780
|
+
async inspect(sessionName2) {
|
|
28781
|
+
const result = await this.run("tmux", [
|
|
28782
|
+
"list-panes",
|
|
28783
|
+
"-t",
|
|
28784
|
+
sessionName2,
|
|
28785
|
+
"-F",
|
|
28786
|
+
"#{pane_dead}:#{pane_pid}"
|
|
28787
|
+
]);
|
|
28788
|
+
if (result.code !== 0) return { alive: false, processId: null };
|
|
28789
|
+
const [dead, pid] = result.stdout.trim().split(":");
|
|
28790
|
+
const processId = Number(pid);
|
|
28791
|
+
return {
|
|
28792
|
+
alive: dead === "0" && Number.isInteger(processId) && processId > 0,
|
|
28793
|
+
processId: Number.isInteger(processId) && processId > 0 ? processId : null
|
|
28794
|
+
};
|
|
28795
|
+
}
|
|
28796
|
+
async capture(sessionName2) {
|
|
28797
|
+
const alternate = await this.run("tmux", [
|
|
28798
|
+
"display-message",
|
|
28799
|
+
"-p",
|
|
28800
|
+
"-t",
|
|
28801
|
+
sessionName2,
|
|
28802
|
+
"#{alternate_on}"
|
|
28803
|
+
]);
|
|
28804
|
+
if (alternate.code !== 0) throw commandError("inspect screen", alternate);
|
|
28805
|
+
const args = alternate.stdout.trim() === "1" ? ["capture-pane", "-p", "-e", "-t", sessionName2] : ["capture-pane", "-p", "-e", "-J", "-S", "-50000", "-t", sessionName2];
|
|
28806
|
+
return (await this.tmux("capture", args)).stdout;
|
|
28807
|
+
}
|
|
28808
|
+
async input(sessionName2, data) {
|
|
28809
|
+
const bufferName = `kody_${randomBytes2(8).toString("hex")}`;
|
|
28810
|
+
await this.tmux("load input", ["load-buffer", "-b", bufferName, "-"], data);
|
|
28811
|
+
await this.tmux("paste input", ["paste-buffer", "-d", "-b", bufferName, "-t", sessionName2]);
|
|
28812
|
+
}
|
|
28813
|
+
async resize(sessionName2, cols, rows) {
|
|
28814
|
+
await this.tmux("resize", [
|
|
28815
|
+
"resize-window",
|
|
28816
|
+
"-t",
|
|
28817
|
+
sessionName2,
|
|
28818
|
+
"-x",
|
|
28819
|
+
String(cols),
|
|
28820
|
+
"-y",
|
|
28821
|
+
String(rows)
|
|
28822
|
+
]);
|
|
28823
|
+
}
|
|
28824
|
+
async stop(sessionName2) {
|
|
28825
|
+
const result = await this.run("tmux", ["kill-session", "-t", sessionName2]);
|
|
28826
|
+
if (result.code !== 0 && !/can't find session|no server running/i.test(result.stderr)) {
|
|
28827
|
+
throw commandError("stop", result);
|
|
28828
|
+
}
|
|
28829
|
+
}
|
|
28830
|
+
};
|
|
28831
|
+
|
|
28832
|
+
// src/servers/brain-terminal-agent.ts
|
|
28833
|
+
var DEFAULT_POLL_INTERVAL_MS = 150;
|
|
28834
|
+
function writeEvent(output, event) {
|
|
28835
|
+
output.write(`${JSON.stringify(event)}
|
|
28836
|
+
`);
|
|
28837
|
+
}
|
|
28838
|
+
async function brainTerminalAgent(options) {
|
|
28839
|
+
const input = options.input ?? process.stdin;
|
|
28840
|
+
const output = options.output ?? process.stdout;
|
|
28841
|
+
const error = options.error ?? process.stderr;
|
|
28842
|
+
const reposRoot = process.env.BRAIN_REPOS_ROOT?.trim() || path57.join(path57.dirname(path57.resolve(options.cwd)), "repos");
|
|
28843
|
+
const stateRoot = process.env.BRAIN_TERMINAL_STATE_ROOT?.trim() || path57.join(path57.dirname(reposRoot), ".kody", "terminal-sessions");
|
|
28844
|
+
const agent = new BrainTerminalSessionAgent({
|
|
28845
|
+
store: new FileBrainTerminalMetadataStore(stateRoot),
|
|
28846
|
+
runtime: new TmuxBrainTerminalRuntime()
|
|
28847
|
+
});
|
|
28848
|
+
const lines = createInterface2({ input, crlfDelay: Infinity });
|
|
28849
|
+
let opened = false;
|
|
28850
|
+
let poll = null;
|
|
28851
|
+
let pollRunning = false;
|
|
28852
|
+
const stopPoll = () => {
|
|
28853
|
+
if (poll) clearInterval(poll);
|
|
28854
|
+
poll = null;
|
|
28855
|
+
};
|
|
28856
|
+
const capture = async () => {
|
|
28857
|
+
if (pollRunning) return;
|
|
28858
|
+
pollRunning = true;
|
|
28859
|
+
try {
|
|
28860
|
+
const event = await agent.captureOutput();
|
|
28861
|
+
if (event) writeEvent(output, event);
|
|
28862
|
+
const status = await agent.status();
|
|
28863
|
+
if (status.state === "exited") {
|
|
28864
|
+
writeEvent(output, {
|
|
28865
|
+
type: "exited",
|
|
28866
|
+
sessionId: status.id,
|
|
28867
|
+
generation: status.generation
|
|
28868
|
+
});
|
|
28869
|
+
stopPoll();
|
|
28870
|
+
}
|
|
28871
|
+
} catch (cause) {
|
|
28872
|
+
error.write(`[brain-terminal-agent] capture failed: ${cause instanceof Error ? cause.message : String(cause)}
|
|
28873
|
+
`);
|
|
28874
|
+
} finally {
|
|
28875
|
+
pollRunning = false;
|
|
28876
|
+
}
|
|
28877
|
+
};
|
|
28878
|
+
try {
|
|
28879
|
+
for await (const line of lines) {
|
|
28880
|
+
if (!line.trim()) continue;
|
|
28881
|
+
const value = JSON.parse(line);
|
|
28882
|
+
if (!opened) {
|
|
28883
|
+
if (value && typeof value === "object" && value.type === "status") {
|
|
28884
|
+
const request = parseBrainTerminalStatusRequest(value);
|
|
28885
|
+
const status = await agent.inspectStored(request.sessionId);
|
|
28886
|
+
if (status) {
|
|
28887
|
+
writeEvent(output, {
|
|
28888
|
+
type: "state",
|
|
28889
|
+
sessionId: status.id,
|
|
28890
|
+
generation: status.generation,
|
|
28891
|
+
state: status.state,
|
|
28892
|
+
processId: status.processId
|
|
28893
|
+
});
|
|
28894
|
+
} else {
|
|
28895
|
+
writeEvent(output, {
|
|
28896
|
+
type: "failed",
|
|
28897
|
+
sessionId: request.sessionId,
|
|
28898
|
+
generation: 1,
|
|
28899
|
+
code: "session_not_found",
|
|
28900
|
+
message: "Terminal session not found"
|
|
28901
|
+
});
|
|
28902
|
+
}
|
|
28903
|
+
return 0;
|
|
28904
|
+
}
|
|
28905
|
+
const requested = parseBrainTerminalOpenRequest(value);
|
|
28906
|
+
const repo = `${requested.session.scope.owner}/${requested.session.scope.repo}`;
|
|
28907
|
+
const workspaceCwd = await ensureRepoCwd({
|
|
28908
|
+
baseCwd: options.cwd,
|
|
28909
|
+
reposRoot,
|
|
28910
|
+
repo,
|
|
28911
|
+
repoToken: process.env.KODY_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_PAT,
|
|
28912
|
+
cloneRepo: defaultCloneRepo
|
|
28913
|
+
});
|
|
28914
|
+
const events = await agent.open({ ...requested, cwd: workspaceCwd });
|
|
28915
|
+
for (const event2 of events) writeEvent(output, event2);
|
|
28916
|
+
opened = true;
|
|
28917
|
+
poll = setInterval(() => void capture(), options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
28918
|
+
poll.unref?.();
|
|
28919
|
+
continue;
|
|
28920
|
+
}
|
|
28921
|
+
const command = parseBrainTerminalCommand(value);
|
|
28922
|
+
const event = await agent.command(command);
|
|
28923
|
+
if (event) writeEvent(output, event);
|
|
28924
|
+
if (command.type === "detach") stopPoll();
|
|
28925
|
+
}
|
|
28926
|
+
if (opened) await agent.detach();
|
|
28927
|
+
return 0;
|
|
28928
|
+
} catch (cause) {
|
|
28929
|
+
stopPoll();
|
|
28930
|
+
error.write(`[brain-terminal-agent] ${cause instanceof Error ? cause.message : String(cause)}
|
|
28931
|
+
`);
|
|
28932
|
+
return 1;
|
|
28933
|
+
} finally {
|
|
28934
|
+
stopPoll();
|
|
28935
|
+
lines.close();
|
|
28936
|
+
}
|
|
28937
|
+
}
|
|
28938
|
+
|
|
28305
28939
|
// src/servers/pool-serve.ts
|
|
28306
28940
|
import { createServer as createServer5 } from "http";
|
|
28307
28941
|
|
|
@@ -28373,8 +29007,8 @@ var FlyClient = class {
|
|
|
28373
29007
|
get fetch() {
|
|
28374
29008
|
return this.opts.fetchImpl ?? fetch;
|
|
28375
29009
|
}
|
|
28376
|
-
async call(
|
|
28377
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
29010
|
+
async call(path58, init = {}) {
|
|
29011
|
+
const res = await this.fetch(`${FLY_API_BASE}${path58}`, {
|
|
28378
29012
|
method: init.method ?? "GET",
|
|
28379
29013
|
headers: {
|
|
28380
29014
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -28385,7 +29019,7 @@ var FlyClient = class {
|
|
|
28385
29019
|
if (res.status === 404 && init.allow404) return null;
|
|
28386
29020
|
if (!res.ok) {
|
|
28387
29021
|
const text2 = await res.text().catch(() => "");
|
|
28388
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
29022
|
+
throw new Error(`Fly API ${res.status} on ${path58}: ${text2.slice(0, 200) || res.statusText}`);
|
|
28389
29023
|
}
|
|
28390
29024
|
if (res.status === 204) return null;
|
|
28391
29025
|
const raw = await res.text();
|
|
@@ -28898,14 +29532,14 @@ function sendJson2(res, status, body) {
|
|
|
28898
29532
|
res.end(JSON.stringify(body));
|
|
28899
29533
|
}
|
|
28900
29534
|
function readJsonBody2(req) {
|
|
28901
|
-
return new Promise((
|
|
29535
|
+
return new Promise((resolve23, reject) => {
|
|
28902
29536
|
const chunks = [];
|
|
28903
29537
|
req.on("data", (c) => chunks.push(c));
|
|
28904
29538
|
req.on("end", () => {
|
|
28905
29539
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
28906
|
-
if (!raw.trim()) return
|
|
29540
|
+
if (!raw.trim()) return resolve23({});
|
|
28907
29541
|
try {
|
|
28908
|
-
|
|
29542
|
+
resolve23(JSON.parse(raw));
|
|
28909
29543
|
} catch (err) {
|
|
28910
29544
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
28911
29545
|
}
|
|
@@ -29059,10 +29693,10 @@ async function poolServe() {
|
|
|
29059
29693
|
}
|
|
29060
29694
|
});
|
|
29061
29695
|
const apiHost = process.env.POOL_API_HOST ?? "::";
|
|
29062
|
-
await new Promise((
|
|
29696
|
+
await new Promise((resolve23) => {
|
|
29063
29697
|
server.listen(apiPort, apiHost, () => {
|
|
29064
29698
|
log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
|
|
29065
|
-
|
|
29699
|
+
resolve23();
|
|
29066
29700
|
});
|
|
29067
29701
|
});
|
|
29068
29702
|
if (loopTickEnabled) void runLoopTick();
|
|
@@ -29080,8 +29714,8 @@ async function poolServe() {
|
|
|
29080
29714
|
}
|
|
29081
29715
|
|
|
29082
29716
|
// src/servers/runner-serve.ts
|
|
29083
|
-
import { spawn as
|
|
29084
|
-
import * as
|
|
29717
|
+
import { spawn as spawn10 } from "child_process";
|
|
29718
|
+
import * as fs57 from "fs";
|
|
29085
29719
|
import { createServer as createServer6 } from "http";
|
|
29086
29720
|
var DEFAULT_PORT2 = 8080;
|
|
29087
29721
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -29102,17 +29736,17 @@ function authOk2(req, expected) {
|
|
|
29102
29736
|
return false;
|
|
29103
29737
|
}
|
|
29104
29738
|
function readJsonBody3(req) {
|
|
29105
|
-
return new Promise((
|
|
29739
|
+
return new Promise((resolve23, reject) => {
|
|
29106
29740
|
const chunks = [];
|
|
29107
29741
|
req.on("data", (c) => chunks.push(c));
|
|
29108
29742
|
req.on("end", () => {
|
|
29109
29743
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
29110
29744
|
if (!raw.trim()) {
|
|
29111
|
-
|
|
29745
|
+
resolve23({});
|
|
29112
29746
|
return;
|
|
29113
29747
|
}
|
|
29114
29748
|
try {
|
|
29115
|
-
|
|
29749
|
+
resolve23(JSON.parse(raw));
|
|
29116
29750
|
} catch (err) {
|
|
29117
29751
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
29118
29752
|
}
|
|
@@ -29157,8 +29791,8 @@ async function defaultRunJob(job) {
|
|
|
29157
29791
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
29158
29792
|
const branch = job.ref ?? "main";
|
|
29159
29793
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
29160
|
-
|
|
29161
|
-
|
|
29794
|
+
fs57.rmSync(workdir, { recursive: true, force: true });
|
|
29795
|
+
fs57.mkdirSync(workdir, { recursive: true });
|
|
29162
29796
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
29163
29797
|
const target = job.runRequest.target;
|
|
29164
29798
|
const interactive = target.type === "chat";
|
|
@@ -29187,13 +29821,13 @@ async function defaultRunJob(job) {
|
|
|
29187
29821
|
...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
|
|
29188
29822
|
...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
|
|
29189
29823
|
};
|
|
29190
|
-
const run = (cmd, args, cwd) => new Promise((
|
|
29191
|
-
const child =
|
|
29192
|
-
child.on("exit", (code) =>
|
|
29824
|
+
const run = (cmd, args, cwd) => new Promise((resolve23) => {
|
|
29825
|
+
const child = spawn10(cmd, args, { stdio: "inherit", env: childEnv, cwd });
|
|
29826
|
+
child.on("exit", (code) => resolve23(code ?? 0));
|
|
29193
29827
|
child.on("error", (err) => {
|
|
29194
29828
|
process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
|
|
29195
29829
|
`);
|
|
29196
|
-
|
|
29830
|
+
resolve23(1);
|
|
29197
29831
|
});
|
|
29198
29832
|
});
|
|
29199
29833
|
process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
|
|
@@ -29269,11 +29903,11 @@ async function runnerServe() {
|
|
|
29269
29903
|
const port = Number(process.env.PORT ?? DEFAULT_PORT2);
|
|
29270
29904
|
const server = buildServer2({ apiKey });
|
|
29271
29905
|
const host = process.env.RUNNER_HOST ?? "::";
|
|
29272
|
-
await new Promise((
|
|
29906
|
+
await new Promise((resolve23) => {
|
|
29273
29907
|
server.listen(port, host, () => {
|
|
29274
29908
|
process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
|
|
29275
29909
|
`);
|
|
29276
|
-
|
|
29910
|
+
resolve23();
|
|
29277
29911
|
});
|
|
29278
29912
|
});
|
|
29279
29913
|
const shutdown = (signal) => {
|
|
@@ -29291,7 +29925,7 @@ async function runnerServe() {
|
|
|
29291
29925
|
// src/servers/serve.ts
|
|
29292
29926
|
init_config();
|
|
29293
29927
|
init_litellm();
|
|
29294
|
-
import { spawn as
|
|
29928
|
+
import { spawn as spawn11 } from "child_process";
|
|
29295
29929
|
function parseTarget(positional) {
|
|
29296
29930
|
if (!Array.isArray(positional) || positional.length === 0) return "none";
|
|
29297
29931
|
const first = String(positional[0]).toLowerCase();
|
|
@@ -29341,15 +29975,15 @@ async function serve(opts) {
|
|
|
29341
29975
|
if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
|
|
29342
29976
|
`);
|
|
29343
29977
|
const args = ["--dangerously-skip-permissions", "--model", model.model];
|
|
29344
|
-
const child =
|
|
29345
|
-
const exitCode = await new Promise((
|
|
29346
|
-
child.on("exit", (code) =>
|
|
29978
|
+
const child = spawn11("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
|
|
29979
|
+
const exitCode = await new Promise((resolve23) => {
|
|
29980
|
+
child.on("exit", (code) => resolve23(code ?? 0));
|
|
29347
29981
|
child.on("error", (err) => {
|
|
29348
29982
|
process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
|
|
29349
29983
|
`);
|
|
29350
29984
|
process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
|
|
29351
29985
|
`);
|
|
29352
|
-
|
|
29986
|
+
resolve23(1);
|
|
29353
29987
|
});
|
|
29354
29988
|
});
|
|
29355
29989
|
killProxy();
|
|
@@ -29361,7 +29995,7 @@ async function serve(opts) {
|
|
|
29361
29995
|
if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
|
|
29362
29996
|
`);
|
|
29363
29997
|
try {
|
|
29364
|
-
const code =
|
|
29998
|
+
const code = spawn11("code", [opts.cwd], { stdio: "inherit", env: editorEnv, detached: true });
|
|
29365
29999
|
code.on("error", (err) => {
|
|
29366
30000
|
process.stderr.write(`[kody serve] failed to launch VS Code: ${err.message}
|
|
29367
30001
|
`);
|
|
@@ -29709,7 +30343,15 @@ function parseArgs(argv) {
|
|
|
29709
30343
|
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
29710
30344
|
return result;
|
|
29711
30345
|
}
|
|
29712
|
-
const SERVER_VERBS = /* @__PURE__ */ new Set([
|
|
30346
|
+
const SERVER_VERBS = /* @__PURE__ */ new Set([
|
|
30347
|
+
"serve",
|
|
30348
|
+
"pool-serve",
|
|
30349
|
+
"runner-serve",
|
|
30350
|
+
"brain-serve",
|
|
30351
|
+
"brain-terminal-agent",
|
|
30352
|
+
"brain-proxy",
|
|
30353
|
+
"mcp-http-server"
|
|
30354
|
+
]);
|
|
29713
30355
|
if (SERVER_VERBS.has(cmd)) {
|
|
29714
30356
|
result.command = "server";
|
|
29715
30357
|
result.serverName = cmd;
|
|
@@ -29813,6 +30455,8 @@ ${HELP_TEXT}`);
|
|
|
29813
30455
|
return await runnerServe();
|
|
29814
30456
|
case "brain-serve":
|
|
29815
30457
|
return await brainServe({ cwd: cwd2 });
|
|
30458
|
+
case "brain-terminal-agent":
|
|
30459
|
+
return await brainTerminalAgent({ cwd: cwd2 });
|
|
29816
30460
|
case "brain-proxy":
|
|
29817
30461
|
return await brainProxy();
|
|
29818
30462
|
case "mcp-http-server":
|