@kody-ade/kody-engine 0.4.564 → 0.4.565
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 +1263 -655
- 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.565",
|
|
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) {
|
|
@@ -2366,51 +2389,51 @@ var init_capabilityFolders = __esm({
|
|
|
2366
2389
|
});
|
|
2367
2390
|
|
|
2368
2391
|
// src/definition-paths.ts
|
|
2369
|
-
import * as
|
|
2370
|
-
import * as
|
|
2392
|
+
import * as fs9 from "fs";
|
|
2393
|
+
import * as path10 from "path";
|
|
2371
2394
|
function definitionsRoot(cwd = process.cwd()) {
|
|
2372
2395
|
const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
|
|
2373
2396
|
const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
|
|
2374
|
-
if (override && overrideCwd &&
|
|
2375
|
-
return storeCatalogRoot(
|
|
2397
|
+
if (override && overrideCwd && path10.resolve(cwd) === path10.resolve(overrideCwd)) {
|
|
2398
|
+
return storeCatalogRoot(path10.resolve(override));
|
|
2376
2399
|
}
|
|
2377
|
-
const hydrated =
|
|
2378
|
-
if (
|
|
2379
|
-
return override ? storeCatalogRoot(
|
|
2400
|
+
const hydrated = path10.join(cwd, ".kody-engine", "definitions");
|
|
2401
|
+
if (fs9.existsSync(hydrated)) return hydrated;
|
|
2402
|
+
return override ? storeCatalogRoot(path10.resolve(override)) : hydrated;
|
|
2380
2403
|
}
|
|
2381
2404
|
function hasExplicitDefinitionsRoot(cwd = process.cwd(), env = process.env) {
|
|
2382
2405
|
const root = env.KODY_DEFINITIONS_ROOT?.trim();
|
|
2383
2406
|
const rootCwd = env.KODY_DEFINITIONS_ROOT_CWD?.trim();
|
|
2384
|
-
return Boolean(root && rootCwd &&
|
|
2407
|
+
return Boolean(root && rootCwd && path10.resolve(cwd) === path10.resolve(rootCwd));
|
|
2385
2408
|
}
|
|
2386
2409
|
function capabilitiesRoot(cwd = process.cwd()) {
|
|
2387
|
-
return storeAssetRoot(cwd, "capabilities") ??
|
|
2410
|
+
return storeAssetRoot(cwd, "capabilities") ?? path10.join(definitionsRoot(cwd), "capabilities");
|
|
2388
2411
|
}
|
|
2389
2412
|
function implementationsRoot(cwd = process.cwd()) {
|
|
2390
|
-
return
|
|
2413
|
+
return path10.join(definitionsRoot(cwd), "implementations");
|
|
2391
2414
|
}
|
|
2392
2415
|
function agentsRoot(cwd = process.cwd()) {
|
|
2393
|
-
return storeAssetRoot(cwd, "agent") ??
|
|
2416
|
+
return storeAssetRoot(cwd, "agent") ?? path10.join(definitionsRoot(cwd), "agents");
|
|
2394
2417
|
}
|
|
2395
2418
|
function storeCatalogRoot(root) {
|
|
2396
2419
|
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 ?
|
|
2420
|
+
const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) => path10.dirname(value));
|
|
2421
|
+
return roots.length === 3 && new Set(roots).size === 1 ? path10.join(root, roots[0]) : root;
|
|
2399
2422
|
}
|
|
2400
2423
|
function storeAssetRoot(cwd, kind) {
|
|
2401
2424
|
const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
|
|
2402
2425
|
if (!override) return null;
|
|
2403
2426
|
const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
|
|
2404
|
-
if (overrideCwd &&
|
|
2405
|
-
const root =
|
|
2427
|
+
if (overrideCwd && path10.resolve(cwd) !== path10.resolve(overrideCwd)) return null;
|
|
2428
|
+
const root = path10.resolve(override);
|
|
2406
2429
|
const configured = readStoreManifest(root)?.assetRoots?.[kind];
|
|
2407
|
-
return typeof configured === "string" && configured.trim() ?
|
|
2430
|
+
return typeof configured === "string" && configured.trim() ? path10.join(root, configured) : null;
|
|
2408
2431
|
}
|
|
2409
2432
|
function readStoreManifest(root) {
|
|
2410
|
-
const file =
|
|
2411
|
-
if (!
|
|
2433
|
+
const file = path10.join(root, "kody-store.json");
|
|
2434
|
+
if (!fs9.existsSync(file)) return null;
|
|
2412
2435
|
try {
|
|
2413
|
-
return JSON.parse(
|
|
2436
|
+
return JSON.parse(fs9.readFileSync(file, "utf8"));
|
|
2414
2437
|
} catch {
|
|
2415
2438
|
return null;
|
|
2416
2439
|
}
|
|
@@ -2422,32 +2445,32 @@ var init_definition_paths = __esm({
|
|
|
2422
2445
|
});
|
|
2423
2446
|
|
|
2424
2447
|
// src/registry.ts
|
|
2425
|
-
import * as
|
|
2426
|
-
import * as
|
|
2448
|
+
import * as fs10 from "fs";
|
|
2449
|
+
import * as path11 from "path";
|
|
2427
2450
|
function getImplementationsRoot() {
|
|
2428
|
-
const here =
|
|
2451
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2429
2452
|
const candidates = [
|
|
2430
|
-
|
|
2453
|
+
path11.join(here, "implementations"),
|
|
2431
2454
|
// dev: src/
|
|
2432
|
-
|
|
2455
|
+
path11.join(here, "..", "implementations"),
|
|
2433
2456
|
// built: dist/bin → dist/implementations
|
|
2434
|
-
|
|
2457
|
+
path11.join(here, "..", "src", "implementations")
|
|
2435
2458
|
// fallback
|
|
2436
2459
|
];
|
|
2437
2460
|
for (const c of candidates) {
|
|
2438
|
-
if (
|
|
2461
|
+
if (fs10.existsSync(c) && fs10.statSync(c).isDirectory()) return c;
|
|
2439
2462
|
}
|
|
2440
2463
|
return candidates[0];
|
|
2441
2464
|
}
|
|
2442
2465
|
function getRuntimeServicesRoot() {
|
|
2443
|
-
const here =
|
|
2466
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2444
2467
|
const candidates = [
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2468
|
+
path11.join(here, "runtime-services"),
|
|
2469
|
+
path11.join(here, "..", "runtime-services"),
|
|
2470
|
+
path11.join(here, "..", "src", "runtime-services")
|
|
2448
2471
|
];
|
|
2449
2472
|
for (const candidate of candidates) {
|
|
2450
|
-
if (
|
|
2473
|
+
if (fs10.existsSync(candidate) && fs10.statSync(candidate).isDirectory()) return candidate;
|
|
2451
2474
|
}
|
|
2452
2475
|
return candidates[0];
|
|
2453
2476
|
}
|
|
@@ -2455,17 +2478,17 @@ function getProjectCapabilitiesRoot() {
|
|
|
2455
2478
|
return capabilitiesRoot();
|
|
2456
2479
|
}
|
|
2457
2480
|
function getBuiltinCapabilitiesRoot() {
|
|
2458
|
-
const here =
|
|
2481
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2459
2482
|
const candidates = [
|
|
2460
|
-
|
|
2483
|
+
path11.join(here, "capabilities"),
|
|
2461
2484
|
// dev: src/
|
|
2462
|
-
|
|
2485
|
+
path11.join(here, "..", "capabilities"),
|
|
2463
2486
|
// built: dist/bin → dist/capabilities
|
|
2464
|
-
|
|
2487
|
+
path11.join(here, "..", "src", "capabilities")
|
|
2465
2488
|
// fallback
|
|
2466
2489
|
];
|
|
2467
2490
|
for (const c of candidates) {
|
|
2468
|
-
if (
|
|
2491
|
+
if (fs10.existsSync(c) && fs10.statSync(c).isDirectory()) return c;
|
|
2469
2492
|
}
|
|
2470
2493
|
return candidates[0];
|
|
2471
2494
|
}
|
|
@@ -2488,14 +2511,14 @@ function listImplementations(roots = getImplementationRoots()) {
|
|
|
2488
2511
|
const seen = /* @__PURE__ */ new Set();
|
|
2489
2512
|
const out = [];
|
|
2490
2513
|
for (const root of rootList) {
|
|
2491
|
-
if (!
|
|
2514
|
+
if (!fs10.existsSync(root)) continue;
|
|
2492
2515
|
const requireImplementationProfile = isCapabilityRoot(root);
|
|
2493
|
-
const entries =
|
|
2516
|
+
const entries = fs10.readdirSync(root, { withFileTypes: true });
|
|
2494
2517
|
for (const ent of entries) {
|
|
2495
2518
|
if (!ent.isDirectory()) continue;
|
|
2496
2519
|
if (seen.has(ent.name)) continue;
|
|
2497
2520
|
const profilePath = implementationRuntimePath(root, ent.name);
|
|
2498
|
-
if (
|
|
2521
|
+
if (fs10.existsSync(profilePath) && fs10.statSync(profilePath).isFile() && isImplementationProfile(profilePath, requireImplementationProfile)) {
|
|
2499
2522
|
out.push({ name: ent.name, profilePath });
|
|
2500
2523
|
seen.add(ent.name);
|
|
2501
2524
|
}
|
|
@@ -2515,7 +2538,7 @@ function resolveImplementationCandidates(name, roots = getRuntimeProfileRootsFor
|
|
|
2515
2538
|
const out = [];
|
|
2516
2539
|
for (const root of rootList) {
|
|
2517
2540
|
const profilePath = implementationRuntimePath(root, name);
|
|
2518
|
-
if (
|
|
2541
|
+
if (fs10.existsSync(profilePath) && fs10.statSync(profilePath).isFile() && isImplementationProfile(profilePath, isCapabilityRoot(root))) {
|
|
2519
2542
|
out.push(profilePath);
|
|
2520
2543
|
}
|
|
2521
2544
|
}
|
|
@@ -2589,7 +2612,7 @@ function implementationDeclaresInput(implementation, inputName, cwd = process.cw
|
|
|
2589
2612
|
const profilePath = resolveImplementation(implementation, getRuntimeProfileRootsForCwd(cwd));
|
|
2590
2613
|
if (!profilePath) return false;
|
|
2591
2614
|
try {
|
|
2592
|
-
const document = JSON.parse(
|
|
2615
|
+
const document = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2593
2616
|
const raw = document.config ?? document;
|
|
2594
2617
|
if (!Array.isArray(raw.inputs)) return false;
|
|
2595
2618
|
return raw.inputs.some((entry) => {
|
|
@@ -2605,29 +2628,29 @@ function isSafeName(name) {
|
|
|
2605
2628
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2606
2629
|
}
|
|
2607
2630
|
function isCapabilityRoot(root) {
|
|
2608
|
-
const normalized =
|
|
2609
|
-
if (
|
|
2631
|
+
const normalized = path11.normalize(root);
|
|
2632
|
+
if (path11.basename(normalized) === "capabilities") return true;
|
|
2610
2633
|
const knownRoots = [getProjectCapabilitiesRoot(), getBuiltinCapabilitiesRoot()];
|
|
2611
|
-
return knownRoots.some((candidate) => candidate &&
|
|
2634
|
+
return knownRoots.some((candidate) => candidate && path11.normalize(candidate) === normalized);
|
|
2612
2635
|
}
|
|
2613
2636
|
function implementationRuntimePath(root, name) {
|
|
2614
|
-
const runtimePath =
|
|
2615
|
-
if (
|
|
2616
|
-
const internalProfilePath =
|
|
2617
|
-
if (
|
|
2618
|
-
return
|
|
2637
|
+
const runtimePath = path11.join(root, name, "runtime.json");
|
|
2638
|
+
if (fs10.existsSync(runtimePath)) return runtimePath;
|
|
2639
|
+
const internalProfilePath = path11.join(root, name, "profile.json");
|
|
2640
|
+
if (fs10.existsSync(internalProfilePath)) return internalProfilePath;
|
|
2641
|
+
return path11.join(root, name, CAPABILITY_PROFILE_FILE);
|
|
2619
2642
|
}
|
|
2620
2643
|
function isImplementationProfile(profilePath, requireImplementationProfile) {
|
|
2621
2644
|
if (!requireImplementationProfile) return true;
|
|
2622
2645
|
try {
|
|
2623
|
-
const raw = JSON.parse(
|
|
2646
|
+
const raw = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2624
2647
|
return typeof raw.role === "string" && PUBLIC_IMPLEMENTATION_ROLES.has(raw.role);
|
|
2625
2648
|
} catch {
|
|
2626
2649
|
return false;
|
|
2627
2650
|
}
|
|
2628
2651
|
}
|
|
2629
2652
|
function listFolderCapabilityActions(root, source) {
|
|
2630
|
-
if (!
|
|
2653
|
+
if (!fs10.existsSync(root) || !fs10.statSync(root).isDirectory()) return [];
|
|
2631
2654
|
const out = [];
|
|
2632
2655
|
for (const slug of listCapabilityFolderSlugs(root)) {
|
|
2633
2656
|
if (!isSafeName(slug)) continue;
|
|
@@ -2659,7 +2682,7 @@ function hasUnresolvedExplicitImplementation(capability, implementation) {
|
|
|
2659
2682
|
return resolveImplementation(implementation) === null;
|
|
2660
2683
|
}
|
|
2661
2684
|
function listBuiltinCapabilityActions(root = getBuiltinCapabilitiesRoot()) {
|
|
2662
|
-
if (!
|
|
2685
|
+
if (!fs10.existsSync(root) || !fs10.statSync(root).isDirectory()) return [];
|
|
2663
2686
|
const out = [];
|
|
2664
2687
|
for (const slug of listCapabilityFolderSlugs(root)) {
|
|
2665
2688
|
if (!isSafeName(slug)) continue;
|
|
@@ -2684,7 +2707,7 @@ function getProfileInputs(name, roots = getImplementationRoots()) {
|
|
|
2684
2707
|
const profilePath = resolveImplementation(name, roots);
|
|
2685
2708
|
if (!profilePath) return null;
|
|
2686
2709
|
try {
|
|
2687
|
-
const document = JSON.parse(
|
|
2710
|
+
const document = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2688
2711
|
if (!document || typeof document !== "object") return [];
|
|
2689
2712
|
const raw = "config" in document && document.config && typeof document.config === "object" ? document.config : document;
|
|
2690
2713
|
if (!Array.isArray(raw.inputs)) return [];
|
|
@@ -3781,8 +3804,8 @@ var init_capabilityMcp = __esm({
|
|
|
3781
3804
|
|
|
3782
3805
|
// src/repoWorkspace.ts
|
|
3783
3806
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
3784
|
-
import * as
|
|
3785
|
-
import * as
|
|
3807
|
+
import * as fs11 from "fs";
|
|
3808
|
+
import * as path12 from "path";
|
|
3786
3809
|
function buildCloneProcess(repo, token, baseEnv = process.env) {
|
|
3787
3810
|
const url = `https://github.com/${repo}.git`;
|
|
3788
3811
|
const env = { ...baseEnv };
|
|
@@ -3797,10 +3820,10 @@ function buildCloneProcess(repo, token, baseEnv = process.env) {
|
|
|
3797
3820
|
async function resolveAndClone(reposRoot, repo, repoToken, cloneRepo) {
|
|
3798
3821
|
const name = repo?.trim();
|
|
3799
3822
|
if (!name || !REPO_RE.test(name)) return null;
|
|
3800
|
-
const root =
|
|
3801
|
-
const dir =
|
|
3802
|
-
if (dir !== root && !dir.startsWith(root +
|
|
3803
|
-
if (
|
|
3823
|
+
const root = path12.resolve(reposRoot);
|
|
3824
|
+
const dir = path12.resolve(root, name);
|
|
3825
|
+
if (dir !== root && !dir.startsWith(root + path12.sep)) return null;
|
|
3826
|
+
if (fs11.existsSync(path12.join(dir, ".git"))) return dir;
|
|
3804
3827
|
const inflight = repoClones.get(dir);
|
|
3805
3828
|
if (inflight) {
|
|
3806
3829
|
await inflight;
|
|
@@ -3832,9 +3855,9 @@ var init_repoWorkspace = __esm({
|
|
|
3832
3855
|
repoClones = /* @__PURE__ */ new Map();
|
|
3833
3856
|
GIT_CREDENTIAL_HELPER = `!f() { if [ "$1" = get ] && [ -n "$GITHUB_TOKEN" ]; then printf '%s\\n' username=x-access-token "password=$GITHUB_TOKEN"; fi; }; f`;
|
|
3834
3857
|
defaultCloneRepo = (repo, token, dir) => {
|
|
3835
|
-
|
|
3858
|
+
fs11.mkdirSync(path12.dirname(dir), { recursive: true });
|
|
3836
3859
|
const clone = buildCloneProcess(repo, token);
|
|
3837
|
-
return new Promise((
|
|
3860
|
+
return new Promise((resolve23, reject) => {
|
|
3838
3861
|
const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
|
|
3839
3862
|
env: clone.env,
|
|
3840
3863
|
stdio: "inherit"
|
|
@@ -3854,7 +3877,7 @@ var init_repoWorkspace = __esm({
|
|
|
3854
3877
|
}
|
|
3855
3878
|
} catch {
|
|
3856
3879
|
}
|
|
3857
|
-
|
|
3880
|
+
resolve23();
|
|
3858
3881
|
});
|
|
3859
3882
|
child.on("error", reject);
|
|
3860
3883
|
});
|
|
@@ -3926,8 +3949,8 @@ var init_fetchRepoMcp = __esm({
|
|
|
3926
3949
|
});
|
|
3927
3950
|
|
|
3928
3951
|
// src/agent.ts
|
|
3929
|
-
import * as
|
|
3930
|
-
import * as
|
|
3952
|
+
import * as fs12 from "fs";
|
|
3953
|
+
import * as path13 from "path";
|
|
3931
3954
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3932
3955
|
function classifySubtype(subtype) {
|
|
3933
3956
|
if (!subtype) return "generic_failed";
|
|
@@ -3996,8 +4019,8 @@ function buildAgentEnvironment(baseEnv, repoToken, requestEnvironment) {
|
|
|
3996
4019
|
}
|
|
3997
4020
|
async function runAgent(opts) {
|
|
3998
4021
|
const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
|
|
3999
|
-
|
|
4000
|
-
const ndjsonPath =
|
|
4022
|
+
fs12.mkdirSync(ndjsonDir, { recursive: true });
|
|
4023
|
+
const ndjsonPath = path13.join(ndjsonDir, "last-run.jsonl");
|
|
4001
4024
|
const env = buildAgentEnvironment(process.env, opts.repoToken, opts.environment);
|
|
4002
4025
|
if (opts.litellmUrl) {
|
|
4003
4026
|
env.ANTHROPIC_BASE_URL = opts.litellmUrl;
|
|
@@ -4015,12 +4038,13 @@ async function runAgent(opts) {
|
|
|
4015
4038
|
let getSubmitted;
|
|
4016
4039
|
const invokedSubagents = /* @__PURE__ */ new Set();
|
|
4017
4040
|
const subagentInvocationHook = createSubagentInvocationHook(invokedSubagents);
|
|
4041
|
+
const missingParentWriteGuard = createMissingParentWriteGuard(opts.cwd);
|
|
4018
4042
|
const outputContractPostWriteHook = opts.outputContract ? createOutputContractPostWriteHook(opts.outputContract) : null;
|
|
4019
4043
|
const outputContractStopHook = opts.outputContract ? createOutputContractStopHook(opts.outputContract) : null;
|
|
4020
4044
|
for (let attempt = 0; ; attempt++) {
|
|
4021
4045
|
let ndjsonWriteFailed = false;
|
|
4022
4046
|
let ndjsonWriteError;
|
|
4023
|
-
const fullLog =
|
|
4047
|
+
const fullLog = fs12.createWriteStream(ndjsonPath, { flags: "w" });
|
|
4024
4048
|
fullLog.on("error", (err) => {
|
|
4025
4049
|
ndjsonWriteFailed = true;
|
|
4026
4050
|
ndjsonWriteError = err instanceof Error ? err.message : String(err);
|
|
@@ -4051,6 +4075,10 @@ async function runAgent(opts) {
|
|
|
4051
4075
|
{
|
|
4052
4076
|
matcher: "Agent",
|
|
4053
4077
|
hooks: [enforceSubagentModelInheritance]
|
|
4078
|
+
},
|
|
4079
|
+
{
|
|
4080
|
+
matcher: "Write",
|
|
4081
|
+
hooks: [missingParentWriteGuard]
|
|
4054
4082
|
}
|
|
4055
4083
|
],
|
|
4056
4084
|
PostToolUse: [
|
|
@@ -4201,10 +4229,10 @@ async function runAgent(opts) {
|
|
|
4201
4229
|
let timer;
|
|
4202
4230
|
let next;
|
|
4203
4231
|
if (turnTimeoutMs > 0) {
|
|
4204
|
-
const timeoutPromise = new Promise((
|
|
4232
|
+
const timeoutPromise = new Promise((resolve23) => {
|
|
4205
4233
|
timer = setTimeout(() => {
|
|
4206
4234
|
timedOut = true;
|
|
4207
|
-
|
|
4235
|
+
resolve23({ done: true, value: void 0 });
|
|
4208
4236
|
}, turnTimeoutMs);
|
|
4209
4237
|
});
|
|
4210
4238
|
next = await Promise.race([nextPromise, timeoutPromise]);
|
|
@@ -4220,7 +4248,7 @@ async function runAgent(opts) {
|
|
|
4220
4248
|
try {
|
|
4221
4249
|
await Promise.race([
|
|
4222
4250
|
iterator.return(void 0).catch(() => void 0),
|
|
4223
|
-
new Promise((
|
|
4251
|
+
new Promise((resolve23) => setTimeout(resolve23, 1e4).unref())
|
|
4224
4252
|
]);
|
|
4225
4253
|
} catch {
|
|
4226
4254
|
}
|
|
@@ -4403,6 +4431,7 @@ var init_agent = __esm({
|
|
|
4403
4431
|
init_claudeBinary();
|
|
4404
4432
|
init_config();
|
|
4405
4433
|
init_format();
|
|
4434
|
+
init_fileEditGuards();
|
|
4406
4435
|
init_outputContractHooks();
|
|
4407
4436
|
init_runtimePaths();
|
|
4408
4437
|
init_subagents();
|
|
@@ -4424,8 +4453,8 @@ var init_agent = __esm({
|
|
|
4424
4453
|
});
|
|
4425
4454
|
|
|
4426
4455
|
// src/agents.ts
|
|
4427
|
-
import * as
|
|
4428
|
-
import * as
|
|
4456
|
+
import * as fs13 from "fs";
|
|
4457
|
+
import * as path14 from "path";
|
|
4429
4458
|
function stripFrontmatter(raw) {
|
|
4430
4459
|
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
4431
4460
|
return (match ? match[1] : raw).trim();
|
|
@@ -4434,8 +4463,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
|
|
|
4434
4463
|
const trimmed = slug.trim();
|
|
4435
4464
|
if (!trimmed) throw new Error("loadAgentIdentity: empty agent slug");
|
|
4436
4465
|
const agentPath = resolveAgentFile2(cwd, trimmed, agentsDir);
|
|
4437
|
-
if (
|
|
4438
|
-
const body = stripFrontmatter(
|
|
4466
|
+
if (fs13.existsSync(agentPath)) {
|
|
4467
|
+
const body = stripFrontmatter(fs13.readFileSync(agentPath, "utf-8"));
|
|
4439
4468
|
if (body) return body;
|
|
4440
4469
|
const builtinForEmpty = BUILTIN_AGENTS[trimmed];
|
|
4441
4470
|
if (builtinForEmpty) return builtinForEmpty;
|
|
@@ -4446,8 +4475,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
|
|
|
4446
4475
|
throw new Error(`loadAgentIdentity: agent '${trimmed}' declared but ${agentPath} does not exist`);
|
|
4447
4476
|
}
|
|
4448
4477
|
function resolveAgentFile2(cwd, slug, agentsDir = agentsRoot(cwd)) {
|
|
4449
|
-
const localPath =
|
|
4450
|
-
if (
|
|
4478
|
+
const localPath = path14.resolve(cwd, agentsDir, `${slug}.md`);
|
|
4479
|
+
if (fs13.existsSync(localPath)) return localPath;
|
|
4451
4480
|
return localPath;
|
|
4452
4481
|
}
|
|
4453
4482
|
function frameAgentIdentity(slug, agent) {
|
|
@@ -4479,14 +4508,14 @@ var init_agents = __esm({
|
|
|
4479
4508
|
});
|
|
4480
4509
|
|
|
4481
4510
|
// src/task-artifacts.ts
|
|
4482
|
-
import
|
|
4483
|
-
import
|
|
4511
|
+
import fs14 from "fs";
|
|
4512
|
+
import path15 from "path";
|
|
4484
4513
|
import posixPath from "path/posix";
|
|
4485
4514
|
function prepareTaskArtifactsDir(cwd, taskId) {
|
|
4486
4515
|
const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4487
4516
|
const absDir = runtimeStatePath(cwd, "task-artifacts", safeId);
|
|
4488
4517
|
const relDir = absDir;
|
|
4489
|
-
|
|
4518
|
+
fs14.mkdirSync(absDir, { recursive: true });
|
|
4490
4519
|
return { taskId: safeId, absDir, relDir };
|
|
4491
4520
|
}
|
|
4492
4521
|
function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
|
|
@@ -4516,16 +4545,16 @@ function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
|
|
|
4516
4545
|
"handoff-notes.md": "Baseline handoff: the agent did not provide additional notes.\n"
|
|
4517
4546
|
};
|
|
4518
4547
|
for (const file of TASK_ARTIFACT_FILES) {
|
|
4519
|
-
const full =
|
|
4520
|
-
if (!
|
|
4548
|
+
const full = path15.join(artifacts.absDir, file);
|
|
4549
|
+
if (!fs14.existsSync(full)) fs14.writeFileSync(full, defaults[file], "utf8");
|
|
4521
4550
|
}
|
|
4522
4551
|
}
|
|
4523
4552
|
function verifyTaskArtifacts(absDir) {
|
|
4524
4553
|
const missing = [];
|
|
4525
4554
|
for (const name of TASK_ARTIFACT_FILES) {
|
|
4526
|
-
const full =
|
|
4555
|
+
const full = path15.join(absDir, name);
|
|
4527
4556
|
try {
|
|
4528
|
-
const stat =
|
|
4557
|
+
const stat = fs14.statSync(full);
|
|
4529
4558
|
if (!stat.isFile() || stat.size === 0) missing.push(name);
|
|
4530
4559
|
} catch {
|
|
4531
4560
|
missing.push(name);
|
|
@@ -4541,11 +4570,11 @@ async function persistTaskArtifactsToState(config, _cwd, artifacts) {
|
|
|
4541
4570
|
if (hasStateBackendConfig() && tenantId2) {
|
|
4542
4571
|
const backend = createStateBackendFromEnv();
|
|
4543
4572
|
for (const file of TASK_ARTIFACT_FILES) {
|
|
4544
|
-
const full =
|
|
4545
|
-
if (!
|
|
4546
|
-
const stat =
|
|
4573
|
+
const full = path15.join(artifacts.absDir, file);
|
|
4574
|
+
if (!fs14.existsSync(full)) continue;
|
|
4575
|
+
const stat = fs14.statSync(full);
|
|
4547
4576
|
if (!stat.isFile() || stat.size === 0) continue;
|
|
4548
|
-
const content =
|
|
4577
|
+
const content = fs14.readFileSync(full, "utf-8");
|
|
4549
4578
|
const kind = file.replace(/\.(json|md)$/, "");
|
|
4550
4579
|
let doc = content;
|
|
4551
4580
|
if (file.endsWith(".json")) {
|
|
@@ -4845,15 +4874,15 @@ function validateWorkflow(value, options = {}) {
|
|
|
4845
4874
|
}
|
|
4846
4875
|
return issues;
|
|
4847
4876
|
}
|
|
4848
|
-
function validateInputBindings(value,
|
|
4877
|
+
function validateInputBindings(value, path58, issues, declaredInputs) {
|
|
4849
4878
|
if (value === void 0) return;
|
|
4850
4879
|
const bindings = asRecord(value);
|
|
4851
4880
|
if (!bindings || Object.keys(bindings).length === 0) {
|
|
4852
|
-
issue(issues, "invalid_inputs",
|
|
4881
|
+
issue(issues, "invalid_inputs", path58, "workflow step inputs must contain at least one named mapping");
|
|
4853
4882
|
return;
|
|
4854
4883
|
}
|
|
4855
4884
|
for (const [name, value2] of Object.entries(bindings)) {
|
|
4856
|
-
const bindingPath = `${
|
|
4885
|
+
const bindingPath = `${path58}.${name}`;
|
|
4857
4886
|
if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(name)) {
|
|
4858
4887
|
issue(issues, "invalid_input_name", bindingPath, `workflow input name ${name} is invalid`);
|
|
4859
4888
|
}
|
|
@@ -4872,7 +4901,7 @@ function validateInputBindings(value, path55, issues, declaredInputs) {
|
|
|
4872
4901
|
}
|
|
4873
4902
|
}
|
|
4874
4903
|
}
|
|
4875
|
-
function validateInputBindingSources(value,
|
|
4904
|
+
function validateInputBindingSources(value, path58, issues, capabilitiesByStep, capabilityOutputs) {
|
|
4876
4905
|
const bindings = asRecord(value);
|
|
4877
4906
|
if (!bindings) return;
|
|
4878
4907
|
for (const [name, rawBinding] of Object.entries(bindings)) {
|
|
@@ -4885,7 +4914,7 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
|
|
|
4885
4914
|
issue(
|
|
4886
4915
|
issues,
|
|
4887
4916
|
"missing_input_step",
|
|
4888
|
-
`${
|
|
4917
|
+
`${path58}.${name}.from`,
|
|
4889
4918
|
`workflow input mapping references missing step ${sourceStep ?? "<none>"}`
|
|
4890
4919
|
);
|
|
4891
4920
|
continue;
|
|
@@ -4896,7 +4925,7 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
|
|
|
4896
4925
|
issue(
|
|
4897
4926
|
issues,
|
|
4898
4927
|
"undeclared_step_output",
|
|
4899
|
-
`${
|
|
4928
|
+
`${path58}.${name}.from`,
|
|
4900
4929
|
`workflow input mapping reads ${outputPath}, but step ${sourceStep} does not declare it`
|
|
4901
4930
|
);
|
|
4902
4931
|
}
|
|
@@ -4905,11 +4934,11 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
|
|
|
4905
4934
|
function formatWorkflowValidationIssues(issues) {
|
|
4906
4935
|
return issues.map((entry) => `${entry.path}: ${entry.message}`);
|
|
4907
4936
|
}
|
|
4908
|
-
function validateDataMatch(value,
|
|
4937
|
+
function validateDataMatch(value, path58, issues, capabilityOutputs) {
|
|
4909
4938
|
if (value === void 0) return;
|
|
4910
4939
|
const match = asRecord(value);
|
|
4911
4940
|
if (!match || Object.keys(match).length === 0) {
|
|
4912
|
-
issue(issues, "invalid_condition",
|
|
4941
|
+
issue(issues, "invalid_condition", path58, "workflow condition must contain at least one match");
|
|
4913
4942
|
return;
|
|
4914
4943
|
}
|
|
4915
4944
|
for (const [field, expected] of Object.entries(match)) {
|
|
@@ -4917,7 +4946,7 @@ function validateDataMatch(value, path55, issues, capabilityOutputs) {
|
|
|
4917
4946
|
issue(
|
|
4918
4947
|
issues,
|
|
4919
4948
|
"invalid_data_path",
|
|
4920
|
-
`${
|
|
4949
|
+
`${path58}.${field}`,
|
|
4921
4950
|
`workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
|
|
4922
4951
|
);
|
|
4923
4952
|
}
|
|
@@ -4925,12 +4954,12 @@ function validateDataMatch(value, path55, issues, capabilityOutputs) {
|
|
|
4925
4954
|
issue(
|
|
4926
4955
|
issues,
|
|
4927
4956
|
"undeclared_result_path",
|
|
4928
|
-
`${
|
|
4957
|
+
`${path58}.${field}`,
|
|
4929
4958
|
`workflow condition reads ${field}, but the source capability does not declare it`
|
|
4930
4959
|
);
|
|
4931
4960
|
}
|
|
4932
4961
|
if (!isComparable(expected)) {
|
|
4933
|
-
issue(issues, "invalid_condition_value", `${
|
|
4962
|
+
issue(issues, "invalid_condition_value", `${path58}.${field}`, "workflow condition value must be a JSON scalar");
|
|
4934
4963
|
}
|
|
4935
4964
|
}
|
|
4936
4965
|
}
|
|
@@ -4954,8 +4983,8 @@ function isJsonValue(value) {
|
|
|
4954
4983
|
if (!value || typeof value !== "object") return false;
|
|
4955
4984
|
return Object.values(value).every(isJsonValue);
|
|
4956
4985
|
}
|
|
4957
|
-
function issue(issues, code,
|
|
4958
|
-
issues.push({ code, path:
|
|
4986
|
+
function issue(issues, code, path58, message) {
|
|
4987
|
+
issues.push({ code, path: path58, message });
|
|
4959
4988
|
}
|
|
4960
4989
|
var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SAFE_INPUT_SOURCE, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
|
|
4961
4990
|
var init_workflowValidation = __esm({
|
|
@@ -4987,8 +5016,8 @@ var init_workflowValidation = __esm({
|
|
|
4987
5016
|
});
|
|
4988
5017
|
|
|
4989
5018
|
// src/workflowDefinitions.ts
|
|
4990
|
-
import * as
|
|
4991
|
-
import * as
|
|
5019
|
+
import * as fs20 from "fs";
|
|
5020
|
+
import * as path21 from "path";
|
|
4992
5021
|
function isWorkflowDefinitionId(value) {
|
|
4993
5022
|
return WORKFLOW_ID_PATTERN.test(value);
|
|
4994
5023
|
}
|
|
@@ -5033,12 +5062,12 @@ function readWorkflowDefinition(_config, cwd, id) {
|
|
|
5033
5062
|
const root = cwd ?? process.cwd();
|
|
5034
5063
|
const relativePath = workflowDefinitionPath(id);
|
|
5035
5064
|
const candidates = [
|
|
5036
|
-
|
|
5037
|
-
|
|
5065
|
+
path21.join(root, ".kody-engine", "runtime", relativePath),
|
|
5066
|
+
path21.join(definitionsRoot(root), relativePath)
|
|
5038
5067
|
];
|
|
5039
5068
|
for (const filePath of candidates) {
|
|
5040
|
-
if (!
|
|
5041
|
-
const workflow = parseWorkflowDefinition(
|
|
5069
|
+
if (!fs20.existsSync(filePath)) continue;
|
|
5070
|
+
const workflow = parseWorkflowDefinition(fs20.readFileSync(filePath, "utf8"));
|
|
5042
5071
|
if (workflow) return workflow;
|
|
5043
5072
|
}
|
|
5044
5073
|
return null;
|
|
@@ -5046,7 +5075,7 @@ function readWorkflowDefinition(_config, cwd, id) {
|
|
|
5046
5075
|
function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
|
|
5047
5076
|
return {
|
|
5048
5077
|
slug: id,
|
|
5049
|
-
dir:
|
|
5078
|
+
dir: path21.dirname(source),
|
|
5050
5079
|
profilePath: source,
|
|
5051
5080
|
bodyPath: source,
|
|
5052
5081
|
title: workflow.name,
|
|
@@ -5102,7 +5131,7 @@ var init_workflowDefinitions = __esm({
|
|
|
5102
5131
|
|
|
5103
5132
|
// src/gha.ts
|
|
5104
5133
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
5105
|
-
import * as
|
|
5134
|
+
import * as fs23 from "fs";
|
|
5106
5135
|
function getRunUrl() {
|
|
5107
5136
|
const server = process.env.GITHUB_SERVER_URL;
|
|
5108
5137
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -5113,10 +5142,10 @@ function getRunUrl() {
|
|
|
5113
5142
|
function reactToTriggerComment(cwd) {
|
|
5114
5143
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5115
5144
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5116
|
-
if (!eventPath || !
|
|
5145
|
+
if (!eventPath || !fs23.existsSync(eventPath)) return;
|
|
5117
5146
|
let event = null;
|
|
5118
5147
|
try {
|
|
5119
|
-
event = JSON.parse(
|
|
5148
|
+
event = JSON.parse(fs23.readFileSync(eventPath, "utf-8"));
|
|
5120
5149
|
} catch {
|
|
5121
5150
|
return;
|
|
5122
5151
|
}
|
|
@@ -5646,15 +5675,15 @@ var init_lifecycles = __esm({
|
|
|
5646
5675
|
|
|
5647
5676
|
// src/profile.ts
|
|
5648
5677
|
import { createHash as createHash3 } from "crypto";
|
|
5649
|
-
import * as
|
|
5650
|
-
import * as
|
|
5678
|
+
import * as fs24 from "fs";
|
|
5679
|
+
import * as path23 from "path";
|
|
5651
5680
|
function loadProfile(profilePath) {
|
|
5652
|
-
if (!
|
|
5681
|
+
if (!fs24.existsSync(profilePath)) {
|
|
5653
5682
|
throw new ProfileError(profilePath, "file not found");
|
|
5654
5683
|
}
|
|
5655
5684
|
let raw;
|
|
5656
5685
|
try {
|
|
5657
|
-
raw = JSON.parse(
|
|
5686
|
+
raw = JSON.parse(fs24.readFileSync(profilePath, "utf-8"));
|
|
5658
5687
|
} catch (err) {
|
|
5659
5688
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
5660
5689
|
}
|
|
@@ -5666,7 +5695,7 @@ function loadProfile(profilePath) {
|
|
|
5666
5695
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
5667
5696
|
if (unknownKeys.length > 0) {
|
|
5668
5697
|
process.stderr.write(
|
|
5669
|
-
`[kody profile] ${
|
|
5698
|
+
`[kody profile] ${path23.basename(path23.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
5670
5699
|
`
|
|
5671
5700
|
);
|
|
5672
5701
|
}
|
|
@@ -5676,7 +5705,7 @@ function loadProfile(profilePath) {
|
|
|
5676
5705
|
if (!refPath) {
|
|
5677
5706
|
throw new ProfileError(profilePath, `capability references unknown implementation '${execRef}'`);
|
|
5678
5707
|
}
|
|
5679
|
-
if (
|
|
5708
|
+
if (path23.resolve(refPath) === path23.resolve(profilePath)) {
|
|
5680
5709
|
} else {
|
|
5681
5710
|
const base = loadProfile(refPath);
|
|
5682
5711
|
return {
|
|
@@ -5774,8 +5803,8 @@ function loadProfile(profilePath) {
|
|
|
5774
5803
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
5775
5804
|
// flip to true after end-to-end verification.
|
|
5776
5805
|
preloadContext: r.preloadContext === true,
|
|
5777
|
-
dir:
|
|
5778
|
-
promptTemplates: readPromptTemplates(
|
|
5806
|
+
dir: path23.dirname(profilePath),
|
|
5807
|
+
promptTemplates: readPromptTemplates(path23.dirname(profilePath))
|
|
5779
5808
|
};
|
|
5780
5809
|
if (lifecycle) {
|
|
5781
5810
|
applyLifecycle(profile, profilePath);
|
|
@@ -5810,19 +5839,19 @@ function loadProfile(profilePath) {
|
|
|
5810
5839
|
return profile;
|
|
5811
5840
|
}
|
|
5812
5841
|
function compileRuntimeDocument(runtimePath, document) {
|
|
5813
|
-
if (
|
|
5842
|
+
if (path23.basename(runtimePath) !== "runtime.json") return document;
|
|
5814
5843
|
if (document.adapter !== "kody-engine-profile") {
|
|
5815
5844
|
throw new ProfileError(runtimePath, "unsupported runtime adapter document");
|
|
5816
5845
|
}
|
|
5817
|
-
const implementationDir =
|
|
5818
|
-
const implementation = readJsonObject(
|
|
5819
|
-
const definitionsRoot2 =
|
|
5846
|
+
const implementationDir = path23.dirname(runtimePath);
|
|
5847
|
+
const implementation = readJsonObject(path23.join(implementationDir, "definition.json"), "Implementation definition");
|
|
5848
|
+
const definitionsRoot2 = path23.dirname(path23.dirname(implementationDir));
|
|
5820
5849
|
const capabilityId = implementation.capabilityRef && typeof implementation.capabilityRef === "object" && !Array.isArray(implementation.capabilityRef) ? implementation.capabilityRef.id : void 0;
|
|
5821
5850
|
if (typeof capabilityId !== "string" || !capabilityId) {
|
|
5822
5851
|
throw new ProfileError(runtimePath, "Implementation capabilityRef is invalid");
|
|
5823
5852
|
}
|
|
5824
5853
|
const capability = readJsonObject(
|
|
5825
|
-
|
|
5854
|
+
path23.join(definitionsRoot2, "capabilities", capabilityId, "definition.json"),
|
|
5826
5855
|
"Capability definition"
|
|
5827
5856
|
);
|
|
5828
5857
|
const {
|
|
@@ -5861,7 +5890,7 @@ function canonical(value) {
|
|
|
5861
5890
|
}
|
|
5862
5891
|
function readJsonObject(filePath, label) {
|
|
5863
5892
|
try {
|
|
5864
|
-
const value = JSON.parse(
|
|
5893
|
+
const value = JSON.parse(fs24.readFileSync(filePath, "utf-8"));
|
|
5865
5894
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5866
5895
|
throw new Error("must be an object");
|
|
5867
5896
|
}
|
|
@@ -5879,17 +5908,17 @@ function readPromptTemplates(dir) {
|
|
|
5879
5908
|
const out = {};
|
|
5880
5909
|
const read = (p) => {
|
|
5881
5910
|
try {
|
|
5882
|
-
out[p] =
|
|
5911
|
+
out[p] = fs24.readFileSync(p, "utf-8");
|
|
5883
5912
|
} catch {
|
|
5884
5913
|
}
|
|
5885
5914
|
};
|
|
5886
|
-
read(
|
|
5887
|
-
read(
|
|
5888
|
-
read(
|
|
5915
|
+
read(path23.join(dir, "prompt.md"));
|
|
5916
|
+
read(path23.join(dir, "capability.md"));
|
|
5917
|
+
read(path23.join(dir, "capability.md"));
|
|
5889
5918
|
try {
|
|
5890
|
-
const promptsDir =
|
|
5891
|
-
for (const ent of
|
|
5892
|
-
if (ent.endsWith(".md")) read(
|
|
5919
|
+
const promptsDir = path23.join(dir, "prompts");
|
|
5920
|
+
for (const ent of fs24.readdirSync(promptsDir)) {
|
|
5921
|
+
if (ent.endsWith(".md")) read(path23.join(promptsDir, ent));
|
|
5893
5922
|
}
|
|
5894
5923
|
} catch {
|
|
5895
5924
|
}
|
|
@@ -6666,16 +6695,16 @@ var init_state = __esm({
|
|
|
6666
6695
|
});
|
|
6667
6696
|
|
|
6668
6697
|
// src/prompt.ts
|
|
6669
|
-
import * as
|
|
6670
|
-
import * as
|
|
6698
|
+
import * as fs25 from "fs";
|
|
6699
|
+
import * as path24 from "path";
|
|
6671
6700
|
function loadProjectConventions(projectDir) {
|
|
6672
6701
|
const out = [];
|
|
6673
6702
|
for (const rel of CONVENTION_FILES) {
|
|
6674
|
-
const abs =
|
|
6675
|
-
if (!
|
|
6703
|
+
const abs = path24.join(projectDir, rel);
|
|
6704
|
+
if (!fs25.existsSync(abs)) continue;
|
|
6676
6705
|
let content;
|
|
6677
6706
|
try {
|
|
6678
|
-
content =
|
|
6707
|
+
content = fs25.readFileSync(abs, "utf-8");
|
|
6679
6708
|
} catch {
|
|
6680
6709
|
continue;
|
|
6681
6710
|
}
|
|
@@ -6910,8 +6939,8 @@ var loadMemoryContext_exports = {};
|
|
|
6910
6939
|
__export(loadMemoryContext_exports, {
|
|
6911
6940
|
loadMemoryContext: () => loadMemoryContext
|
|
6912
6941
|
});
|
|
6913
|
-
import * as
|
|
6914
|
-
import * as
|
|
6942
|
+
import * as fs26 from "fs";
|
|
6943
|
+
import * as path25 from "path";
|
|
6915
6944
|
function formatBlockFromBackend(docs) {
|
|
6916
6945
|
const pages = docs.flatMap((record2) => {
|
|
6917
6946
|
if (!record2.doc || typeof record2.doc !== "object") return [];
|
|
@@ -6934,21 +6963,21 @@ function collectPages(memoryAbs) {
|
|
|
6934
6963
|
walkMd(memoryAbs, (file) => {
|
|
6935
6964
|
let stat;
|
|
6936
6965
|
try {
|
|
6937
|
-
stat =
|
|
6966
|
+
stat = fs26.statSync(file);
|
|
6938
6967
|
} catch {
|
|
6939
6968
|
return;
|
|
6940
6969
|
}
|
|
6941
6970
|
let raw;
|
|
6942
6971
|
try {
|
|
6943
|
-
raw =
|
|
6972
|
+
raw = fs26.readFileSync(file, "utf-8");
|
|
6944
6973
|
} catch {
|
|
6945
6974
|
return;
|
|
6946
6975
|
}
|
|
6947
6976
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
6948
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
6977
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path25.basename(file, ".md");
|
|
6949
6978
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
6950
6979
|
out.push({
|
|
6951
|
-
relPath:
|
|
6980
|
+
relPath: path25.relative(memoryAbs, file),
|
|
6952
6981
|
title,
|
|
6953
6982
|
updated,
|
|
6954
6983
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -7016,16 +7045,16 @@ function walkMd(root, visit) {
|
|
|
7016
7045
|
const dir = stack.pop();
|
|
7017
7046
|
let names;
|
|
7018
7047
|
try {
|
|
7019
|
-
names =
|
|
7048
|
+
names = fs26.readdirSync(dir);
|
|
7020
7049
|
} catch {
|
|
7021
7050
|
continue;
|
|
7022
7051
|
}
|
|
7023
7052
|
for (const name of names) {
|
|
7024
7053
|
if (name.startsWith(".")) continue;
|
|
7025
|
-
const full =
|
|
7054
|
+
const full = path25.join(dir, name);
|
|
7026
7055
|
let stat;
|
|
7027
7056
|
try {
|
|
7028
|
-
stat =
|
|
7057
|
+
stat = fs26.statSync(full);
|
|
7029
7058
|
} catch {
|
|
7030
7059
|
continue;
|
|
7031
7060
|
}
|
|
@@ -7060,8 +7089,8 @@ var init_loadMemoryContext = __esm({
|
|
|
7060
7089
|
}
|
|
7061
7090
|
return;
|
|
7062
7091
|
}
|
|
7063
|
-
const memoryAbs =
|
|
7064
|
-
if (!
|
|
7092
|
+
const memoryAbs = path25.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
7093
|
+
if (!fs26.existsSync(memoryAbs)) {
|
|
7065
7094
|
ctx.data.memoryContext = "";
|
|
7066
7095
|
return;
|
|
7067
7096
|
}
|
|
@@ -7105,11 +7134,11 @@ var init_loadCoverageRules = __esm({
|
|
|
7105
7134
|
|
|
7106
7135
|
// src/container.ts
|
|
7107
7136
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
7108
|
-
import * as
|
|
7137
|
+
import * as fs27 from "fs";
|
|
7109
7138
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
7110
7139
|
try {
|
|
7111
7140
|
const profilePath = resolveProfilePath(profileName);
|
|
7112
|
-
if (!
|
|
7141
|
+
if (!fs27.existsSync(profilePath)) return null;
|
|
7113
7142
|
return loadProfile(profilePath).inputs;
|
|
7114
7143
|
} catch {
|
|
7115
7144
|
return null;
|
|
@@ -7573,10 +7602,10 @@ var init_lifecycleLabels = __esm({
|
|
|
7573
7602
|
|
|
7574
7603
|
// src/litellm.ts
|
|
7575
7604
|
import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
|
|
7576
|
-
import * as
|
|
7605
|
+
import * as fs28 from "fs";
|
|
7577
7606
|
import * as net from "net";
|
|
7578
7607
|
import * as os4 from "os";
|
|
7579
|
-
import * as
|
|
7608
|
+
import * as path26 from "path";
|
|
7580
7609
|
async function checkLitellmHealth(url) {
|
|
7581
7610
|
try {
|
|
7582
7611
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -7646,7 +7675,7 @@ function locateLitellmScript() {
|
|
|
7646
7675
|
}
|
|
7647
7676
|
function resolveLitellmCommand() {
|
|
7648
7677
|
const imageScript = "/opt/venv/bin/litellm";
|
|
7649
|
-
if (
|
|
7678
|
+
if (fs28.existsSync(imageScript)) return imageScript;
|
|
7650
7679
|
try {
|
|
7651
7680
|
execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
7652
7681
|
return "litellm";
|
|
@@ -7689,13 +7718,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
7689
7718
|
const spawnProxy = () => {
|
|
7690
7719
|
const portMatch = activeUrl.match(/:(\d+)/);
|
|
7691
7720
|
const port = portMatch ? portMatch[1] : "4000";
|
|
7692
|
-
const configPath =
|
|
7693
|
-
|
|
7721
|
+
const configPath = path26.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
7722
|
+
fs28.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
7694
7723
|
const args = ["--config", configPath, "--port", port];
|
|
7695
|
-
const nextLogPath =
|
|
7696
|
-
const outFd =
|
|
7724
|
+
const nextLogPath = path26.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
7725
|
+
const outFd = fs28.openSync(nextLogPath, "w");
|
|
7697
7726
|
child = spawn4(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
7698
|
-
|
|
7727
|
+
fs28.closeSync(outFd);
|
|
7699
7728
|
logPath = nextLogPath;
|
|
7700
7729
|
};
|
|
7701
7730
|
const waitForHealth = async () => {
|
|
@@ -7709,7 +7738,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
7709
7738
|
const readLogTail = () => {
|
|
7710
7739
|
if (!logPath) return "";
|
|
7711
7740
|
try {
|
|
7712
|
-
return
|
|
7741
|
+
return fs28.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
7713
7742
|
} catch {
|
|
7714
7743
|
return "";
|
|
7715
7744
|
}
|
|
@@ -7782,20 +7811,20 @@ async function nextAvailableLitellmUrl(url) {
|
|
|
7782
7811
|
throw new Error(`no free LiteLLM port found after ${startPort}`);
|
|
7783
7812
|
}
|
|
7784
7813
|
function canListen(port, host) {
|
|
7785
|
-
return new Promise((
|
|
7814
|
+
return new Promise((resolve23) => {
|
|
7786
7815
|
const server = net.createServer();
|
|
7787
|
-
server.once("error", () =>
|
|
7816
|
+
server.once("error", () => resolve23(false));
|
|
7788
7817
|
server.once("listening", () => {
|
|
7789
|
-
server.close(() =>
|
|
7818
|
+
server.close(() => resolve23(true));
|
|
7790
7819
|
});
|
|
7791
7820
|
server.listen(port, host);
|
|
7792
7821
|
});
|
|
7793
7822
|
}
|
|
7794
7823
|
function readDotenvApiKeys(projectDir) {
|
|
7795
|
-
const dotenvPath =
|
|
7796
|
-
if (!
|
|
7824
|
+
const dotenvPath = path26.join(projectDir, ".env");
|
|
7825
|
+
if (!fs28.existsSync(dotenvPath)) return {};
|
|
7797
7826
|
const result = {};
|
|
7798
|
-
for (const rawLine of
|
|
7827
|
+
for (const rawLine of fs28.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
7799
7828
|
const line = rawLine.trim();
|
|
7800
7829
|
if (!line || line.startsWith("#")) continue;
|
|
7801
7830
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -8454,8 +8483,8 @@ var init_pushWithRetry = __esm({
|
|
|
8454
8483
|
|
|
8455
8484
|
// src/commit.ts
|
|
8456
8485
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
8457
|
-
import * as
|
|
8458
|
-
import * as
|
|
8486
|
+
import * as fs29 from "fs";
|
|
8487
|
+
import * as path27 from "path";
|
|
8459
8488
|
function isGitHubYamlPath(filePath) {
|
|
8460
8489
|
const normalized = filePath.replace(/^\.\/+/, "");
|
|
8461
8490
|
return normalized.startsWith(".github/") && /\.ya?ml$/i.test(normalized);
|
|
@@ -8497,18 +8526,18 @@ function ensureGitIdentity(cwd) {
|
|
|
8497
8526
|
}
|
|
8498
8527
|
function abortUnfinishedGitOps(cwd) {
|
|
8499
8528
|
const aborted = [];
|
|
8500
|
-
const gitDir =
|
|
8501
|
-
if (!
|
|
8502
|
-
if (
|
|
8529
|
+
const gitDir = path27.join(cwd ?? process.cwd(), ".git");
|
|
8530
|
+
if (!fs29.existsSync(gitDir)) return aborted;
|
|
8531
|
+
if (fs29.existsSync(path27.join(gitDir, "MERGE_HEAD"))) {
|
|
8503
8532
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
8504
8533
|
}
|
|
8505
|
-
if (
|
|
8534
|
+
if (fs29.existsSync(path27.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
8506
8535
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
8507
8536
|
}
|
|
8508
|
-
if (
|
|
8537
|
+
if (fs29.existsSync(path27.join(gitDir, "REVERT_HEAD"))) {
|
|
8509
8538
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
8510
8539
|
}
|
|
8511
|
-
if (
|
|
8540
|
+
if (fs29.existsSync(path27.join(gitDir, "rebase-merge")) || fs29.existsSync(path27.join(gitDir, "rebase-apply"))) {
|
|
8512
8541
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
8513
8542
|
}
|
|
8514
8543
|
try {
|
|
@@ -8565,7 +8594,7 @@ function normalizeCommitMessage(raw) {
|
|
|
8565
8594
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
8566
8595
|
const allChanged = listChangedFiles(cwd);
|
|
8567
8596
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
8568
|
-
const mergeHeadExists =
|
|
8597
|
+
const mergeHeadExists = fs29.existsSync(path27.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
8569
8598
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
8570
8599
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
8571
8600
|
}
|
|
@@ -9205,13 +9234,13 @@ var init_state2 = __esm({
|
|
|
9205
9234
|
});
|
|
9206
9235
|
|
|
9207
9236
|
// src/goal/runLog.ts
|
|
9208
|
-
import * as
|
|
9237
|
+
import * as fs30 from "fs";
|
|
9209
9238
|
function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
|
|
9210
9239
|
const logs = goalRunLogs(data);
|
|
9211
9240
|
const existing = logs[goalId];
|
|
9212
|
-
const
|
|
9241
|
+
const path58 = existing?.path ?? goalRunLogPath(goalId, data);
|
|
9213
9242
|
logs[goalId] = {
|
|
9214
|
-
path:
|
|
9243
|
+
path: path58,
|
|
9215
9244
|
events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
|
|
9216
9245
|
};
|
|
9217
9246
|
}
|
|
@@ -9547,8 +9576,8 @@ function readGithubEvent() {
|
|
|
9547
9576
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
9548
9577
|
if (!eventPath) return null;
|
|
9549
9578
|
try {
|
|
9550
|
-
if (!
|
|
9551
|
-
const parsed = JSON.parse(
|
|
9579
|
+
if (!fs30.existsSync(eventPath)) return null;
|
|
9580
|
+
const parsed = JSON.parse(fs30.readFileSync(eventPath, "utf-8"));
|
|
9552
9581
|
return recordValue3(parsed);
|
|
9553
9582
|
} catch {
|
|
9554
9583
|
return null;
|
|
@@ -9658,8 +9687,8 @@ var init_stateStore = __esm({
|
|
|
9658
9687
|
});
|
|
9659
9688
|
|
|
9660
9689
|
// src/goal/targetLoopResolution.ts
|
|
9661
|
-
import * as
|
|
9662
|
-
import * as
|
|
9690
|
+
import * as fs31 from "fs";
|
|
9691
|
+
import * as path28 from "path";
|
|
9663
9692
|
async function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
|
|
9664
9693
|
const targetId = loopGoal.loopTarget?.id.trim() ?? "";
|
|
9665
9694
|
assertSafeGoalId(targetId, "loop target");
|
|
@@ -9737,11 +9766,11 @@ function goalInstanceTime(state) {
|
|
|
9737
9766
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
9738
9767
|
}
|
|
9739
9768
|
function loadGoalTemplate(cwd, targetId) {
|
|
9740
|
-
return readJsonObject2(
|
|
9769
|
+
return readJsonObject2(path28.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
|
|
9741
9770
|
}
|
|
9742
9771
|
function readJsonObject2(filePath) {
|
|
9743
|
-
if (!
|
|
9744
|
-
const parsed = JSON.parse(
|
|
9772
|
+
if (!fs31.existsSync(filePath)) return null;
|
|
9773
|
+
const parsed = JSON.parse(fs31.readFileSync(filePath, "utf8"));
|
|
9745
9774
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9746
9775
|
throw new Error(`goal template ${filePath} must be a JSON object`);
|
|
9747
9776
|
}
|
|
@@ -10088,15 +10117,15 @@ var init_backendStateBackend = __esm({
|
|
|
10088
10117
|
this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
|
|
10089
10118
|
}
|
|
10090
10119
|
async load(slug) {
|
|
10091
|
-
const
|
|
10120
|
+
const path58 = stateFilePath(this.jobsDir, slug);
|
|
10092
10121
|
const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
|
|
10093
10122
|
if (!loaded) {
|
|
10094
|
-
return { path:
|
|
10123
|
+
return { path: path58, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
10095
10124
|
}
|
|
10096
10125
|
if (!isStateEnvelope(loaded.doc)) {
|
|
10097
10126
|
throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
|
|
10098
10127
|
}
|
|
10099
|
-
return { path:
|
|
10128
|
+
return { path: path58, handle: loaded.updatedAt, state: loaded.doc, created: false };
|
|
10100
10129
|
}
|
|
10101
10130
|
async save(loaded, next) {
|
|
10102
10131
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
|
|
@@ -10116,8 +10145,8 @@ var init_backendStateBackend = __esm({
|
|
|
10116
10145
|
});
|
|
10117
10146
|
|
|
10118
10147
|
// src/scripts/jobState/localFileBackend.ts
|
|
10119
|
-
import * as
|
|
10120
|
-
import * as
|
|
10148
|
+
import * as fs32 from "fs";
|
|
10149
|
+
import * as path29 from "path";
|
|
10121
10150
|
function sanitizeKey(s) {
|
|
10122
10151
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
10123
10152
|
}
|
|
@@ -10173,7 +10202,7 @@ var init_localFileBackend = __esm({
|
|
|
10173
10202
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
10174
10203
|
this.cwd = opts.cwd;
|
|
10175
10204
|
this.jobsDir = opts.jobsDir;
|
|
10176
|
-
this.absDir =
|
|
10205
|
+
this.absDir = path29.resolve(opts.cwd, opts.jobsDir);
|
|
10177
10206
|
this.owner = opts.owner;
|
|
10178
10207
|
this.repo = opts.repo;
|
|
10179
10208
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -10188,7 +10217,7 @@ var init_localFileBackend = __esm({
|
|
|
10188
10217
|
`);
|
|
10189
10218
|
return;
|
|
10190
10219
|
}
|
|
10191
|
-
|
|
10220
|
+
fs32.mkdirSync(this.absDir, { recursive: true });
|
|
10192
10221
|
const prefix = this.cacheKeyPrefix();
|
|
10193
10222
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
10194
10223
|
try {
|
|
@@ -10217,7 +10246,7 @@ var init_localFileBackend = __esm({
|
|
|
10217
10246
|
`);
|
|
10218
10247
|
return;
|
|
10219
10248
|
}
|
|
10220
|
-
if (!
|
|
10249
|
+
if (!fs32.existsSync(this.absDir)) {
|
|
10221
10250
|
return;
|
|
10222
10251
|
}
|
|
10223
10252
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -10233,11 +10262,11 @@ var init_localFileBackend = __esm({
|
|
|
10233
10262
|
}
|
|
10234
10263
|
load(slug) {
|
|
10235
10264
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
10236
|
-
const absPath =
|
|
10237
|
-
if (!
|
|
10265
|
+
const absPath = path29.resolve(this.cwd, relPath);
|
|
10266
|
+
if (!fs32.existsSync(absPath)) {
|
|
10238
10267
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
10239
10268
|
}
|
|
10240
|
-
const raw =
|
|
10269
|
+
const raw = fs32.readFileSync(absPath, "utf-8");
|
|
10241
10270
|
let parsed;
|
|
10242
10271
|
try {
|
|
10243
10272
|
parsed = JSON.parse(raw);
|
|
@@ -10254,13 +10283,13 @@ var init_localFileBackend = __esm({
|
|
|
10254
10283
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
10255
10284
|
return false;
|
|
10256
10285
|
}
|
|
10257
|
-
const absPath =
|
|
10258
|
-
|
|
10286
|
+
const absPath = path29.resolve(this.cwd, loaded.path);
|
|
10287
|
+
fs32.mkdirSync(path29.dirname(absPath), { recursive: true });
|
|
10259
10288
|
const body = `${JSON.stringify(next, null, 2)}
|
|
10260
10289
|
`;
|
|
10261
10290
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
10262
|
-
|
|
10263
|
-
|
|
10291
|
+
fs32.writeFileSync(tmpPath, body, "utf-8");
|
|
10292
|
+
fs32.renameSync(tmpPath, absPath);
|
|
10264
10293
|
return true;
|
|
10265
10294
|
}
|
|
10266
10295
|
cacheKeyPrefix() {
|
|
@@ -10292,7 +10321,7 @@ var init_jobState = __esm({
|
|
|
10292
10321
|
});
|
|
10293
10322
|
|
|
10294
10323
|
// src/scripts/goalCapabilityScheduling.ts
|
|
10295
|
-
import * as
|
|
10324
|
+
import * as path30 from "path";
|
|
10296
10325
|
function isCapabilityCadenceGoal(goal, extra) {
|
|
10297
10326
|
return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
|
|
10298
10327
|
}
|
|
@@ -10348,7 +10377,7 @@ function planTargetLoopSchedule(opts) {
|
|
|
10348
10377
|
}
|
|
10349
10378
|
async function planGoalCapabilitySchedule(opts) {
|
|
10350
10379
|
const jobsDir = opts.jobsDir ?? capabilitiesRoot(opts.cwd);
|
|
10351
|
-
const jobsRoot =
|
|
10380
|
+
const jobsRoot = path30.resolve(opts.cwd, jobsDir);
|
|
10352
10381
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
10353
10382
|
const at = now.toISOString();
|
|
10354
10383
|
const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
|
|
@@ -12164,8 +12193,8 @@ var init_classifyByLabel = __esm({
|
|
|
12164
12193
|
|
|
12165
12194
|
// src/scripts/commitAndPush.ts
|
|
12166
12195
|
import { createHash as createHash5 } from "crypto";
|
|
12167
|
-
import * as
|
|
12168
|
-
import * as
|
|
12196
|
+
import * as fs33 from "fs";
|
|
12197
|
+
import * as path31 from "path";
|
|
12169
12198
|
function sentinelPathForStage(cwd, profileName, workflowExecutionKey) {
|
|
12170
12199
|
const runId = resolveRunId();
|
|
12171
12200
|
const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash5("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
|
|
@@ -12187,9 +12216,9 @@ var init_commitAndPush = __esm({
|
|
|
12187
12216
|
}
|
|
12188
12217
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
12189
12218
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name, ctx.data.workflowExecutionKey) : null;
|
|
12190
|
-
if (sentinel &&
|
|
12219
|
+
if (sentinel && fs33.existsSync(sentinel)) {
|
|
12191
12220
|
try {
|
|
12192
|
-
const replay = JSON.parse(
|
|
12221
|
+
const replay = JSON.parse(fs33.readFileSync(sentinel, "utf-8"));
|
|
12193
12222
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
12194
12223
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
12195
12224
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -12249,8 +12278,8 @@ var init_commitAndPush = __esm({
|
|
|
12249
12278
|
const result = ctx.data.commitResult;
|
|
12250
12279
|
if (sentinel && result?.committed) {
|
|
12251
12280
|
try {
|
|
12252
|
-
|
|
12253
|
-
|
|
12281
|
+
fs33.mkdirSync(path31.dirname(sentinel), { recursive: true });
|
|
12282
|
+
fs33.writeFileSync(
|
|
12254
12283
|
sentinel,
|
|
12255
12284
|
JSON.stringify(
|
|
12256
12285
|
{
|
|
@@ -12344,8 +12373,8 @@ var init_commitGoalState = __esm({
|
|
|
12344
12373
|
});
|
|
12345
12374
|
|
|
12346
12375
|
// src/scripts/composePrompt.ts
|
|
12347
|
-
import * as
|
|
12348
|
-
import * as
|
|
12376
|
+
import * as fs34 from "fs";
|
|
12377
|
+
import * as path32 from "path";
|
|
12349
12378
|
function fenceUntrusted(value) {
|
|
12350
12379
|
if (value.trim().length === 0) return value;
|
|
12351
12380
|
const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
@@ -12469,10 +12498,10 @@ var init_composePrompt = __esm({
|
|
|
12469
12498
|
const explicit = ctx.data.promptTemplate;
|
|
12470
12499
|
const mode = ctx.args.mode;
|
|
12471
12500
|
const candidates = [
|
|
12472
|
-
explicit ?
|
|
12473
|
-
mode ?
|
|
12474
|
-
|
|
12475
|
-
|
|
12501
|
+
explicit ? path32.join(profile.dir, explicit) : null,
|
|
12502
|
+
mode ? path32.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
12503
|
+
path32.join(profile.dir, "prompt.md"),
|
|
12504
|
+
path32.join(profile.dir, "capability.md")
|
|
12476
12505
|
].filter(Boolean);
|
|
12477
12506
|
let templatePath = "";
|
|
12478
12507
|
let template = "";
|
|
@@ -12485,7 +12514,7 @@ var init_composePrompt = __esm({
|
|
|
12485
12514
|
break;
|
|
12486
12515
|
}
|
|
12487
12516
|
try {
|
|
12488
|
-
template =
|
|
12517
|
+
template = fs34.readFileSync(c, "utf-8");
|
|
12489
12518
|
templatePath = c;
|
|
12490
12519
|
break;
|
|
12491
12520
|
} catch (err) {
|
|
@@ -12496,7 +12525,7 @@ var init_composePrompt = __esm({
|
|
|
12496
12525
|
if (!templatePath) {
|
|
12497
12526
|
let dirState;
|
|
12498
12527
|
try {
|
|
12499
|
-
dirState = `dir contents: [${
|
|
12528
|
+
dirState = `dir contents: [${fs34.readdirSync(profile.dir).join(", ")}]`;
|
|
12500
12529
|
} catch (err) {
|
|
12501
12530
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
12502
12531
|
}
|
|
@@ -13230,19 +13259,19 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
13230
13259
|
|
|
13231
13260
|
// src/scripts/diagMcp.ts
|
|
13232
13261
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
13233
|
-
import * as
|
|
13262
|
+
import * as fs35 from "fs";
|
|
13234
13263
|
import * as os5 from "os";
|
|
13235
|
-
import * as
|
|
13264
|
+
import * as path33 from "path";
|
|
13236
13265
|
var diagMcp;
|
|
13237
13266
|
var init_diagMcp = __esm({
|
|
13238
13267
|
"src/scripts/diagMcp.ts"() {
|
|
13239
13268
|
"use strict";
|
|
13240
13269
|
diagMcp = async (_ctx) => {
|
|
13241
13270
|
const home = os5.homedir();
|
|
13242
|
-
const cacheDir =
|
|
13271
|
+
const cacheDir = path33.join(home, ".cache", "ms-playwright");
|
|
13243
13272
|
let entries = [];
|
|
13244
13273
|
try {
|
|
13245
|
-
entries =
|
|
13274
|
+
entries = fs35.readdirSync(cacheDir);
|
|
13246
13275
|
} catch {
|
|
13247
13276
|
}
|
|
13248
13277
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -13270,13 +13299,13 @@ var init_diagMcp = __esm({
|
|
|
13270
13299
|
});
|
|
13271
13300
|
|
|
13272
13301
|
// src/scripts/frameworkDetectors.ts
|
|
13273
|
-
import * as
|
|
13274
|
-
import * as
|
|
13302
|
+
import * as fs36 from "fs";
|
|
13303
|
+
import * as path34 from "path";
|
|
13275
13304
|
function detectFrameworks(cwd) {
|
|
13276
13305
|
const out = [];
|
|
13277
13306
|
let deps = {};
|
|
13278
13307
|
try {
|
|
13279
|
-
const pkg = JSON.parse(
|
|
13308
|
+
const pkg = JSON.parse(fs36.readFileSync(path34.join(cwd, "package.json"), "utf-8"));
|
|
13280
13309
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13281
13310
|
} catch {
|
|
13282
13311
|
return out;
|
|
@@ -13313,25 +13342,25 @@ function detectFrameworks(cwd) {
|
|
|
13313
13342
|
}
|
|
13314
13343
|
function findFile(cwd, candidates) {
|
|
13315
13344
|
for (const c of candidates) {
|
|
13316
|
-
if (
|
|
13345
|
+
if (fs36.existsSync(path34.join(cwd, c))) return c;
|
|
13317
13346
|
}
|
|
13318
13347
|
return null;
|
|
13319
13348
|
}
|
|
13320
13349
|
function discoverPayloadCollections(cwd) {
|
|
13321
13350
|
const out = [];
|
|
13322
13351
|
for (const dir of COLLECTION_DIRS) {
|
|
13323
|
-
const full =
|
|
13324
|
-
if (!
|
|
13352
|
+
const full = path34.join(cwd, dir);
|
|
13353
|
+
if (!fs36.existsSync(full)) continue;
|
|
13325
13354
|
let files;
|
|
13326
13355
|
try {
|
|
13327
|
-
files =
|
|
13356
|
+
files = fs36.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
13328
13357
|
} catch {
|
|
13329
13358
|
continue;
|
|
13330
13359
|
}
|
|
13331
13360
|
for (const file of files) {
|
|
13332
13361
|
try {
|
|
13333
|
-
const filePath =
|
|
13334
|
-
const content =
|
|
13362
|
+
const filePath = path34.join(full, file);
|
|
13363
|
+
const content = fs36.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
13335
13364
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
13336
13365
|
if (!slugMatch) continue;
|
|
13337
13366
|
const slug = slugMatch[1];
|
|
@@ -13345,7 +13374,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
13345
13374
|
out.push({
|
|
13346
13375
|
name,
|
|
13347
13376
|
slug,
|
|
13348
|
-
filePath:
|
|
13377
|
+
filePath: path34.relative(cwd, filePath),
|
|
13349
13378
|
fields: fields.slice(0, 20),
|
|
13350
13379
|
hasAdmin
|
|
13351
13380
|
});
|
|
@@ -13358,28 +13387,28 @@ function discoverPayloadCollections(cwd) {
|
|
|
13358
13387
|
function discoverAdminComponents(cwd, collections) {
|
|
13359
13388
|
const out = [];
|
|
13360
13389
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
13361
|
-
const full =
|
|
13362
|
-
if (!
|
|
13390
|
+
const full = path34.join(cwd, dir);
|
|
13391
|
+
if (!fs36.existsSync(full)) continue;
|
|
13363
13392
|
let entries;
|
|
13364
13393
|
try {
|
|
13365
|
-
entries =
|
|
13394
|
+
entries = fs36.readdirSync(full, { withFileTypes: true });
|
|
13366
13395
|
} catch {
|
|
13367
13396
|
continue;
|
|
13368
13397
|
}
|
|
13369
13398
|
for (const entry of entries) {
|
|
13370
|
-
const entryPath =
|
|
13399
|
+
const entryPath = path34.join(full, entry.name);
|
|
13371
13400
|
let name;
|
|
13372
13401
|
let filePath;
|
|
13373
13402
|
if (entry.isDirectory()) {
|
|
13374
13403
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
13375
|
-
(f) =>
|
|
13404
|
+
(f) => fs36.existsSync(path34.join(entryPath, f))
|
|
13376
13405
|
);
|
|
13377
13406
|
if (!indexFile) continue;
|
|
13378
13407
|
name = entry.name;
|
|
13379
|
-
filePath =
|
|
13408
|
+
filePath = path34.relative(cwd, path34.join(entryPath, indexFile));
|
|
13380
13409
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
13381
13410
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
13382
|
-
filePath =
|
|
13411
|
+
filePath = path34.relative(cwd, entryPath);
|
|
13383
13412
|
} else {
|
|
13384
13413
|
continue;
|
|
13385
13414
|
}
|
|
@@ -13387,7 +13416,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
13387
13416
|
if (collections) {
|
|
13388
13417
|
for (const col of collections) {
|
|
13389
13418
|
try {
|
|
13390
|
-
const colContent =
|
|
13419
|
+
const colContent = fs36.readFileSync(path34.join(cwd, col.filePath), "utf-8");
|
|
13391
13420
|
if (colContent.includes(name)) {
|
|
13392
13421
|
usedInCollection = col.slug;
|
|
13393
13422
|
break;
|
|
@@ -13405,8 +13434,8 @@ function scanApiRoutes(cwd) {
|
|
|
13405
13434
|
const out = [];
|
|
13406
13435
|
const appDirs = ["src/app", "app"];
|
|
13407
13436
|
for (const appDir of appDirs) {
|
|
13408
|
-
const apiDir =
|
|
13409
|
-
if (!
|
|
13437
|
+
const apiDir = path34.join(cwd, appDir, "api");
|
|
13438
|
+
if (!fs36.existsSync(apiDir)) continue;
|
|
13410
13439
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
13411
13440
|
break;
|
|
13412
13441
|
}
|
|
@@ -13415,14 +13444,14 @@ function scanApiRoutes(cwd) {
|
|
|
13415
13444
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
13416
13445
|
let entries;
|
|
13417
13446
|
try {
|
|
13418
|
-
entries =
|
|
13447
|
+
entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
13419
13448
|
} catch {
|
|
13420
13449
|
return;
|
|
13421
13450
|
}
|
|
13422
13451
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
13423
13452
|
if (routeFile) {
|
|
13424
13453
|
try {
|
|
13425
|
-
const content =
|
|
13454
|
+
const content = fs36.readFileSync(path34.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
13426
13455
|
const methods = HTTP_METHODS.filter(
|
|
13427
13456
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
13428
13457
|
);
|
|
@@ -13430,7 +13459,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
13430
13459
|
out.push({
|
|
13431
13460
|
path: prefix,
|
|
13432
13461
|
methods,
|
|
13433
|
-
filePath:
|
|
13462
|
+
filePath: path34.relative(cwd, path34.join(dir, routeFile.name))
|
|
13434
13463
|
});
|
|
13435
13464
|
}
|
|
13436
13465
|
} catch {
|
|
@@ -13441,7 +13470,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
13441
13470
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
13442
13471
|
let segment = entry.name;
|
|
13443
13472
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
13444
|
-
walkApiRoutes(
|
|
13473
|
+
walkApiRoutes(path34.join(dir, entry.name), prefix, cwd, out);
|
|
13445
13474
|
continue;
|
|
13446
13475
|
}
|
|
13447
13476
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -13449,16 +13478,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
13449
13478
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
13450
13479
|
segment = `:${segment.slice(1, -1)}`;
|
|
13451
13480
|
}
|
|
13452
|
-
walkApiRoutes(
|
|
13481
|
+
walkApiRoutes(path34.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
13453
13482
|
}
|
|
13454
13483
|
}
|
|
13455
13484
|
function scanEnvVars(cwd) {
|
|
13456
13485
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
13457
13486
|
for (const envFile of candidates) {
|
|
13458
|
-
const envPath =
|
|
13459
|
-
if (!
|
|
13487
|
+
const envPath = path34.join(cwd, envFile);
|
|
13488
|
+
if (!fs36.existsSync(envPath)) continue;
|
|
13460
13489
|
try {
|
|
13461
|
-
const content =
|
|
13490
|
+
const content = fs36.readFileSync(envPath, "utf-8");
|
|
13462
13491
|
const vars = [];
|
|
13463
13492
|
for (const line of content.split("\n")) {
|
|
13464
13493
|
const trimmed = line.trim();
|
|
@@ -13503,8 +13532,8 @@ var init_frameworkDetectors = __esm({
|
|
|
13503
13532
|
});
|
|
13504
13533
|
|
|
13505
13534
|
// src/scripts/discoverQaContext.ts
|
|
13506
|
-
import * as
|
|
13507
|
-
import * as
|
|
13535
|
+
import * as fs37 from "fs";
|
|
13536
|
+
import * as path35 from "path";
|
|
13508
13537
|
function runQaDiscovery(cwd) {
|
|
13509
13538
|
const out = {
|
|
13510
13539
|
routes: [],
|
|
@@ -13535,9 +13564,9 @@ function runQaDiscovery(cwd) {
|
|
|
13535
13564
|
}
|
|
13536
13565
|
function detectDevServer(cwd, out) {
|
|
13537
13566
|
try {
|
|
13538
|
-
const pkg = JSON.parse(
|
|
13567
|
+
const pkg = JSON.parse(fs37.readFileSync(path35.join(cwd, "package.json"), "utf-8"));
|
|
13539
13568
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13540
|
-
const pm =
|
|
13569
|
+
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
13570
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
13542
13571
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
13543
13572
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -13547,8 +13576,8 @@ function detectDevServer(cwd, out) {
|
|
|
13547
13576
|
function scanFrontendRoutes(cwd, out) {
|
|
13548
13577
|
const appDirs = ["src/app", "app"];
|
|
13549
13578
|
for (const appDir of appDirs) {
|
|
13550
|
-
const full =
|
|
13551
|
-
if (!
|
|
13579
|
+
const full = path35.join(cwd, appDir);
|
|
13580
|
+
if (!fs37.existsSync(full)) continue;
|
|
13552
13581
|
walkFrontendRoutes(full, "", out);
|
|
13553
13582
|
break;
|
|
13554
13583
|
}
|
|
@@ -13556,7 +13585,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
13556
13585
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
13557
13586
|
let entries;
|
|
13558
13587
|
try {
|
|
13559
|
-
entries =
|
|
13588
|
+
entries = fs37.readdirSync(dir, { withFileTypes: true });
|
|
13560
13589
|
} catch {
|
|
13561
13590
|
return;
|
|
13562
13591
|
}
|
|
@@ -13573,7 +13602,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
13573
13602
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
13574
13603
|
let segment = entry.name;
|
|
13575
13604
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
13576
|
-
walkFrontendRoutes(
|
|
13605
|
+
walkFrontendRoutes(path35.join(dir, entry.name), prefix, out);
|
|
13577
13606
|
continue;
|
|
13578
13607
|
}
|
|
13579
13608
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -13581,7 +13610,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
13581
13610
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
13582
13611
|
segment = `:${segment.slice(1, -1)}`;
|
|
13583
13612
|
}
|
|
13584
|
-
walkFrontendRoutes(
|
|
13613
|
+
walkFrontendRoutes(path35.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
13585
13614
|
}
|
|
13586
13615
|
}
|
|
13587
13616
|
function detectAuthFiles(cwd, out) {
|
|
@@ -13598,23 +13627,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
13598
13627
|
"src/app/api/oauth"
|
|
13599
13628
|
];
|
|
13600
13629
|
for (const c of candidates) {
|
|
13601
|
-
if (
|
|
13630
|
+
if (fs37.existsSync(path35.join(cwd, c))) out.authFiles.push(c);
|
|
13602
13631
|
}
|
|
13603
13632
|
}
|
|
13604
13633
|
function detectRoles(cwd, out) {
|
|
13605
13634
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
13606
13635
|
for (const rp of rolePaths) {
|
|
13607
|
-
const dir =
|
|
13608
|
-
if (!
|
|
13636
|
+
const dir = path35.join(cwd, rp);
|
|
13637
|
+
if (!fs37.existsSync(dir)) continue;
|
|
13609
13638
|
let files;
|
|
13610
13639
|
try {
|
|
13611
|
-
files =
|
|
13640
|
+
files = fs37.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
13612
13641
|
} catch {
|
|
13613
13642
|
continue;
|
|
13614
13643
|
}
|
|
13615
13644
|
for (const f of files) {
|
|
13616
13645
|
try {
|
|
13617
|
-
const content =
|
|
13646
|
+
const content = fs37.readFileSync(path35.join(dir, f), "utf-8").slice(0, 5e3);
|
|
13618
13647
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
13619
13648
|
if (roleMatches) {
|
|
13620
13649
|
for (const m of roleMatches) {
|
|
@@ -13875,8 +13904,8 @@ var init_dispatchClassified = __esm({
|
|
|
13875
13904
|
});
|
|
13876
13905
|
|
|
13877
13906
|
// src/loopDefinitions.ts
|
|
13878
|
-
import * as
|
|
13879
|
-
import * as
|
|
13907
|
+
import * as fs38 from "fs";
|
|
13908
|
+
import * as path36 from "path";
|
|
13880
13909
|
function normalizeLoopDefinition(value) {
|
|
13881
13910
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
13882
13911
|
const raw = value;
|
|
@@ -13903,10 +13932,10 @@ function readLoopDefinition(cwd, id) {
|
|
|
13903
13932
|
if (!ID.test(id)) return null;
|
|
13904
13933
|
const roots = loopRoots(cwd);
|
|
13905
13934
|
for (const root of roots) {
|
|
13906
|
-
const filePath =
|
|
13907
|
-
if (!
|
|
13935
|
+
const filePath = path36.join(root, "loops", id, "loop.json");
|
|
13936
|
+
if (!fs38.existsSync(filePath)) continue;
|
|
13908
13937
|
try {
|
|
13909
|
-
const loop = normalizeLoopDefinition(JSON.parse(
|
|
13938
|
+
const loop = normalizeLoopDefinition(JSON.parse(fs38.readFileSync(filePath, "utf8")));
|
|
13910
13939
|
if (loop?.id === id) return loop;
|
|
13911
13940
|
process.stderr.write(`[kody] invalid Loop definition: ${filePath}
|
|
13912
13941
|
`);
|
|
@@ -13916,7 +13945,7 @@ function readLoopDefinition(cwd, id) {
|
|
|
13916
13945
|
}
|
|
13917
13946
|
}
|
|
13918
13947
|
process.stderr.write(
|
|
13919
|
-
`[kody] Loop not found: ${id} (${roots.map((root) =>
|
|
13948
|
+
`[kody] Loop not found: ${id} (${roots.map((root) => path36.join(root, "loops", id, "loop.json")).join(", ")})
|
|
13920
13949
|
`
|
|
13921
13950
|
);
|
|
13922
13951
|
return null;
|
|
@@ -13925,14 +13954,14 @@ function listLoopDefinitions(cwd) {
|
|
|
13925
13954
|
const roots = loopRoots(cwd);
|
|
13926
13955
|
const byId = /* @__PURE__ */ new Map();
|
|
13927
13956
|
for (const root of roots.reverse()) {
|
|
13928
|
-
const loopsDir =
|
|
13929
|
-
if (!
|
|
13930
|
-
for (const id of
|
|
13957
|
+
const loopsDir = path36.join(root, "loops");
|
|
13958
|
+
if (!fs38.existsSync(loopsDir)) continue;
|
|
13959
|
+
for (const id of fs38.readdirSync(loopsDir).sort()) {
|
|
13931
13960
|
if (!ID.test(id)) continue;
|
|
13932
|
-
const filePath =
|
|
13933
|
-
if (!
|
|
13961
|
+
const filePath = path36.join(loopsDir, id, "loop.json");
|
|
13962
|
+
if (!fs38.existsSync(filePath)) continue;
|
|
13934
13963
|
try {
|
|
13935
|
-
const loop = normalizeLoopDefinition(JSON.parse(
|
|
13964
|
+
const loop = normalizeLoopDefinition(JSON.parse(fs38.readFileSync(filePath, "utf8")));
|
|
13936
13965
|
if (loop?.id === id) byId.set(id, loop);
|
|
13937
13966
|
} catch {
|
|
13938
13967
|
process.stderr.write(`[kody] unreadable Loop definition: ${filePath}
|
|
@@ -13944,8 +13973,8 @@ function listLoopDefinitions(cwd) {
|
|
|
13944
13973
|
}
|
|
13945
13974
|
function loopRoots(cwd) {
|
|
13946
13975
|
return [
|
|
13947
|
-
|
|
13948
|
-
|
|
13976
|
+
path36.join(cwd, ".kody-engine", "runtime"),
|
|
13977
|
+
path36.join(cwd, ".kody-engine", "definitions"),
|
|
13949
13978
|
definitionsRoot(cwd)
|
|
13950
13979
|
].filter((root, index, roots) => roots.indexOf(root) === index);
|
|
13951
13980
|
}
|
|
@@ -15223,15 +15252,15 @@ var init_fixFlow = __esm({
|
|
|
15223
15252
|
});
|
|
15224
15253
|
|
|
15225
15254
|
// src/workflow-template.ts
|
|
15226
|
-
import * as
|
|
15227
|
-
import * as
|
|
15255
|
+
import * as fs39 from "fs";
|
|
15256
|
+
import * as path37 from "path";
|
|
15228
15257
|
import { fileURLToPath } from "url";
|
|
15229
15258
|
function loadKodyWorkflowTemplate() {
|
|
15230
|
-
const here =
|
|
15231
|
-
const candidates = [
|
|
15232
|
-
const source = candidates.find((candidate) =>
|
|
15259
|
+
const here = path37.dirname(fileURLToPath(import.meta.url));
|
|
15260
|
+
const candidates = [path37.resolve(here, "../templates/kody.yml"), path37.resolve(here, "../../templates/kody.yml")];
|
|
15261
|
+
const source = candidates.find((candidate) => fs39.existsSync(candidate));
|
|
15233
15262
|
if (!source) throw new Error(`Kody workflow template is missing: ${KODY_WORKFLOW_TEMPLATE_PATH}`);
|
|
15234
|
-
return
|
|
15263
|
+
return fs39.readFileSync(source, "utf8");
|
|
15235
15264
|
}
|
|
15236
15265
|
var KODY_WORKFLOW_TEMPLATE_PATH;
|
|
15237
15266
|
var init_workflow_template = __esm({
|
|
@@ -15243,12 +15272,12 @@ var init_workflow_template = __esm({
|
|
|
15243
15272
|
|
|
15244
15273
|
// src/scripts/initFlow.ts
|
|
15245
15274
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
15246
|
-
import * as
|
|
15247
|
-
import * as
|
|
15275
|
+
import * as fs40 from "fs";
|
|
15276
|
+
import * as path38 from "path";
|
|
15248
15277
|
function detectPackageManager(cwd) {
|
|
15249
|
-
if (
|
|
15250
|
-
if (
|
|
15251
|
-
if (
|
|
15278
|
+
if (fs40.existsSync(path38.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
15279
|
+
if (fs40.existsSync(path38.join(cwd, "yarn.lock"))) return "yarn";
|
|
15280
|
+
if (fs40.existsSync(path38.join(cwd, "bun.lockb"))) return "bun";
|
|
15252
15281
|
return "npm";
|
|
15253
15282
|
}
|
|
15254
15283
|
function qualityCommandsFor(pm) {
|
|
@@ -15320,22 +15349,22 @@ function performInit(cwd, force) {
|
|
|
15320
15349
|
const pm = detectPackageManager(cwd);
|
|
15321
15350
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
15322
15351
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
15323
|
-
const configPath =
|
|
15324
|
-
if (
|
|
15352
|
+
const configPath = path38.join(cwd, "kody.config.json");
|
|
15353
|
+
if (fs40.existsSync(configPath) && !force) {
|
|
15325
15354
|
skipped.push("kody.config.json");
|
|
15326
15355
|
} else {
|
|
15327
15356
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
15328
|
-
|
|
15357
|
+
fs40.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
15329
15358
|
`);
|
|
15330
15359
|
wrote.push("kody.config.json");
|
|
15331
15360
|
}
|
|
15332
|
-
const workflowDir =
|
|
15333
|
-
const workflowPath =
|
|
15334
|
-
if (
|
|
15361
|
+
const workflowDir = path38.join(cwd, ".github", "workflows");
|
|
15362
|
+
const workflowPath = path38.join(workflowDir, "kody.yml");
|
|
15363
|
+
if (fs40.existsSync(workflowPath) && !force) {
|
|
15335
15364
|
skipped.push(".github/workflows/kody.yml");
|
|
15336
15365
|
} else {
|
|
15337
|
-
|
|
15338
|
-
|
|
15366
|
+
fs40.mkdirSync(workflowDir, { recursive: true });
|
|
15367
|
+
fs40.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
|
|
15339
15368
|
wrote.push(".github/workflows/kody.yml");
|
|
15340
15369
|
}
|
|
15341
15370
|
let labels;
|
|
@@ -15386,7 +15415,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
|
|
|
15386
15415
|
});
|
|
15387
15416
|
|
|
15388
15417
|
// src/scripts/loadAgentAdhoc.ts
|
|
15389
|
-
import * as
|
|
15418
|
+
import * as fs41 from "fs";
|
|
15390
15419
|
function resolveMessage(messageArg) {
|
|
15391
15420
|
const fromComment = readCommentBody();
|
|
15392
15421
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -15394,9 +15423,9 @@ function resolveMessage(messageArg) {
|
|
|
15394
15423
|
}
|
|
15395
15424
|
function readCommentBody() {
|
|
15396
15425
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15397
|
-
if (!eventPath || !
|
|
15426
|
+
if (!eventPath || !fs41.existsSync(eventPath)) return "";
|
|
15398
15427
|
try {
|
|
15399
|
-
const event = JSON.parse(
|
|
15428
|
+
const event = JSON.parse(fs41.readFileSync(eventPath, "utf-8"));
|
|
15400
15429
|
return String(event.comment?.body ?? "");
|
|
15401
15430
|
} catch {
|
|
15402
15431
|
return "";
|
|
@@ -15450,10 +15479,10 @@ var init_loadAgentAdhoc = __esm({
|
|
|
15450
15479
|
throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
|
|
15451
15480
|
}
|
|
15452
15481
|
const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
|
|
15453
|
-
if (!
|
|
15482
|
+
if (!fs41.existsSync(agentPath)) {
|
|
15454
15483
|
throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
|
|
15455
15484
|
}
|
|
15456
|
-
const { title, body } = parseAgentFile(
|
|
15485
|
+
const { title, body } = parseAgentFile(fs41.readFileSync(agentPath, "utf-8"), agentSlug);
|
|
15457
15486
|
const message = resolveMessage(ctx.args.message);
|
|
15458
15487
|
if (!message) {
|
|
15459
15488
|
throw new Error(
|
|
@@ -15525,13 +15554,13 @@ var init_loadCapabilityState = __esm({
|
|
|
15525
15554
|
function isCompanyIntentId(value) {
|
|
15526
15555
|
return SLUG_RE2.test(value);
|
|
15527
15556
|
}
|
|
15528
|
-
function normalizeCompanyIntent(
|
|
15557
|
+
function normalizeCompanyIntent(path58, raw) {
|
|
15529
15558
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15530
|
-
throw new Error(`${
|
|
15559
|
+
throw new Error(`${path58}: intent must be JSON object`);
|
|
15531
15560
|
}
|
|
15532
15561
|
const input = raw;
|
|
15533
15562
|
const id = stringField4(input.id);
|
|
15534
|
-
if (!id || !isCompanyIntentId(id)) throw new Error(`${
|
|
15563
|
+
if (!id || !isCompanyIntentId(id)) throw new Error(`${path58}: invalid intent id`);
|
|
15535
15564
|
const createdAt = stringField4(input.createdAt) || nowIso();
|
|
15536
15565
|
const updatedAt = stringField4(input.updatedAt) || createdAt;
|
|
15537
15566
|
const description = stringField4(input.description);
|
|
@@ -15693,7 +15722,7 @@ function retryDelaysMs() {
|
|
|
15693
15722
|
}
|
|
15694
15723
|
function sleep(ms) {
|
|
15695
15724
|
if (ms <= 0) return Promise.resolve();
|
|
15696
|
-
return new Promise((
|
|
15725
|
+
return new Promise((resolve23) => setTimeout(resolve23, ms));
|
|
15697
15726
|
}
|
|
15698
15727
|
async function fetchGoalStateWithRetry(config, goalId, cwd) {
|
|
15699
15728
|
let state = await fetchGoalStateAsync(config, goalId, cwd);
|
|
@@ -15822,8 +15851,8 @@ var init_loadIssueStateComment = __esm({
|
|
|
15822
15851
|
});
|
|
15823
15852
|
|
|
15824
15853
|
// src/scripts/loadJobFromFile.ts
|
|
15825
|
-
import * as
|
|
15826
|
-
import * as
|
|
15854
|
+
import * as fs42 from "fs";
|
|
15855
|
+
import * as path39 from "path";
|
|
15827
15856
|
function parseJobFile(raw, slug) {
|
|
15828
15857
|
let stripped = raw;
|
|
15829
15858
|
if (stripped.startsWith("---\n")) {
|
|
@@ -15862,10 +15891,10 @@ var init_loadJobFromFile = __esm({
|
|
|
15862
15891
|
if (!slug) {
|
|
15863
15892
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
15864
15893
|
}
|
|
15865
|
-
const capability = resolveCapabilityFolder(slug,
|
|
15894
|
+
const capability = resolveCapabilityFolder(slug, path39.resolve(ctx.cwd, jobsDir));
|
|
15866
15895
|
if (!capability) {
|
|
15867
15896
|
throw new Error(
|
|
15868
|
-
`loadJobFromFile: capability folder not found or incomplete: ${
|
|
15897
|
+
`loadJobFromFile: capability folder not found or incomplete: ${path39.resolve(ctx.cwd, jobsDir, slug)}`
|
|
15869
15898
|
);
|
|
15870
15899
|
}
|
|
15871
15900
|
const { title, body, config } = capability;
|
|
@@ -15875,12 +15904,12 @@ var init_loadJobFromFile = __esm({
|
|
|
15875
15904
|
let agentIdentity = "";
|
|
15876
15905
|
if (agentSlug) {
|
|
15877
15906
|
const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
|
|
15878
|
-
if (!
|
|
15907
|
+
if (!fs42.existsSync(agentPath)) {
|
|
15879
15908
|
throw new Error(
|
|
15880
15909
|
`loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
|
|
15881
15910
|
);
|
|
15882
15911
|
}
|
|
15883
|
-
const agentRaw =
|
|
15912
|
+
const agentRaw = fs42.readFileSync(agentPath, "utf-8");
|
|
15884
15913
|
const parsed = parseJobFile(agentRaw, agentSlug);
|
|
15885
15914
|
agentTitle = parsed.title;
|
|
15886
15915
|
agentIdentity = parsed.body;
|
|
@@ -15960,13 +15989,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
15960
15989
|
});
|
|
15961
15990
|
|
|
15962
15991
|
// src/scripts/kodyVariables.ts
|
|
15963
|
-
import * as
|
|
15964
|
-
import * as
|
|
15992
|
+
import * as fs43 from "fs";
|
|
15993
|
+
import * as path40 from "path";
|
|
15965
15994
|
function readKodyVariables(cwd) {
|
|
15966
|
-
const full =
|
|
15995
|
+
const full = path40.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
15967
15996
|
let raw;
|
|
15968
15997
|
try {
|
|
15969
|
-
raw =
|
|
15998
|
+
raw = fs43.readFileSync(full, "utf-8");
|
|
15970
15999
|
} catch {
|
|
15971
16000
|
return {};
|
|
15972
16001
|
}
|
|
@@ -15991,8 +16020,8 @@ var init_kodyVariables = __esm({
|
|
|
15991
16020
|
});
|
|
15992
16021
|
|
|
15993
16022
|
// src/scripts/loadQaContext.ts
|
|
15994
|
-
import * as
|
|
15995
|
-
import * as
|
|
16023
|
+
import * as fs44 from "fs";
|
|
16024
|
+
import * as path41 from "path";
|
|
15996
16025
|
function parseSlugList(value) {
|
|
15997
16026
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
15998
16027
|
return inner.split(",").map(
|
|
@@ -16021,18 +16050,18 @@ function readProfileAgents(raw) {
|
|
|
16021
16050
|
return { agent: agent ?? legacy ?? ["kody"], body };
|
|
16022
16051
|
}
|
|
16023
16052
|
function readProfile(cwd) {
|
|
16024
|
-
const dir =
|
|
16025
|
-
if (!
|
|
16053
|
+
const dir = path41.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
16054
|
+
if (!fs44.existsSync(dir)) return "";
|
|
16026
16055
|
let entries;
|
|
16027
16056
|
try {
|
|
16028
|
-
entries =
|
|
16057
|
+
entries = fs44.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
16029
16058
|
} catch {
|
|
16030
16059
|
return "";
|
|
16031
16060
|
}
|
|
16032
16061
|
const blocks = [];
|
|
16033
16062
|
for (const file of entries) {
|
|
16034
16063
|
try {
|
|
16035
|
-
const raw =
|
|
16064
|
+
const raw = fs44.readFileSync(path41.join(dir, file), "utf-8");
|
|
16036
16065
|
const { agent, body } = readProfileAgents(raw);
|
|
16037
16066
|
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
16038
16067
|
blocks.push(`## ${file}
|
|
@@ -16082,9 +16111,9 @@ var init_loadQaContext = __esm({
|
|
|
16082
16111
|
|
|
16083
16112
|
// src/scripts/loadSimpleCapability.ts
|
|
16084
16113
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
16085
|
-
import * as
|
|
16114
|
+
import * as fs45 from "fs";
|
|
16086
16115
|
import * as os6 from "os";
|
|
16087
|
-
import * as
|
|
16116
|
+
import * as path42 from "path";
|
|
16088
16117
|
function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
|
|
16089
16118
|
const subagentFiles = toolFiles.flatMap((file) => {
|
|
16090
16119
|
const match = /^agents\/([a-z][a-z0-9-]{0,63})\.md$/.exec(file);
|
|
@@ -16097,7 +16126,7 @@ function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
|
|
|
16097
16126
|
profile.subagentTemplates = {
|
|
16098
16127
|
...profile.subagentTemplates ?? {},
|
|
16099
16128
|
...Object.fromEntries(
|
|
16100
|
-
subagentFiles.map(({ name, file }) => [name,
|
|
16129
|
+
subagentFiles.map(({ name, file }) => [name, fs45.readFileSync(path42.join(toolRoot, file), "utf-8")])
|
|
16101
16130
|
)
|
|
16102
16131
|
};
|
|
16103
16132
|
if (!profile.claudeCode.tools.includes("Agent")) {
|
|
@@ -16138,14 +16167,14 @@ function scalar(value) {
|
|
|
16138
16167
|
return value;
|
|
16139
16168
|
}
|
|
16140
16169
|
function listFiles(root) {
|
|
16141
|
-
if (!
|
|
16170
|
+
if (!fs45.existsSync(root)) return [];
|
|
16142
16171
|
const files = [];
|
|
16143
16172
|
const visit = (dir) => {
|
|
16144
|
-
for (const entry of
|
|
16145
|
-
const absolute =
|
|
16173
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
16174
|
+
const absolute = path42.join(dir, entry.name);
|
|
16146
16175
|
if (entry.isSymbolicLink()) continue;
|
|
16147
16176
|
if (entry.isDirectory()) visit(absolute);
|
|
16148
|
-
else if (entry.isFile()) files.push(
|
|
16177
|
+
else if (entry.isFile()) files.push(path42.relative(root, absolute));
|
|
16149
16178
|
}
|
|
16150
16179
|
};
|
|
16151
16180
|
visit(root);
|
|
@@ -16168,8 +16197,8 @@ var init_loadSimpleCapability = __esm({
|
|
|
16168
16197
|
if (!capability) {
|
|
16169
16198
|
throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
|
|
16170
16199
|
}
|
|
16171
|
-
const toolRoot =
|
|
16172
|
-
const skillRoot =
|
|
16200
|
+
const toolRoot = path42.join(capability.dir, "tools");
|
|
16201
|
+
const skillRoot = path42.join(capability.dir, "skills");
|
|
16173
16202
|
const toolFiles = listFiles(toolRoot);
|
|
16174
16203
|
const skillFiles = listFiles(skillRoot);
|
|
16175
16204
|
const parsedInput = parseInput(ctx.args.input);
|
|
@@ -16194,14 +16223,14 @@ var init_loadSimpleCapability = __esm({
|
|
|
16194
16223
|
}
|
|
16195
16224
|
if (requiredSubagents.length > 0) ctx.data.requiredSubagents = requiredSubagents;
|
|
16196
16225
|
if (capability.contract?.execution === "script") {
|
|
16197
|
-
ctx.data.capabilityScriptPath =
|
|
16226
|
+
ctx.data.capabilityScriptPath = path42.join(capability.dir, "tools", "run.sh");
|
|
16198
16227
|
ctx.data.capabilitySecretNames = capability.contract.secrets ?? [];
|
|
16199
16228
|
ctx.data.capabilityScriptTimeoutMs = capability.contract.timeoutMs;
|
|
16200
16229
|
}
|
|
16201
16230
|
if (capability.config.outputSchema) {
|
|
16202
16231
|
ctx.data.capabilityOutputSchema = capability.config.outputSchema;
|
|
16203
16232
|
}
|
|
16204
|
-
const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ?
|
|
16233
|
+
const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ? path42.join(os6.tmpdir(), `kody-capability-output-${randomUUID2()}.json`) : void 0;
|
|
16205
16234
|
if (outputPath) ctx.data.capabilityOutputPath = outputPath;
|
|
16206
16235
|
ctx.data.capabilityEnvironment = {
|
|
16207
16236
|
...capabilityInputEnvironment(input),
|
|
@@ -16224,7 +16253,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
16224
16253
|
...skillFiles.flatMap((file) => [
|
|
16225
16254
|
`### ${file}`,
|
|
16226
16255
|
"",
|
|
16227
|
-
|
|
16256
|
+
fs45.readFileSync(path42.join(skillRoot, file), "utf-8"),
|
|
16228
16257
|
""
|
|
16229
16258
|
])
|
|
16230
16259
|
] : [],
|
|
@@ -16233,7 +16262,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
16233
16262
|
"## Tools",
|
|
16234
16263
|
"",
|
|
16235
16264
|
"Inspect or run these capability-owned files when needed:",
|
|
16236
|
-
...toolFiles.map((file) => `- ${
|
|
16265
|
+
...toolFiles.map((file) => `- ${path42.join(toolRoot, file)}`)
|
|
16237
16266
|
] : [],
|
|
16238
16267
|
"",
|
|
16239
16268
|
...capability.config.outputSchema ? [
|
|
@@ -16264,8 +16293,8 @@ var init_loadSimpleCapability = __esm({
|
|
|
16264
16293
|
});
|
|
16265
16294
|
|
|
16266
16295
|
// src/taskContext.ts
|
|
16267
|
-
import * as
|
|
16268
|
-
import * as
|
|
16296
|
+
import * as fs46 from "fs";
|
|
16297
|
+
import * as path43 from "path";
|
|
16269
16298
|
function buildTaskContext(args) {
|
|
16270
16299
|
return {
|
|
16271
16300
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -16281,9 +16310,9 @@ function buildTaskContext(args) {
|
|
|
16281
16310
|
function persistTaskContext(cwd, ctx) {
|
|
16282
16311
|
try {
|
|
16283
16312
|
const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
|
|
16284
|
-
|
|
16285
|
-
const file =
|
|
16286
|
-
|
|
16313
|
+
fs46.mkdirSync(dir, { recursive: true });
|
|
16314
|
+
const file = path43.join(dir, "task-context.json");
|
|
16315
|
+
fs46.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
16287
16316
|
`);
|
|
16288
16317
|
return file;
|
|
16289
16318
|
} catch (err) {
|
|
@@ -16710,19 +16739,19 @@ function parseAgencyModelProposal(raw) {
|
|
|
16710
16739
|
function normalizeBundleFiles(bundle) {
|
|
16711
16740
|
const seen = /* @__PURE__ */ new Set();
|
|
16712
16741
|
return bundle.files.map((file, index) => {
|
|
16713
|
-
const
|
|
16714
|
-
const parts =
|
|
16715
|
-
if (!
|
|
16742
|
+
const path58 = file.path.replace(/^\/+/, "");
|
|
16743
|
+
const parts = path58.split("/");
|
|
16744
|
+
if (!path58 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
|
|
16716
16745
|
throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
|
|
16717
16746
|
}
|
|
16718
16747
|
if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
|
|
16719
|
-
|
|
16748
|
+
path58
|
|
16720
16749
|
)) {
|
|
16721
16750
|
throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
|
|
16722
16751
|
}
|
|
16723
|
-
if (seen.has(
|
|
16724
|
-
seen.add(
|
|
16725
|
-
return { path:
|
|
16752
|
+
if (seen.has(path58)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path58}`);
|
|
16753
|
+
seen.add(path58);
|
|
16754
|
+
return { path: path58, content: file.content.replace(/\r\n?/g, "\n") };
|
|
16726
16755
|
});
|
|
16727
16756
|
}
|
|
16728
16757
|
function buildProposalId(issueNumber, bundle, sourceLabel) {
|
|
@@ -17204,16 +17233,16 @@ var init_parseReproOutput = __esm({
|
|
|
17204
17233
|
});
|
|
17205
17234
|
|
|
17206
17235
|
// src/scripts/parseSimpleCapabilityOutput.ts
|
|
17207
|
-
import * as
|
|
17236
|
+
import * as fs47 from "fs";
|
|
17208
17237
|
function stringList2(value) {
|
|
17209
17238
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17210
17239
|
}
|
|
17211
17240
|
function readOutputFile(outputPath) {
|
|
17212
|
-
if (!outputPath || !
|
|
17241
|
+
if (!outputPath || !fs47.existsSync(outputPath)) return { found: false };
|
|
17213
17242
|
try {
|
|
17214
|
-
return { found: true, value: JSON.parse(
|
|
17243
|
+
return { found: true, value: JSON.parse(fs47.readFileSync(outputPath, "utf-8")) };
|
|
17215
17244
|
} finally {
|
|
17216
|
-
|
|
17245
|
+
fs47.rmSync(outputPath, { force: true });
|
|
17217
17246
|
}
|
|
17218
17247
|
}
|
|
17219
17248
|
function parseOutput(text2) {
|
|
@@ -17832,9 +17861,9 @@ var init_postResearchComment = __esm({
|
|
|
17832
17861
|
});
|
|
17833
17862
|
|
|
17834
17863
|
// src/scripts/prepareBrowserAuth.ts
|
|
17835
|
-
import * as
|
|
17864
|
+
import * as fs48 from "fs";
|
|
17836
17865
|
import * as os7 from "os";
|
|
17837
|
-
import * as
|
|
17866
|
+
import * as path44 from "path";
|
|
17838
17867
|
function appendAuthMessage(ctx, message) {
|
|
17839
17868
|
const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
|
|
17840
17869
|
ctx.data.qaAuthBlock = current ? `${current}
|
|
@@ -17873,9 +17902,9 @@ async function githubJson(url, token) {
|
|
|
17873
17902
|
return await response.json();
|
|
17874
17903
|
}
|
|
17875
17904
|
function writeKodyStorageState(input) {
|
|
17876
|
-
const directory =
|
|
17877
|
-
|
|
17878
|
-
const file =
|
|
17905
|
+
const directory = fs48.mkdtempSync(path44.join(os7.tmpdir(), "kody-browser-auth-"));
|
|
17906
|
+
fs48.chmodSync(directory, 448);
|
|
17907
|
+
const file = path44.join(directory, "storage-state.json");
|
|
17879
17908
|
const now = Date.now();
|
|
17880
17909
|
const repoEntry = {
|
|
17881
17910
|
repoUrl: input.repoUrl,
|
|
@@ -17905,7 +17934,7 @@ function writeKodyStorageState(input) {
|
|
|
17905
17934
|
}
|
|
17906
17935
|
]
|
|
17907
17936
|
};
|
|
17908
|
-
|
|
17937
|
+
fs48.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
|
|
17909
17938
|
return { directory, file };
|
|
17910
17939
|
}
|
|
17911
17940
|
function configurePlaywright(profile, storageStatePath) {
|
|
@@ -17987,7 +18016,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
17987
18016
|
configurePlaywright(profile, state.file);
|
|
17988
18017
|
const authDirectory = state.directory;
|
|
17989
18018
|
registerRuntimeCleanup(ctx, () => {
|
|
17990
|
-
|
|
18019
|
+
fs48.rmSync(authDirectory, { recursive: true, force: true });
|
|
17991
18020
|
});
|
|
17992
18021
|
appendAuthMessage(
|
|
17993
18022
|
ctx,
|
|
@@ -17995,7 +18024,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
17995
18024
|
);
|
|
17996
18025
|
return true;
|
|
17997
18026
|
} catch (error) {
|
|
17998
|
-
if (state)
|
|
18027
|
+
if (state) fs48.rmSync(state.directory, { recursive: true, force: true });
|
|
17999
18028
|
const reason = error instanceof Error ? error.message : String(error);
|
|
18000
18029
|
appendAuthMessage(
|
|
18001
18030
|
ctx,
|
|
@@ -18134,7 +18163,7 @@ var init_prepareCapabilityDelivery = __esm({
|
|
|
18134
18163
|
|
|
18135
18164
|
// src/scripts/prepareSimpleCapabilityRuntime.ts
|
|
18136
18165
|
import { isIP } from "net";
|
|
18137
|
-
import * as
|
|
18166
|
+
import * as path45 from "path";
|
|
18138
18167
|
function requirementsFrom(ctx) {
|
|
18139
18168
|
const raw = ctx.data.capabilityRequirements;
|
|
18140
18169
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
@@ -18178,7 +18207,7 @@ function browserRuntime(ctx, requirements) {
|
|
|
18178
18207
|
"--allowed-origins",
|
|
18179
18208
|
origin,
|
|
18180
18209
|
"--output-dir",
|
|
18181
|
-
|
|
18210
|
+
path45.resolve(ctx.cwd, "test-results", "quality-runs", qualityRunId)
|
|
18182
18211
|
]
|
|
18183
18212
|
};
|
|
18184
18213
|
}
|
|
@@ -18543,9 +18572,9 @@ function latestResult(raw, agentResult) {
|
|
|
18543
18572
|
function recordField4(value) {
|
|
18544
18573
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
18545
18574
|
}
|
|
18546
|
-
function resolveDotted(root,
|
|
18547
|
-
if (!
|
|
18548
|
-
return
|
|
18575
|
+
function resolveDotted(root, path58) {
|
|
18576
|
+
if (!path58) return void 0;
|
|
18577
|
+
return path58.split(".").reduce((value, key) => recordField4(value)?.[key], root);
|
|
18549
18578
|
}
|
|
18550
18579
|
function stringValue5(value) {
|
|
18551
18580
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
@@ -19387,7 +19416,7 @@ var init_previewBuildHelpers = __esm({
|
|
|
19387
19416
|
// src/scripts/previewBuildRun.ts
|
|
19388
19417
|
import { spawn as spawn5 } from "child_process";
|
|
19389
19418
|
async function runCmd(cmd, args, opts = {}) {
|
|
19390
|
-
await new Promise((
|
|
19419
|
+
await new Promise((resolve23, reject) => {
|
|
19391
19420
|
const child = spawn5(cmd, args, {
|
|
19392
19421
|
cwd: opts.cwd,
|
|
19393
19422
|
env: { ...process.env, ...opts.env ?? {} },
|
|
@@ -19399,7 +19428,7 @@ async function runCmd(cmd, args, opts = {}) {
|
|
|
19399
19428
|
}
|
|
19400
19429
|
child.on("error", reject);
|
|
19401
19430
|
child.on("close", (code) => {
|
|
19402
|
-
if (code === 0)
|
|
19431
|
+
if (code === 0) resolve23();
|
|
19403
19432
|
else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}`));
|
|
19404
19433
|
});
|
|
19405
19434
|
});
|
|
@@ -19471,12 +19500,12 @@ fi
|
|
|
19471
19500
|
|
|
19472
19501
|
// src/scripts/runPreviewBuild.ts
|
|
19473
19502
|
import { copyFile, writeFile } from "fs/promises";
|
|
19474
|
-
import * as
|
|
19503
|
+
import * as path46 from "path";
|
|
19475
19504
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19476
19505
|
function bundledDockerfilePath(mode) {
|
|
19477
|
-
const here =
|
|
19506
|
+
const here = path46.dirname(fileURLToPath2(import.meta.url));
|
|
19478
19507
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
19479
|
-
return
|
|
19508
|
+
return path46.join(here, "preview-build-templates", file);
|
|
19480
19509
|
}
|
|
19481
19510
|
function required(name) {
|
|
19482
19511
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -19711,10 +19740,10 @@ var init_runPreviewBuild = __esm({
|
|
|
19711
19740
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
19712
19741
|
if (Object.keys(buildEnv).length > 0) {
|
|
19713
19742
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
19714
|
-
await writeFile(
|
|
19743
|
+
await writeFile(path46.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
19715
19744
|
`, "utf8");
|
|
19716
19745
|
}
|
|
19717
|
-
const consumerDockerfile =
|
|
19746
|
+
const consumerDockerfile = path46.join(ctx.cwd, "Dockerfile.preview");
|
|
19718
19747
|
const { stat } = await import("fs/promises");
|
|
19719
19748
|
let hasConsumerDockerfile = false;
|
|
19720
19749
|
try {
|
|
@@ -19898,8 +19927,8 @@ var init_tickShellRunner = __esm({
|
|
|
19898
19927
|
});
|
|
19899
19928
|
|
|
19900
19929
|
// src/scripts/runScheduledImplementationTick.ts
|
|
19901
|
-
import * as
|
|
19902
|
-
import * as
|
|
19930
|
+
import * as fs49 from "fs";
|
|
19931
|
+
import * as path47 from "path";
|
|
19903
19932
|
var runScheduledImplementationTick;
|
|
19904
19933
|
var init_runScheduledImplementationTick = __esm({
|
|
19905
19934
|
"src/scripts/runScheduledImplementationTick.ts"() {
|
|
@@ -19920,14 +19949,14 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
19920
19949
|
ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
|
|
19921
19950
|
return;
|
|
19922
19951
|
}
|
|
19923
|
-
const capability = resolveCapabilityFolder(slug,
|
|
19952
|
+
const capability = resolveCapabilityFolder(slug, path47.resolve(ctx.cwd, jobsDir));
|
|
19924
19953
|
if (!capability) {
|
|
19925
19954
|
ctx.output.exitCode = 99;
|
|
19926
19955
|
ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
|
|
19927
19956
|
return;
|
|
19928
19957
|
}
|
|
19929
|
-
const shellPath =
|
|
19930
|
-
if (!
|
|
19958
|
+
const shellPath = path47.join(profile.dir, shell);
|
|
19959
|
+
if (!fs49.existsSync(shellPath)) {
|
|
19931
19960
|
ctx.output.exitCode = 99;
|
|
19932
19961
|
ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
19933
19962
|
return;
|
|
@@ -19959,13 +19988,13 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
19959
19988
|
|
|
19960
19989
|
// src/scripts/runSimpleCapabilityScript.ts
|
|
19961
19990
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
19962
|
-
import * as
|
|
19991
|
+
import * as fs50 from "fs";
|
|
19963
19992
|
function formatDuration2(timeoutMs) {
|
|
19964
19993
|
return timeoutMs % 6e4 === 0 ? `${timeoutMs / 6e4} minutes` : `${timeoutMs}ms`;
|
|
19965
19994
|
}
|
|
19966
19995
|
function isRegularFile2(filePath) {
|
|
19967
19996
|
try {
|
|
19968
|
-
const stat =
|
|
19997
|
+
const stat = fs50.lstatSync(filePath);
|
|
19969
19998
|
return stat.isFile() && !stat.isSymbolicLink();
|
|
19970
19999
|
} catch {
|
|
19971
20000
|
return false;
|
|
@@ -20044,8 +20073,8 @@ var init_runSimpleCapabilityScript = __esm({
|
|
|
20044
20073
|
});
|
|
20045
20074
|
|
|
20046
20075
|
// src/scripts/runTickScript.ts
|
|
20047
|
-
import * as
|
|
20048
|
-
import * as
|
|
20076
|
+
import * as fs51 from "fs";
|
|
20077
|
+
import * as path48 from "path";
|
|
20049
20078
|
var runTickScript;
|
|
20050
20079
|
var init_runTickScript = __esm({
|
|
20051
20080
|
"src/scripts/runTickScript.ts"() {
|
|
@@ -20065,10 +20094,10 @@ var init_runTickScript = __esm({
|
|
|
20065
20094
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
20066
20095
|
return;
|
|
20067
20096
|
}
|
|
20068
|
-
const capability = readCapabilityFolder(
|
|
20097
|
+
const capability = readCapabilityFolder(path48.resolve(ctx.cwd, jobsDir), slug);
|
|
20069
20098
|
if (!capability) {
|
|
20070
20099
|
ctx.output.exitCode = 99;
|
|
20071
|
-
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${
|
|
20100
|
+
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path48.resolve(ctx.cwd, jobsDir, slug)}`;
|
|
20072
20101
|
return;
|
|
20073
20102
|
}
|
|
20074
20103
|
const tickScript = capability.config.tickScript;
|
|
@@ -20077,8 +20106,8 @@ var init_runTickScript = __esm({
|
|
|
20077
20106
|
ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
|
|
20078
20107
|
return;
|
|
20079
20108
|
}
|
|
20080
|
-
const scriptPath =
|
|
20081
|
-
if (!
|
|
20109
|
+
const scriptPath = path48.isAbsolute(tickScript) ? tickScript : path48.join(ctx.cwd, tickScript);
|
|
20110
|
+
if (!fs51.existsSync(scriptPath)) {
|
|
20082
20111
|
ctx.output.exitCode = 99;
|
|
20083
20112
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
20084
20113
|
return;
|
|
@@ -20360,7 +20389,7 @@ var init_syncFlow = __esm({
|
|
|
20360
20389
|
});
|
|
20361
20390
|
|
|
20362
20391
|
// src/scripts/validateAgencyModelProposal.ts
|
|
20363
|
-
import * as
|
|
20392
|
+
import * as path49 from "path";
|
|
20364
20393
|
function validateModelBundle(bundle, expectedKind, options = {}) {
|
|
20365
20394
|
const failures = [];
|
|
20366
20395
|
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
|
|
@@ -20678,7 +20707,7 @@ var init_validateAgencyModelProposal = __esm({
|
|
|
20678
20707
|
const bundle = parseAgencyModelProposal(raw);
|
|
20679
20708
|
const expectedKind = readExpectedModelKind(args);
|
|
20680
20709
|
const failures = validateModelBundle(bundle, expectedKind, {
|
|
20681
|
-
capabilityRoot:
|
|
20710
|
+
capabilityRoot: path49.join(ctx.cwd, ".kody", "capabilities")
|
|
20682
20711
|
});
|
|
20683
20712
|
if (failures.length > 0) {
|
|
20684
20713
|
throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
|
|
@@ -20741,7 +20770,7 @@ function stripAnsi2(s) {
|
|
|
20741
20770
|
return s.replace(ANSI_RE2, "");
|
|
20742
20771
|
}
|
|
20743
20772
|
function runCommand2(command, cwd) {
|
|
20744
|
-
return new Promise((
|
|
20773
|
+
return new Promise((resolve23) => {
|
|
20745
20774
|
const child = spawn6(command, {
|
|
20746
20775
|
cwd,
|
|
20747
20776
|
shell: true,
|
|
@@ -20768,11 +20797,11 @@ function runCommand2(command, cwd) {
|
|
|
20768
20797
|
}, TEST_TIMEOUT_MS);
|
|
20769
20798
|
child.on("exit", (code) => {
|
|
20770
20799
|
clearTimeout(timer);
|
|
20771
|
-
|
|
20800
|
+
resolve23({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
|
|
20772
20801
|
});
|
|
20773
20802
|
child.on("error", (err) => {
|
|
20774
20803
|
clearTimeout(timer);
|
|
20775
|
-
|
|
20804
|
+
resolve23({ exitCode: -1, output: err.message });
|
|
20776
20805
|
});
|
|
20777
20806
|
});
|
|
20778
20807
|
}
|
|
@@ -21178,21 +21207,21 @@ function lineStream(stream) {
|
|
|
21178
21207
|
tryDeliver();
|
|
21179
21208
|
});
|
|
21180
21209
|
return {
|
|
21181
|
-
next: (timeoutMs) => new Promise((
|
|
21210
|
+
next: (timeoutMs) => new Promise((resolve23) => {
|
|
21182
21211
|
if (queue.length > 0) {
|
|
21183
|
-
|
|
21212
|
+
resolve23(queue.shift());
|
|
21184
21213
|
return;
|
|
21185
21214
|
}
|
|
21186
21215
|
if (ended) {
|
|
21187
|
-
|
|
21216
|
+
resolve23(null);
|
|
21188
21217
|
return;
|
|
21189
21218
|
}
|
|
21190
|
-
waiter =
|
|
21219
|
+
waiter = resolve23;
|
|
21191
21220
|
const t = setTimeout(
|
|
21192
21221
|
() => {
|
|
21193
|
-
if (waiter ===
|
|
21222
|
+
if (waiter === resolve23) {
|
|
21194
21223
|
waiter = null;
|
|
21195
|
-
|
|
21224
|
+
resolve23(null);
|
|
21196
21225
|
}
|
|
21197
21226
|
},
|
|
21198
21227
|
Math.max(0, timeoutMs)
|
|
@@ -21229,7 +21258,7 @@ var init_warmupMcp = __esm({
|
|
|
21229
21258
|
});
|
|
21230
21259
|
|
|
21231
21260
|
// src/scripts/writeAgentRunSummary.ts
|
|
21232
|
-
import * as
|
|
21261
|
+
import * as fs52 from "fs";
|
|
21233
21262
|
var writeAgentRunSummary;
|
|
21234
21263
|
var init_writeAgentRunSummary = __esm({
|
|
21235
21264
|
"src/scripts/writeAgentRunSummary.ts"() {
|
|
@@ -21255,7 +21284,7 @@ var init_writeAgentRunSummary = __esm({
|
|
|
21255
21284
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
21256
21285
|
lines.push("");
|
|
21257
21286
|
try {
|
|
21258
|
-
|
|
21287
|
+
fs52.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
21259
21288
|
`);
|
|
21260
21289
|
} catch {
|
|
21261
21290
|
}
|
|
@@ -21593,17 +21622,17 @@ var init_scripts = __esm({
|
|
|
21593
21622
|
});
|
|
21594
21623
|
|
|
21595
21624
|
// src/stateWorkspace.ts
|
|
21596
|
-
import * as
|
|
21597
|
-
import * as
|
|
21625
|
+
import * as fs53 from "fs";
|
|
21626
|
+
import * as path50 from "path";
|
|
21598
21627
|
function tenantId(config) {
|
|
21599
21628
|
const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
|
|
21600
21629
|
const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
|
|
21601
21630
|
return owner && repo ? `${owner}/${repo}` : null;
|
|
21602
21631
|
}
|
|
21603
21632
|
function writeRuntimeFile(cwd, relativePath, content) {
|
|
21604
|
-
const target =
|
|
21605
|
-
|
|
21606
|
-
|
|
21633
|
+
const target = path50.join(cwd, RUNTIME_ROOT, relativePath);
|
|
21634
|
+
fs53.mkdirSync(path50.dirname(target), { recursive: true });
|
|
21635
|
+
fs53.writeFileSync(target, content, "utf8");
|
|
21607
21636
|
}
|
|
21608
21637
|
function record(value) {
|
|
21609
21638
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -21668,11 +21697,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
|
|
|
21668
21697
|
throw new Error("Kody backend access is required for runtime workspace documents");
|
|
21669
21698
|
return;
|
|
21670
21699
|
}
|
|
21671
|
-
const key = `${
|
|
21700
|
+
const key = `${path50.resolve(cwd)}|${tenant}`;
|
|
21672
21701
|
if (hydratedWorkspaces.has(key)) return;
|
|
21673
21702
|
const backend = backendOverride ?? createStateBackendFromEnv();
|
|
21674
|
-
const root =
|
|
21675
|
-
|
|
21703
|
+
const root = path50.join(cwd, RUNTIME_ROOT);
|
|
21704
|
+
fs53.rmSync(root, { recursive: true, force: true });
|
|
21676
21705
|
await Promise.all([
|
|
21677
21706
|
hydratePrefix(backend, tenant, cwd, "context:"),
|
|
21678
21707
|
hydratePrefix(backend, tenant, cwd, "memory:"),
|
|
@@ -21688,7 +21717,7 @@ var init_stateWorkspace = __esm({
|
|
|
21688
21717
|
"src/stateWorkspace.ts"() {
|
|
21689
21718
|
"use strict";
|
|
21690
21719
|
init_state_backend();
|
|
21691
|
-
RUNTIME_ROOT =
|
|
21720
|
+
RUNTIME_ROOT = path50.join(".kody-engine", "runtime");
|
|
21692
21721
|
hydratedWorkspaces = /* @__PURE__ */ new Set();
|
|
21693
21722
|
}
|
|
21694
21723
|
});
|
|
@@ -21759,9 +21788,9 @@ var init_tools = __esm({
|
|
|
21759
21788
|
|
|
21760
21789
|
// src/executor.ts
|
|
21761
21790
|
import { spawn as spawn8 } from "child_process";
|
|
21762
|
-
import * as
|
|
21791
|
+
import * as fs54 from "fs";
|
|
21763
21792
|
import * as os8 from "os";
|
|
21764
|
-
import * as
|
|
21793
|
+
import * as path51 from "path";
|
|
21765
21794
|
function isMutatingPostflight(scriptName) {
|
|
21766
21795
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
21767
21796
|
}
|
|
@@ -22013,7 +22042,7 @@ async function runImplementation(profileName, input) {
|
|
|
22013
22042
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
22014
22043
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
22015
22044
|
const invokeAgent = async (prompt) => {
|
|
22016
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
22045
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path51.isAbsolute(p) ? p : path51.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
22017
22046
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
22018
22047
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
22019
22048
|
const agents = loadSubagents(profile);
|
|
@@ -22493,17 +22522,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
22493
22522
|
function resolveProfilePath(profileName, cwd = process.cwd()) {
|
|
22494
22523
|
const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
|
|
22495
22524
|
if (found) return found;
|
|
22496
|
-
const here =
|
|
22525
|
+
const here = path51.dirname(new URL(import.meta.url).pathname);
|
|
22497
22526
|
const candidates = [
|
|
22498
|
-
|
|
22527
|
+
path51.join(here, "implementations", profileName, "profile.json"),
|
|
22499
22528
|
// same-dir sibling (dev)
|
|
22500
|
-
|
|
22529
|
+
path51.join(here, "..", "implementations", profileName, "profile.json"),
|
|
22501
22530
|
// up one (prod: dist/bin → dist/implementations)
|
|
22502
|
-
|
|
22531
|
+
path51.join(here, "..", "src", "implementations", profileName, "profile.json")
|
|
22503
22532
|
// fallback
|
|
22504
22533
|
];
|
|
22505
22534
|
for (const c of candidates) {
|
|
22506
|
-
if (
|
|
22535
|
+
if (fs54.existsSync(c)) return c;
|
|
22507
22536
|
}
|
|
22508
22537
|
return candidates[0];
|
|
22509
22538
|
}
|
|
@@ -22618,15 +22647,15 @@ function resolveShellTimeoutMs(entry) {
|
|
|
22618
22647
|
}
|
|
22619
22648
|
async function runShellEntry(entry, ctx, profile) {
|
|
22620
22649
|
const shellName = entry.shell;
|
|
22621
|
-
const shellPath =
|
|
22622
|
-
if (!
|
|
22650
|
+
const shellPath = path51.join(profile.dir, shellName);
|
|
22651
|
+
if (!fs54.existsSync(shellPath)) {
|
|
22623
22652
|
ctx.skipAgent = true;
|
|
22624
22653
|
ctx.output.exitCode = 99;
|
|
22625
22654
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
22626
22655
|
return;
|
|
22627
22656
|
}
|
|
22628
22657
|
const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
|
|
22629
|
-
const outputFile =
|
|
22658
|
+
const outputFile = path51.join(
|
|
22630
22659
|
os8.tmpdir(),
|
|
22631
22660
|
`kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
22632
22661
|
);
|
|
@@ -22656,14 +22685,14 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
22656
22685
|
let killTimer;
|
|
22657
22686
|
let escalateTimer;
|
|
22658
22687
|
const result = await new Promise(
|
|
22659
|
-
(
|
|
22688
|
+
(resolve23) => {
|
|
22660
22689
|
let settled = false;
|
|
22661
22690
|
const settle = (code, signal, spawnErr) => {
|
|
22662
22691
|
if (settled) return;
|
|
22663
22692
|
settled = true;
|
|
22664
22693
|
if (killTimer) clearTimeout(killTimer);
|
|
22665
22694
|
if (escalateTimer) clearTimeout(escalateTimer);
|
|
22666
|
-
|
|
22695
|
+
resolve23({ code, signal, spawnErr });
|
|
22667
22696
|
};
|
|
22668
22697
|
child.on("error", (err) => settle(null, null, err));
|
|
22669
22698
|
child.on("close", (code, signal) => settle(code, signal));
|
|
@@ -22693,9 +22722,9 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
22693
22722
|
}
|
|
22694
22723
|
let sideChannelText = "";
|
|
22695
22724
|
try {
|
|
22696
|
-
if (
|
|
22697
|
-
sideChannelText =
|
|
22698
|
-
|
|
22725
|
+
if (fs54.existsSync(outputFile)) {
|
|
22726
|
+
sideChannelText = fs54.readFileSync(outputFile, "utf-8");
|
|
22727
|
+
fs54.rmSync(outputFile, { force: true });
|
|
22699
22728
|
}
|
|
22700
22729
|
} catch {
|
|
22701
22730
|
}
|
|
@@ -23699,11 +23728,11 @@ function exhaustedWorkflowTransitions(step, data, counts) {
|
|
|
23699
23728
|
}
|
|
23700
23729
|
function workflowResultConditionPaths(transitions) {
|
|
23701
23730
|
return transitions.flatMap(
|
|
23702
|
-
(transition) => Object.keys(transition.when ?? {}).filter((
|
|
23731
|
+
(transition) => Object.keys(transition.when ?? {}).filter((path58) => path58.startsWith("result."))
|
|
23703
23732
|
);
|
|
23704
23733
|
}
|
|
23705
23734
|
function conditionMatches(condition, context) {
|
|
23706
|
-
return Object.entries(condition).every(([
|
|
23735
|
+
return Object.entries(condition).every(([path58, expected]) => valueMatches(resolveDottedPath2(context, path58), expected));
|
|
23707
23736
|
}
|
|
23708
23737
|
function withWorkflowBoundaryEval(capability, result) {
|
|
23709
23738
|
const capabilityKind = capability.config.capabilityKind;
|
|
@@ -24133,7 +24162,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
24133
24162
|
|
|
24134
24163
|
// src/servers/brain-serve.ts
|
|
24135
24164
|
import { createServer as createServer2 } from "http";
|
|
24136
|
-
import * as
|
|
24165
|
+
import * as path54 from "path";
|
|
24137
24166
|
|
|
24138
24167
|
// src/chat/loop.ts
|
|
24139
24168
|
init_agent();
|
|
@@ -24141,13 +24170,13 @@ init_agents();
|
|
|
24141
24170
|
init_config();
|
|
24142
24171
|
init_registry();
|
|
24143
24172
|
init_task_artifacts();
|
|
24144
|
-
import * as
|
|
24145
|
-
import * as
|
|
24173
|
+
import * as fs18 from "fs";
|
|
24174
|
+
import * as path19 from "path";
|
|
24146
24175
|
|
|
24147
24176
|
// src/chat/attachments.ts
|
|
24148
24177
|
init_runtimePaths();
|
|
24149
|
-
import * as
|
|
24150
|
-
import * as
|
|
24178
|
+
import * as fs15 from "fs";
|
|
24179
|
+
import * as path16 from "path";
|
|
24151
24180
|
var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
24152
24181
|
var EXT_BY_MIME = {
|
|
24153
24182
|
"image/png": "png",
|
|
@@ -24180,11 +24209,11 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
24180
24209
|
if (!isImage) return `[File: ${name}]`;
|
|
24181
24210
|
try {
|
|
24182
24211
|
if (!dirEnsured) {
|
|
24183
|
-
|
|
24212
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
24184
24213
|
dirEnsured = true;
|
|
24185
24214
|
}
|
|
24186
|
-
const filePath =
|
|
24187
|
-
|
|
24215
|
+
const filePath = path16.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
24216
|
+
fs15.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
24188
24217
|
imageCounter += 1;
|
|
24189
24218
|
imagePaths.push(filePath);
|
|
24190
24219
|
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
|
|
@@ -24201,8 +24230,8 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
24201
24230
|
|
|
24202
24231
|
// src/chat/codex-app-server.ts
|
|
24203
24232
|
import { spawn as spawn3 } from "child_process";
|
|
24204
|
-
import * as
|
|
24205
|
-
import * as
|
|
24233
|
+
import * as fs16 from "fs";
|
|
24234
|
+
import * as path17 from "path";
|
|
24206
24235
|
import { createInterface } from "readline";
|
|
24207
24236
|
function codexThreadStartParams(args) {
|
|
24208
24237
|
return {
|
|
@@ -24287,9 +24316,9 @@ var CodexAppServerClient = class {
|
|
|
24287
24316
|
await this.request("thread/resume", { threadId });
|
|
24288
24317
|
}
|
|
24289
24318
|
async runTurn(args) {
|
|
24290
|
-
await new Promise((
|
|
24319
|
+
await new Promise((resolve23, reject) => {
|
|
24291
24320
|
this.process.turnWaiters.set(args.threadId, {
|
|
24292
|
-
resolve:
|
|
24321
|
+
resolve: resolve23,
|
|
24293
24322
|
reject,
|
|
24294
24323
|
onNotification: args.onNotification,
|
|
24295
24324
|
queue: Promise.resolve()
|
|
@@ -24306,8 +24335,8 @@ var CodexAppServerClient = class {
|
|
|
24306
24335
|
}
|
|
24307
24336
|
request(method, params) {
|
|
24308
24337
|
const id = this.process.nextId++;
|
|
24309
|
-
return new Promise((
|
|
24310
|
-
this.process.pending.set(id, { resolve:
|
|
24338
|
+
return new Promise((resolve23, reject) => {
|
|
24339
|
+
this.process.pending.set(id, { resolve: resolve23, reject });
|
|
24311
24340
|
this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
|
|
24312
24341
|
`);
|
|
24313
24342
|
});
|
|
@@ -24373,11 +24402,11 @@ var CodexAppServerClient = class {
|
|
|
24373
24402
|
};
|
|
24374
24403
|
var clients = /* @__PURE__ */ new Map();
|
|
24375
24404
|
function threadMapPath(cwd) {
|
|
24376
|
-
return
|
|
24405
|
+
return path17.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
|
|
24377
24406
|
}
|
|
24378
24407
|
function readThreadMap(cwd) {
|
|
24379
24408
|
try {
|
|
24380
|
-
const value = JSON.parse(
|
|
24409
|
+
const value = JSON.parse(fs16.readFileSync(threadMapPath(cwd), "utf8"));
|
|
24381
24410
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
24382
24411
|
return Object.fromEntries(
|
|
24383
24412
|
Object.entries(value).filter(
|
|
@@ -24390,8 +24419,8 @@ function readThreadMap(cwd) {
|
|
|
24390
24419
|
}
|
|
24391
24420
|
function writeThreadMap(cwd, map) {
|
|
24392
24421
|
const file = threadMapPath(cwd);
|
|
24393
|
-
|
|
24394
|
-
|
|
24422
|
+
fs16.mkdirSync(path17.dirname(file), { recursive: true });
|
|
24423
|
+
fs16.writeFileSync(file, `${JSON.stringify(map, null, 2)}
|
|
24395
24424
|
`);
|
|
24396
24425
|
}
|
|
24397
24426
|
async function runCodexChatTurn(args) {
|
|
@@ -24481,8 +24510,8 @@ async function runCodexChatTurn(args) {
|
|
|
24481
24510
|
}
|
|
24482
24511
|
|
|
24483
24512
|
// src/chat/events.ts
|
|
24484
|
-
import * as
|
|
24485
|
-
import * as
|
|
24513
|
+
import * as fs17 from "fs";
|
|
24514
|
+
import * as path18 from "path";
|
|
24486
24515
|
import posixPath2 from "path/posix";
|
|
24487
24516
|
var BackendEventSink = class {
|
|
24488
24517
|
constructor(append, tenantId2, sessionId) {
|
|
@@ -24498,7 +24527,7 @@ var BackendEventSink = class {
|
|
|
24498
24527
|
}
|
|
24499
24528
|
};
|
|
24500
24529
|
function eventsFilePath(cwd, sessionId) {
|
|
24501
|
-
return
|
|
24530
|
+
return path18.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
|
|
24502
24531
|
}
|
|
24503
24532
|
var FileSink = class {
|
|
24504
24533
|
constructor(file) {
|
|
@@ -24506,8 +24535,8 @@ var FileSink = class {
|
|
|
24506
24535
|
}
|
|
24507
24536
|
file;
|
|
24508
24537
|
async emit(event) {
|
|
24509
|
-
|
|
24510
|
-
|
|
24538
|
+
fs17.mkdirSync(path18.dirname(this.file), { recursive: true });
|
|
24539
|
+
fs17.appendFileSync(this.file, `${JSON.stringify(event)}
|
|
24511
24540
|
`);
|
|
24512
24541
|
}
|
|
24513
24542
|
};
|
|
@@ -24772,7 +24801,7 @@ function buildImplementationCatalog() {
|
|
|
24772
24801
|
const entries = [];
|
|
24773
24802
|
for (const { name, profilePath } of discovered) {
|
|
24774
24803
|
try {
|
|
24775
|
-
const raw = JSON.parse(
|
|
24804
|
+
const raw = JSON.parse(fs18.readFileSync(profilePath, "utf-8"));
|
|
24776
24805
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
24777
24806
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
24778
24807
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -24894,7 +24923,7 @@ async function runChatTurn(opts) {
|
|
|
24894
24923
|
quiet: opts.quiet,
|
|
24895
24924
|
additionalDirectories: [
|
|
24896
24925
|
taskArtifactsPaths.absDir,
|
|
24897
|
-
...Array.from(new Set(imagePaths.map((p2) =>
|
|
24926
|
+
...Array.from(new Set(imagePaths.map((p2) => path19.dirname(p2))))
|
|
24898
24927
|
],
|
|
24899
24928
|
systemPromptAppend: systemPrompt,
|
|
24900
24929
|
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
|
|
@@ -25082,10 +25111,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
25082
25111
|
var MEMORY_INDEX_REL = ".kody-engine/runtime/memory/INDEX.md";
|
|
25083
25112
|
var MAX_INDEX_BYTES = 8e3;
|
|
25084
25113
|
function readMemoryIndexBlock(cwd) {
|
|
25085
|
-
const indexPath =
|
|
25114
|
+
const indexPath = path19.join(cwd, MEMORY_INDEX_REL);
|
|
25086
25115
|
let raw;
|
|
25087
25116
|
try {
|
|
25088
|
-
raw =
|
|
25117
|
+
raw = fs18.readFileSync(indexPath, "utf-8");
|
|
25089
25118
|
} catch {
|
|
25090
25119
|
return "";
|
|
25091
25120
|
}
|
|
@@ -25105,17 +25134,17 @@ _\u2026 (memory index truncated; use recall_search to read more)_` : trimmed;
|
|
|
25105
25134
|
var CONTEXT_DIR_REL = ".kody-engine/runtime/context";
|
|
25106
25135
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
25107
25136
|
function readContextBlock(cwd) {
|
|
25108
|
-
const dir =
|
|
25137
|
+
const dir = path19.join(cwd, CONTEXT_DIR_REL);
|
|
25109
25138
|
let files;
|
|
25110
25139
|
try {
|
|
25111
|
-
files =
|
|
25140
|
+
files = fs18.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
25112
25141
|
} catch {
|
|
25113
25142
|
return "";
|
|
25114
25143
|
}
|
|
25115
25144
|
const sections = [];
|
|
25116
25145
|
for (const file of files) {
|
|
25117
25146
|
try {
|
|
25118
|
-
const content =
|
|
25147
|
+
const content = fs18.readFileSync(path19.join(dir, file), "utf-8").trim();
|
|
25119
25148
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
25120
25149
|
|
|
25121
25150
|
${content}`);
|
|
@@ -25141,7 +25170,7 @@ var SYSTEM_PROMPT_OVERRIDE_REL = ".kody-engine/runtime/system-prompt.md";
|
|
|
25141
25170
|
function readSystemPromptOverride(cwd) {
|
|
25142
25171
|
let raw;
|
|
25143
25172
|
try {
|
|
25144
|
-
raw =
|
|
25173
|
+
raw = fs18.readFileSync(path19.join(cwd, SYSTEM_PROMPT_OVERRIDE_REL), "utf-8");
|
|
25145
25174
|
} catch {
|
|
25146
25175
|
return null;
|
|
25147
25176
|
}
|
|
@@ -25149,10 +25178,10 @@ function readSystemPromptOverride(cwd) {
|
|
|
25149
25178
|
return trimmed.length > 0 ? trimmed : null;
|
|
25150
25179
|
}
|
|
25151
25180
|
function readInstructionsBlock(cwd) {
|
|
25152
|
-
const instructionsPath =
|
|
25181
|
+
const instructionsPath = path19.join(cwd, INSTRUCTIONS_REL);
|
|
25153
25182
|
let raw;
|
|
25154
25183
|
try {
|
|
25155
|
-
raw =
|
|
25184
|
+
raw = fs18.readFileSync(instructionsPath, "utf-8");
|
|
25156
25185
|
} catch {
|
|
25157
25186
|
return "";
|
|
25158
25187
|
}
|
|
@@ -25186,15 +25215,15 @@ function resolveBrainDriver(runtime) {
|
|
|
25186
25215
|
}
|
|
25187
25216
|
|
|
25188
25217
|
// src/chat/session.ts
|
|
25189
|
-
import * as
|
|
25190
|
-
import * as
|
|
25218
|
+
import * as fs19 from "fs";
|
|
25219
|
+
import * as path20 from "path";
|
|
25191
25220
|
import posixPath3 from "path/posix";
|
|
25192
25221
|
function sessionFilePath(cwd, sessionId) {
|
|
25193
|
-
return
|
|
25222
|
+
return path20.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
|
|
25194
25223
|
}
|
|
25195
25224
|
function readSession(file) {
|
|
25196
|
-
if (!
|
|
25197
|
-
const raw =
|
|
25225
|
+
if (!fs19.existsSync(file)) return [];
|
|
25226
|
+
const raw = fs19.readFileSync(file, "utf-8").trim();
|
|
25198
25227
|
if (!raw) return [];
|
|
25199
25228
|
const turns = [];
|
|
25200
25229
|
for (const line of raw.split("\n")) {
|
|
@@ -25217,8 +25246,8 @@ init_config();
|
|
|
25217
25246
|
init_state_backend();
|
|
25218
25247
|
init_workflowDefinitions();
|
|
25219
25248
|
import { createHash as createHash2 } from "crypto";
|
|
25220
|
-
import * as
|
|
25221
|
-
import * as
|
|
25249
|
+
import * as fs21 from "fs";
|
|
25250
|
+
import * as path22 from "path";
|
|
25222
25251
|
var SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,127}$/;
|
|
25223
25252
|
var REPOSITORY_OWNED_NAMESPACES = ["loops"];
|
|
25224
25253
|
function assertSafeDefinitionPath(filePath) {
|
|
@@ -25250,9 +25279,9 @@ function verifyDefinition(definition) {
|
|
|
25250
25279
|
}
|
|
25251
25280
|
function writeBundle(root, bundle) {
|
|
25252
25281
|
for (const [filePath, contents] of Object.entries(bundle.files)) {
|
|
25253
|
-
const target =
|
|
25254
|
-
|
|
25255
|
-
|
|
25282
|
+
const target = path22.join(root, filePath);
|
|
25283
|
+
fs21.mkdirSync(path22.dirname(target), { recursive: true });
|
|
25284
|
+
fs21.writeFileSync(target, contents, "utf8");
|
|
25256
25285
|
}
|
|
25257
25286
|
}
|
|
25258
25287
|
function writeDefinition(root, kind, definition) {
|
|
@@ -25260,22 +25289,22 @@ function writeDefinition(root, kind, definition) {
|
|
|
25260
25289
|
if (kind === "agent") {
|
|
25261
25290
|
const raw = bundle.files["agent.md"];
|
|
25262
25291
|
if (typeof raw !== "string") throw new Error(`agent definition ${definition.slug} is missing agent.md`);
|
|
25263
|
-
|
|
25292
|
+
fs21.writeFileSync(path22.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
|
|
25264
25293
|
return;
|
|
25265
25294
|
}
|
|
25266
25295
|
if (kind === "goal") {
|
|
25267
|
-
writeBundle(
|
|
25296
|
+
writeBundle(path22.join(root, "goals", definition.slug), bundle);
|
|
25268
25297
|
return;
|
|
25269
25298
|
}
|
|
25270
25299
|
if (kind === "implementation") {
|
|
25271
|
-
writeBundle(
|
|
25300
|
+
writeBundle(path22.join(root, "implementations", definition.slug), bundle);
|
|
25272
25301
|
return;
|
|
25273
25302
|
}
|
|
25274
25303
|
if (kind === "asset") {
|
|
25275
|
-
writeBundle(
|
|
25304
|
+
writeBundle(path22.join(root, "shared"), bundle);
|
|
25276
25305
|
return;
|
|
25277
25306
|
}
|
|
25278
|
-
writeBundle(
|
|
25307
|
+
writeBundle(path22.join(root, "capabilities", definition.slug), bundle);
|
|
25279
25308
|
}
|
|
25280
25309
|
function writeWorkflow(root, document) {
|
|
25281
25310
|
const workflow = normalizeWorkflowDefinition(document.definition);
|
|
@@ -25283,28 +25312,28 @@ function writeWorkflow(root, document) {
|
|
|
25283
25312
|
const contents = `${JSON.stringify(workflow, null, 2)}
|
|
25284
25313
|
`;
|
|
25285
25314
|
const bundle = { schemaVersion: 1, files: { "workflow.json": contents } };
|
|
25286
|
-
const target =
|
|
25287
|
-
|
|
25288
|
-
|
|
25315
|
+
const target = path22.join(root, workflowDefinitionPath(document.workflowId));
|
|
25316
|
+
fs21.mkdirSync(path22.dirname(target), { recursive: true });
|
|
25317
|
+
fs21.writeFileSync(target, contents, "utf8");
|
|
25289
25318
|
return definitionVersion(bundle);
|
|
25290
25319
|
}
|
|
25291
25320
|
function preserveRepositoryDefinitions(root, staging) {
|
|
25292
25321
|
for (const namespace of REPOSITORY_OWNED_NAMESPACES) {
|
|
25293
|
-
const source =
|
|
25294
|
-
if (!
|
|
25295
|
-
|
|
25322
|
+
const source = path22.join(root, namespace);
|
|
25323
|
+
if (!fs21.existsSync(source)) continue;
|
|
25324
|
+
fs21.cpSync(source, path22.join(staging, namespace), { recursive: true });
|
|
25296
25325
|
}
|
|
25297
25326
|
}
|
|
25298
25327
|
async function hydrateDefinitions(options) {
|
|
25299
|
-
const root =
|
|
25328
|
+
const root = path22.join(options.cwd, ".kody-engine", "definitions");
|
|
25300
25329
|
const staging = `${root}.tmp-${process.pid}-${Date.now()}`;
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25330
|
+
fs21.rmSync(staging, { recursive: true, force: true });
|
|
25331
|
+
fs21.mkdirSync(path22.join(staging, "agents"), { recursive: true });
|
|
25332
|
+
fs21.mkdirSync(path22.join(staging, "capabilities"), { recursive: true });
|
|
25333
|
+
fs21.mkdirSync(path22.join(staging, "goals"), { recursive: true });
|
|
25334
|
+
fs21.mkdirSync(path22.join(staging, "implementations"), { recursive: true });
|
|
25335
|
+
fs21.mkdirSync(path22.join(staging, "shared"), { recursive: true });
|
|
25336
|
+
fs21.mkdirSync(path22.join(staging, "workflows"), { recursive: true });
|
|
25308
25337
|
try {
|
|
25309
25338
|
const [capabilities, agents, goals, implementations, assets, workflows] = await Promise.all([
|
|
25310
25339
|
options.backend.listDefinitions(options.tenantId, "capability"),
|
|
@@ -25345,13 +25374,13 @@ async function hydrateDefinitions(options) {
|
|
|
25345
25374
|
hydratedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25346
25375
|
versions: Object.fromEntries(Object.entries(versions).sort(([left], [right]) => left.localeCompare(right)))
|
|
25347
25376
|
};
|
|
25348
|
-
|
|
25377
|
+
fs21.writeFileSync(path22.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
25349
25378
|
`, "utf8");
|
|
25350
|
-
|
|
25351
|
-
|
|
25379
|
+
fs21.rmSync(root, { recursive: true, force: true });
|
|
25380
|
+
fs21.renameSync(staging, root);
|
|
25352
25381
|
return { root, tenantId: options.tenantId, versions: manifest.versions };
|
|
25353
25382
|
} catch (error) {
|
|
25354
|
-
|
|
25383
|
+
fs21.rmSync(staging, { recursive: true, force: true });
|
|
25355
25384
|
throw error;
|
|
25356
25385
|
}
|
|
25357
25386
|
}
|
|
@@ -25373,8 +25402,8 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
|
|
|
25373
25402
|
|
|
25374
25403
|
// src/kody-cli.ts
|
|
25375
25404
|
import { execFileSync as execFileSync24 } from "child_process";
|
|
25376
|
-
import * as
|
|
25377
|
-
import * as
|
|
25405
|
+
import * as fs55 from "fs";
|
|
25406
|
+
import * as path52 from "path";
|
|
25378
25407
|
|
|
25379
25408
|
// src/app-auth.ts
|
|
25380
25409
|
import { createSign } from "crypto";
|
|
@@ -25503,7 +25532,7 @@ init_definition_paths();
|
|
|
25503
25532
|
|
|
25504
25533
|
// src/dispatch.ts
|
|
25505
25534
|
init_config();
|
|
25506
|
-
import * as
|
|
25535
|
+
import * as fs22 from "fs";
|
|
25507
25536
|
|
|
25508
25537
|
// src/cron-match.ts
|
|
25509
25538
|
var FIELD_BOUNDS = [
|
|
@@ -25610,10 +25639,10 @@ function autoDispatch(opts) {
|
|
|
25610
25639
|
}
|
|
25611
25640
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
25612
25641
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
25613
|
-
if (!eventName || !eventPath || !
|
|
25642
|
+
if (!eventName || !eventPath || !fs22.existsSync(eventPath)) return null;
|
|
25614
25643
|
let event = {};
|
|
25615
25644
|
try {
|
|
25616
|
-
event = JSON.parse(
|
|
25645
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
25617
25646
|
} catch {
|
|
25618
25647
|
return null;
|
|
25619
25648
|
}
|
|
@@ -25737,7 +25766,7 @@ function autoDispatchTyped(opts) {
|
|
|
25737
25766
|
if (legacy) return { kind: "route", ...legacy };
|
|
25738
25767
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
25739
25768
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
25740
|
-
if (!eventName || !eventPath || !
|
|
25769
|
+
if (!eventName || !eventPath || !fs22.existsSync(eventPath)) {
|
|
25741
25770
|
return { kind: "silent", reason: "no GHA event context" };
|
|
25742
25771
|
}
|
|
25743
25772
|
if (eventName !== "issue_comment") {
|
|
@@ -25745,7 +25774,7 @@ function autoDispatchTyped(opts) {
|
|
|
25745
25774
|
}
|
|
25746
25775
|
let event = {};
|
|
25747
25776
|
try {
|
|
25748
|
-
event = JSON.parse(
|
|
25777
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
25749
25778
|
} catch {
|
|
25750
25779
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
25751
25780
|
}
|
|
@@ -25799,7 +25828,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
25799
25828
|
for (const exe of listRuntimeProfilesForCwd(opts?.cwd ?? process.cwd())) {
|
|
25800
25829
|
let raw;
|
|
25801
25830
|
try {
|
|
25802
|
-
raw =
|
|
25831
|
+
raw = fs22.readFileSync(exe.profilePath, "utf-8");
|
|
25803
25832
|
} catch {
|
|
25804
25833
|
continue;
|
|
25805
25834
|
}
|
|
@@ -26176,9 +26205,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
26176
26205
|
return void 0;
|
|
26177
26206
|
}
|
|
26178
26207
|
function detectPackageManager2(cwd) {
|
|
26179
|
-
if (
|
|
26180
|
-
if (
|
|
26181
|
-
if (
|
|
26208
|
+
if (fs55.existsSync(path52.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
26209
|
+
if (fs55.existsSync(path52.join(cwd, "yarn.lock"))) return "yarn";
|
|
26210
|
+
if (fs55.existsSync(path52.join(cwd, "bun.lockb"))) return "bun";
|
|
26182
26211
|
return "npm";
|
|
26183
26212
|
}
|
|
26184
26213
|
function shouldChainScheduledWatch(match) {
|
|
@@ -26281,8 +26310,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
26281
26310
|
const logPath = lastRunLogPath(cwd);
|
|
26282
26311
|
let tail = "";
|
|
26283
26312
|
try {
|
|
26284
|
-
if (
|
|
26285
|
-
const content =
|
|
26313
|
+
if (fs55.existsSync(logPath)) {
|
|
26314
|
+
const content = fs55.readFileSync(logPath, "utf-8");
|
|
26286
26315
|
tail = content.slice(-3e3);
|
|
26287
26316
|
}
|
|
26288
26317
|
} catch {
|
|
@@ -26311,7 +26340,7 @@ async function runCi(argv) {
|
|
|
26311
26340
|
return 0;
|
|
26312
26341
|
}
|
|
26313
26342
|
const args = parseCiArgs(argv);
|
|
26314
|
-
const cwd = args.cwd ?
|
|
26343
|
+
const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
|
|
26315
26344
|
try {
|
|
26316
26345
|
const n = unpackAllSecrets();
|
|
26317
26346
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -26377,9 +26406,9 @@ async function runCi(argv) {
|
|
|
26377
26406
|
forceRunCliArgs = { goal: envForceMessage };
|
|
26378
26407
|
}
|
|
26379
26408
|
}
|
|
26380
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
26409
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs55.existsSync(dispatchEventPath)) {
|
|
26381
26410
|
try {
|
|
26382
|
-
const evt = JSON.parse(
|
|
26411
|
+
const evt = JSON.parse(fs55.readFileSync(dispatchEventPath, "utf-8"));
|
|
26383
26412
|
const inputs = objectValue2(evt.inputs);
|
|
26384
26413
|
const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
|
|
26385
26414
|
const sessionInput = String(inputs?.sessionId ?? "");
|
|
@@ -26794,8 +26823,8 @@ init_repoWorkspace();
|
|
|
26794
26823
|
|
|
26795
26824
|
// src/scripts/brainTurnLog.ts
|
|
26796
26825
|
init_runtimePaths();
|
|
26797
|
-
import * as
|
|
26798
|
-
import * as
|
|
26826
|
+
import * as fs56 from "fs";
|
|
26827
|
+
import * as path53 from "path";
|
|
26799
26828
|
import posixPath4 from "path/posix";
|
|
26800
26829
|
var live = /* @__PURE__ */ new Map();
|
|
26801
26830
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -26803,8 +26832,8 @@ function brainEventsFilePath(dir, chatId) {
|
|
|
26803
26832
|
}
|
|
26804
26833
|
function lastPersistedSeq(dir, chatId) {
|
|
26805
26834
|
const p = brainEventsFilePath(dir, chatId);
|
|
26806
|
-
if (!
|
|
26807
|
-
const lines =
|
|
26835
|
+
if (!fs56.existsSync(p)) return 0;
|
|
26836
|
+
const lines = fs56.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
26808
26837
|
if (lines.length === 0) return 0;
|
|
26809
26838
|
try {
|
|
26810
26839
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -26814,9 +26843,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
26814
26843
|
}
|
|
26815
26844
|
function readSince(dir, chatId, since) {
|
|
26816
26845
|
const p = brainEventsFilePath(dir, chatId);
|
|
26817
|
-
if (!
|
|
26846
|
+
if (!fs56.existsSync(p)) return [];
|
|
26818
26847
|
const out = [];
|
|
26819
|
-
for (const line of
|
|
26848
|
+
for (const line of fs56.readFileSync(p, "utf-8").split("\n")) {
|
|
26820
26849
|
if (!line) continue;
|
|
26821
26850
|
try {
|
|
26822
26851
|
const rec = JSON.parse(line);
|
|
@@ -26842,12 +26871,12 @@ function beginTurn(dir, chatId) {
|
|
|
26842
26871
|
};
|
|
26843
26872
|
live.set(chatId, state);
|
|
26844
26873
|
const p = brainEventsFilePath(dir, chatId);
|
|
26845
|
-
|
|
26874
|
+
fs56.mkdirSync(path53.dirname(p), { recursive: true });
|
|
26846
26875
|
return (event) => {
|
|
26847
26876
|
state.seq += 1;
|
|
26848
26877
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
26849
26878
|
try {
|
|
26850
|
-
|
|
26879
|
+
fs56.appendFileSync(p, `${JSON.stringify(rec)}
|
|
26851
26880
|
`);
|
|
26852
26881
|
} catch (err) {
|
|
26853
26882
|
process.stderr.write(
|
|
@@ -26886,7 +26915,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
26886
26915
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
26887
26916
|
};
|
|
26888
26917
|
try {
|
|
26889
|
-
|
|
26918
|
+
fs56.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
|
|
26890
26919
|
`);
|
|
26891
26920
|
} catch {
|
|
26892
26921
|
}
|
|
@@ -26980,17 +27009,17 @@ function authOk(req, expected) {
|
|
|
26980
27009
|
return false;
|
|
26981
27010
|
}
|
|
26982
27011
|
function readJsonBody(req) {
|
|
26983
|
-
return new Promise((
|
|
27012
|
+
return new Promise((resolve23, reject) => {
|
|
26984
27013
|
const chunks = [];
|
|
26985
27014
|
req.on("data", (c) => chunks.push(c));
|
|
26986
27015
|
req.on("end", () => {
|
|
26987
27016
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
26988
27017
|
if (!raw.trim()) {
|
|
26989
|
-
|
|
27018
|
+
resolve23({});
|
|
26990
27019
|
return;
|
|
26991
27020
|
}
|
|
26992
27021
|
try {
|
|
26993
|
-
|
|
27022
|
+
resolve23(JSON.parse(raw));
|
|
26994
27023
|
} catch (err) {
|
|
26995
27024
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
26996
27025
|
}
|
|
@@ -27282,7 +27311,7 @@ function buildServer(opts) {
|
|
|
27282
27311
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
27283
27312
|
const createStore = opts.createStore ?? createSessionStore;
|
|
27284
27313
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
27285
|
-
const reposRoot = opts.reposRoot ??
|
|
27314
|
+
const reposRoot = opts.reposRoot ?? path54.join(path54.dirname(path54.resolve(opts.cwd)), "repos");
|
|
27286
27315
|
return createServer2(async (req, res) => {
|
|
27287
27316
|
if (!req.method || !req.url) {
|
|
27288
27317
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -27363,11 +27392,11 @@ async function brainServe(opts) {
|
|
|
27363
27392
|
litellmUrl,
|
|
27364
27393
|
driver
|
|
27365
27394
|
});
|
|
27366
|
-
await new Promise((
|
|
27395
|
+
await new Promise((resolve23) => {
|
|
27367
27396
|
server.listen(port, "0.0.0.0", () => {
|
|
27368
27397
|
process.stdout.write(`[brain-serve] listening on 0.0.0.0:${port} (cwd=${opts.cwd})
|
|
27369
27398
|
`);
|
|
27370
|
-
|
|
27399
|
+
resolve23();
|
|
27371
27400
|
});
|
|
27372
27401
|
});
|
|
27373
27402
|
const shutdown = (signal) => {
|
|
@@ -27622,14 +27651,14 @@ async function startBrainProxy(opts) {
|
|
|
27622
27651
|
const { httpServer, handler } = buildBrainProxy(opts);
|
|
27623
27652
|
const port = opts.port ?? 0;
|
|
27624
27653
|
const host = opts.host ?? "127.0.0.1";
|
|
27625
|
-
await new Promise((
|
|
27654
|
+
await new Promise((resolve23) => httpServer.listen(port, host, () => resolve23()));
|
|
27626
27655
|
const addr = httpServer.address();
|
|
27627
27656
|
return {
|
|
27628
27657
|
httpServer,
|
|
27629
27658
|
port: addr.port,
|
|
27630
27659
|
url: `http://${host}:${addr.port}`,
|
|
27631
|
-
stop: () => new Promise((
|
|
27632
|
-
httpServer.close(() =>
|
|
27660
|
+
stop: () => new Promise((resolve23) => {
|
|
27661
|
+
httpServer.close(() => resolve23());
|
|
27633
27662
|
}),
|
|
27634
27663
|
handler
|
|
27635
27664
|
};
|
|
@@ -27779,23 +27808,23 @@ function buildMcpHttpServer(opts) {
|
|
|
27779
27808
|
httpServer,
|
|
27780
27809
|
routes,
|
|
27781
27810
|
port,
|
|
27782
|
-
stop: () => new Promise((
|
|
27811
|
+
stop: () => new Promise((resolve23) => {
|
|
27783
27812
|
let pending = transports.size;
|
|
27784
27813
|
if (pending === 0) {
|
|
27785
|
-
httpServer.close(() =>
|
|
27814
|
+
httpServer.close(() => resolve23());
|
|
27786
27815
|
return;
|
|
27787
27816
|
}
|
|
27788
27817
|
for (const transport of transports.values()) {
|
|
27789
27818
|
void transport.close().finally(() => {
|
|
27790
27819
|
pending--;
|
|
27791
|
-
if (pending === 0) httpServer.close(() =>
|
|
27820
|
+
if (pending === 0) httpServer.close(() => resolve23());
|
|
27792
27821
|
});
|
|
27793
27822
|
}
|
|
27794
27823
|
})
|
|
27795
27824
|
};
|
|
27796
27825
|
}
|
|
27797
27826
|
function listenMcpHttpServer(server, host = "127.0.0.1") {
|
|
27798
|
-
return new Promise((
|
|
27827
|
+
return new Promise((resolve23, reject) => {
|
|
27799
27828
|
server.httpServer.once("error", reject);
|
|
27800
27829
|
server.httpServer.listen(server.port, host, () => {
|
|
27801
27830
|
server.httpServer.off("error", reject);
|
|
@@ -27803,7 +27832,7 @@ function listenMcpHttpServer(server, host = "127.0.0.1") {
|
|
|
27803
27832
|
if (addr && typeof addr === "object") {
|
|
27804
27833
|
server.port = addr.port;
|
|
27805
27834
|
}
|
|
27806
|
-
|
|
27835
|
+
resolve23();
|
|
27807
27836
|
});
|
|
27808
27837
|
});
|
|
27809
27838
|
}
|
|
@@ -27886,7 +27915,7 @@ async function loadConfigSafe() {
|
|
|
27886
27915
|
}
|
|
27887
27916
|
|
|
27888
27917
|
// src/chat-cli.ts
|
|
27889
|
-
import * as
|
|
27918
|
+
import * as path55 from "path";
|
|
27890
27919
|
|
|
27891
27920
|
// src/chat/inbox.ts
|
|
27892
27921
|
import { execFileSync as execFileSync25 } from "child_process";
|
|
@@ -27953,7 +27982,7 @@ async function waitForNextUserMessage(opts) {
|
|
|
27953
27982
|
}
|
|
27954
27983
|
}
|
|
27955
27984
|
function sleep3(ms) {
|
|
27956
|
-
return new Promise((
|
|
27985
|
+
return new Promise((resolve23) => setTimeout(resolve23, ms));
|
|
27957
27986
|
}
|
|
27958
27987
|
function currentBranch(cwd) {
|
|
27959
27988
|
try {
|
|
@@ -28177,7 +28206,7 @@ async function runChat(argv) {
|
|
|
28177
28206
|
${CHAT_HELP}`);
|
|
28178
28207
|
return 64;
|
|
28179
28208
|
}
|
|
28180
|
-
const cwd = args.cwd ?
|
|
28209
|
+
const cwd = args.cwd ? path55.resolve(args.cwd) : process.cwd();
|
|
28181
28210
|
const sessionId = args.sessionId;
|
|
28182
28211
|
const runRequest = readRunRequestFromEnv();
|
|
28183
28212
|
if (runRequest && "request" in runRequest) {
|
|
@@ -28302,6 +28331,575 @@ init_definition_paths();
|
|
|
28302
28331
|
init_job();
|
|
28303
28332
|
init_registry();
|
|
28304
28333
|
|
|
28334
|
+
// src/servers/brain-terminal-agent.ts
|
|
28335
|
+
init_repoWorkspace();
|
|
28336
|
+
import * as path57 from "path";
|
|
28337
|
+
import { createInterface as createInterface2 } from "readline";
|
|
28338
|
+
|
|
28339
|
+
// src/terminal/brain-terminal-session.ts
|
|
28340
|
+
import { createHash as createHash9 } from "crypto";
|
|
28341
|
+
var MAX_CAPTURE_CHARS = 2e5;
|
|
28342
|
+
function requiredIdentifier(value, name, max = 240) {
|
|
28343
|
+
if (typeof value !== "string" || !value.trim() || value.length > max) {
|
|
28344
|
+
throw new Error(`${name} must be a non-empty string of at most ${max} characters`);
|
|
28345
|
+
}
|
|
28346
|
+
return value.trim();
|
|
28347
|
+
}
|
|
28348
|
+
function terminalSize(value, name) {
|
|
28349
|
+
if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > 1e3) {
|
|
28350
|
+
throw new Error(`${name} must be an integer between 1 and 1000`);
|
|
28351
|
+
}
|
|
28352
|
+
return Number(value);
|
|
28353
|
+
}
|
|
28354
|
+
function revision(value) {
|
|
28355
|
+
if (value === void 0) return void 0;
|
|
28356
|
+
if (!Number.isInteger(value) || Number(value) < 0) {
|
|
28357
|
+
throw new Error("afterRevision must be a non-negative integer");
|
|
28358
|
+
}
|
|
28359
|
+
return Number(value);
|
|
28360
|
+
}
|
|
28361
|
+
function parseBrainTerminalOpenRequest(value) {
|
|
28362
|
+
if (!value || typeof value !== "object") throw new Error("open request must be an object");
|
|
28363
|
+
const request = value;
|
|
28364
|
+
if (request.type !== "open") throw new Error("first terminal message must be open");
|
|
28365
|
+
if (!request.session || typeof request.session !== "object") throw new Error("session is required");
|
|
28366
|
+
const session = request.session;
|
|
28367
|
+
if (!session.scope || typeof session.scope !== "object") throw new Error("session scope is required");
|
|
28368
|
+
const scope = session.scope;
|
|
28369
|
+
return {
|
|
28370
|
+
type: "open",
|
|
28371
|
+
session: {
|
|
28372
|
+
id: requiredIdentifier(session.id, "session.id"),
|
|
28373
|
+
scope: {
|
|
28374
|
+
owner: requiredIdentifier(scope.owner, "scope.owner", 100),
|
|
28375
|
+
repo: requiredIdentifier(scope.repo, "scope.repo", 100),
|
|
28376
|
+
conversationId: requiredIdentifier(scope.conversationId, "scope.conversationId")
|
|
28377
|
+
}
|
|
28378
|
+
},
|
|
28379
|
+
cwd: requiredIdentifier(request.cwd, "cwd", 1e3),
|
|
28380
|
+
afterRevision: revision(request.afterRevision),
|
|
28381
|
+
cols: terminalSize(request.cols, "cols"),
|
|
28382
|
+
rows: terminalSize(request.rows, "rows")
|
|
28383
|
+
};
|
|
28384
|
+
}
|
|
28385
|
+
function parseBrainTerminalStatusRequest(value) {
|
|
28386
|
+
if (!value || typeof value !== "object") throw new Error("status request must be an object");
|
|
28387
|
+
const request = value;
|
|
28388
|
+
if (request.type !== "status") throw new Error("terminal request must be status");
|
|
28389
|
+
return { type: "status", sessionId: requiredIdentifier(request.sessionId, "sessionId") };
|
|
28390
|
+
}
|
|
28391
|
+
function parseBrainTerminalCommand(value) {
|
|
28392
|
+
if (!value || typeof value !== "object") throw new Error("terminal command must be an object");
|
|
28393
|
+
const command = value;
|
|
28394
|
+
const sessionId = requiredIdentifier(command.sessionId, "sessionId");
|
|
28395
|
+
switch (command.type) {
|
|
28396
|
+
case "attach":
|
|
28397
|
+
return { type: "attach", sessionId, afterRevision: revision(command.afterRevision) };
|
|
28398
|
+
case "input": {
|
|
28399
|
+
const inputId = requiredIdentifier(command.inputId, "inputId");
|
|
28400
|
+
if (typeof command.data !== "string" || command.data.length === 0) {
|
|
28401
|
+
throw new Error("data must be a non-empty string");
|
|
28402
|
+
}
|
|
28403
|
+
return { type: "input", sessionId, inputId, data: command.data };
|
|
28404
|
+
}
|
|
28405
|
+
case "resize":
|
|
28406
|
+
return {
|
|
28407
|
+
type: "resize",
|
|
28408
|
+
sessionId,
|
|
28409
|
+
cols: terminalSize(command.cols, "cols"),
|
|
28410
|
+
rows: terminalSize(command.rows, "rows")
|
|
28411
|
+
};
|
|
28412
|
+
case "detach":
|
|
28413
|
+
return { type: "detach", sessionId };
|
|
28414
|
+
case "restart":
|
|
28415
|
+
return { type: "restart", sessionId };
|
|
28416
|
+
default:
|
|
28417
|
+
throw new Error("unknown terminal command");
|
|
28418
|
+
}
|
|
28419
|
+
}
|
|
28420
|
+
function sessionName(id) {
|
|
28421
|
+
return `kody_${createHash9("sha256").update(id).digest("hex").slice(0, 32)}`;
|
|
28422
|
+
}
|
|
28423
|
+
function stateEvent(session) {
|
|
28424
|
+
return {
|
|
28425
|
+
type: "state",
|
|
28426
|
+
sessionId: session.id,
|
|
28427
|
+
generation: session.generation,
|
|
28428
|
+
state: session.state,
|
|
28429
|
+
processId: session.processId
|
|
28430
|
+
};
|
|
28431
|
+
}
|
|
28432
|
+
function failedEvent(session, code, cause) {
|
|
28433
|
+
return {
|
|
28434
|
+
type: "failed",
|
|
28435
|
+
sessionId: session.id,
|
|
28436
|
+
generation: session.generation,
|
|
28437
|
+
code,
|
|
28438
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
28439
|
+
};
|
|
28440
|
+
}
|
|
28441
|
+
var BrainTerminalSessionAgent = class {
|
|
28442
|
+
constructor(dependencies) {
|
|
28443
|
+
this.dependencies = dependencies;
|
|
28444
|
+
}
|
|
28445
|
+
dependencies;
|
|
28446
|
+
session = null;
|
|
28447
|
+
now() {
|
|
28448
|
+
return (this.dependencies.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
28449
|
+
}
|
|
28450
|
+
requireSession() {
|
|
28451
|
+
if (!this.session) throw new Error("terminal session is not open");
|
|
28452
|
+
return this.session;
|
|
28453
|
+
}
|
|
28454
|
+
async persist(session) {
|
|
28455
|
+
this.session = session;
|
|
28456
|
+
await this.dependencies.store.write(session);
|
|
28457
|
+
}
|
|
28458
|
+
async open(rawRequest) {
|
|
28459
|
+
const request = parseBrainTerminalOpenRequest(rawRequest);
|
|
28460
|
+
let session = await this.dependencies.store.read(request.session.id);
|
|
28461
|
+
if (session && (session.scope.owner !== request.session.scope.owner || session.scope.repo !== request.session.scope.repo || session.scope.conversationId !== request.session.scope.conversationId)) {
|
|
28462
|
+
throw new Error("terminal session scope does not match stored identity");
|
|
28463
|
+
}
|
|
28464
|
+
if (!session) {
|
|
28465
|
+
session = {
|
|
28466
|
+
version: 1,
|
|
28467
|
+
id: request.session.id,
|
|
28468
|
+
scope: request.session.scope,
|
|
28469
|
+
sessionName: sessionName(request.session.id),
|
|
28470
|
+
cwd: request.cwd,
|
|
28471
|
+
generation: 1,
|
|
28472
|
+
state: "starting",
|
|
28473
|
+
revision: 0,
|
|
28474
|
+
output: "",
|
|
28475
|
+
processId: null,
|
|
28476
|
+
cols: request.cols,
|
|
28477
|
+
rows: request.rows,
|
|
28478
|
+
updatedAt: this.now()
|
|
28479
|
+
};
|
|
28480
|
+
await this.persist(session);
|
|
28481
|
+
let started;
|
|
28482
|
+
try {
|
|
28483
|
+
started = await this.dependencies.runtime.start(
|
|
28484
|
+
session.sessionName,
|
|
28485
|
+
session.cwd,
|
|
28486
|
+
session.cols,
|
|
28487
|
+
session.rows
|
|
28488
|
+
);
|
|
28489
|
+
} catch (cause) {
|
|
28490
|
+
session = { ...session, state: "failed", updatedAt: this.now() };
|
|
28491
|
+
await this.persist(session);
|
|
28492
|
+
return [failedEvent(session, "runtime_start_failed", cause)];
|
|
28493
|
+
}
|
|
28494
|
+
session = { ...session, state: "ready", processId: started.processId, updatedAt: this.now() };
|
|
28495
|
+
await this.persist(session);
|
|
28496
|
+
} else {
|
|
28497
|
+
const runtime = await this.dependencies.runtime.inspect(session.sessionName);
|
|
28498
|
+
session = {
|
|
28499
|
+
...session,
|
|
28500
|
+
state: runtime.alive ? "ready" : session.state === "failed" ? "failed" : "exited",
|
|
28501
|
+
processId: runtime.processId,
|
|
28502
|
+
cols: request.cols,
|
|
28503
|
+
rows: request.rows,
|
|
28504
|
+
updatedAt: this.now()
|
|
28505
|
+
};
|
|
28506
|
+
if (runtime.alive) {
|
|
28507
|
+
await this.dependencies.runtime.resize(session.sessionName, request.cols, request.rows);
|
|
28508
|
+
}
|
|
28509
|
+
await this.persist(session);
|
|
28510
|
+
}
|
|
28511
|
+
const events = [stateEvent(session)];
|
|
28512
|
+
if (session.output && (request.afterRevision === void 0 || request.afterRevision < session.revision)) {
|
|
28513
|
+
events.push({
|
|
28514
|
+
type: "output",
|
|
28515
|
+
sessionId: session.id,
|
|
28516
|
+
generation: session.generation,
|
|
28517
|
+
revision: session.revision,
|
|
28518
|
+
data: `\x1B[2J\x1B[H${session.output}`
|
|
28519
|
+
});
|
|
28520
|
+
}
|
|
28521
|
+
return events;
|
|
28522
|
+
}
|
|
28523
|
+
async inspectStored(sessionId) {
|
|
28524
|
+
const id = requiredIdentifier(sessionId, "sessionId");
|
|
28525
|
+
const session = await this.dependencies.store.read(id);
|
|
28526
|
+
if (!session) return null;
|
|
28527
|
+
const runtime = await this.dependencies.runtime.inspect(session.sessionName);
|
|
28528
|
+
const next = {
|
|
28529
|
+
...session,
|
|
28530
|
+
state: runtime.alive ? session.state : "exited",
|
|
28531
|
+
processId: runtime.processId,
|
|
28532
|
+
updatedAt: this.now()
|
|
28533
|
+
};
|
|
28534
|
+
await this.persist(next);
|
|
28535
|
+
return {
|
|
28536
|
+
id: next.id,
|
|
28537
|
+
generation: next.generation,
|
|
28538
|
+
state: next.state,
|
|
28539
|
+
revision: next.revision,
|
|
28540
|
+
processId: next.processId
|
|
28541
|
+
};
|
|
28542
|
+
}
|
|
28543
|
+
async status() {
|
|
28544
|
+
const session = this.requireSession();
|
|
28545
|
+
const runtime = await this.dependencies.runtime.inspect(session.sessionName);
|
|
28546
|
+
const nextState = runtime.alive ? session.state : session.state === "failed" ? "failed" : "exited";
|
|
28547
|
+
if (nextState !== session.state || runtime.processId !== session.processId) {
|
|
28548
|
+
await this.persist({
|
|
28549
|
+
...session,
|
|
28550
|
+
state: nextState,
|
|
28551
|
+
processId: runtime.processId,
|
|
28552
|
+
updatedAt: this.now()
|
|
28553
|
+
});
|
|
28554
|
+
}
|
|
28555
|
+
const current = this.requireSession();
|
|
28556
|
+
return {
|
|
28557
|
+
id: current.id,
|
|
28558
|
+
generation: current.generation,
|
|
28559
|
+
state: current.state,
|
|
28560
|
+
revision: current.revision,
|
|
28561
|
+
processId: current.processId
|
|
28562
|
+
};
|
|
28563
|
+
}
|
|
28564
|
+
async captureOutput() {
|
|
28565
|
+
const session = this.requireSession();
|
|
28566
|
+
if (session.state !== "ready" && session.state !== "detached") return null;
|
|
28567
|
+
const output = (await this.dependencies.runtime.capture(session.sessionName)).slice(-MAX_CAPTURE_CHARS);
|
|
28568
|
+
if (output === session.output) return null;
|
|
28569
|
+
const next = {
|
|
28570
|
+
...session,
|
|
28571
|
+
output,
|
|
28572
|
+
revision: session.revision + 1,
|
|
28573
|
+
updatedAt: this.now()
|
|
28574
|
+
};
|
|
28575
|
+
await this.persist(next);
|
|
28576
|
+
return {
|
|
28577
|
+
type: "output",
|
|
28578
|
+
sessionId: next.id,
|
|
28579
|
+
generation: next.generation,
|
|
28580
|
+
revision: next.revision,
|
|
28581
|
+
data: `\x1B[2J\x1B[H${output}`
|
|
28582
|
+
};
|
|
28583
|
+
}
|
|
28584
|
+
async detach() {
|
|
28585
|
+
const session = this.requireSession();
|
|
28586
|
+
const next = { ...session, state: "detached", updatedAt: this.now() };
|
|
28587
|
+
await this.persist(next);
|
|
28588
|
+
return stateEvent(next);
|
|
28589
|
+
}
|
|
28590
|
+
async command(rawCommand) {
|
|
28591
|
+
const command = parseBrainTerminalCommand(rawCommand);
|
|
28592
|
+
const session = this.requireSession();
|
|
28593
|
+
if (command.sessionId !== session.id) throw new Error("terminal command session identity mismatch");
|
|
28594
|
+
switch (command.type) {
|
|
28595
|
+
case "attach":
|
|
28596
|
+
return stateEvent(session);
|
|
28597
|
+
case "input":
|
|
28598
|
+
if (session.state !== "ready") throw new Error(`input is not allowed while terminal is ${session.state}`);
|
|
28599
|
+
await this.dependencies.runtime.input(session.sessionName, command.data);
|
|
28600
|
+
return {
|
|
28601
|
+
type: "input-accepted",
|
|
28602
|
+
sessionId: session.id,
|
|
28603
|
+
generation: session.generation,
|
|
28604
|
+
inputId: command.inputId
|
|
28605
|
+
};
|
|
28606
|
+
case "resize":
|
|
28607
|
+
if (session.state !== "ready") throw new Error(`resize is not allowed while terminal is ${session.state}`);
|
|
28608
|
+
await this.dependencies.runtime.resize(session.sessionName, command.cols, command.rows);
|
|
28609
|
+
await this.persist({ ...session, cols: command.cols, rows: command.rows, updatedAt: this.now() });
|
|
28610
|
+
return null;
|
|
28611
|
+
case "detach":
|
|
28612
|
+
return this.detach();
|
|
28613
|
+
case "restart": {
|
|
28614
|
+
if (session.state === "starting") throw new Error("restart is not allowed while terminal is starting");
|
|
28615
|
+
await this.dependencies.runtime.stop(session.sessionName);
|
|
28616
|
+
const starting = {
|
|
28617
|
+
...session,
|
|
28618
|
+
generation: session.generation + 1,
|
|
28619
|
+
state: "starting",
|
|
28620
|
+
revision: 0,
|
|
28621
|
+
output: "",
|
|
28622
|
+
processId: null,
|
|
28623
|
+
updatedAt: this.now()
|
|
28624
|
+
};
|
|
28625
|
+
await this.persist(starting);
|
|
28626
|
+
let started;
|
|
28627
|
+
try {
|
|
28628
|
+
started = await this.dependencies.runtime.start(
|
|
28629
|
+
starting.sessionName,
|
|
28630
|
+
starting.cwd,
|
|
28631
|
+
starting.cols,
|
|
28632
|
+
starting.rows
|
|
28633
|
+
);
|
|
28634
|
+
} catch (cause) {
|
|
28635
|
+
const failed = { ...starting, state: "failed", updatedAt: this.now() };
|
|
28636
|
+
await this.persist(failed);
|
|
28637
|
+
return failedEvent(failed, "runtime_start_failed", cause);
|
|
28638
|
+
}
|
|
28639
|
+
const ready = { ...starting, state: "ready", processId: started.processId, updatedAt: this.now() };
|
|
28640
|
+
await this.persist(ready);
|
|
28641
|
+
return stateEvent(ready);
|
|
28642
|
+
}
|
|
28643
|
+
}
|
|
28644
|
+
}
|
|
28645
|
+
};
|
|
28646
|
+
|
|
28647
|
+
// src/terminal/brain-terminal-adapters.ts
|
|
28648
|
+
import { createHash as createHash10, randomBytes as randomBytes2 } from "crypto";
|
|
28649
|
+
import { mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
|
|
28650
|
+
import * as path56 from "path";
|
|
28651
|
+
import { spawn as spawn9 } from "child_process";
|
|
28652
|
+
function runTerminalCommand(command, args, input) {
|
|
28653
|
+
return new Promise((resolve23, reject) => {
|
|
28654
|
+
const child = spawn9(command, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
28655
|
+
const stdout = [];
|
|
28656
|
+
const stderr = [];
|
|
28657
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
28658
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
28659
|
+
child.on("error", reject);
|
|
28660
|
+
child.on("close", (code) => {
|
|
28661
|
+
resolve23({
|
|
28662
|
+
code: code ?? 1,
|
|
28663
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
28664
|
+
stderr: Buffer.concat(stderr).toString("utf8")
|
|
28665
|
+
});
|
|
28666
|
+
});
|
|
28667
|
+
if (input !== void 0) child.stdin.end(input);
|
|
28668
|
+
else child.stdin.end();
|
|
28669
|
+
});
|
|
28670
|
+
}
|
|
28671
|
+
function storeKey(id) {
|
|
28672
|
+
return createHash10("sha256").update(id).digest("hex");
|
|
28673
|
+
}
|
|
28674
|
+
function isStoredSession(value) {
|
|
28675
|
+
if (!value || typeof value !== "object") return false;
|
|
28676
|
+
const session = value;
|
|
28677
|
+
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";
|
|
28678
|
+
}
|
|
28679
|
+
var FileBrainTerminalMetadataStore = class {
|
|
28680
|
+
constructor(root) {
|
|
28681
|
+
this.root = root;
|
|
28682
|
+
}
|
|
28683
|
+
root;
|
|
28684
|
+
file(id) {
|
|
28685
|
+
return path56.join(this.root, `${storeKey(id)}.json`);
|
|
28686
|
+
}
|
|
28687
|
+
async read(id) {
|
|
28688
|
+
try {
|
|
28689
|
+
const parsed = JSON.parse(await readFile(this.file(id), "utf8"));
|
|
28690
|
+
if (!isStoredSession(parsed) || parsed.id !== id) {
|
|
28691
|
+
throw new Error("stored terminal session is invalid");
|
|
28692
|
+
}
|
|
28693
|
+
return parsed;
|
|
28694
|
+
} catch (error) {
|
|
28695
|
+
if (error.code === "ENOENT") return null;
|
|
28696
|
+
throw error;
|
|
28697
|
+
}
|
|
28698
|
+
}
|
|
28699
|
+
async write(session) {
|
|
28700
|
+
await mkdir(this.root, { recursive: true, mode: 448 });
|
|
28701
|
+
const target = this.file(session.id);
|
|
28702
|
+
const temporary = `${target}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
|
|
28703
|
+
await writeFile2(temporary, `${JSON.stringify(session)}
|
|
28704
|
+
`, { mode: 384 });
|
|
28705
|
+
await rename(temporary, target);
|
|
28706
|
+
}
|
|
28707
|
+
};
|
|
28708
|
+
function commandError(action, result) {
|
|
28709
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
|
|
28710
|
+
return new Error(`tmux ${action} failed: ${detail.slice(0, 500)}`);
|
|
28711
|
+
}
|
|
28712
|
+
var TmuxBrainTerminalRuntime = class {
|
|
28713
|
+
constructor(run = runTerminalCommand) {
|
|
28714
|
+
this.run = run;
|
|
28715
|
+
}
|
|
28716
|
+
run;
|
|
28717
|
+
async tmux(action, args, input) {
|
|
28718
|
+
const result = await this.run("tmux", args, input);
|
|
28719
|
+
if (result.code !== 0) throw commandError(action, result);
|
|
28720
|
+
return result;
|
|
28721
|
+
}
|
|
28722
|
+
async start(sessionName2, cwd, cols, rows) {
|
|
28723
|
+
await this.tmux("start", [
|
|
28724
|
+
"new-session",
|
|
28725
|
+
"-d",
|
|
28726
|
+
"-s",
|
|
28727
|
+
sessionName2,
|
|
28728
|
+
"-x",
|
|
28729
|
+
String(cols),
|
|
28730
|
+
"-y",
|
|
28731
|
+
String(rows),
|
|
28732
|
+
"-c",
|
|
28733
|
+
cwd,
|
|
28734
|
+
"/bin/bash",
|
|
28735
|
+
"-l"
|
|
28736
|
+
]);
|
|
28737
|
+
await this.tmux("configure", ["set-option", "-t", sessionName2, "status", "off"]);
|
|
28738
|
+
await this.tmux("configure", ["set-option", "-t", sessionName2, "history-limit", "50000"]);
|
|
28739
|
+
await this.tmux("configure", ["set-option", "-w", "-t", sessionName2, "remain-on-exit", "on"]);
|
|
28740
|
+
const inspected = await this.inspect(sessionName2);
|
|
28741
|
+
if (!inspected.alive || inspected.processId === null) throw new Error("tmux terminal did not start");
|
|
28742
|
+
return { processId: inspected.processId };
|
|
28743
|
+
}
|
|
28744
|
+
async inspect(sessionName2) {
|
|
28745
|
+
const result = await this.run("tmux", [
|
|
28746
|
+
"list-panes",
|
|
28747
|
+
"-t",
|
|
28748
|
+
sessionName2,
|
|
28749
|
+
"-F",
|
|
28750
|
+
"#{pane_dead}:#{pane_pid}"
|
|
28751
|
+
]);
|
|
28752
|
+
if (result.code !== 0) return { alive: false, processId: null };
|
|
28753
|
+
const [dead, pid] = result.stdout.trim().split(":");
|
|
28754
|
+
const processId = Number(pid);
|
|
28755
|
+
return {
|
|
28756
|
+
alive: dead === "0" && Number.isInteger(processId) && processId > 0,
|
|
28757
|
+
processId: Number.isInteger(processId) && processId > 0 ? processId : null
|
|
28758
|
+
};
|
|
28759
|
+
}
|
|
28760
|
+
async capture(sessionName2) {
|
|
28761
|
+
const alternate = await this.run("tmux", [
|
|
28762
|
+
"display-message",
|
|
28763
|
+
"-p",
|
|
28764
|
+
"-t",
|
|
28765
|
+
sessionName2,
|
|
28766
|
+
"#{alternate_on}"
|
|
28767
|
+
]);
|
|
28768
|
+
if (alternate.code !== 0) throw commandError("inspect screen", alternate);
|
|
28769
|
+
const args = alternate.stdout.trim() === "1" ? ["capture-pane", "-p", "-e", "-t", sessionName2] : ["capture-pane", "-p", "-e", "-J", "-S", "-50000", "-t", sessionName2];
|
|
28770
|
+
return (await this.tmux("capture", args)).stdout;
|
|
28771
|
+
}
|
|
28772
|
+
async input(sessionName2, data) {
|
|
28773
|
+
const bufferName = `kody_${randomBytes2(8).toString("hex")}`;
|
|
28774
|
+
await this.tmux("load input", ["load-buffer", "-b", bufferName, "-"], data);
|
|
28775
|
+
await this.tmux("paste input", ["paste-buffer", "-d", "-b", bufferName, "-t", sessionName2]);
|
|
28776
|
+
}
|
|
28777
|
+
async resize(sessionName2, cols, rows) {
|
|
28778
|
+
await this.tmux("resize", [
|
|
28779
|
+
"resize-window",
|
|
28780
|
+
"-t",
|
|
28781
|
+
sessionName2,
|
|
28782
|
+
"-x",
|
|
28783
|
+
String(cols),
|
|
28784
|
+
"-y",
|
|
28785
|
+
String(rows)
|
|
28786
|
+
]);
|
|
28787
|
+
}
|
|
28788
|
+
async stop(sessionName2) {
|
|
28789
|
+
const result = await this.run("tmux", ["kill-session", "-t", sessionName2]);
|
|
28790
|
+
if (result.code !== 0 && !/can't find session|no server running/i.test(result.stderr)) {
|
|
28791
|
+
throw commandError("stop", result);
|
|
28792
|
+
}
|
|
28793
|
+
}
|
|
28794
|
+
};
|
|
28795
|
+
|
|
28796
|
+
// src/servers/brain-terminal-agent.ts
|
|
28797
|
+
var DEFAULT_POLL_INTERVAL_MS = 150;
|
|
28798
|
+
function writeEvent(output, event) {
|
|
28799
|
+
output.write(`${JSON.stringify(event)}
|
|
28800
|
+
`);
|
|
28801
|
+
}
|
|
28802
|
+
async function brainTerminalAgent(options) {
|
|
28803
|
+
const input = options.input ?? process.stdin;
|
|
28804
|
+
const output = options.output ?? process.stdout;
|
|
28805
|
+
const error = options.error ?? process.stderr;
|
|
28806
|
+
const reposRoot = process.env.BRAIN_REPOS_ROOT?.trim() || path57.join(path57.dirname(path57.resolve(options.cwd)), "repos");
|
|
28807
|
+
const stateRoot = process.env.BRAIN_TERMINAL_STATE_ROOT?.trim() || path57.join(path57.dirname(reposRoot), ".kody", "terminal-sessions");
|
|
28808
|
+
const agent = new BrainTerminalSessionAgent({
|
|
28809
|
+
store: new FileBrainTerminalMetadataStore(stateRoot),
|
|
28810
|
+
runtime: new TmuxBrainTerminalRuntime()
|
|
28811
|
+
});
|
|
28812
|
+
const lines = createInterface2({ input, crlfDelay: Infinity });
|
|
28813
|
+
let opened = false;
|
|
28814
|
+
let poll = null;
|
|
28815
|
+
let pollRunning = false;
|
|
28816
|
+
const stopPoll = () => {
|
|
28817
|
+
if (poll) clearInterval(poll);
|
|
28818
|
+
poll = null;
|
|
28819
|
+
};
|
|
28820
|
+
const capture = async () => {
|
|
28821
|
+
if (pollRunning) return;
|
|
28822
|
+
pollRunning = true;
|
|
28823
|
+
try {
|
|
28824
|
+
const event = await agent.captureOutput();
|
|
28825
|
+
if (event) writeEvent(output, event);
|
|
28826
|
+
const status = await agent.status();
|
|
28827
|
+
if (status.state === "exited") {
|
|
28828
|
+
writeEvent(output, {
|
|
28829
|
+
type: "exited",
|
|
28830
|
+
sessionId: status.id,
|
|
28831
|
+
generation: status.generation
|
|
28832
|
+
});
|
|
28833
|
+
stopPoll();
|
|
28834
|
+
}
|
|
28835
|
+
} catch (cause) {
|
|
28836
|
+
error.write(`[brain-terminal-agent] capture failed: ${cause instanceof Error ? cause.message : String(cause)}
|
|
28837
|
+
`);
|
|
28838
|
+
} finally {
|
|
28839
|
+
pollRunning = false;
|
|
28840
|
+
}
|
|
28841
|
+
};
|
|
28842
|
+
try {
|
|
28843
|
+
for await (const line of lines) {
|
|
28844
|
+
if (!line.trim()) continue;
|
|
28845
|
+
const value = JSON.parse(line);
|
|
28846
|
+
if (!opened) {
|
|
28847
|
+
if (value && typeof value === "object" && value.type === "status") {
|
|
28848
|
+
const request = parseBrainTerminalStatusRequest(value);
|
|
28849
|
+
const status = await agent.inspectStored(request.sessionId);
|
|
28850
|
+
if (status) {
|
|
28851
|
+
writeEvent(output, {
|
|
28852
|
+
type: "state",
|
|
28853
|
+
sessionId: status.id,
|
|
28854
|
+
generation: status.generation,
|
|
28855
|
+
state: status.state,
|
|
28856
|
+
processId: status.processId
|
|
28857
|
+
});
|
|
28858
|
+
} else {
|
|
28859
|
+
writeEvent(output, {
|
|
28860
|
+
type: "failed",
|
|
28861
|
+
sessionId: request.sessionId,
|
|
28862
|
+
generation: 1,
|
|
28863
|
+
code: "session_not_found",
|
|
28864
|
+
message: "Terminal session not found"
|
|
28865
|
+
});
|
|
28866
|
+
}
|
|
28867
|
+
return 0;
|
|
28868
|
+
}
|
|
28869
|
+
const requested = parseBrainTerminalOpenRequest(value);
|
|
28870
|
+
const repo = `${requested.session.scope.owner}/${requested.session.scope.repo}`;
|
|
28871
|
+
const workspaceCwd = await ensureRepoCwd({
|
|
28872
|
+
baseCwd: options.cwd,
|
|
28873
|
+
reposRoot,
|
|
28874
|
+
repo,
|
|
28875
|
+
repoToken: process.env.KODY_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_PAT,
|
|
28876
|
+
cloneRepo: defaultCloneRepo
|
|
28877
|
+
});
|
|
28878
|
+
const events = await agent.open({ ...requested, cwd: workspaceCwd });
|
|
28879
|
+
for (const event2 of events) writeEvent(output, event2);
|
|
28880
|
+
opened = true;
|
|
28881
|
+
poll = setInterval(() => void capture(), options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
28882
|
+
poll.unref?.();
|
|
28883
|
+
continue;
|
|
28884
|
+
}
|
|
28885
|
+
const command = parseBrainTerminalCommand(value);
|
|
28886
|
+
const event = await agent.command(command);
|
|
28887
|
+
if (event) writeEvent(output, event);
|
|
28888
|
+
if (command.type === "detach") stopPoll();
|
|
28889
|
+
}
|
|
28890
|
+
if (opened) await agent.detach();
|
|
28891
|
+
return 0;
|
|
28892
|
+
} catch (cause) {
|
|
28893
|
+
stopPoll();
|
|
28894
|
+
error.write(`[brain-terminal-agent] ${cause instanceof Error ? cause.message : String(cause)}
|
|
28895
|
+
`);
|
|
28896
|
+
return 1;
|
|
28897
|
+
} finally {
|
|
28898
|
+
stopPoll();
|
|
28899
|
+
lines.close();
|
|
28900
|
+
}
|
|
28901
|
+
}
|
|
28902
|
+
|
|
28305
28903
|
// src/servers/pool-serve.ts
|
|
28306
28904
|
import { createServer as createServer5 } from "http";
|
|
28307
28905
|
|
|
@@ -28373,8 +28971,8 @@ var FlyClient = class {
|
|
|
28373
28971
|
get fetch() {
|
|
28374
28972
|
return this.opts.fetchImpl ?? fetch;
|
|
28375
28973
|
}
|
|
28376
|
-
async call(
|
|
28377
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
28974
|
+
async call(path58, init = {}) {
|
|
28975
|
+
const res = await this.fetch(`${FLY_API_BASE}${path58}`, {
|
|
28378
28976
|
method: init.method ?? "GET",
|
|
28379
28977
|
headers: {
|
|
28380
28978
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -28385,7 +28983,7 @@ var FlyClient = class {
|
|
|
28385
28983
|
if (res.status === 404 && init.allow404) return null;
|
|
28386
28984
|
if (!res.ok) {
|
|
28387
28985
|
const text2 = await res.text().catch(() => "");
|
|
28388
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
28986
|
+
throw new Error(`Fly API ${res.status} on ${path58}: ${text2.slice(0, 200) || res.statusText}`);
|
|
28389
28987
|
}
|
|
28390
28988
|
if (res.status === 204) return null;
|
|
28391
28989
|
const raw = await res.text();
|
|
@@ -28898,14 +29496,14 @@ function sendJson2(res, status, body) {
|
|
|
28898
29496
|
res.end(JSON.stringify(body));
|
|
28899
29497
|
}
|
|
28900
29498
|
function readJsonBody2(req) {
|
|
28901
|
-
return new Promise((
|
|
29499
|
+
return new Promise((resolve23, reject) => {
|
|
28902
29500
|
const chunks = [];
|
|
28903
29501
|
req.on("data", (c) => chunks.push(c));
|
|
28904
29502
|
req.on("end", () => {
|
|
28905
29503
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
28906
|
-
if (!raw.trim()) return
|
|
29504
|
+
if (!raw.trim()) return resolve23({});
|
|
28907
29505
|
try {
|
|
28908
|
-
|
|
29506
|
+
resolve23(JSON.parse(raw));
|
|
28909
29507
|
} catch (err) {
|
|
28910
29508
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
28911
29509
|
}
|
|
@@ -29059,10 +29657,10 @@ async function poolServe() {
|
|
|
29059
29657
|
}
|
|
29060
29658
|
});
|
|
29061
29659
|
const apiHost = process.env.POOL_API_HOST ?? "::";
|
|
29062
|
-
await new Promise((
|
|
29660
|
+
await new Promise((resolve23) => {
|
|
29063
29661
|
server.listen(apiPort, apiHost, () => {
|
|
29064
29662
|
log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
|
|
29065
|
-
|
|
29663
|
+
resolve23();
|
|
29066
29664
|
});
|
|
29067
29665
|
});
|
|
29068
29666
|
if (loopTickEnabled) void runLoopTick();
|
|
@@ -29080,8 +29678,8 @@ async function poolServe() {
|
|
|
29080
29678
|
}
|
|
29081
29679
|
|
|
29082
29680
|
// src/servers/runner-serve.ts
|
|
29083
|
-
import { spawn as
|
|
29084
|
-
import * as
|
|
29681
|
+
import { spawn as spawn10 } from "child_process";
|
|
29682
|
+
import * as fs57 from "fs";
|
|
29085
29683
|
import { createServer as createServer6 } from "http";
|
|
29086
29684
|
var DEFAULT_PORT2 = 8080;
|
|
29087
29685
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -29102,17 +29700,17 @@ function authOk2(req, expected) {
|
|
|
29102
29700
|
return false;
|
|
29103
29701
|
}
|
|
29104
29702
|
function readJsonBody3(req) {
|
|
29105
|
-
return new Promise((
|
|
29703
|
+
return new Promise((resolve23, reject) => {
|
|
29106
29704
|
const chunks = [];
|
|
29107
29705
|
req.on("data", (c) => chunks.push(c));
|
|
29108
29706
|
req.on("end", () => {
|
|
29109
29707
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
29110
29708
|
if (!raw.trim()) {
|
|
29111
|
-
|
|
29709
|
+
resolve23({});
|
|
29112
29710
|
return;
|
|
29113
29711
|
}
|
|
29114
29712
|
try {
|
|
29115
|
-
|
|
29713
|
+
resolve23(JSON.parse(raw));
|
|
29116
29714
|
} catch (err) {
|
|
29117
29715
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
29118
29716
|
}
|
|
@@ -29157,8 +29755,8 @@ async function defaultRunJob(job) {
|
|
|
29157
29755
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
29158
29756
|
const branch = job.ref ?? "main";
|
|
29159
29757
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
29160
|
-
|
|
29161
|
-
|
|
29758
|
+
fs57.rmSync(workdir, { recursive: true, force: true });
|
|
29759
|
+
fs57.mkdirSync(workdir, { recursive: true });
|
|
29162
29760
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
29163
29761
|
const target = job.runRequest.target;
|
|
29164
29762
|
const interactive = target.type === "chat";
|
|
@@ -29187,13 +29785,13 @@ async function defaultRunJob(job) {
|
|
|
29187
29785
|
...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
|
|
29188
29786
|
...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
|
|
29189
29787
|
};
|
|
29190
|
-
const run = (cmd, args, cwd) => new Promise((
|
|
29191
|
-
const child =
|
|
29192
|
-
child.on("exit", (code) =>
|
|
29788
|
+
const run = (cmd, args, cwd) => new Promise((resolve23) => {
|
|
29789
|
+
const child = spawn10(cmd, args, { stdio: "inherit", env: childEnv, cwd });
|
|
29790
|
+
child.on("exit", (code) => resolve23(code ?? 0));
|
|
29193
29791
|
child.on("error", (err) => {
|
|
29194
29792
|
process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
|
|
29195
29793
|
`);
|
|
29196
|
-
|
|
29794
|
+
resolve23(1);
|
|
29197
29795
|
});
|
|
29198
29796
|
});
|
|
29199
29797
|
process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
|
|
@@ -29269,11 +29867,11 @@ async function runnerServe() {
|
|
|
29269
29867
|
const port = Number(process.env.PORT ?? DEFAULT_PORT2);
|
|
29270
29868
|
const server = buildServer2({ apiKey });
|
|
29271
29869
|
const host = process.env.RUNNER_HOST ?? "::";
|
|
29272
|
-
await new Promise((
|
|
29870
|
+
await new Promise((resolve23) => {
|
|
29273
29871
|
server.listen(port, host, () => {
|
|
29274
29872
|
process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
|
|
29275
29873
|
`);
|
|
29276
|
-
|
|
29874
|
+
resolve23();
|
|
29277
29875
|
});
|
|
29278
29876
|
});
|
|
29279
29877
|
const shutdown = (signal) => {
|
|
@@ -29291,7 +29889,7 @@ async function runnerServe() {
|
|
|
29291
29889
|
// src/servers/serve.ts
|
|
29292
29890
|
init_config();
|
|
29293
29891
|
init_litellm();
|
|
29294
|
-
import { spawn as
|
|
29892
|
+
import { spawn as spawn11 } from "child_process";
|
|
29295
29893
|
function parseTarget(positional) {
|
|
29296
29894
|
if (!Array.isArray(positional) || positional.length === 0) return "none";
|
|
29297
29895
|
const first = String(positional[0]).toLowerCase();
|
|
@@ -29341,15 +29939,15 @@ async function serve(opts) {
|
|
|
29341
29939
|
if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
|
|
29342
29940
|
`);
|
|
29343
29941
|
const args = ["--dangerously-skip-permissions", "--model", model.model];
|
|
29344
|
-
const child =
|
|
29345
|
-
const exitCode = await new Promise((
|
|
29346
|
-
child.on("exit", (code) =>
|
|
29942
|
+
const child = spawn11("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
|
|
29943
|
+
const exitCode = await new Promise((resolve23) => {
|
|
29944
|
+
child.on("exit", (code) => resolve23(code ?? 0));
|
|
29347
29945
|
child.on("error", (err) => {
|
|
29348
29946
|
process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
|
|
29349
29947
|
`);
|
|
29350
29948
|
process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
|
|
29351
29949
|
`);
|
|
29352
|
-
|
|
29950
|
+
resolve23(1);
|
|
29353
29951
|
});
|
|
29354
29952
|
});
|
|
29355
29953
|
killProxy();
|
|
@@ -29361,7 +29959,7 @@ async function serve(opts) {
|
|
|
29361
29959
|
if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
|
|
29362
29960
|
`);
|
|
29363
29961
|
try {
|
|
29364
|
-
const code =
|
|
29962
|
+
const code = spawn11("code", [opts.cwd], { stdio: "inherit", env: editorEnv, detached: true });
|
|
29365
29963
|
code.on("error", (err) => {
|
|
29366
29964
|
process.stderr.write(`[kody serve] failed to launch VS Code: ${err.message}
|
|
29367
29965
|
`);
|
|
@@ -29709,7 +30307,15 @@ function parseArgs(argv) {
|
|
|
29709
30307
|
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
29710
30308
|
return result;
|
|
29711
30309
|
}
|
|
29712
|
-
const SERVER_VERBS = /* @__PURE__ */ new Set([
|
|
30310
|
+
const SERVER_VERBS = /* @__PURE__ */ new Set([
|
|
30311
|
+
"serve",
|
|
30312
|
+
"pool-serve",
|
|
30313
|
+
"runner-serve",
|
|
30314
|
+
"brain-serve",
|
|
30315
|
+
"brain-terminal-agent",
|
|
30316
|
+
"brain-proxy",
|
|
30317
|
+
"mcp-http-server"
|
|
30318
|
+
]);
|
|
29713
30319
|
if (SERVER_VERBS.has(cmd)) {
|
|
29714
30320
|
result.command = "server";
|
|
29715
30321
|
result.serverName = cmd;
|
|
@@ -29813,6 +30419,8 @@ ${HELP_TEXT}`);
|
|
|
29813
30419
|
return await runnerServe();
|
|
29814
30420
|
case "brain-serve":
|
|
29815
30421
|
return await brainServe({ cwd: cwd2 });
|
|
30422
|
+
case "brain-terminal-agent":
|
|
30423
|
+
return await brainTerminalAgent({ cwd: cwd2 });
|
|
29816
30424
|
case "brain-proxy":
|
|
29817
30425
|
return await brainProxy();
|
|
29818
30426
|
case "mcp-http-server":
|