@kody-ade/kody-engine 0.4.306 → 0.4.308
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 +1003 -851
- package/package.json +24 -25
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.308",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -549,11 +549,11 @@ function branchApiPath(config, targetPath) {
|
|
|
549
549
|
}
|
|
550
550
|
function ensureStateBranch(config, cwd) {
|
|
551
551
|
const parsed = parseStateRepo(config);
|
|
552
|
-
const
|
|
553
|
-
if (ensuredStateBranches.has(
|
|
552
|
+
const cacheKey4 = `${parsed.owner}/${parsed.repo}:${parsed.branch}`;
|
|
553
|
+
if (ensuredStateBranches.has(cacheKey4)) return;
|
|
554
554
|
try {
|
|
555
555
|
gh(["api", `/repos/${parsed.owner}/${parsed.repo}/git/ref/heads/${parsed.branch}`], { cwd });
|
|
556
|
-
ensuredStateBranches.add(
|
|
556
|
+
ensuredStateBranches.add(cacheKey4);
|
|
557
557
|
return;
|
|
558
558
|
} catch (err) {
|
|
559
559
|
if (!is404(err)) throw err;
|
|
@@ -572,7 +572,7 @@ function ensureStateBranch(config, cwd) {
|
|
|
572
572
|
} catch (err) {
|
|
573
573
|
if (!isAlreadyExists(err)) throw err;
|
|
574
574
|
}
|
|
575
|
-
ensuredStateBranches.add(
|
|
575
|
+
ensuredStateBranches.add(cacheKey4);
|
|
576
576
|
}
|
|
577
577
|
function readStateText(config, cwd, filePath) {
|
|
578
578
|
const targetPath = stateRepoPath(config, filePath);
|
|
@@ -1610,7 +1610,7 @@ function cmsHeaders(opts) {
|
|
|
1610
1610
|
}
|
|
1611
1611
|
};
|
|
1612
1612
|
}
|
|
1613
|
-
async function callDashboardCms(opts,
|
|
1613
|
+
async function callDashboardCms(opts, path51, init = {}) {
|
|
1614
1614
|
const baseUrl = dashboardBaseUrl(opts);
|
|
1615
1615
|
if (!baseUrl) {
|
|
1616
1616
|
return {
|
|
@@ -1622,7 +1622,7 @@ async function callDashboardCms(opts, path50, init = {}) {
|
|
|
1622
1622
|
const headerResult = cmsHeaders(opts);
|
|
1623
1623
|
if (!headerResult.ok) return headerResult;
|
|
1624
1624
|
try {
|
|
1625
|
-
const res = await fetch(`${baseUrl}${
|
|
1625
|
+
const res = await fetch(`${baseUrl}${path51}`, {
|
|
1626
1626
|
...init,
|
|
1627
1627
|
headers: {
|
|
1628
1628
|
...headerResult.headers,
|
|
@@ -1694,8 +1694,8 @@ function documentArg(value) {
|
|
|
1694
1694
|
function normalizeCmsDocumentIdInput(input) {
|
|
1695
1695
|
const trimmed = stripWrappingQuotes(input.trim());
|
|
1696
1696
|
const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
|
|
1697
|
-
const
|
|
1698
|
-
return
|
|
1697
|
+
const path51 = parseDocumentPath(withoutQuery);
|
|
1698
|
+
return path51 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
|
|
1699
1699
|
}
|
|
1700
1700
|
function stripWrappingQuotes(value) {
|
|
1701
1701
|
let current = value;
|
|
@@ -1706,9 +1706,9 @@ function stripWrappingQuotes(value) {
|
|
|
1706
1706
|
}
|
|
1707
1707
|
}
|
|
1708
1708
|
function parseDocumentPath(value) {
|
|
1709
|
-
const
|
|
1710
|
-
if (!
|
|
1711
|
-
const parts =
|
|
1709
|
+
const path51 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
|
|
1710
|
+
if (!path51?.includes("/content/entries/")) return null;
|
|
1711
|
+
const parts = path51.split("/").filter(Boolean).map(decodePathPart);
|
|
1712
1712
|
const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
|
|
1713
1713
|
const idPart = parts[entriesIndex + 3];
|
|
1714
1714
|
if (!idPart || idPart === "new") return null;
|
|
@@ -2041,6 +2041,23 @@ function getCompanyStoreAssetRoot(kind) {
|
|
|
2041
2041
|
if (fs5.existsSync(rootLayoutPath)) return rootLayoutPath;
|
|
2042
2042
|
return path7.join(root, ".kody", folderByKind[kind]);
|
|
2043
2043
|
}
|
|
2044
|
+
function resetCompanyStoreCacheForTests() {
|
|
2045
|
+
memo = null;
|
|
2046
|
+
}
|
|
2047
|
+
function applyCompanyStoreRuntimeConfig(input, env = process.env) {
|
|
2048
|
+
const repo = stringValue(input?.storeRepoUrl ?? input?.storeRepo);
|
|
2049
|
+
const ref = stringValue(input?.storeRef);
|
|
2050
|
+
let changed = false;
|
|
2051
|
+
if (repo && env[STORE_ENV] !== repo) {
|
|
2052
|
+
env[STORE_ENV] = repo;
|
|
2053
|
+
changed = true;
|
|
2054
|
+
}
|
|
2055
|
+
if (ref && env[REF_ENV] !== ref) {
|
|
2056
|
+
env[REF_ENV] = ref;
|
|
2057
|
+
changed = true;
|
|
2058
|
+
}
|
|
2059
|
+
if (changed) resetCompanyStoreCacheForTests();
|
|
2060
|
+
}
|
|
2044
2061
|
function resolveCompanyStore() {
|
|
2045
2062
|
const envStore = process.env[STORE_ENV]?.trim();
|
|
2046
2063
|
if (!envStore && process.env.VITEST) return null;
|
|
@@ -2050,6 +2067,9 @@ function resolveCompanyStore() {
|
|
|
2050
2067
|
if (!ref) return null;
|
|
2051
2068
|
return { repo, ref };
|
|
2052
2069
|
}
|
|
2070
|
+
function stringValue(value) {
|
|
2071
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
2072
|
+
}
|
|
2053
2073
|
function fetchCompanyStore(repo, ref) {
|
|
2054
2074
|
const localRoot = localStoreRoot(repo);
|
|
2055
2075
|
if (localRoot) return localRoot;
|
|
@@ -6897,10 +6917,10 @@ var init_state2 = __esm({
|
|
|
6897
6917
|
"use strict";
|
|
6898
6918
|
VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
6899
6919
|
GoalStateError = class extends Error {
|
|
6900
|
-
constructor(
|
|
6901
|
-
super(`Invalid goal state at ${
|
|
6920
|
+
constructor(path51, message) {
|
|
6921
|
+
super(`Invalid goal state at ${path51}:
|
|
6902
6922
|
${message}`);
|
|
6903
|
-
this.path =
|
|
6923
|
+
this.path = path51;
|
|
6904
6924
|
this.name = "GoalStateError";
|
|
6905
6925
|
}
|
|
6906
6926
|
path;
|
|
@@ -6913,9 +6933,9 @@ import * as fs25 from "fs";
|
|
|
6913
6933
|
function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
|
|
6914
6934
|
const logs = goalRunLogs(data);
|
|
6915
6935
|
const existing = logs[goalId];
|
|
6916
|
-
const
|
|
6936
|
+
const path51 = existing?.path ?? goalRunLogPath(goalId, data);
|
|
6917
6937
|
logs[goalId] = {
|
|
6918
|
-
path:
|
|
6938
|
+
path: path51,
|
|
6919
6939
|
events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
|
|
6920
6940
|
};
|
|
6921
6941
|
}
|
|
@@ -7002,7 +7022,7 @@ function goalRunStartedAt(data) {
|
|
|
7002
7022
|
function goalRunId(data) {
|
|
7003
7023
|
const existing = data[LOG_RUN_KEY];
|
|
7004
7024
|
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
7005
|
-
const raw =
|
|
7025
|
+
const raw = stringValue2(data.jobId) ?? githubRunId() ?? `local-${process.pid}-${Date.now()}`;
|
|
7006
7026
|
const safe = safePathSegment(raw);
|
|
7007
7027
|
data[LOG_RUN_KEY] = safe;
|
|
7008
7028
|
return safe;
|
|
@@ -7101,7 +7121,7 @@ function triggerContext() {
|
|
|
7101
7121
|
issue: numberValue(recordValue2(event?.issue)?.number),
|
|
7102
7122
|
pullRequest: numberValue(recordValue2(event?.pull_request)?.number),
|
|
7103
7123
|
comment: numberValue(recordValue2(event?.comment)?.id),
|
|
7104
|
-
schedule:
|
|
7124
|
+
schedule: stringValue2(event?.schedule),
|
|
7105
7125
|
inputs: inputs ? pickRecord(inputs, ["issue_number", "sessionId", "message", "model", "title", "executable", "base"]) : void 0
|
|
7106
7126
|
});
|
|
7107
7127
|
return Object.keys(trigger).length > 0 ? trigger : void 0;
|
|
@@ -7120,16 +7140,16 @@ function triggerActorRole(eventName) {
|
|
|
7120
7140
|
}
|
|
7121
7141
|
function jobContext(data) {
|
|
7122
7142
|
const job = pruneUndefined({
|
|
7123
|
-
id:
|
|
7124
|
-
key:
|
|
7125
|
-
flavor:
|
|
7126
|
-
action:
|
|
7127
|
-
capability:
|
|
7128
|
-
executable:
|
|
7129
|
-
agent:
|
|
7130
|
-
schedule:
|
|
7143
|
+
id: stringValue2(data.jobId) ?? void 0,
|
|
7144
|
+
key: stringValue2(data.jobKey) ?? void 0,
|
|
7145
|
+
flavor: stringValue2(data.jobFlavor) ?? void 0,
|
|
7146
|
+
action: stringValue2(data.jobAction) ?? void 0,
|
|
7147
|
+
capability: stringValue2(data.jobCapability) ?? void 0,
|
|
7148
|
+
executable: stringValue2(data.jobExecutable) ?? void 0,
|
|
7149
|
+
agent: stringValue2(data.jobAgent) ?? void 0,
|
|
7150
|
+
schedule: stringValue2(data.jobSchedule) ?? void 0,
|
|
7131
7151
|
target: data.jobTarget ?? void 0,
|
|
7132
|
-
why: truncateString(
|
|
7152
|
+
why: truncateString(stringValue2(data.jobWhy), 1e3),
|
|
7133
7153
|
saveReport: data.jobSaveReport === true ? true : void 0
|
|
7134
7154
|
});
|
|
7135
7155
|
return Object.keys(job).length > 0 ? job : void 0;
|
|
@@ -7139,21 +7159,21 @@ function dispatchContext(event, trigger, job) {
|
|
|
7139
7159
|
const dispatch2 = event.dispatch ?? {};
|
|
7140
7160
|
const context = pruneUndefined({
|
|
7141
7161
|
triggeredBy: triggerLabel(trigger),
|
|
7142
|
-
triggerKind:
|
|
7162
|
+
triggerKind: stringValue2(trigger?.kind),
|
|
7143
7163
|
dispatchMode: dispatchMode(trigger),
|
|
7144
|
-
githubActor:
|
|
7145
|
-
githubActorRole:
|
|
7164
|
+
githubActor: stringValue2(trigger?.githubActor) ?? stringValue2(trigger?.actor),
|
|
7165
|
+
githubActorRole: stringValue2(trigger?.actorRole),
|
|
7146
7166
|
decidedBy: event.source,
|
|
7147
7167
|
dispatchedBy: event.source,
|
|
7148
7168
|
target: event.target,
|
|
7149
|
-
action: dispatch2.action ??
|
|
7150
|
-
capability: dispatch2.capability ??
|
|
7169
|
+
action: dispatch2.action ?? stringValue2(job?.action),
|
|
7170
|
+
capability: dispatch2.capability ?? stringValue2(job?.capability),
|
|
7151
7171
|
reason: event.reason
|
|
7152
7172
|
});
|
|
7153
7173
|
return Object.keys(context).length > 0 ? context : void 0;
|
|
7154
7174
|
}
|
|
7155
7175
|
function triggerLabel(trigger) {
|
|
7156
|
-
const kind =
|
|
7176
|
+
const kind = stringValue2(trigger?.kind);
|
|
7157
7177
|
if (kind === "schedule") return "GitHub schedule";
|
|
7158
7178
|
if (kind === "manual-workflow-dispatch") return "manual workflow dispatch";
|
|
7159
7179
|
if (kind === "issue_comment") return "GitHub issue comment";
|
|
@@ -7162,7 +7182,7 @@ function triggerLabel(trigger) {
|
|
|
7162
7182
|
return "local run";
|
|
7163
7183
|
}
|
|
7164
7184
|
function dispatchMode(trigger) {
|
|
7165
|
-
const kind =
|
|
7185
|
+
const kind = stringValue2(trigger?.kind);
|
|
7166
7186
|
if (kind === "schedule") return "automated";
|
|
7167
7187
|
if (kind === "manual-workflow-dispatch") return "manual";
|
|
7168
7188
|
if (kind) return "event-driven";
|
|
@@ -7174,10 +7194,10 @@ function linkContext(stateRepo) {
|
|
|
7174
7194
|
const repository = process.env.GITHUB_REPOSITORY?.trim();
|
|
7175
7195
|
const server = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
|
|
7176
7196
|
if (runId && repository) links.workflowRun = `${server}/${repository}/actions/runs/${runId}`;
|
|
7177
|
-
const repo =
|
|
7178
|
-
const branch =
|
|
7179
|
-
const goalStatePath2 =
|
|
7180
|
-
const logPath =
|
|
7197
|
+
const repo = stringValue2(stateRepo?.repo);
|
|
7198
|
+
const branch = stringValue2(stateRepo?.branch);
|
|
7199
|
+
const goalStatePath2 = stringValue2(stateRepo?.goalStatePath);
|
|
7200
|
+
const logPath = stringValue2(stateRepo?.logPath);
|
|
7181
7201
|
if (repo && branch && goalStatePath2) links.goalState = githubBlobUrl(repo, branch, goalStatePath2);
|
|
7182
7202
|
if (repo && branch && logPath) links.log = githubBlobUrl(repo, branch, logPath);
|
|
7183
7203
|
return Object.keys(links).length > 0 ? links : void 0;
|
|
@@ -7257,7 +7277,7 @@ function truncateString(value, max) {
|
|
|
7257
7277
|
if (!value) return void 0;
|
|
7258
7278
|
return value.length > max ? `${value.slice(0, max)}...` : value;
|
|
7259
7279
|
}
|
|
7260
|
-
function
|
|
7280
|
+
function stringValue2(value) {
|
|
7261
7281
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
7262
7282
|
}
|
|
7263
7283
|
function safePathSegment(value) {
|
|
@@ -7430,6 +7450,8 @@ var init_managedTodoState = __esm({
|
|
|
7430
7450
|
});
|
|
7431
7451
|
|
|
7432
7452
|
// src/goal/stateStore.ts
|
|
7453
|
+
import * as fs26 from "fs";
|
|
7454
|
+
import * as path24 from "path";
|
|
7433
7455
|
function goalStatePath(goalId) {
|
|
7434
7456
|
return `todos/${goalId}.json`;
|
|
7435
7457
|
}
|
|
@@ -7438,7 +7460,55 @@ function fetchGoalState(config, goalId, cwd) {
|
|
|
7438
7460
|
const loaded = readStateText(config, cwd, filePath);
|
|
7439
7461
|
if (!loaded) return null;
|
|
7440
7462
|
if (!isManagedTodoRaw(loaded.content)) return null;
|
|
7441
|
-
return parseTodoGoalState(goalId, loaded.path, loaded.content);
|
|
7463
|
+
return resolveStoreBackedGoalState(parseTodoGoalState(goalId, loaded.path, loaded.content));
|
|
7464
|
+
}
|
|
7465
|
+
function resolveStoreBackedGoalState(state) {
|
|
7466
|
+
const templateId = templateIdFromGoalState(state);
|
|
7467
|
+
if (!templateId) return state;
|
|
7468
|
+
const template = readStoreGoalTemplate(templateId);
|
|
7469
|
+
if (!template) return state;
|
|
7470
|
+
const nextExtra = { ...state.extra };
|
|
7471
|
+
for (const key of [
|
|
7472
|
+
"type",
|
|
7473
|
+
"destination",
|
|
7474
|
+
"capabilities",
|
|
7475
|
+
"route",
|
|
7476
|
+
"schedule",
|
|
7477
|
+
"scheduleMode",
|
|
7478
|
+
"loopTarget",
|
|
7479
|
+
"preferredRunTime",
|
|
7480
|
+
"saveReport"
|
|
7481
|
+
]) {
|
|
7482
|
+
if (Object.hasOwn(template, key)) nextExtra[key] = template[key];
|
|
7483
|
+
else if (["schedule", "loopTarget", "preferredRunTime", "saveReport"].includes(key)) delete nextExtra[key];
|
|
7484
|
+
}
|
|
7485
|
+
nextExtra.facts = {
|
|
7486
|
+
...recordField2(template.facts) ?? {},
|
|
7487
|
+
...recordField2(state.extra.facts) ?? {}
|
|
7488
|
+
};
|
|
7489
|
+
return { ...state, extra: nextExtra };
|
|
7490
|
+
}
|
|
7491
|
+
function templateIdFromGoalState(state) {
|
|
7492
|
+
for (const key of ["sourceTemplate", "templateId", "template"]) {
|
|
7493
|
+
const value = state.extra[key];
|
|
7494
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
7495
|
+
}
|
|
7496
|
+
return "";
|
|
7497
|
+
}
|
|
7498
|
+
function readStoreGoalTemplate(templateId) {
|
|
7499
|
+
const root = getCompanyStoreAssetRoot("goals");
|
|
7500
|
+
if (!root) return null;
|
|
7501
|
+
const file = path24.join(root, "templates", templateId, "state.json");
|
|
7502
|
+
if (!fs26.existsSync(file)) return null;
|
|
7503
|
+
try {
|
|
7504
|
+
const parsed = JSON.parse(fs26.readFileSync(file, "utf8"));
|
|
7505
|
+
return recordField2(parsed);
|
|
7506
|
+
} catch {
|
|
7507
|
+
return null;
|
|
7508
|
+
}
|
|
7509
|
+
}
|
|
7510
|
+
function recordField2(value) {
|
|
7511
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
7442
7512
|
}
|
|
7443
7513
|
function putGoalState(config, goalId, state, message = `chore(goals): update ${goalId}`, cwd) {
|
|
7444
7514
|
const previous = readStateText(config, cwd, goalStatePath(goalId));
|
|
@@ -7463,12 +7533,13 @@ var init_stateStore = __esm({
|
|
|
7463
7533
|
"use strict";
|
|
7464
7534
|
init_stateRepo();
|
|
7465
7535
|
init_managedTodoState();
|
|
7536
|
+
init_companyStore();
|
|
7466
7537
|
}
|
|
7467
7538
|
});
|
|
7468
7539
|
|
|
7469
7540
|
// src/goal/targetLoopResolution.ts
|
|
7470
|
-
import * as
|
|
7471
|
-
import * as
|
|
7541
|
+
import * as fs27 from "fs";
|
|
7542
|
+
import * as path25 from "path";
|
|
7472
7543
|
function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
|
|
7473
7544
|
const targetId = loopGoal.loopTarget?.id.trim() ?? "";
|
|
7474
7545
|
assertSafeGoalId(targetId, "loop target");
|
|
@@ -7554,16 +7625,16 @@ function goalInstanceTime(state) {
|
|
|
7554
7625
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
7555
7626
|
}
|
|
7556
7627
|
function loadGoalTemplate(cwd, targetId) {
|
|
7557
|
-
const local =
|
|
7628
|
+
const local = path25.join(cwd, ".kody", "goals", "templates", targetId, "state.json");
|
|
7558
7629
|
const localTemplate = readJsonObject(local);
|
|
7559
7630
|
if (localTemplate) return localTemplate;
|
|
7560
7631
|
const storeGoalRoot = getCompanyStoreAssetRoot("goals");
|
|
7561
7632
|
if (!storeGoalRoot) return null;
|
|
7562
|
-
return readJsonObject(
|
|
7633
|
+
return readJsonObject(path25.join(storeGoalRoot, "templates", targetId, "state.json"));
|
|
7563
7634
|
}
|
|
7564
7635
|
function readJsonObject(filePath) {
|
|
7565
|
-
if (!
|
|
7566
|
-
const parsed = JSON.parse(
|
|
7636
|
+
if (!fs27.existsSync(filePath)) return null;
|
|
7637
|
+
const parsed = JSON.parse(fs27.readFileSync(filePath, "utf8"));
|
|
7567
7638
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
7568
7639
|
throw new Error(`goal template ${filePath} must be a JSON object`);
|
|
7569
7640
|
}
|
|
@@ -7959,8 +8030,8 @@ var init_contentsApiBackend = __esm({
|
|
|
7959
8030
|
});
|
|
7960
8031
|
|
|
7961
8032
|
// src/scripts/jobState/localFileBackend.ts
|
|
7962
|
-
import * as
|
|
7963
|
-
import * as
|
|
8033
|
+
import * as fs28 from "fs";
|
|
8034
|
+
import * as path26 from "path";
|
|
7964
8035
|
function sanitizeKey(s) {
|
|
7965
8036
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
7966
8037
|
}
|
|
@@ -8016,7 +8087,7 @@ var init_localFileBackend = __esm({
|
|
|
8016
8087
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
8017
8088
|
this.cwd = opts.cwd;
|
|
8018
8089
|
this.jobsDir = opts.jobsDir;
|
|
8019
|
-
this.absDir =
|
|
8090
|
+
this.absDir = path26.join(opts.cwd, opts.jobsDir);
|
|
8020
8091
|
this.owner = opts.owner;
|
|
8021
8092
|
this.repo = opts.repo;
|
|
8022
8093
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -8031,7 +8102,7 @@ var init_localFileBackend = __esm({
|
|
|
8031
8102
|
`);
|
|
8032
8103
|
return;
|
|
8033
8104
|
}
|
|
8034
|
-
|
|
8105
|
+
fs28.mkdirSync(this.absDir, { recursive: true });
|
|
8035
8106
|
const prefix = this.cacheKeyPrefix();
|
|
8036
8107
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
8037
8108
|
try {
|
|
@@ -8060,7 +8131,7 @@ var init_localFileBackend = __esm({
|
|
|
8060
8131
|
`);
|
|
8061
8132
|
return;
|
|
8062
8133
|
}
|
|
8063
|
-
if (!
|
|
8134
|
+
if (!fs28.existsSync(this.absDir)) {
|
|
8064
8135
|
return;
|
|
8065
8136
|
}
|
|
8066
8137
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -8076,11 +8147,11 @@ var init_localFileBackend = __esm({
|
|
|
8076
8147
|
}
|
|
8077
8148
|
load(slug2) {
|
|
8078
8149
|
const relPath = stateFilePath(this.jobsDir, slug2);
|
|
8079
|
-
const absPath =
|
|
8080
|
-
if (!
|
|
8150
|
+
const absPath = path26.join(this.cwd, relPath);
|
|
8151
|
+
if (!fs28.existsSync(absPath)) {
|
|
8081
8152
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
8082
8153
|
}
|
|
8083
|
-
const raw =
|
|
8154
|
+
const raw = fs28.readFileSync(absPath, "utf-8");
|
|
8084
8155
|
let parsed;
|
|
8085
8156
|
try {
|
|
8086
8157
|
parsed = JSON.parse(raw);
|
|
@@ -8097,13 +8168,13 @@ var init_localFileBackend = __esm({
|
|
|
8097
8168
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
8098
8169
|
return false;
|
|
8099
8170
|
}
|
|
8100
|
-
const absPath =
|
|
8101
|
-
|
|
8171
|
+
const absPath = path26.join(this.cwd, loaded.path);
|
|
8172
|
+
fs28.mkdirSync(path26.dirname(absPath), { recursive: true });
|
|
8102
8173
|
const body = `${JSON.stringify(next, null, 2)}
|
|
8103
8174
|
`;
|
|
8104
8175
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
8105
|
-
|
|
8106
|
-
|
|
8176
|
+
fs28.writeFileSync(tmpPath, body, "utf-8");
|
|
8177
|
+
fs28.renameSync(tmpPath, absPath);
|
|
8107
8178
|
return true;
|
|
8108
8179
|
}
|
|
8109
8180
|
cacheKeyPrefix() {
|
|
@@ -8143,7 +8214,7 @@ var init_jobState = __esm({
|
|
|
8143
8214
|
});
|
|
8144
8215
|
|
|
8145
8216
|
// src/scripts/goalCapabilityScheduling.ts
|
|
8146
|
-
import * as
|
|
8217
|
+
import * as path27 from "path";
|
|
8147
8218
|
function isCapabilityCadenceGoal(goal, extra) {
|
|
8148
8219
|
return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
|
|
8149
8220
|
}
|
|
@@ -8199,7 +8270,7 @@ function planTargetLoopSchedule(opts) {
|
|
|
8199
8270
|
}
|
|
8200
8271
|
async function planGoalCapabilitySchedule(opts) {
|
|
8201
8272
|
const jobsDir = opts.jobsDir ?? ".kody/capabilities";
|
|
8202
|
-
const jobsRoot =
|
|
8273
|
+
const jobsRoot = path27.join(opts.cwd, jobsDir);
|
|
8203
8274
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
8204
8275
|
const at = now.toISOString();
|
|
8205
8276
|
const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
|
|
@@ -8828,12 +8899,12 @@ var init_appendCompanyActivity = __esm({
|
|
|
8828
8899
|
}
|
|
8829
8900
|
});
|
|
8830
8901
|
|
|
8831
|
-
// src/
|
|
8832
|
-
function
|
|
8902
|
+
// src/agencyArchitectDecision.ts
|
|
8903
|
+
function parseAgencyArchitectDecisionText(finalText) {
|
|
8833
8904
|
const raw = extractDecisionJson(finalText);
|
|
8834
8905
|
const parsed = JSON.parse(raw);
|
|
8835
8906
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8836
|
-
throw new Error("
|
|
8907
|
+
throw new Error("agency-architect decision must be JSON object");
|
|
8837
8908
|
}
|
|
8838
8909
|
const input = parsed;
|
|
8839
8910
|
const actions = Array.isArray(input.actions) ? input.actions.map(parseAction) : [];
|
|
@@ -8881,15 +8952,15 @@ function buildAgentLoopState(action) {
|
|
|
8881
8952
|
};
|
|
8882
8953
|
}
|
|
8883
8954
|
function extractDecisionJson(finalText) {
|
|
8884
|
-
const fence = finalText.match(/```(?:kody-
|
|
8955
|
+
const fence = finalText.match(/```(?:kody-agency-architect-decision|json)\s*([\s\S]*?)```/i);
|
|
8885
8956
|
if (fence?.[1]) return fence[1].trim();
|
|
8886
|
-
const line = finalText.match(/
|
|
8957
|
+
const line = finalText.match(/KODY_AGENCY_ARCHITECT_DECISION=(\{[\s\S]*\})/);
|
|
8887
8958
|
if (line?.[1]) return line[1].trim();
|
|
8888
|
-
throw new Error("missing kody-
|
|
8959
|
+
throw new Error("missing kody-agency-architect-decision JSON");
|
|
8889
8960
|
}
|
|
8890
8961
|
function parseAction(value) {
|
|
8891
8962
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
8892
|
-
throw new Error("
|
|
8963
|
+
throw new Error("agency-architect action must be object");
|
|
8893
8964
|
}
|
|
8894
8965
|
const input = value;
|
|
8895
8966
|
const kind = input.kind;
|
|
@@ -8898,7 +8969,7 @@ function parseAction(value) {
|
|
|
8898
8969
|
if (kind === "setGoalLifecycle") return parseSetGoalLifecycle(input);
|
|
8899
8970
|
if (kind === "updateIntentPortfolio") return parseUpdateIntentPortfolio(input);
|
|
8900
8971
|
if (kind === "note") return parseNote(input);
|
|
8901
|
-
throw new Error(`unsupported
|
|
8972
|
+
throw new Error(`unsupported agency-architect action kind: ${String(kind)}`);
|
|
8902
8973
|
}
|
|
8903
8974
|
function parseCreateManagedGoal(input) {
|
|
8904
8975
|
const route = Array.isArray(input.route) ? input.route.map(parseRouteStep) : [];
|
|
@@ -8994,8 +9065,8 @@ function oneOf(value, allowed, fallback) {
|
|
|
8994
9065
|
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
8995
9066
|
}
|
|
8996
9067
|
var SLUG_RE;
|
|
8997
|
-
var
|
|
8998
|
-
"src/
|
|
9068
|
+
var init_agencyArchitectDecision = __esm({
|
|
9069
|
+
"src/agencyArchitectDecision.ts"() {
|
|
8999
9070
|
"use strict";
|
|
9000
9071
|
init_state2();
|
|
9001
9072
|
SLUG_RE = /^[a-z][a-z0-9-]{0,63}$/;
|
|
@@ -9010,20 +9081,22 @@ function companyIntentPath(id) {
|
|
|
9010
9081
|
assertIntentId(id);
|
|
9011
9082
|
return `intents/${id}/intent.json`;
|
|
9012
9083
|
}
|
|
9013
|
-
function normalizeCompanyIntent(
|
|
9084
|
+
function normalizeCompanyIntent(path51, raw) {
|
|
9014
9085
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
9015
|
-
throw new Error(`${
|
|
9086
|
+
throw new Error(`${path51}: intent must be JSON object`);
|
|
9016
9087
|
}
|
|
9017
9088
|
const input = raw;
|
|
9018
9089
|
const id = stringField3(input.id);
|
|
9019
|
-
if (!id || !isCompanyIntentId(id)) throw new Error(`${
|
|
9090
|
+
if (!id || !isCompanyIntentId(id)) throw new Error(`${path51}: invalid intent id`);
|
|
9020
9091
|
const createdAt = stringField3(input.createdAt) || nowIso();
|
|
9021
9092
|
const updatedAt = stringField3(input.updatedAt) || createdAt;
|
|
9093
|
+
const description = stringField3(input.description);
|
|
9022
9094
|
return {
|
|
9023
9095
|
version: 1,
|
|
9024
9096
|
id,
|
|
9025
9097
|
status: oneOf2(input.status, ["active", "paused", "archived"], "active"),
|
|
9026
9098
|
for: stringField3(input.for),
|
|
9099
|
+
...description ? { description } : {},
|
|
9027
9100
|
priority: numberField(input.priority, 100),
|
|
9028
9101
|
posture: oneOf2(
|
|
9029
9102
|
input.posture,
|
|
@@ -9031,21 +9104,21 @@ function normalizeCompanyIntent(path50, raw) {
|
|
|
9031
9104
|
"balanced"
|
|
9032
9105
|
),
|
|
9033
9106
|
scope: {
|
|
9034
|
-
repos: stringArray4(
|
|
9035
|
-
areas: stringArray4(
|
|
9107
|
+
repos: stringArray4(recordField3(input.scope)?.repos),
|
|
9108
|
+
areas: stringArray4(recordField3(input.scope)?.areas)
|
|
9036
9109
|
},
|
|
9037
9110
|
principles: stringArray4(input.principles),
|
|
9038
9111
|
metrics: stringArray4(input.metrics),
|
|
9039
9112
|
policy: {
|
|
9040
|
-
release: normalizeReleasePolicy(
|
|
9041
|
-
automation: normalizeAutomationPolicy(
|
|
9113
|
+
release: normalizeReleasePolicy(recordField3(recordField3(input.policy)?.release)),
|
|
9114
|
+
automation: normalizeAutomationPolicy(recordField3(recordField3(input.policy)?.automation))
|
|
9042
9115
|
},
|
|
9043
9116
|
portfolio: {
|
|
9044
|
-
goals: stringArray4(
|
|
9045
|
-
loops: stringArray4(
|
|
9046
|
-
capabilities: stringArray4(
|
|
9117
|
+
goals: stringArray4(recordField3(input.portfolio)?.goals).filter(isCompanyIntentId),
|
|
9118
|
+
loops: stringArray4(recordField3(input.portfolio)?.loops).filter(isCompanyIntentId),
|
|
9119
|
+
capabilities: stringArray4(recordField3(input.portfolio)?.capabilities).filter(isCompanyIntentId)
|
|
9047
9120
|
},
|
|
9048
|
-
manager: normalizeManager(
|
|
9121
|
+
manager: normalizeManager(recordField3(input.manager)),
|
|
9049
9122
|
createdAt,
|
|
9050
9123
|
updatedAt
|
|
9051
9124
|
};
|
|
@@ -9055,8 +9128,8 @@ function listCompanyIntents(config, cwd) {
|
|
|
9055
9128
|
const records = [];
|
|
9056
9129
|
for (const entry of entries) {
|
|
9057
9130
|
if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
|
|
9058
|
-
const
|
|
9059
|
-
const file = readStateText(config, cwd,
|
|
9131
|
+
const path51 = companyIntentPath(entry.name);
|
|
9132
|
+
const file = readStateText(config, cwd, path51);
|
|
9060
9133
|
if (!file) continue;
|
|
9061
9134
|
records.push({
|
|
9062
9135
|
id: entry.name,
|
|
@@ -9067,8 +9140,8 @@ function listCompanyIntents(config, cwd) {
|
|
|
9067
9140
|
return records.sort((a, b) => a.intent.priority - b.intent.priority || a.id.localeCompare(b.id));
|
|
9068
9141
|
}
|
|
9069
9142
|
function readCompanyIntent(config, cwd, id) {
|
|
9070
|
-
const
|
|
9071
|
-
const file = readStateText(config, cwd,
|
|
9143
|
+
const path51 = companyIntentPath(id);
|
|
9144
|
+
const file = readStateText(config, cwd, path51);
|
|
9072
9145
|
if (!file) return null;
|
|
9073
9146
|
return { id, path: file.path, intent: normalizeCompanyIntent(file.path, JSON.parse(file.content)) };
|
|
9074
9147
|
}
|
|
@@ -9086,7 +9159,7 @@ function listCompanyPortfolio(config, cwd) {
|
|
|
9086
9159
|
if (!isCompanyIntentId(id)) continue;
|
|
9087
9160
|
const state = fetchGoalState(config, id, cwd);
|
|
9088
9161
|
if (!state) continue;
|
|
9089
|
-
const destination =
|
|
9162
|
+
const destination = recordField3(state.extra.destination);
|
|
9090
9163
|
goals.push({
|
|
9091
9164
|
id,
|
|
9092
9165
|
state: state.state,
|
|
@@ -9112,7 +9185,7 @@ function stringField3(value) {
|
|
|
9112
9185
|
function numberField(value, fallback) {
|
|
9113
9186
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
9114
9187
|
}
|
|
9115
|
-
function
|
|
9188
|
+
function recordField3(value) {
|
|
9116
9189
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
9117
9190
|
}
|
|
9118
9191
|
function stringArray4(value) {
|
|
@@ -9142,8 +9215,8 @@ function normalizeAutomationPolicy(raw) {
|
|
|
9142
9215
|
function normalizeManager(raw) {
|
|
9143
9216
|
return {
|
|
9144
9217
|
agent: "cto",
|
|
9145
|
-
loop: "
|
|
9146
|
-
capability: "
|
|
9218
|
+
loop: "agency-architect-loop",
|
|
9219
|
+
capability: "agency-architect",
|
|
9147
9220
|
reviewEvery: oneOf2(raw?.reviewEvery, ["1d", "1w"], "1d"),
|
|
9148
9221
|
...typeof raw?.lastReviewedAt === "string" ? { lastReviewedAt: raw.lastReviewedAt } : {}
|
|
9149
9222
|
};
|
|
@@ -9159,7 +9232,7 @@ var init_companyIntent = __esm({
|
|
|
9159
9232
|
}
|
|
9160
9233
|
});
|
|
9161
9234
|
|
|
9162
|
-
// src/scripts/
|
|
9235
|
+
// src/scripts/applyAgencyArchitectDecision.ts
|
|
9163
9236
|
function applyAction(config, cwd, action) {
|
|
9164
9237
|
if (action.kind === "createManagedGoal") {
|
|
9165
9238
|
const existing = fetchGoalState(config, action.id, cwd);
|
|
@@ -9228,7 +9301,7 @@ function applied(action, changed, reason, resource) {
|
|
|
9228
9301
|
function mergeUnique(left, right) {
|
|
9229
9302
|
return [.../* @__PURE__ */ new Set([...left, ...right])].sort();
|
|
9230
9303
|
}
|
|
9231
|
-
function
|
|
9304
|
+
function logAppliedAgencyArchitectActions(config, cwd, appliedActions) {
|
|
9232
9305
|
const at = nowIso();
|
|
9233
9306
|
for (const action of appliedActions) {
|
|
9234
9307
|
if (!action.intentId) continue;
|
|
@@ -9243,24 +9316,24 @@ function logAppliedCompanyManagerActions(config, cwd, appliedActions) {
|
|
|
9243
9316
|
});
|
|
9244
9317
|
}
|
|
9245
9318
|
}
|
|
9246
|
-
var
|
|
9247
|
-
var
|
|
9248
|
-
"src/scripts/
|
|
9319
|
+
var applyAgencyArchitectDecision;
|
|
9320
|
+
var init_applyAgencyArchitectDecision = __esm({
|
|
9321
|
+
"src/scripts/applyAgencyArchitectDecision.ts"() {
|
|
9249
9322
|
"use strict";
|
|
9250
|
-
|
|
9323
|
+
init_agencyArchitectDecision();
|
|
9251
9324
|
init_companyIntent();
|
|
9252
9325
|
init_stateStore();
|
|
9253
9326
|
init_state2();
|
|
9254
|
-
|
|
9255
|
-
const decision = ctx.data.
|
|
9327
|
+
applyAgencyArchitectDecision = async (ctx) => {
|
|
9328
|
+
const decision = ctx.data.agencyArchitectDecision;
|
|
9256
9329
|
if (!decision || !Array.isArray(decision.actions)) return;
|
|
9257
9330
|
if (ctx.output.exitCode !== 0) return;
|
|
9258
9331
|
const applied2 = [];
|
|
9259
9332
|
for (const action of decision.actions) {
|
|
9260
9333
|
applied2.push(applyAction(ctx.config, ctx.cwd, action));
|
|
9261
9334
|
}
|
|
9262
|
-
ctx.data.
|
|
9263
|
-
ctx.data.
|
|
9335
|
+
ctx.data.agencyArchitectApplied = applied2;
|
|
9336
|
+
ctx.data.agencyArchitectApplySummary = `agency-architect applied ${applied2.filter((item) => item.changed).length}/${applied2.length} action(s)`;
|
|
9264
9337
|
};
|
|
9265
9338
|
}
|
|
9266
9339
|
});
|
|
@@ -9270,15 +9343,15 @@ var appendCompanyIntentDecision2;
|
|
|
9270
9343
|
var init_appendCompanyIntentDecision = __esm({
|
|
9271
9344
|
"src/scripts/appendCompanyIntentDecision.ts"() {
|
|
9272
9345
|
"use strict";
|
|
9273
|
-
|
|
9346
|
+
init_applyAgencyArchitectDecision();
|
|
9274
9347
|
appendCompanyIntentDecision2 = async (ctx) => {
|
|
9275
|
-
const applied2 = ctx.data.
|
|
9348
|
+
const applied2 = ctx.data.agencyArchitectApplied;
|
|
9276
9349
|
if (!applied2 || applied2.length === 0) return;
|
|
9277
9350
|
try {
|
|
9278
|
-
|
|
9351
|
+
logAppliedAgencyArchitectActions(ctx.config, ctx.cwd, applied2);
|
|
9279
9352
|
} catch (err) {
|
|
9280
9353
|
process.stderr.write(
|
|
9281
|
-
`[
|
|
9354
|
+
`[agency-architect] failed append intent decision log: ${err instanceof Error ? err.message : String(err)}
|
|
9282
9355
|
`
|
|
9283
9356
|
);
|
|
9284
9357
|
}
|
|
@@ -9500,7 +9573,7 @@ function capabilityEvidenceOutput(evidence) {
|
|
|
9500
9573
|
function goalReportBody(goalId, state, snapshot, latestEvent, evidenceItems) {
|
|
9501
9574
|
const outputs = evidenceItems.map(capabilityEvidenceOutput);
|
|
9502
9575
|
const latestOutput = outputs.at(-1);
|
|
9503
|
-
const facts =
|
|
9576
|
+
const facts = recordField4(snapshot, "facts") ?? recordField4(state.extra, "facts") ?? {};
|
|
9504
9577
|
const blockers = uniqueStrings2([
|
|
9505
9578
|
...stringArrayField(snapshot, "blockers"),
|
|
9506
9579
|
...stringArrayField(latestEvent, "blockers"),
|
|
@@ -9552,7 +9625,7 @@ function capabilityEvidenceMarkdown(outputs) {
|
|
|
9552
9625
|
function decisionReason(state, latestEvent, latestOutput, missingEvidence, blockers) {
|
|
9553
9626
|
if (state.state === "done") return "destination evidence satisfied";
|
|
9554
9627
|
if (blockers.length > 0) return blockers[0] ?? "blocked";
|
|
9555
|
-
const eventReason = stringField4(latestEvent, "reason") ?? stringField4(
|
|
9628
|
+
const eventReason = stringField4(latestEvent, "reason") ?? stringField4(recordField4(latestEvent, "decision"), "reason");
|
|
9556
9629
|
if (eventReason) return eventReason;
|
|
9557
9630
|
const summary = stringField4(latestOutput, "summary");
|
|
9558
9631
|
if (summary) return summary;
|
|
@@ -9565,18 +9638,18 @@ function evidenceOutputMarkdown(index, output) {
|
|
|
9565
9638
|
`- Status: ${stringField4(output, "status") ?? "unknown"}`,
|
|
9566
9639
|
`- Summary: ${stringField4(output, "summary") ?? "no summary"}`,
|
|
9567
9640
|
`- Sources: ${listOrNone(stringArrayField(output, "sources"))}`,
|
|
9568
|
-
`- Evidence values: ${inlineJson(
|
|
9641
|
+
`- Evidence values: ${inlineJson(recordField4(output, "evidence") ?? {})}`,
|
|
9569
9642
|
`- Missing evidence: ${listOrNone(stringArrayField(output, "missingEvidence"))}`,
|
|
9570
9643
|
`- Blockers: ${listOrNone(stringArrayField(output, "blockers"))}`,
|
|
9571
9644
|
""
|
|
9572
9645
|
];
|
|
9573
9646
|
}
|
|
9574
9647
|
function dispatchContextMarkdown(latestEvent) {
|
|
9575
|
-
const context =
|
|
9648
|
+
const context = recordField4(latestEvent, "dispatchContext");
|
|
9576
9649
|
if (!context) return ["- none"];
|
|
9577
9650
|
const githubActor = stringField4(context, "githubActor");
|
|
9578
9651
|
const githubActorRole = stringField4(context, "githubActorRole");
|
|
9579
|
-
const target = dispatchTargetLabel(
|
|
9652
|
+
const target = dispatchTargetLabel(recordField4(context, "target"));
|
|
9580
9653
|
return [
|
|
9581
9654
|
`- Triggered by: ${stringField4(context, "triggeredBy") ?? "unknown"}`,
|
|
9582
9655
|
`- Mode: ${stringField4(context, "dispatchMode") ?? "unknown"}`,
|
|
@@ -9615,7 +9688,7 @@ function stringField4(record2, key) {
|
|
|
9615
9688
|
const value = record2?.[key];
|
|
9616
9689
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
9617
9690
|
}
|
|
9618
|
-
function
|
|
9691
|
+
function recordField4(record2, key) {
|
|
9619
9692
|
const value = record2?.[key];
|
|
9620
9693
|
return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : void 0;
|
|
9621
9694
|
}
|
|
@@ -9648,7 +9721,7 @@ ${artifact.path ?? ""}`;
|
|
|
9648
9721
|
}
|
|
9649
9722
|
function nextStepFromEvent(state, goalAfter, capabilityOutput, latestEvent) {
|
|
9650
9723
|
if (state.state === "done") return "done";
|
|
9651
|
-
const decisionKind = stringField4(
|
|
9724
|
+
const decisionKind = stringField4(recordField4(latestEvent, "decision"), "kind") ?? stringField4(latestEvent, "status");
|
|
9652
9725
|
if (decisionKind === "done") return "done";
|
|
9653
9726
|
if (decisionKind === "dispatch") return "dispatch";
|
|
9654
9727
|
if (decisionKind === "blocked" || decisionKind === "reject-evidence") return "block";
|
|
@@ -10111,8 +10184,8 @@ var init_classifyByLabel = __esm({
|
|
|
10111
10184
|
});
|
|
10112
10185
|
|
|
10113
10186
|
// src/scripts/commitAndPush.ts
|
|
10114
|
-
import * as
|
|
10115
|
-
import * as
|
|
10187
|
+
import * as fs29 from "fs";
|
|
10188
|
+
import * as path28 from "path";
|
|
10116
10189
|
function sentinelPathForStage(cwd, profileName) {
|
|
10117
10190
|
const runId = resolveRunId();
|
|
10118
10191
|
return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
|
|
@@ -10133,9 +10206,9 @@ var init_commitAndPush = __esm({
|
|
|
10133
10206
|
}
|
|
10134
10207
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
10135
10208
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
10136
|
-
if (sentinel &&
|
|
10209
|
+
if (sentinel && fs29.existsSync(sentinel)) {
|
|
10137
10210
|
try {
|
|
10138
|
-
const replay = JSON.parse(
|
|
10211
|
+
const replay = JSON.parse(fs29.readFileSync(sentinel, "utf-8"));
|
|
10139
10212
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
10140
10213
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
10141
10214
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -10188,8 +10261,8 @@ var init_commitAndPush = __esm({
|
|
|
10188
10261
|
const result = ctx.data.commitResult;
|
|
10189
10262
|
if (sentinel && result?.committed) {
|
|
10190
10263
|
try {
|
|
10191
|
-
|
|
10192
|
-
|
|
10264
|
+
fs29.mkdirSync(path28.dirname(sentinel), { recursive: true });
|
|
10265
|
+
fs29.writeFileSync(
|
|
10193
10266
|
sentinel,
|
|
10194
10267
|
JSON.stringify(
|
|
10195
10268
|
{
|
|
@@ -10280,8 +10353,8 @@ var init_commitGoalState = __esm({
|
|
|
10280
10353
|
});
|
|
10281
10354
|
|
|
10282
10355
|
// src/scripts/composePrompt.ts
|
|
10283
|
-
import * as
|
|
10284
|
-
import * as
|
|
10356
|
+
import * as fs30 from "fs";
|
|
10357
|
+
import * as path29 from "path";
|
|
10285
10358
|
function fenceUntrusted(value) {
|
|
10286
10359
|
if (value.trim().length === 0) return value;
|
|
10287
10360
|
const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
@@ -10404,10 +10477,10 @@ var init_composePrompt = __esm({
|
|
|
10404
10477
|
const explicit = ctx.data.promptTemplate;
|
|
10405
10478
|
const mode = ctx.args.mode;
|
|
10406
10479
|
const candidates = [
|
|
10407
|
-
explicit ?
|
|
10408
|
-
mode ?
|
|
10409
|
-
|
|
10410
|
-
|
|
10480
|
+
explicit ? path29.join(profile.dir, explicit) : null,
|
|
10481
|
+
mode ? path29.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
10482
|
+
path29.join(profile.dir, "prompt.md"),
|
|
10483
|
+
path29.join(profile.dir, "capability.md")
|
|
10411
10484
|
].filter(Boolean);
|
|
10412
10485
|
let templatePath = "";
|
|
10413
10486
|
let template = "";
|
|
@@ -10420,7 +10493,7 @@ var init_composePrompt = __esm({
|
|
|
10420
10493
|
break;
|
|
10421
10494
|
}
|
|
10422
10495
|
try {
|
|
10423
|
-
template =
|
|
10496
|
+
template = fs30.readFileSync(c, "utf-8");
|
|
10424
10497
|
templatePath = c;
|
|
10425
10498
|
break;
|
|
10426
10499
|
} catch (err) {
|
|
@@ -10431,7 +10504,7 @@ var init_composePrompt = __esm({
|
|
|
10431
10504
|
if (!templatePath) {
|
|
10432
10505
|
let dirState;
|
|
10433
10506
|
try {
|
|
10434
|
-
dirState = `dir contents: [${
|
|
10507
|
+
dirState = `dir contents: [${fs30.readdirSync(profile.dir).join(", ")}]`;
|
|
10435
10508
|
} catch (err) {
|
|
10436
10509
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
10437
10510
|
}
|
|
@@ -11116,19 +11189,19 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
11116
11189
|
|
|
11117
11190
|
// src/scripts/diagMcp.ts
|
|
11118
11191
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
11119
|
-
import * as
|
|
11192
|
+
import * as fs31 from "fs";
|
|
11120
11193
|
import * as os6 from "os";
|
|
11121
|
-
import * as
|
|
11194
|
+
import * as path30 from "path";
|
|
11122
11195
|
var diagMcp;
|
|
11123
11196
|
var init_diagMcp = __esm({
|
|
11124
11197
|
"src/scripts/diagMcp.ts"() {
|
|
11125
11198
|
"use strict";
|
|
11126
11199
|
diagMcp = async (_ctx) => {
|
|
11127
11200
|
const home = os6.homedir();
|
|
11128
|
-
const cacheDir =
|
|
11201
|
+
const cacheDir = path30.join(home, ".cache", "ms-playwright");
|
|
11129
11202
|
let entries = [];
|
|
11130
11203
|
try {
|
|
11131
|
-
entries =
|
|
11204
|
+
entries = fs31.readdirSync(cacheDir);
|
|
11132
11205
|
} catch {
|
|
11133
11206
|
}
|
|
11134
11207
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -11156,13 +11229,13 @@ var init_diagMcp = __esm({
|
|
|
11156
11229
|
});
|
|
11157
11230
|
|
|
11158
11231
|
// src/scripts/frameworkDetectors.ts
|
|
11159
|
-
import * as
|
|
11160
|
-
import * as
|
|
11232
|
+
import * as fs32 from "fs";
|
|
11233
|
+
import * as path31 from "path";
|
|
11161
11234
|
function detectFrameworks(cwd) {
|
|
11162
11235
|
const out = [];
|
|
11163
11236
|
let deps = {};
|
|
11164
11237
|
try {
|
|
11165
|
-
const pkg = JSON.parse(
|
|
11238
|
+
const pkg = JSON.parse(fs32.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
|
|
11166
11239
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11167
11240
|
} catch {
|
|
11168
11241
|
return out;
|
|
@@ -11199,25 +11272,25 @@ function detectFrameworks(cwd) {
|
|
|
11199
11272
|
}
|
|
11200
11273
|
function findFile(cwd, candidates) {
|
|
11201
11274
|
for (const c of candidates) {
|
|
11202
|
-
if (
|
|
11275
|
+
if (fs32.existsSync(path31.join(cwd, c))) return c;
|
|
11203
11276
|
}
|
|
11204
11277
|
return null;
|
|
11205
11278
|
}
|
|
11206
11279
|
function discoverPayloadCollections(cwd) {
|
|
11207
11280
|
const out = [];
|
|
11208
11281
|
for (const dir of COLLECTION_DIRS) {
|
|
11209
|
-
const full =
|
|
11210
|
-
if (!
|
|
11282
|
+
const full = path31.join(cwd, dir);
|
|
11283
|
+
if (!fs32.existsSync(full)) continue;
|
|
11211
11284
|
let files;
|
|
11212
11285
|
try {
|
|
11213
|
-
files =
|
|
11286
|
+
files = fs32.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
11214
11287
|
} catch {
|
|
11215
11288
|
continue;
|
|
11216
11289
|
}
|
|
11217
11290
|
for (const file of files) {
|
|
11218
11291
|
try {
|
|
11219
|
-
const filePath =
|
|
11220
|
-
const content =
|
|
11292
|
+
const filePath = path31.join(full, file);
|
|
11293
|
+
const content = fs32.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
11221
11294
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
11222
11295
|
if (!slugMatch) continue;
|
|
11223
11296
|
const slug2 = slugMatch[1];
|
|
@@ -11231,7 +11304,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
11231
11304
|
out.push({
|
|
11232
11305
|
name,
|
|
11233
11306
|
slug: slug2,
|
|
11234
|
-
filePath:
|
|
11307
|
+
filePath: path31.relative(cwd, filePath),
|
|
11235
11308
|
fields: fields.slice(0, 20),
|
|
11236
11309
|
hasAdmin
|
|
11237
11310
|
});
|
|
@@ -11244,28 +11317,28 @@ function discoverPayloadCollections(cwd) {
|
|
|
11244
11317
|
function discoverAdminComponents(cwd, collections) {
|
|
11245
11318
|
const out = [];
|
|
11246
11319
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
11247
|
-
const full =
|
|
11248
|
-
if (!
|
|
11320
|
+
const full = path31.join(cwd, dir);
|
|
11321
|
+
if (!fs32.existsSync(full)) continue;
|
|
11249
11322
|
let entries;
|
|
11250
11323
|
try {
|
|
11251
|
-
entries =
|
|
11324
|
+
entries = fs32.readdirSync(full, { withFileTypes: true });
|
|
11252
11325
|
} catch {
|
|
11253
11326
|
continue;
|
|
11254
11327
|
}
|
|
11255
11328
|
for (const entry of entries) {
|
|
11256
|
-
const entryPath =
|
|
11329
|
+
const entryPath = path31.join(full, entry.name);
|
|
11257
11330
|
let name;
|
|
11258
11331
|
let filePath;
|
|
11259
11332
|
if (entry.isDirectory()) {
|
|
11260
11333
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
11261
|
-
(f) =>
|
|
11334
|
+
(f) => fs32.existsSync(path31.join(entryPath, f))
|
|
11262
11335
|
);
|
|
11263
11336
|
if (!indexFile) continue;
|
|
11264
11337
|
name = entry.name;
|
|
11265
|
-
filePath =
|
|
11338
|
+
filePath = path31.relative(cwd, path31.join(entryPath, indexFile));
|
|
11266
11339
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
11267
11340
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
11268
|
-
filePath =
|
|
11341
|
+
filePath = path31.relative(cwd, entryPath);
|
|
11269
11342
|
} else {
|
|
11270
11343
|
continue;
|
|
11271
11344
|
}
|
|
@@ -11273,7 +11346,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
11273
11346
|
if (collections) {
|
|
11274
11347
|
for (const col of collections) {
|
|
11275
11348
|
try {
|
|
11276
|
-
const colContent =
|
|
11349
|
+
const colContent = fs32.readFileSync(path31.join(cwd, col.filePath), "utf-8");
|
|
11277
11350
|
if (colContent.includes(name)) {
|
|
11278
11351
|
usedInCollection = col.slug;
|
|
11279
11352
|
break;
|
|
@@ -11291,8 +11364,8 @@ function scanApiRoutes(cwd) {
|
|
|
11291
11364
|
const out = [];
|
|
11292
11365
|
const appDirs = ["src/app", "app"];
|
|
11293
11366
|
for (const appDir of appDirs) {
|
|
11294
|
-
const apiDir =
|
|
11295
|
-
if (!
|
|
11367
|
+
const apiDir = path31.join(cwd, appDir, "api");
|
|
11368
|
+
if (!fs32.existsSync(apiDir)) continue;
|
|
11296
11369
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
11297
11370
|
break;
|
|
11298
11371
|
}
|
|
@@ -11301,14 +11374,14 @@ function scanApiRoutes(cwd) {
|
|
|
11301
11374
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
11302
11375
|
let entries;
|
|
11303
11376
|
try {
|
|
11304
|
-
entries =
|
|
11377
|
+
entries = fs32.readdirSync(dir, { withFileTypes: true });
|
|
11305
11378
|
} catch {
|
|
11306
11379
|
return;
|
|
11307
11380
|
}
|
|
11308
11381
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
11309
11382
|
if (routeFile) {
|
|
11310
11383
|
try {
|
|
11311
|
-
const content =
|
|
11384
|
+
const content = fs32.readFileSync(path31.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
11312
11385
|
const methods = HTTP_METHODS.filter(
|
|
11313
11386
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
11314
11387
|
);
|
|
@@ -11316,7 +11389,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
11316
11389
|
out.push({
|
|
11317
11390
|
path: prefix,
|
|
11318
11391
|
methods,
|
|
11319
|
-
filePath:
|
|
11392
|
+
filePath: path31.relative(cwd, path31.join(dir, routeFile.name))
|
|
11320
11393
|
});
|
|
11321
11394
|
}
|
|
11322
11395
|
} catch {
|
|
@@ -11327,7 +11400,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
11327
11400
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
11328
11401
|
let segment = entry.name;
|
|
11329
11402
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
11330
|
-
walkApiRoutes(
|
|
11403
|
+
walkApiRoutes(path31.join(dir, entry.name), prefix, cwd, out);
|
|
11331
11404
|
continue;
|
|
11332
11405
|
}
|
|
11333
11406
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -11335,16 +11408,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
11335
11408
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
11336
11409
|
segment = `:${segment.slice(1, -1)}`;
|
|
11337
11410
|
}
|
|
11338
|
-
walkApiRoutes(
|
|
11411
|
+
walkApiRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
11339
11412
|
}
|
|
11340
11413
|
}
|
|
11341
11414
|
function scanEnvVars(cwd) {
|
|
11342
11415
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
11343
11416
|
for (const envFile of candidates) {
|
|
11344
|
-
const envPath =
|
|
11345
|
-
if (!
|
|
11417
|
+
const envPath = path31.join(cwd, envFile);
|
|
11418
|
+
if (!fs32.existsSync(envPath)) continue;
|
|
11346
11419
|
try {
|
|
11347
|
-
const content =
|
|
11420
|
+
const content = fs32.readFileSync(envPath, "utf-8");
|
|
11348
11421
|
const vars = [];
|
|
11349
11422
|
for (const line of content.split("\n")) {
|
|
11350
11423
|
const trimmed = line.trim();
|
|
@@ -11389,8 +11462,8 @@ var init_frameworkDetectors = __esm({
|
|
|
11389
11462
|
});
|
|
11390
11463
|
|
|
11391
11464
|
// src/scripts/discoverQaContext.ts
|
|
11392
|
-
import * as
|
|
11393
|
-
import * as
|
|
11465
|
+
import * as fs33 from "fs";
|
|
11466
|
+
import * as path32 from "path";
|
|
11394
11467
|
function runQaDiscovery(cwd) {
|
|
11395
11468
|
const out = {
|
|
11396
11469
|
routes: [],
|
|
@@ -11421,9 +11494,9 @@ function runQaDiscovery(cwd) {
|
|
|
11421
11494
|
}
|
|
11422
11495
|
function detectDevServer(cwd, out) {
|
|
11423
11496
|
try {
|
|
11424
|
-
const pkg = JSON.parse(
|
|
11497
|
+
const pkg = JSON.parse(fs33.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
|
|
11425
11498
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11426
|
-
const pm =
|
|
11499
|
+
const pm = fs33.existsSync(path32.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs33.existsSync(path32.join(cwd, "yarn.lock")) ? "yarn" : fs33.existsSync(path32.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
11427
11500
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
11428
11501
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
11429
11502
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -11433,8 +11506,8 @@ function detectDevServer(cwd, out) {
|
|
|
11433
11506
|
function scanFrontendRoutes(cwd, out) {
|
|
11434
11507
|
const appDirs = ["src/app", "app"];
|
|
11435
11508
|
for (const appDir of appDirs) {
|
|
11436
|
-
const full =
|
|
11437
|
-
if (!
|
|
11509
|
+
const full = path32.join(cwd, appDir);
|
|
11510
|
+
if (!fs33.existsSync(full)) continue;
|
|
11438
11511
|
walkFrontendRoutes(full, "", out);
|
|
11439
11512
|
break;
|
|
11440
11513
|
}
|
|
@@ -11442,7 +11515,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
11442
11515
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
11443
11516
|
let entries;
|
|
11444
11517
|
try {
|
|
11445
|
-
entries =
|
|
11518
|
+
entries = fs33.readdirSync(dir, { withFileTypes: true });
|
|
11446
11519
|
} catch {
|
|
11447
11520
|
return;
|
|
11448
11521
|
}
|
|
@@ -11459,7 +11532,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
11459
11532
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
11460
11533
|
let segment = entry.name;
|
|
11461
11534
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
11462
|
-
walkFrontendRoutes(
|
|
11535
|
+
walkFrontendRoutes(path32.join(dir, entry.name), prefix, out);
|
|
11463
11536
|
continue;
|
|
11464
11537
|
}
|
|
11465
11538
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -11467,7 +11540,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
11467
11540
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
11468
11541
|
segment = `:${segment.slice(1, -1)}`;
|
|
11469
11542
|
}
|
|
11470
|
-
walkFrontendRoutes(
|
|
11543
|
+
walkFrontendRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
11471
11544
|
}
|
|
11472
11545
|
}
|
|
11473
11546
|
function detectAuthFiles(cwd, out) {
|
|
@@ -11484,23 +11557,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
11484
11557
|
"src/app/api/oauth"
|
|
11485
11558
|
];
|
|
11486
11559
|
for (const c of candidates) {
|
|
11487
|
-
if (
|
|
11560
|
+
if (fs33.existsSync(path32.join(cwd, c))) out.authFiles.push(c);
|
|
11488
11561
|
}
|
|
11489
11562
|
}
|
|
11490
11563
|
function detectRoles(cwd, out) {
|
|
11491
11564
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
11492
11565
|
for (const rp of rolePaths) {
|
|
11493
|
-
const dir =
|
|
11494
|
-
if (!
|
|
11566
|
+
const dir = path32.join(cwd, rp);
|
|
11567
|
+
if (!fs33.existsSync(dir)) continue;
|
|
11495
11568
|
let files;
|
|
11496
11569
|
try {
|
|
11497
|
-
files =
|
|
11570
|
+
files = fs33.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
11498
11571
|
} catch {
|
|
11499
11572
|
continue;
|
|
11500
11573
|
}
|
|
11501
11574
|
for (const f of files) {
|
|
11502
11575
|
try {
|
|
11503
|
-
const content =
|
|
11576
|
+
const content = fs33.readFileSync(path32.join(dir, f), "utf-8").slice(0, 5e3);
|
|
11504
11577
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
11505
11578
|
if (roleMatches) {
|
|
11506
11579
|
for (const m of roleMatches) {
|
|
@@ -12740,12 +12813,12 @@ var init_fixFlow = __esm({
|
|
|
12740
12813
|
|
|
12741
12814
|
// src/scripts/initFlow.ts
|
|
12742
12815
|
import { execFileSync as execFileSync15 } from "child_process";
|
|
12743
|
-
import * as
|
|
12744
|
-
import * as
|
|
12816
|
+
import * as fs34 from "fs";
|
|
12817
|
+
import * as path33 from "path";
|
|
12745
12818
|
function detectPackageManager(cwd) {
|
|
12746
|
-
if (
|
|
12747
|
-
if (
|
|
12748
|
-
if (
|
|
12819
|
+
if (fs34.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
12820
|
+
if (fs34.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
|
|
12821
|
+
if (fs34.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
|
|
12749
12822
|
return "npm";
|
|
12750
12823
|
}
|
|
12751
12824
|
function qualityCommandsFor(pm) {
|
|
@@ -12817,22 +12890,22 @@ function performInit(cwd, force) {
|
|
|
12817
12890
|
const pm = detectPackageManager(cwd);
|
|
12818
12891
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
12819
12892
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
12820
|
-
const configPath =
|
|
12821
|
-
if (
|
|
12893
|
+
const configPath = path33.join(cwd, "kody.config.json");
|
|
12894
|
+
if (fs34.existsSync(configPath) && !force) {
|
|
12822
12895
|
skipped.push("kody.config.json");
|
|
12823
12896
|
} else {
|
|
12824
12897
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
12825
|
-
|
|
12898
|
+
fs34.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
12826
12899
|
`);
|
|
12827
12900
|
wrote.push("kody.config.json");
|
|
12828
12901
|
}
|
|
12829
|
-
const workflowDir =
|
|
12830
|
-
const workflowPath =
|
|
12831
|
-
if (
|
|
12902
|
+
const workflowDir = path33.join(cwd, ".github", "workflows");
|
|
12903
|
+
const workflowPath = path33.join(workflowDir, "kody.yml");
|
|
12904
|
+
if (fs34.existsSync(workflowPath) && !force) {
|
|
12832
12905
|
skipped.push(".github/workflows/kody.yml");
|
|
12833
12906
|
} else {
|
|
12834
|
-
|
|
12835
|
-
|
|
12907
|
+
fs34.mkdirSync(workflowDir, { recursive: true });
|
|
12908
|
+
fs34.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
12836
12909
|
wrote.push(".github/workflows/kody.yml");
|
|
12837
12910
|
}
|
|
12838
12911
|
for (const exe of listExecutables()) {
|
|
@@ -12843,12 +12916,12 @@ function performInit(cwd, force) {
|
|
|
12843
12916
|
continue;
|
|
12844
12917
|
}
|
|
12845
12918
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
12846
|
-
const target =
|
|
12847
|
-
if (
|
|
12919
|
+
const target = path33.join(workflowDir, `kody-${exe.name}.yml`);
|
|
12920
|
+
if (fs34.existsSync(target) && !force) {
|
|
12848
12921
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
12849
12922
|
continue;
|
|
12850
12923
|
}
|
|
12851
|
-
|
|
12924
|
+
fs34.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
12852
12925
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
12853
12926
|
}
|
|
12854
12927
|
let labels;
|
|
@@ -12996,7 +13069,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
|
|
|
12996
13069
|
});
|
|
12997
13070
|
|
|
12998
13071
|
// src/scripts/loadAgentAdhoc.ts
|
|
12999
|
-
import * as
|
|
13072
|
+
import * as fs35 from "fs";
|
|
13000
13073
|
function resolveMessage(messageArg) {
|
|
13001
13074
|
const fromComment = readCommentBody();
|
|
13002
13075
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -13004,9 +13077,9 @@ function resolveMessage(messageArg) {
|
|
|
13004
13077
|
}
|
|
13005
13078
|
function readCommentBody() {
|
|
13006
13079
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
13007
|
-
if (!eventPath || !
|
|
13080
|
+
if (!eventPath || !fs35.existsSync(eventPath)) return "";
|
|
13008
13081
|
try {
|
|
13009
|
-
const event = JSON.parse(
|
|
13082
|
+
const event = JSON.parse(fs35.readFileSync(eventPath, "utf-8"));
|
|
13010
13083
|
return String(event.comment?.body ?? "");
|
|
13011
13084
|
} catch {
|
|
13012
13085
|
return "";
|
|
@@ -13059,10 +13132,10 @@ var init_loadAgentAdhoc = __esm({
|
|
|
13059
13132
|
throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
|
|
13060
13133
|
}
|
|
13061
13134
|
const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
|
|
13062
|
-
if (!
|
|
13135
|
+
if (!fs35.existsSync(agentPath)) {
|
|
13063
13136
|
throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
|
|
13064
13137
|
}
|
|
13065
|
-
const { title, body } = parseAgentFile(
|
|
13138
|
+
const { title, body } = parseAgentFile(fs35.readFileSync(agentPath, "utf-8"), agentSlug);
|
|
13066
13139
|
const message = resolveMessage(ctx.args.message);
|
|
13067
13140
|
if (!message) {
|
|
13068
13141
|
throw new Error(
|
|
@@ -13306,8 +13379,8 @@ var init_loadIssueStateComment = __esm({
|
|
|
13306
13379
|
});
|
|
13307
13380
|
|
|
13308
13381
|
// src/scripts/loadJobFromFile.ts
|
|
13309
|
-
import * as
|
|
13310
|
-
import * as
|
|
13382
|
+
import * as fs36 from "fs";
|
|
13383
|
+
import * as path34 from "path";
|
|
13311
13384
|
function parseJobFile(raw, slug2) {
|
|
13312
13385
|
let stripped = raw;
|
|
13313
13386
|
if (stripped.startsWith("---\n")) {
|
|
@@ -13345,9 +13418,9 @@ var init_loadJobFromFile = __esm({
|
|
|
13345
13418
|
if (!slug2) {
|
|
13346
13419
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
13347
13420
|
}
|
|
13348
|
-
const capability = resolveCapabilityFolder(slug2,
|
|
13421
|
+
const capability = resolveCapabilityFolder(slug2, path34.join(ctx.cwd, jobsDir));
|
|
13349
13422
|
if (!capability) {
|
|
13350
|
-
throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${
|
|
13423
|
+
throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path34.join(ctx.cwd, jobsDir, slug2)}`);
|
|
13351
13424
|
}
|
|
13352
13425
|
const { title, body, config } = capability;
|
|
13353
13426
|
const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
@@ -13356,12 +13429,12 @@ var init_loadJobFromFile = __esm({
|
|
|
13356
13429
|
let agentIdentity = "";
|
|
13357
13430
|
if (agentSlug) {
|
|
13358
13431
|
const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
|
|
13359
|
-
if (!
|
|
13432
|
+
if (!fs36.existsSync(agentPath)) {
|
|
13360
13433
|
throw new Error(
|
|
13361
13434
|
`loadJobFromFile: capability '${slug2}' declares agent '${agentSlug}' but ${agentPath} does not exist`
|
|
13362
13435
|
);
|
|
13363
13436
|
}
|
|
13364
|
-
const agentRaw =
|
|
13437
|
+
const agentRaw = fs36.readFileSync(agentPath, "utf-8");
|
|
13365
13438
|
const parsed = parseJobFile(agentRaw, agentSlug);
|
|
13366
13439
|
agentTitle = parsed.title;
|
|
13367
13440
|
agentIdentity = parsed.body;
|
|
@@ -13441,13 +13514,13 @@ ${truncate(issue.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
13441
13514
|
});
|
|
13442
13515
|
|
|
13443
13516
|
// src/scripts/kodyVariables.ts
|
|
13444
|
-
import * as
|
|
13445
|
-
import * as
|
|
13517
|
+
import * as fs37 from "fs";
|
|
13518
|
+
import * as path35 from "path";
|
|
13446
13519
|
function readKodyVariables(cwd) {
|
|
13447
|
-
const full =
|
|
13520
|
+
const full = path35.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
13448
13521
|
let raw;
|
|
13449
13522
|
try {
|
|
13450
|
-
raw =
|
|
13523
|
+
raw = fs37.readFileSync(full, "utf-8");
|
|
13451
13524
|
} catch {
|
|
13452
13525
|
return {};
|
|
13453
13526
|
}
|
|
@@ -13471,155 +13544,489 @@ var init_kodyVariables = __esm({
|
|
|
13471
13544
|
}
|
|
13472
13545
|
});
|
|
13473
13546
|
|
|
13474
|
-
// src/
|
|
13475
|
-
import
|
|
13476
|
-
|
|
13477
|
-
|
|
13478
|
-
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
).filter(Boolean);
|
|
13482
|
-
}
|
|
13483
|
-
function readProfileAgents(raw) {
|
|
13484
|
-
const m = FRONTMATTER_RE.exec(raw);
|
|
13485
|
-
if (!m) return { agent: ["kody"], body: raw };
|
|
13486
|
-
const body = raw.slice(m[0].length);
|
|
13487
|
-
let agent = null;
|
|
13488
|
-
let legacy = null;
|
|
13489
|
-
for (const line of (m[1] ?? "").split(/\r?\n/)) {
|
|
13490
|
-
const t = line.trim();
|
|
13491
|
-
const c = t.indexOf(":");
|
|
13492
|
-
if (c < 0) continue;
|
|
13493
|
-
const key = t.slice(0, c).trim();
|
|
13494
|
-
const value = t.slice(c + 1).trim();
|
|
13495
|
-
if (key === "agent") {
|
|
13496
|
-
agent = parseSlugList(value);
|
|
13497
|
-
} else if (key === "audience" || key === "for") {
|
|
13498
|
-
const mapped = parseSlugList(value).map((tok) => LEGACY_AUDIENCE_TO_AGENT[tok]).filter(Boolean);
|
|
13499
|
-
if (mapped.length > 0) legacy = mapped;
|
|
13500
|
-
}
|
|
13547
|
+
// src/pool/keys.ts
|
|
13548
|
+
import { hkdfSync } from "crypto";
|
|
13549
|
+
function masterKeyBytes(raw) {
|
|
13550
|
+
const v = raw.trim();
|
|
13551
|
+
if (!v) throw new Error("KODY_MASTER_KEY is empty");
|
|
13552
|
+
if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
|
|
13553
|
+
return Buffer.from(v, "hex");
|
|
13501
13554
|
}
|
|
13502
|
-
return
|
|
13555
|
+
return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
13503
13556
|
}
|
|
13504
|
-
function
|
|
13505
|
-
|
|
13506
|
-
if (!fs37.existsSync(dir)) return "";
|
|
13507
|
-
let entries;
|
|
13508
|
-
try {
|
|
13509
|
-
entries = fs37.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
13510
|
-
} catch {
|
|
13511
|
-
return "";
|
|
13512
|
-
}
|
|
13513
|
-
const blocks = [];
|
|
13514
|
-
for (const file of entries) {
|
|
13515
|
-
try {
|
|
13516
|
-
const raw = fs37.readFileSync(path35.join(dir, file), "utf-8");
|
|
13517
|
-
const { agent, body } = readProfileAgents(raw);
|
|
13518
|
-
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
13519
|
-
blocks.push(`## ${file}
|
|
13520
|
-
|
|
13521
|
-
${body.trim()}`);
|
|
13522
|
-
} catch {
|
|
13523
|
-
}
|
|
13524
|
-
}
|
|
13525
|
-
return blocks.join("\n\n");
|
|
13557
|
+
function deriveKey(master, info, length = 32) {
|
|
13558
|
+
return Buffer.from(hkdfSync("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
|
|
13526
13559
|
}
|
|
13527
|
-
function
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
|
|
13531
|
-
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13560
|
+
function derivePoolApiKey(master) {
|
|
13561
|
+
return deriveKey(master, POOL_API_KEY_INFO);
|
|
13562
|
+
}
|
|
13563
|
+
function deriveRunnerApiKey(master) {
|
|
13564
|
+
return deriveKey(master, RUNNER_API_KEY_INFO);
|
|
13565
|
+
}
|
|
13566
|
+
function bearerOk(headerAuth, xApiKey, expected) {
|
|
13567
|
+
const x = (xApiKey ?? "").trim();
|
|
13568
|
+
if (x && timingEqual(x, expected)) return true;
|
|
13569
|
+
const a = (headerAuth ?? "").trim();
|
|
13570
|
+
if (a.toLowerCase().startsWith("bearer ")) {
|
|
13571
|
+
return timingEqual(a.slice(7).trim(), expected);
|
|
13536
13572
|
}
|
|
13537
|
-
return
|
|
13573
|
+
return false;
|
|
13538
13574
|
}
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
13575
|
+
function timingEqual(a, b) {
|
|
13576
|
+
if (a.length !== b.length) return false;
|
|
13577
|
+
let diff = 0;
|
|
13578
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
13579
|
+
return diff === 0;
|
|
13580
|
+
}
|
|
13581
|
+
var POOL_API_KEY_INFO, RUNNER_API_KEY_INFO;
|
|
13582
|
+
var init_keys = __esm({
|
|
13583
|
+
"src/pool/keys.ts"() {
|
|
13542
13584
|
"use strict";
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
QA_AGENT = "qa-engineer";
|
|
13546
|
-
ALL_AGENTS = "*";
|
|
13547
|
-
LEGACY_AUDIENCE_TO_AGENT = { chat: "kody", qa: QA_AGENT };
|
|
13548
|
-
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
13549
|
-
loadQaContext = async (ctx) => {
|
|
13550
|
-
const vars = readKodyVariables(ctx.cwd);
|
|
13551
|
-
const login = vars.LOGIN_USER ?? "";
|
|
13552
|
-
const password = process.env.LOGIN_PASSWORD ?? "";
|
|
13553
|
-
const authProfile = ctx.args.authProfile;
|
|
13554
|
-
ctx.data.qaLogin = login;
|
|
13555
|
-
ctx.data.qaProfile = readProfile(ctx.cwd);
|
|
13556
|
-
ctx.data.qaAuthBlock = composeAuthBlock(authProfile, login, password);
|
|
13557
|
-
};
|
|
13585
|
+
POOL_API_KEY_INFO = "kody-pool-api:v1";
|
|
13586
|
+
RUNNER_API_KEY_INFO = "kody-runner-api:v1";
|
|
13558
13587
|
}
|
|
13559
13588
|
});
|
|
13560
13589
|
|
|
13561
|
-
// src/
|
|
13590
|
+
// src/stateRepoGithub.ts
|
|
13562
13591
|
import * as fs38 from "fs";
|
|
13563
13592
|
import * as path36 from "path";
|
|
13564
|
-
function
|
|
13565
|
-
return
|
|
13566
|
-
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
13567
|
-
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13568
|
-
runId: args.runId,
|
|
13569
|
-
issue: args.issue,
|
|
13570
|
-
conventions: args.conventions ?? [],
|
|
13571
|
-
priorArt: args.priorArt ?? "",
|
|
13572
|
-
memoryContext: args.memoryContext ?? "",
|
|
13573
|
-
coverageRules: args.coverageRules ?? []
|
|
13574
|
-
};
|
|
13593
|
+
function recordValue3(value) {
|
|
13594
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
13575
13595
|
}
|
|
13576
|
-
function
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
13581
|
-
|
|
13582
|
-
|
|
13583
|
-
|
|
13584
|
-
|
|
13585
|
-
|
|
13586
|
-
|
|
13587
|
-
|
|
13588
|
-
|
|
13596
|
+
function contentsUrl(owner, repo, filePath) {
|
|
13597
|
+
const encodedPath = filePath.split("/").filter(Boolean).map((part) => encodeURIComponent(part)).join("/");
|
|
13598
|
+
return `${GITHUB_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}`;
|
|
13599
|
+
}
|
|
13600
|
+
async function githubContentsFile(opts) {
|
|
13601
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
13602
|
+
const res = await doFetch(contentsUrl(opts.owner, opts.repo, opts.path), {
|
|
13603
|
+
headers: {
|
|
13604
|
+
Authorization: `Bearer ${opts.githubToken}`,
|
|
13605
|
+
Accept: "application/vnd.github+json",
|
|
13606
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
13607
|
+
"User-Agent": "kody-engine"
|
|
13608
|
+
},
|
|
13609
|
+
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
13610
|
+
});
|
|
13611
|
+
if (res.status === 404) return null;
|
|
13612
|
+
if (!res.ok) {
|
|
13613
|
+
throw new Error(
|
|
13614
|
+
`GitHub ${opts.owner}/${opts.repo}:${opts.path}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
13615
|
+
);
|
|
13589
13616
|
}
|
|
13617
|
+
return await res.json();
|
|
13590
13618
|
}
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
"use strict";
|
|
13595
|
-
init_runtimePaths();
|
|
13596
|
-
TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
13619
|
+
function decodeContentsFile(file, label) {
|
|
13620
|
+
if (file.type !== "file" || file.encoding !== "base64" || typeof file.content !== "string") {
|
|
13621
|
+
throw new Error(`${label} is not a base64 file`);
|
|
13597
13622
|
}
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13601
|
-
|
|
13602
|
-
|
|
13603
|
-
|
|
13604
|
-
|
|
13605
|
-
|
|
13606
|
-
|
|
13607
|
-
|
|
13608
|
-
|
|
13609
|
-
|
|
13610
|
-
|
|
13611
|
-
|
|
13612
|
-
|
|
13613
|
-
|
|
13614
|
-
|
|
13615
|
-
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
|
|
13623
|
+
if (typeof file.sha !== "string") {
|
|
13624
|
+
throw new Error(`${label} response missing sha`);
|
|
13625
|
+
}
|
|
13626
|
+
return {
|
|
13627
|
+
path: label,
|
|
13628
|
+
content: Buffer.from(file.content, "base64").toString("utf-8"),
|
|
13629
|
+
sha: file.sha
|
|
13630
|
+
};
|
|
13631
|
+
}
|
|
13632
|
+
async function loadGithubStateConfig(opts) {
|
|
13633
|
+
const configFile = await githubContentsFile({
|
|
13634
|
+
owner: opts.owner,
|
|
13635
|
+
repo: opts.repo,
|
|
13636
|
+
path: "kody.config.json",
|
|
13637
|
+
githubToken: opts.githubToken,
|
|
13638
|
+
fetchImpl: opts.fetchImpl
|
|
13639
|
+
});
|
|
13640
|
+
let raw = {};
|
|
13641
|
+
if (configFile) {
|
|
13642
|
+
const decoded = decodeContentsFile(configFile, "kody.config.json");
|
|
13643
|
+
try {
|
|
13644
|
+
raw = JSON.parse(decoded.content);
|
|
13645
|
+
} catch (err) {
|
|
13646
|
+
throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
13647
|
+
}
|
|
13648
|
+
}
|
|
13649
|
+
const githubRaw = recordValue3(raw.github) ?? {};
|
|
13650
|
+
const github = {
|
|
13651
|
+
owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
|
|
13652
|
+
repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
|
|
13653
|
+
};
|
|
13654
|
+
const nestedState = recordValue3(raw.state) ?? {};
|
|
13655
|
+
const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
|
|
13656
|
+
const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
|
|
13657
|
+
const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
|
|
13658
|
+
parseStateRepoSlug(stateRepo);
|
|
13659
|
+
const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
|
|
13660
|
+
return {
|
|
13661
|
+
github,
|
|
13662
|
+
state: {
|
|
13663
|
+
repo: stateRepo,
|
|
13664
|
+
path: normalizeStatePath(statePath)
|
|
13665
|
+
}
|
|
13666
|
+
};
|
|
13667
|
+
}
|
|
13668
|
+
async function readGithubStateText(opts) {
|
|
13669
|
+
const config = await loadGithubStateConfig(opts);
|
|
13670
|
+
return readGithubStateTextWithConfig({
|
|
13671
|
+
config,
|
|
13672
|
+
filePath: opts.filePath,
|
|
13673
|
+
githubToken: opts.githubToken,
|
|
13674
|
+
fetchImpl: opts.fetchImpl
|
|
13675
|
+
});
|
|
13676
|
+
}
|
|
13677
|
+
async function readGithubStateTextWithConfig(opts) {
|
|
13678
|
+
const target = stateRepoPath(opts.config, opts.filePath);
|
|
13679
|
+
const parsed = parseStateRepo(opts.config);
|
|
13680
|
+
const file = await githubContentsFile({
|
|
13681
|
+
owner: parsed.owner,
|
|
13682
|
+
repo: parsed.repo,
|
|
13683
|
+
path: target,
|
|
13684
|
+
githubToken: opts.githubToken,
|
|
13685
|
+
fetchImpl: opts.fetchImpl
|
|
13686
|
+
});
|
|
13687
|
+
return file ? decodeContentsFile(file, target) : null;
|
|
13688
|
+
}
|
|
13689
|
+
async function writeGithubStateTextWithConfig(opts) {
|
|
13690
|
+
const target = stateRepoPath(opts.config, opts.filePath);
|
|
13691
|
+
const parsed = parseStateRepo(opts.config);
|
|
13692
|
+
const payload = {
|
|
13693
|
+
message: opts.message,
|
|
13694
|
+
content: Buffer.from(opts.content, "utf-8").toString("base64")
|
|
13695
|
+
};
|
|
13696
|
+
if (opts.sha) payload.sha = opts.sha;
|
|
13697
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
13698
|
+
const res = await doFetch(contentsUrl(parsed.owner, parsed.repo, target), {
|
|
13699
|
+
method: "PUT",
|
|
13700
|
+
headers: {
|
|
13701
|
+
Authorization: `Bearer ${opts.githubToken}`,
|
|
13702
|
+
Accept: "application/vnd.github+json",
|
|
13703
|
+
"Content-Type": "application/json",
|
|
13704
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
13705
|
+
"User-Agent": "kody-engine"
|
|
13706
|
+
},
|
|
13707
|
+
body: JSON.stringify(payload),
|
|
13708
|
+
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
13709
|
+
});
|
|
13710
|
+
if (!res.ok) {
|
|
13711
|
+
throw new Error(
|
|
13712
|
+
`GitHub write ${parsed.owner}/${parsed.repo}:${target}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
13713
|
+
);
|
|
13714
|
+
}
|
|
13715
|
+
}
|
|
13716
|
+
function jsonlLines(text) {
|
|
13717
|
+
return text.split("\n").filter((line) => line.length > 0);
|
|
13718
|
+
}
|
|
13719
|
+
function renderJsonl(lines) {
|
|
13720
|
+
return lines.length > 0 ? `${lines.join("\n")}
|
|
13721
|
+
` : "";
|
|
13722
|
+
}
|
|
13723
|
+
function mergeJsonl(localText, remoteText) {
|
|
13724
|
+
const remoteLines = jsonlLines(remoteText);
|
|
13725
|
+
const seen = new Set(remoteLines);
|
|
13726
|
+
const localOnly = jsonlLines(localText).filter((line) => !seen.has(line));
|
|
13727
|
+
return renderJsonl([...remoteLines, ...localOnly]);
|
|
13728
|
+
}
|
|
13729
|
+
async function syncJsonlFileFromGithubState(opts) {
|
|
13730
|
+
const remote = await readGithubStateTextWithConfig(opts);
|
|
13731
|
+
if (!remote) return;
|
|
13732
|
+
const local = fs38.existsSync(opts.localPath) ? fs38.readFileSync(opts.localPath, "utf-8") : "";
|
|
13733
|
+
const next = mergeJsonl(local, remote.content);
|
|
13734
|
+
if (next === local) return;
|
|
13735
|
+
fs38.mkdirSync(path36.dirname(opts.localPath), { recursive: true });
|
|
13736
|
+
fs38.writeFileSync(opts.localPath, next);
|
|
13737
|
+
}
|
|
13738
|
+
async function persistJsonlFileToGithubState(opts) {
|
|
13739
|
+
if (!fs38.existsSync(opts.localPath)) return;
|
|
13740
|
+
const localText = fs38.readFileSync(opts.localPath, "utf-8");
|
|
13741
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
13742
|
+
const remote = await readGithubStateTextWithConfig(opts);
|
|
13743
|
+
const body = mergeJsonl(localText, remote?.content ?? "");
|
|
13744
|
+
try {
|
|
13745
|
+
await writeGithubStateTextWithConfig({
|
|
13746
|
+
config: opts.config,
|
|
13747
|
+
filePath: opts.filePath,
|
|
13748
|
+
content: body,
|
|
13749
|
+
message: opts.message,
|
|
13750
|
+
githubToken: opts.githubToken,
|
|
13751
|
+
sha: remote?.sha,
|
|
13752
|
+
fetchImpl: opts.fetchImpl
|
|
13753
|
+
});
|
|
13754
|
+
return;
|
|
13755
|
+
} catch (err) {
|
|
13756
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13757
|
+
const conflict = /409|422|does not match|is at|but expected/i.test(msg);
|
|
13758
|
+
if (!conflict || attempt === 3) throw err;
|
|
13759
|
+
}
|
|
13760
|
+
}
|
|
13761
|
+
}
|
|
13762
|
+
var GITHUB_API, REQ_TIMEOUT_MS;
|
|
13763
|
+
var init_stateRepoGithub = __esm({
|
|
13764
|
+
"src/stateRepoGithub.ts"() {
|
|
13765
|
+
"use strict";
|
|
13766
|
+
init_stateRepo();
|
|
13767
|
+
GITHUB_API = "https://api.github.com";
|
|
13768
|
+
REQ_TIMEOUT_MS = 3e4;
|
|
13769
|
+
}
|
|
13770
|
+
});
|
|
13771
|
+
|
|
13772
|
+
// src/stateRepoVault.ts
|
|
13773
|
+
import { createDecipheriv, createHash as createHash3 } from "crypto";
|
|
13774
|
+
function cacheKey2(owner, repo, masterKey) {
|
|
13775
|
+
const keyHash = createHash3("sha256").update(masterKey).digest("hex").slice(0, 16);
|
|
13776
|
+
return `${owner}/${repo}:${keyHash}`.toLowerCase();
|
|
13777
|
+
}
|
|
13778
|
+
function decryptVault(payload, masterKey) {
|
|
13779
|
+
const parts = payload.split(":");
|
|
13780
|
+
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
13781
|
+
throw new Error("invalid vault payload format");
|
|
13782
|
+
}
|
|
13783
|
+
const [, ivB64, ctB64, tagB64] = parts;
|
|
13784
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
13785
|
+
const ct = Buffer.from(ctB64, "base64");
|
|
13786
|
+
const tag = Buffer.from(tagB64, "base64");
|
|
13787
|
+
const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
|
|
13788
|
+
decipher.setAuthTag(tag);
|
|
13789
|
+
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
13790
|
+
}
|
|
13791
|
+
async function readVaultSecrets(opts) {
|
|
13792
|
+
const key = cacheKey2(opts.owner, opts.repo, opts.masterKey);
|
|
13793
|
+
const hit = cache.get(key);
|
|
13794
|
+
if (hit && hit.expiresAt > Date.now()) return hit.secrets;
|
|
13795
|
+
const file = await readGithubStateText({
|
|
13796
|
+
owner: opts.owner,
|
|
13797
|
+
repo: opts.repo,
|
|
13798
|
+
filePath: VAULT_PATH,
|
|
13799
|
+
githubToken: opts.githubToken,
|
|
13800
|
+
fetchImpl: opts.fetchImpl
|
|
13801
|
+
});
|
|
13802
|
+
if (!file) {
|
|
13803
|
+
cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
13804
|
+
return {};
|
|
13805
|
+
}
|
|
13806
|
+
const ciphertext = file.content.trim();
|
|
13807
|
+
const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
|
|
13808
|
+
const flat = {};
|
|
13809
|
+
for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
|
|
13810
|
+
if (entry && typeof entry.value === "string") flat[name] = entry.value;
|
|
13811
|
+
}
|
|
13812
|
+
cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
13813
|
+
return flat;
|
|
13814
|
+
}
|
|
13815
|
+
async function readRepoSecret(opts) {
|
|
13816
|
+
const secrets = await readVaultSecrets(opts);
|
|
13817
|
+
const v = secrets[opts.name];
|
|
13818
|
+
return v?.trim() ? v : null;
|
|
13819
|
+
}
|
|
13820
|
+
async function readRepoSecrets(opts) {
|
|
13821
|
+
return readVaultSecrets(opts);
|
|
13822
|
+
}
|
|
13823
|
+
var VAULT_PATH, CACHE_TTL_MS, cache;
|
|
13824
|
+
var init_stateRepoVault = __esm({
|
|
13825
|
+
"src/stateRepoVault.ts"() {
|
|
13826
|
+
"use strict";
|
|
13827
|
+
init_stateRepoGithub();
|
|
13828
|
+
VAULT_PATH = "secrets.enc";
|
|
13829
|
+
CACHE_TTL_MS = 6e4;
|
|
13830
|
+
cache = /* @__PURE__ */ new Map();
|
|
13831
|
+
}
|
|
13832
|
+
});
|
|
13833
|
+
|
|
13834
|
+
// src/scripts/runtimeSecrets.ts
|
|
13835
|
+
function tokenFromEnv(env) {
|
|
13836
|
+
return (env.KODY_TOKEN ?? env.GH_TOKEN ?? env.GITHUB_TOKEN ?? env.GH_PAT ?? "").trim();
|
|
13837
|
+
}
|
|
13838
|
+
function envSecret(name, env) {
|
|
13839
|
+
const value = env[name]?.trim() ? env[name] : "";
|
|
13840
|
+
return value ? { value, source: "env" } : { value: "", source: "missing" };
|
|
13841
|
+
}
|
|
13842
|
+
async function resolveRuntimeSecret(name, ctx, opts = {}) {
|
|
13843
|
+
const env = opts.env ?? process.env;
|
|
13844
|
+
const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
|
|
13845
|
+
const githubToken2 = tokenFromEnv(env);
|
|
13846
|
+
if (!masterRaw || !githubToken2) return envSecret(name, env);
|
|
13847
|
+
try {
|
|
13848
|
+
const masterKey = masterKeyBytes(masterRaw);
|
|
13849
|
+
if (masterKey.length !== 32) {
|
|
13850
|
+
throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
|
|
13851
|
+
}
|
|
13852
|
+
const value = await readRepoSecret({
|
|
13853
|
+
owner: ctx.config.github.owner,
|
|
13854
|
+
repo: ctx.config.github.repo,
|
|
13855
|
+
name,
|
|
13856
|
+
githubToken: githubToken2,
|
|
13857
|
+
masterKey,
|
|
13858
|
+
fetchImpl: opts.fetchImpl
|
|
13859
|
+
});
|
|
13860
|
+
if (value) return { value, source: "vault" };
|
|
13861
|
+
} catch (err) {
|
|
13862
|
+
const fallback = envSecret(name, env);
|
|
13863
|
+
return {
|
|
13864
|
+
...fallback,
|
|
13865
|
+
warning: `vault read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
13866
|
+
};
|
|
13867
|
+
}
|
|
13868
|
+
return envSecret(name, env);
|
|
13869
|
+
}
|
|
13870
|
+
var init_runtimeSecrets = __esm({
|
|
13871
|
+
"src/scripts/runtimeSecrets.ts"() {
|
|
13872
|
+
"use strict";
|
|
13873
|
+
init_keys();
|
|
13874
|
+
init_stateRepoVault();
|
|
13875
|
+
}
|
|
13876
|
+
});
|
|
13877
|
+
|
|
13878
|
+
// src/scripts/loadQaContext.ts
|
|
13879
|
+
import * as fs39 from "fs";
|
|
13880
|
+
import * as path37 from "path";
|
|
13881
|
+
function parseSlugList(value) {
|
|
13882
|
+
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
13883
|
+
return inner.split(",").map(
|
|
13884
|
+
(s) => s.trim().replace(/^["']|["']$/g, "").toLowerCase()
|
|
13885
|
+
).filter(Boolean);
|
|
13886
|
+
}
|
|
13887
|
+
function readProfileAgents(raw) {
|
|
13888
|
+
const m = FRONTMATTER_RE.exec(raw);
|
|
13889
|
+
if (!m) return { agent: ["kody"], body: raw };
|
|
13890
|
+
const body = raw.slice(m[0].length);
|
|
13891
|
+
let agent = null;
|
|
13892
|
+
let legacy = null;
|
|
13893
|
+
for (const line of (m[1] ?? "").split(/\r?\n/)) {
|
|
13894
|
+
const t = line.trim();
|
|
13895
|
+
const c = t.indexOf(":");
|
|
13896
|
+
if (c < 0) continue;
|
|
13897
|
+
const key = t.slice(0, c).trim();
|
|
13898
|
+
const value = t.slice(c + 1).trim();
|
|
13899
|
+
if (key === "agent") {
|
|
13900
|
+
agent = parseSlugList(value);
|
|
13901
|
+
} else if (key === "audience" || key === "for") {
|
|
13902
|
+
const mapped = parseSlugList(value).map((tok) => LEGACY_AUDIENCE_TO_AGENT[tok]).filter(Boolean);
|
|
13903
|
+
if (mapped.length > 0) legacy = mapped;
|
|
13904
|
+
}
|
|
13905
|
+
}
|
|
13906
|
+
return { agent: agent ?? legacy ?? ["kody"], body };
|
|
13907
|
+
}
|
|
13908
|
+
function readProfile(cwd) {
|
|
13909
|
+
const dir = path37.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
13910
|
+
if (!fs39.existsSync(dir)) return "";
|
|
13911
|
+
let entries;
|
|
13912
|
+
try {
|
|
13913
|
+
entries = fs39.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
13914
|
+
} catch {
|
|
13915
|
+
return "";
|
|
13916
|
+
}
|
|
13917
|
+
const blocks = [];
|
|
13918
|
+
for (const file of entries) {
|
|
13919
|
+
try {
|
|
13920
|
+
const raw = fs39.readFileSync(path37.join(dir, file), "utf-8");
|
|
13921
|
+
const { agent, body } = readProfileAgents(raw);
|
|
13922
|
+
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
13923
|
+
blocks.push(`## ${file}
|
|
13924
|
+
|
|
13925
|
+
${body.trim()}`);
|
|
13926
|
+
} catch {
|
|
13927
|
+
}
|
|
13928
|
+
}
|
|
13929
|
+
return blocks.join("\n\n");
|
|
13930
|
+
}
|
|
13931
|
+
function composeAuthBlock(authProfile, login, password) {
|
|
13932
|
+
if (authProfile && authProfile.trim().length > 0) {
|
|
13933
|
+
return `Auth: a saved Playwright \`storageState.json\` is available at \`${authProfile}\`. Pass it to the browser via the \`storageState\` parameter so the session starts pre-authenticated.`;
|
|
13934
|
+
}
|
|
13935
|
+
if (login && password) {
|
|
13936
|
+
return `Auth: log in once at the app's login page. Username: \`${login}\` \xB7 Password: \`${password}\`. Type each field key-by-key (Playwright \`locator.pressSequentially()\` / the MCP \`browser_type\` tool), NOT a one-shot \`fill()\` or value assignment: pasting a value in a single step often fails to fire the login form's framework onChange handler, so the form submits empty and you get a FALSE "invalid email or password". After typing, confirm the field shows the value before clicking submit; if the first attempt is rejected, re-type key-by-key before treating the credentials as wrong. If a login form's inputs don't respond to typing-by-label/placeholder, inspect the DOM and target them by their \`id\`/\`name\` attribute instead \u2014 e.g. a Payload CMS admin login at \`/admin\` uses \`#field-email\` and \`#field-password\`. The app may have TWO separate logins (a public/frontend one and a Payload \`/admin\` one); if a change you must verify lives behind the admin, log into that form too. Re-use the session afterwards.`;
|
|
13937
|
+
}
|
|
13938
|
+
if (login) {
|
|
13939
|
+
return `Auth: username \`${login}\` is configured but no \`LOGIN_PASSWORD\` secret was found. Note auth-gated surfaces as gaps.`;
|
|
13940
|
+
}
|
|
13941
|
+
return "Auth: no QA credentials configured (set the `LOGIN_USER` variable and the `LOGIN_PASSWORD` vault secret). Browse public routes only; note auth-gated surfaces as gaps.";
|
|
13942
|
+
}
|
|
13943
|
+
var CONTEXT_DIR_REL_PATH, QA_AGENT, ALL_AGENTS, LEGACY_AUDIENCE_TO_AGENT, FRONTMATTER_RE, loadQaContext;
|
|
13944
|
+
var init_loadQaContext = __esm({
|
|
13945
|
+
"src/scripts/loadQaContext.ts"() {
|
|
13946
|
+
"use strict";
|
|
13947
|
+
init_kodyVariables();
|
|
13948
|
+
init_runtimeSecrets();
|
|
13949
|
+
CONTEXT_DIR_REL_PATH = ".kody/context";
|
|
13950
|
+
QA_AGENT = "qa-engineer";
|
|
13951
|
+
ALL_AGENTS = "*";
|
|
13952
|
+
LEGACY_AUDIENCE_TO_AGENT = { chat: "kody", qa: QA_AGENT };
|
|
13953
|
+
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
13954
|
+
loadQaContext = async (ctx) => {
|
|
13955
|
+
const vars = readKodyVariables(ctx.cwd);
|
|
13956
|
+
const login = vars.LOGIN_USER ?? "";
|
|
13957
|
+
const password = await resolveRuntimeSecret("LOGIN_PASSWORD", ctx);
|
|
13958
|
+
const authProfile = ctx.args.authProfile;
|
|
13959
|
+
ctx.data.qaLogin = login;
|
|
13960
|
+
ctx.data.qaPasswordSource = password.source;
|
|
13961
|
+
if (password.warning) ctx.data.qaPasswordWarning = password.warning;
|
|
13962
|
+
ctx.data.qaProfile = readProfile(ctx.cwd);
|
|
13963
|
+
ctx.data.qaAuthBlock = composeAuthBlock(authProfile, login, password.value);
|
|
13964
|
+
};
|
|
13965
|
+
}
|
|
13966
|
+
});
|
|
13967
|
+
|
|
13968
|
+
// src/taskContext.ts
|
|
13969
|
+
import * as fs40 from "fs";
|
|
13970
|
+
import * as path38 from "path";
|
|
13971
|
+
function buildTaskContext(args) {
|
|
13972
|
+
return {
|
|
13973
|
+
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
13974
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13975
|
+
runId: args.runId,
|
|
13976
|
+
issue: args.issue,
|
|
13977
|
+
conventions: args.conventions ?? [],
|
|
13978
|
+
priorArt: args.priorArt ?? "",
|
|
13979
|
+
memoryContext: args.memoryContext ?? "",
|
|
13980
|
+
coverageRules: args.coverageRules ?? []
|
|
13981
|
+
};
|
|
13982
|
+
}
|
|
13983
|
+
function persistTaskContext(cwd, ctx) {
|
|
13984
|
+
try {
|
|
13985
|
+
const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
|
|
13986
|
+
fs40.mkdirSync(dir, { recursive: true });
|
|
13987
|
+
const file = path38.join(dir, "task-context.json");
|
|
13988
|
+
fs40.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
13989
|
+
`);
|
|
13990
|
+
return file;
|
|
13991
|
+
} catch (err) {
|
|
13992
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13993
|
+
process.stderr.write(`[kody taskContext] persist failed: ${msg}
|
|
13994
|
+
`);
|
|
13995
|
+
return null;
|
|
13996
|
+
}
|
|
13997
|
+
}
|
|
13998
|
+
var TASK_CONTEXT_SCHEMA_VERSION;
|
|
13999
|
+
var init_taskContext = __esm({
|
|
14000
|
+
"src/taskContext.ts"() {
|
|
14001
|
+
"use strict";
|
|
14002
|
+
init_runtimePaths();
|
|
14003
|
+
TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
14004
|
+
}
|
|
14005
|
+
});
|
|
14006
|
+
|
|
14007
|
+
// src/scripts/loadTaskContext.ts
|
|
14008
|
+
var loadTaskContext;
|
|
14009
|
+
var init_loadTaskContext = __esm({
|
|
14010
|
+
"src/scripts/loadTaskContext.ts"() {
|
|
14011
|
+
"use strict";
|
|
14012
|
+
init_events();
|
|
14013
|
+
init_taskContext();
|
|
14014
|
+
loadTaskContext = async (ctx) => {
|
|
14015
|
+
const runId = resolveRunId();
|
|
14016
|
+
const rawIssue = ctx.data.issue;
|
|
14017
|
+
const issue = rawIssue ? {
|
|
14018
|
+
...rawIssue,
|
|
14019
|
+
commentsFormatted: rawIssue.commentsFormatted ?? "",
|
|
14020
|
+
labelsFormatted: rawIssue.labelsFormatted ?? ""
|
|
14021
|
+
} : void 0;
|
|
14022
|
+
const taskContext = buildTaskContext({
|
|
14023
|
+
runId,
|
|
14024
|
+
issue,
|
|
14025
|
+
conventions: ctx.data.conventions,
|
|
14026
|
+
priorArt: typeof ctx.data.priorArt === "string" ? ctx.data.priorArt : "",
|
|
14027
|
+
memoryContext: typeof ctx.data.memoryContext === "string" ? ctx.data.memoryContext : "",
|
|
14028
|
+
coverageRules: ctx.data.coverageRules
|
|
14029
|
+
});
|
|
13623
14030
|
ctx.data.taskContext = taskContext;
|
|
13624
14031
|
const persistedPath = persistTaskContext(ctx.cwd, taskContext);
|
|
13625
14032
|
if (persistedPath && ctx.verbose) {
|
|
@@ -14349,32 +14756,32 @@ var init_parseAgentResult = __esm({
|
|
|
14349
14756
|
}
|
|
14350
14757
|
});
|
|
14351
14758
|
|
|
14352
|
-
// src/scripts/
|
|
14759
|
+
// src/scripts/parseAgencyArchitectDecision.ts
|
|
14353
14760
|
function makeAction3(type, payload) {
|
|
14354
14761
|
return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
14355
14762
|
}
|
|
14356
|
-
var
|
|
14357
|
-
var
|
|
14358
|
-
"src/scripts/
|
|
14763
|
+
var parseAgencyArchitectDecision;
|
|
14764
|
+
var init_parseAgencyArchitectDecision = __esm({
|
|
14765
|
+
"src/scripts/parseAgencyArchitectDecision.ts"() {
|
|
14359
14766
|
"use strict";
|
|
14360
|
-
|
|
14361
|
-
|
|
14767
|
+
init_agencyArchitectDecision();
|
|
14768
|
+
parseAgencyArchitectDecision = async (ctx, _profile, agentResult) => {
|
|
14362
14769
|
if (!agentResult) {
|
|
14363
|
-
ctx.data.
|
|
14364
|
-
ctx.data.action = makeAction3("
|
|
14770
|
+
ctx.data.agencyArchitectDecision = { summary: "", actions: [] };
|
|
14771
|
+
ctx.data.action = makeAction3("AGENCY_ARCHITECT_NOT_RUN", { reason: "no agent result" });
|
|
14365
14772
|
return;
|
|
14366
14773
|
}
|
|
14367
14774
|
try {
|
|
14368
|
-
const decision =
|
|
14369
|
-
ctx.data.
|
|
14370
|
-
ctx.data.action = makeAction3("
|
|
14775
|
+
const decision = parseAgencyArchitectDecisionText(agentResult.finalText);
|
|
14776
|
+
ctx.data.agencyArchitectDecision = decision;
|
|
14777
|
+
ctx.data.action = makeAction3("AGENCY_ARCHITECT_DECIDED", {
|
|
14371
14778
|
summary: decision.summary,
|
|
14372
14779
|
actionCount: decision.actions.length
|
|
14373
14780
|
});
|
|
14374
14781
|
} catch (err) {
|
|
14375
14782
|
const reason = err instanceof Error ? err.message : String(err);
|
|
14376
|
-
ctx.data.
|
|
14377
|
-
ctx.data.action = makeAction3("
|
|
14783
|
+
ctx.data.agencyArchitectDecisionError = reason;
|
|
14784
|
+
ctx.data.action = makeAction3("AGENCY_ARCHITECT_FAILED", { reason });
|
|
14378
14785
|
ctx.output.exitCode = 1;
|
|
14379
14786
|
ctx.output.reason = reason;
|
|
14380
14787
|
}
|
|
@@ -15764,301 +16171,119 @@ var init_revertFlow = __esm({
|
|
|
15764
16171
|
const runUrl = getRunUrl();
|
|
15765
16172
|
const runSuffix = runUrl ? `, run ${runUrl}` : "";
|
|
15766
16173
|
const shaList = resolved.map((r) => `\`${r.full.slice(0, 7)}\``).join(", ");
|
|
15767
|
-
tryPostPr4(prNumber, `\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`, ctx.cwd);
|
|
15768
|
-
};
|
|
15769
|
-
}
|
|
15770
|
-
});
|
|
15771
|
-
|
|
15772
|
-
// src/scripts/reviewFlow.ts
|
|
15773
|
-
function tryPostPr5(prNumber, body, cwd) {
|
|
15774
|
-
try {
|
|
15775
|
-
postPrReviewComment(prNumber, body, cwd);
|
|
15776
|
-
} catch {
|
|
15777
|
-
}
|
|
15778
|
-
}
|
|
15779
|
-
var reviewFlow;
|
|
15780
|
-
var init_reviewFlow = __esm({
|
|
15781
|
-
"src/scripts/reviewFlow.ts"() {
|
|
15782
|
-
"use strict";
|
|
15783
|
-
init_branch();
|
|
15784
|
-
init_gha();
|
|
15785
|
-
init_issue();
|
|
15786
|
-
reviewFlow = async (ctx) => {
|
|
15787
|
-
const prNumber = ctx.args.pr;
|
|
15788
|
-
const pr = getPr(prNumber, ctx.cwd);
|
|
15789
|
-
if (pr.state !== "OPEN") {
|
|
15790
|
-
ctx.output.exitCode = 1;
|
|
15791
|
-
ctx.output.reason = `PR #${prNumber} is not OPEN (state: ${pr.state})`;
|
|
15792
|
-
ctx.skipAgent = true;
|
|
15793
|
-
return;
|
|
15794
|
-
}
|
|
15795
|
-
ctx.data.pr = pr;
|
|
15796
|
-
ctx.data.commentTargetType = "pr";
|
|
15797
|
-
ctx.data.commentTargetNumber = prNumber;
|
|
15798
|
-
checkoutPrBranch(prNumber, ctx.cwd);
|
|
15799
|
-
ctx.data.branch = getCurrentBranch(ctx.cwd);
|
|
15800
|
-
ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
|
|
15801
|
-
const runUrl = getRunUrl();
|
|
15802
|
-
const runSuffix = runUrl ? `, run ${runUrl}` : "";
|
|
15803
|
-
tryPostPr5(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
|
|
15804
|
-
};
|
|
15805
|
-
}
|
|
15806
|
-
});
|
|
15807
|
-
|
|
15808
|
-
// src/scripts/runFlow.ts
|
|
15809
|
-
function tryPost(issueNumber, body, cwd) {
|
|
15810
|
-
try {
|
|
15811
|
-
postIssueComment(issueNumber, body, cwd);
|
|
15812
|
-
} catch {
|
|
15813
|
-
}
|
|
15814
|
-
}
|
|
15815
|
-
function resolveBaseOverride(value) {
|
|
15816
|
-
if (!value) return null;
|
|
15817
|
-
if (value.length > 200) return null;
|
|
15818
|
-
if (value.includes("..")) return null;
|
|
15819
|
-
if (!/^[a-z0-9][a-z0-9/._-]*$/.test(value)) return null;
|
|
15820
|
-
return value;
|
|
15821
|
-
}
|
|
15822
|
-
var runFlow;
|
|
15823
|
-
var init_runFlow = __esm({
|
|
15824
|
-
"src/scripts/runFlow.ts"() {
|
|
15825
|
-
"use strict";
|
|
15826
|
-
init_branch();
|
|
15827
|
-
init_gha();
|
|
15828
|
-
init_issue();
|
|
15829
|
-
runFlow = async (ctx) => {
|
|
15830
|
-
const issueNumber = ctx.args.issue;
|
|
15831
|
-
const issue = getIssue(issueNumber, ctx.cwd);
|
|
15832
|
-
const cfgCtx = ctx.config.issueContext ?? {};
|
|
15833
|
-
const commentsFormatted = formatIssueComments(
|
|
15834
|
-
issue.comments,
|
|
15835
|
-
cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
|
|
15836
|
-
cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
|
|
15837
|
-
);
|
|
15838
|
-
ctx.data.issue = { ...issue, commentsFormatted };
|
|
15839
|
-
if (issue.isPullRequest) {
|
|
15840
|
-
ctx.data.commentTargetType = "pr";
|
|
15841
|
-
ctx.data.commentTargetNumber = issueNumber;
|
|
15842
|
-
ctx.skipAgent = true;
|
|
15843
|
-
ctx.output.exitCode = 1;
|
|
15844
|
-
ctx.output.reason = `run target #${issueNumber} is a pull request; dispatch a PR action or the source issue instead`;
|
|
15845
|
-
return;
|
|
15846
|
-
}
|
|
15847
|
-
ctx.data.commentTargetType = "issue";
|
|
15848
|
-
ctx.data.commentTargetNumber = issueNumber;
|
|
15849
|
-
const argBase = resolveBaseOverride(ctx.args.base);
|
|
15850
|
-
const baseRaw = ctx.args.base;
|
|
15851
|
-
if (baseRaw && !argBase) {
|
|
15852
|
-
process.stderr.write(`[kody runFlow] ignoring --base "${baseRaw}" (must match kody-task or goal-branch pattern)
|
|
15853
|
-
`);
|
|
15854
|
-
}
|
|
15855
|
-
const base = argBase;
|
|
15856
|
-
if (base) {
|
|
15857
|
-
ctx.data.baseBranch = base;
|
|
15858
|
-
process.stderr.write(`[kody runFlow] resolved base branch: ${base} (from --base)
|
|
15859
|
-
`);
|
|
15860
|
-
}
|
|
15861
|
-
const branchInfo = ensureFeatureBranch(
|
|
15862
|
-
issueNumber,
|
|
15863
|
-
issue.title,
|
|
15864
|
-
ctx.config.git.defaultBranch,
|
|
15865
|
-
ctx.cwd,
|
|
15866
|
-
base ?? void 0
|
|
15867
|
-
);
|
|
15868
|
-
ctx.data.branch = branchInfo.branch;
|
|
15869
|
-
const runUrl = getRunUrl();
|
|
15870
|
-
const startMsg = runUrl ? `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\`, run ${runUrl}` : `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\``;
|
|
15871
|
-
tryPost(issueNumber, startMsg, ctx.cwd);
|
|
15872
|
-
};
|
|
15873
|
-
}
|
|
15874
|
-
});
|
|
15875
|
-
|
|
15876
|
-
// src/stateRepoGithub.ts
|
|
15877
|
-
import * as fs39 from "fs";
|
|
15878
|
-
import * as path37 from "path";
|
|
15879
|
-
function recordValue3(value) {
|
|
15880
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
15881
|
-
}
|
|
15882
|
-
function contentsUrl(owner, repo, filePath) {
|
|
15883
|
-
const encodedPath = filePath.split("/").filter(Boolean).map((part) => encodeURIComponent(part)).join("/");
|
|
15884
|
-
return `${GITHUB_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}`;
|
|
15885
|
-
}
|
|
15886
|
-
async function githubContentsFile(opts) {
|
|
15887
|
-
const doFetch = opts.fetchImpl ?? fetch;
|
|
15888
|
-
const res = await doFetch(contentsUrl(opts.owner, opts.repo, opts.path), {
|
|
15889
|
-
headers: {
|
|
15890
|
-
Authorization: `Bearer ${opts.githubToken}`,
|
|
15891
|
-
Accept: "application/vnd.github+json",
|
|
15892
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
15893
|
-
"User-Agent": "kody-engine"
|
|
15894
|
-
},
|
|
15895
|
-
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
15896
|
-
});
|
|
15897
|
-
if (res.status === 404) return null;
|
|
15898
|
-
if (!res.ok) {
|
|
15899
|
-
throw new Error(
|
|
15900
|
-
`GitHub ${opts.owner}/${opts.repo}:${opts.path}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
15901
|
-
);
|
|
15902
|
-
}
|
|
15903
|
-
return await res.json();
|
|
15904
|
-
}
|
|
15905
|
-
function decodeContentsFile(file, label) {
|
|
15906
|
-
if (file.type !== "file" || file.encoding !== "base64" || typeof file.content !== "string") {
|
|
15907
|
-
throw new Error(`${label} is not a base64 file`);
|
|
15908
|
-
}
|
|
15909
|
-
if (typeof file.sha !== "string") {
|
|
15910
|
-
throw new Error(`${label} response missing sha`);
|
|
15911
|
-
}
|
|
15912
|
-
return {
|
|
15913
|
-
path: label,
|
|
15914
|
-
content: Buffer.from(file.content, "base64").toString("utf-8"),
|
|
15915
|
-
sha: file.sha
|
|
15916
|
-
};
|
|
15917
|
-
}
|
|
15918
|
-
async function loadGithubStateConfig(opts) {
|
|
15919
|
-
const configFile = await githubContentsFile({
|
|
15920
|
-
owner: opts.owner,
|
|
15921
|
-
repo: opts.repo,
|
|
15922
|
-
path: "kody.config.json",
|
|
15923
|
-
githubToken: opts.githubToken,
|
|
15924
|
-
fetchImpl: opts.fetchImpl
|
|
15925
|
-
});
|
|
15926
|
-
let raw = {};
|
|
15927
|
-
if (configFile) {
|
|
15928
|
-
const decoded = decodeContentsFile(configFile, "kody.config.json");
|
|
15929
|
-
try {
|
|
15930
|
-
raw = JSON.parse(decoded.content);
|
|
15931
|
-
} catch (err) {
|
|
15932
|
-
throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
15933
|
-
}
|
|
15934
|
-
}
|
|
15935
|
-
const githubRaw = recordValue3(raw.github) ?? {};
|
|
15936
|
-
const github = {
|
|
15937
|
-
owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
|
|
15938
|
-
repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
|
|
15939
|
-
};
|
|
15940
|
-
const nestedState = recordValue3(raw.state) ?? {};
|
|
15941
|
-
const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
|
|
15942
|
-
const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
|
|
15943
|
-
const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
|
|
15944
|
-
parseStateRepoSlug(stateRepo);
|
|
15945
|
-
const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
|
|
15946
|
-
return {
|
|
15947
|
-
github,
|
|
15948
|
-
state: {
|
|
15949
|
-
repo: stateRepo,
|
|
15950
|
-
path: normalizeStatePath(statePath)
|
|
15951
|
-
}
|
|
15952
|
-
};
|
|
15953
|
-
}
|
|
15954
|
-
async function readGithubStateText(opts) {
|
|
15955
|
-
const config = await loadGithubStateConfig(opts);
|
|
15956
|
-
return readGithubStateTextWithConfig({
|
|
15957
|
-
config,
|
|
15958
|
-
filePath: opts.filePath,
|
|
15959
|
-
githubToken: opts.githubToken,
|
|
15960
|
-
fetchImpl: opts.fetchImpl
|
|
15961
|
-
});
|
|
15962
|
-
}
|
|
15963
|
-
async function readGithubStateTextWithConfig(opts) {
|
|
15964
|
-
const target = stateRepoPath(opts.config, opts.filePath);
|
|
15965
|
-
const parsed = parseStateRepo(opts.config);
|
|
15966
|
-
const file = await githubContentsFile({
|
|
15967
|
-
owner: parsed.owner,
|
|
15968
|
-
repo: parsed.repo,
|
|
15969
|
-
path: target,
|
|
15970
|
-
githubToken: opts.githubToken,
|
|
15971
|
-
fetchImpl: opts.fetchImpl
|
|
15972
|
-
});
|
|
15973
|
-
return file ? decodeContentsFile(file, target) : null;
|
|
15974
|
-
}
|
|
15975
|
-
async function writeGithubStateTextWithConfig(opts) {
|
|
15976
|
-
const target = stateRepoPath(opts.config, opts.filePath);
|
|
15977
|
-
const parsed = parseStateRepo(opts.config);
|
|
15978
|
-
const payload = {
|
|
15979
|
-
message: opts.message,
|
|
15980
|
-
content: Buffer.from(opts.content, "utf-8").toString("base64")
|
|
15981
|
-
};
|
|
15982
|
-
if (opts.sha) payload.sha = opts.sha;
|
|
15983
|
-
const doFetch = opts.fetchImpl ?? fetch;
|
|
15984
|
-
const res = await doFetch(contentsUrl(parsed.owner, parsed.repo, target), {
|
|
15985
|
-
method: "PUT",
|
|
15986
|
-
headers: {
|
|
15987
|
-
Authorization: `Bearer ${opts.githubToken}`,
|
|
15988
|
-
Accept: "application/vnd.github+json",
|
|
15989
|
-
"Content-Type": "application/json",
|
|
15990
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
15991
|
-
"User-Agent": "kody-engine"
|
|
15992
|
-
},
|
|
15993
|
-
body: JSON.stringify(payload),
|
|
15994
|
-
signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
|
|
15995
|
-
});
|
|
15996
|
-
if (!res.ok) {
|
|
15997
|
-
throw new Error(
|
|
15998
|
-
`GitHub write ${parsed.owner}/${parsed.repo}:${target}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
|
|
15999
|
-
);
|
|
16000
|
-
}
|
|
16001
|
-
}
|
|
16002
|
-
function jsonlLines(text) {
|
|
16003
|
-
return text.split("\n").filter((line) => line.length > 0);
|
|
16004
|
-
}
|
|
16005
|
-
function renderJsonl(lines) {
|
|
16006
|
-
return lines.length > 0 ? `${lines.join("\n")}
|
|
16007
|
-
` : "";
|
|
16008
|
-
}
|
|
16009
|
-
function mergeJsonl(localText, remoteText) {
|
|
16010
|
-
const remoteLines = jsonlLines(remoteText);
|
|
16011
|
-
const seen = new Set(remoteLines);
|
|
16012
|
-
const localOnly = jsonlLines(localText).filter((line) => !seen.has(line));
|
|
16013
|
-
return renderJsonl([...remoteLines, ...localOnly]);
|
|
16014
|
-
}
|
|
16015
|
-
async function syncJsonlFileFromGithubState(opts) {
|
|
16016
|
-
const remote = await readGithubStateTextWithConfig(opts);
|
|
16017
|
-
if (!remote) return;
|
|
16018
|
-
const local = fs39.existsSync(opts.localPath) ? fs39.readFileSync(opts.localPath, "utf-8") : "";
|
|
16019
|
-
const next = mergeJsonl(local, remote.content);
|
|
16020
|
-
if (next === local) return;
|
|
16021
|
-
fs39.mkdirSync(path37.dirname(opts.localPath), { recursive: true });
|
|
16022
|
-
fs39.writeFileSync(opts.localPath, next);
|
|
16174
|
+
tryPostPr4(prNumber, `\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`, ctx.cwd);
|
|
16175
|
+
};
|
|
16176
|
+
}
|
|
16177
|
+
});
|
|
16178
|
+
|
|
16179
|
+
// src/scripts/reviewFlow.ts
|
|
16180
|
+
function tryPostPr5(prNumber, body, cwd) {
|
|
16181
|
+
try {
|
|
16182
|
+
postPrReviewComment(prNumber, body, cwd);
|
|
16183
|
+
} catch {
|
|
16184
|
+
}
|
|
16023
16185
|
}
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
}
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16186
|
+
var reviewFlow;
|
|
16187
|
+
var init_reviewFlow = __esm({
|
|
16188
|
+
"src/scripts/reviewFlow.ts"() {
|
|
16189
|
+
"use strict";
|
|
16190
|
+
init_branch();
|
|
16191
|
+
init_gha();
|
|
16192
|
+
init_issue();
|
|
16193
|
+
reviewFlow = async (ctx) => {
|
|
16194
|
+
const prNumber = ctx.args.pr;
|
|
16195
|
+
const pr = getPr(prNumber, ctx.cwd);
|
|
16196
|
+
if (pr.state !== "OPEN") {
|
|
16197
|
+
ctx.output.exitCode = 1;
|
|
16198
|
+
ctx.output.reason = `PR #${prNumber} is not OPEN (state: ${pr.state})`;
|
|
16199
|
+
ctx.skipAgent = true;
|
|
16200
|
+
return;
|
|
16201
|
+
}
|
|
16202
|
+
ctx.data.pr = pr;
|
|
16203
|
+
ctx.data.commentTargetType = "pr";
|
|
16204
|
+
ctx.data.commentTargetNumber = prNumber;
|
|
16205
|
+
checkoutPrBranch(prNumber, ctx.cwd);
|
|
16206
|
+
ctx.data.branch = getCurrentBranch(ctx.cwd);
|
|
16207
|
+
ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
|
|
16208
|
+
const runUrl = getRunUrl();
|
|
16209
|
+
const runSuffix = runUrl ? `, run ${runUrl}` : "";
|
|
16210
|
+
tryPostPr5(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
|
|
16211
|
+
};
|
|
16212
|
+
}
|
|
16213
|
+
});
|
|
16214
|
+
|
|
16215
|
+
// src/scripts/runFlow.ts
|
|
16216
|
+
function tryPost(issueNumber, body, cwd) {
|
|
16217
|
+
try {
|
|
16218
|
+
postIssueComment(issueNumber, body, cwd);
|
|
16219
|
+
} catch {
|
|
16046
16220
|
}
|
|
16047
16221
|
}
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16222
|
+
function resolveBaseOverride(value) {
|
|
16223
|
+
if (!value) return null;
|
|
16224
|
+
if (value.length > 200) return null;
|
|
16225
|
+
if (value.includes("..")) return null;
|
|
16226
|
+
if (!/^[a-z0-9][a-z0-9/._-]*$/.test(value)) return null;
|
|
16227
|
+
return value;
|
|
16228
|
+
}
|
|
16229
|
+
var runFlow;
|
|
16230
|
+
var init_runFlow = __esm({
|
|
16231
|
+
"src/scripts/runFlow.ts"() {
|
|
16051
16232
|
"use strict";
|
|
16052
|
-
|
|
16053
|
-
|
|
16054
|
-
|
|
16233
|
+
init_branch();
|
|
16234
|
+
init_gha();
|
|
16235
|
+
init_issue();
|
|
16236
|
+
runFlow = async (ctx) => {
|
|
16237
|
+
const issueNumber = ctx.args.issue;
|
|
16238
|
+
const issue = getIssue(issueNumber, ctx.cwd);
|
|
16239
|
+
const cfgCtx = ctx.config.issueContext ?? {};
|
|
16240
|
+
const commentsFormatted = formatIssueComments(
|
|
16241
|
+
issue.comments,
|
|
16242
|
+
cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
|
|
16243
|
+
cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
|
|
16244
|
+
);
|
|
16245
|
+
ctx.data.issue = { ...issue, commentsFormatted };
|
|
16246
|
+
if (issue.isPullRequest) {
|
|
16247
|
+
ctx.data.commentTargetType = "pr";
|
|
16248
|
+
ctx.data.commentTargetNumber = issueNumber;
|
|
16249
|
+
ctx.skipAgent = true;
|
|
16250
|
+
ctx.output.exitCode = 1;
|
|
16251
|
+
ctx.output.reason = `run target #${issueNumber} is a pull request; dispatch a PR action or the source issue instead`;
|
|
16252
|
+
return;
|
|
16253
|
+
}
|
|
16254
|
+
ctx.data.commentTargetType = "issue";
|
|
16255
|
+
ctx.data.commentTargetNumber = issueNumber;
|
|
16256
|
+
const argBase = resolveBaseOverride(ctx.args.base);
|
|
16257
|
+
const baseRaw = ctx.args.base;
|
|
16258
|
+
if (baseRaw && !argBase) {
|
|
16259
|
+
process.stderr.write(`[kody runFlow] ignoring --base "${baseRaw}" (must match kody-task or goal-branch pattern)
|
|
16260
|
+
`);
|
|
16261
|
+
}
|
|
16262
|
+
const base = argBase;
|
|
16263
|
+
if (base) {
|
|
16264
|
+
ctx.data.baseBranch = base;
|
|
16265
|
+
process.stderr.write(`[kody runFlow] resolved base branch: ${base} (from --base)
|
|
16266
|
+
`);
|
|
16267
|
+
}
|
|
16268
|
+
const branchInfo = ensureFeatureBranch(
|
|
16269
|
+
issueNumber,
|
|
16270
|
+
issue.title,
|
|
16271
|
+
ctx.config.git.defaultBranch,
|
|
16272
|
+
ctx.cwd,
|
|
16273
|
+
base ?? void 0
|
|
16274
|
+
);
|
|
16275
|
+
ctx.data.branch = branchInfo.branch;
|
|
16276
|
+
const runUrl = getRunUrl();
|
|
16277
|
+
const startMsg = runUrl ? `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\`, run ${runUrl}` : `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\``;
|
|
16278
|
+
tryPost(issueNumber, startMsg, ctx.cwd);
|
|
16279
|
+
};
|
|
16055
16280
|
}
|
|
16056
16281
|
});
|
|
16057
16282
|
|
|
16058
16283
|
// src/scripts/previewBuildHelpers.ts
|
|
16059
|
-
import { createDecipheriv, createHash as
|
|
16284
|
+
import { createDecipheriv as createDecipheriv2, createHash as createHash4, hkdfSync as hkdfSync2 } from "crypto";
|
|
16060
16285
|
function shortHash(s) {
|
|
16061
|
-
return
|
|
16286
|
+
return createHash4("sha256").update(s).digest("hex").slice(0, 6);
|
|
16062
16287
|
}
|
|
16063
16288
|
function previewAppName(repo, pr) {
|
|
16064
16289
|
const [owner, name] = repo.split("/");
|
|
@@ -16087,7 +16312,7 @@ function decryptVaultPayload(payload, keyRaw) {
|
|
|
16087
16312
|
const iv = Buffer.from(ivB64, "base64");
|
|
16088
16313
|
const ct = Buffer.from(ctB64, "base64");
|
|
16089
16314
|
const tag = Buffer.from(tagB64, "base64");
|
|
16090
|
-
const decipher =
|
|
16315
|
+
const decipher = createDecipheriv2("aes-256-gcm", key, iv);
|
|
16091
16316
|
decipher.setAuthTag(tag);
|
|
16092
16317
|
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
16093
16318
|
}
|
|
@@ -16100,7 +16325,7 @@ function derivePreviewVerifyKey(masterKeyRaw) {
|
|
|
16100
16325
|
if (masterKey.length !== 32) {
|
|
16101
16326
|
throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
|
|
16102
16327
|
}
|
|
16103
|
-
return Buffer.from(
|
|
16328
|
+
return Buffer.from(hkdfSync2("sha256", masterKey, Buffer.alloc(0), PREVIEW_KEY_INFO, 32)).toString("hex");
|
|
16104
16329
|
}
|
|
16105
16330
|
function previewRuntimeEnv(args) {
|
|
16106
16331
|
return {
|
|
@@ -16131,7 +16356,7 @@ function formatPreviewComment(args) {
|
|
|
16131
16356
|
].join("\n");
|
|
16132
16357
|
}
|
|
16133
16358
|
function defaultImageTag(repo, ref) {
|
|
16134
|
-
return
|
|
16359
|
+
return createHash4("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
|
|
16135
16360
|
}
|
|
16136
16361
|
var NEVER_PASS_TO_BUILD, PREVIEW_KEY_INFO;
|
|
16137
16362
|
var init_previewBuildHelpers = __esm({
|
|
@@ -16240,12 +16465,12 @@ fi
|
|
|
16240
16465
|
|
|
16241
16466
|
// src/scripts/runPreviewBuild.ts
|
|
16242
16467
|
import { copyFile, writeFile } from "fs/promises";
|
|
16243
|
-
import * as
|
|
16468
|
+
import * as path39 from "path";
|
|
16244
16469
|
import { fileURLToPath } from "url";
|
|
16245
16470
|
function bundledDockerfilePath(mode) {
|
|
16246
|
-
const here =
|
|
16471
|
+
const here = path39.dirname(fileURLToPath(import.meta.url));
|
|
16247
16472
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
16248
|
-
return
|
|
16473
|
+
return path39.join(here, "preview-build-templates", file);
|
|
16249
16474
|
}
|
|
16250
16475
|
function required(name) {
|
|
16251
16476
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -16496,10 +16721,10 @@ var init_runPreviewBuild = __esm({
|
|
|
16496
16721
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
16497
16722
|
if (Object.keys(buildEnv).length > 0) {
|
|
16498
16723
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
16499
|
-
await writeFile(
|
|
16724
|
+
await writeFile(path39.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
16500
16725
|
`, "utf8");
|
|
16501
16726
|
}
|
|
16502
|
-
const consumerDockerfile =
|
|
16727
|
+
const consumerDockerfile = path39.join(ctx.cwd, "Dockerfile.preview");
|
|
16503
16728
|
const { stat } = await import("fs/promises");
|
|
16504
16729
|
let hasConsumerDockerfile = false;
|
|
16505
16730
|
try {
|
|
@@ -16683,8 +16908,8 @@ var init_tickShellRunner = __esm({
|
|
|
16683
16908
|
});
|
|
16684
16909
|
|
|
16685
16910
|
// src/scripts/runScheduledExecutableTick.ts
|
|
16686
|
-
import * as
|
|
16687
|
-
import * as
|
|
16911
|
+
import * as fs41 from "fs";
|
|
16912
|
+
import * as path40 from "path";
|
|
16688
16913
|
var runScheduledExecutableTick;
|
|
16689
16914
|
var init_runScheduledExecutableTick = __esm({
|
|
16690
16915
|
"src/scripts/runScheduledExecutableTick.ts"() {
|
|
@@ -16704,14 +16929,14 @@ var init_runScheduledExecutableTick = __esm({
|
|
|
16704
16929
|
ctx.output.reason = `runScheduledExecutableTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
|
|
16705
16930
|
return;
|
|
16706
16931
|
}
|
|
16707
|
-
const capability = resolveCapabilityFolder(slug2,
|
|
16932
|
+
const capability = resolveCapabilityFolder(slug2, path40.join(ctx.cwd, jobsDir));
|
|
16708
16933
|
if (!capability) {
|
|
16709
16934
|
ctx.output.exitCode = 99;
|
|
16710
16935
|
ctx.output.reason = `runScheduledExecutableTick: capability folder not found or incomplete: ${slug2} (searched ${jobsDir} and company store)`;
|
|
16711
16936
|
return;
|
|
16712
16937
|
}
|
|
16713
|
-
const shellPath =
|
|
16714
|
-
if (!
|
|
16938
|
+
const shellPath = path40.join(profile.dir, shell);
|
|
16939
|
+
if (!fs41.existsSync(shellPath)) {
|
|
16715
16940
|
ctx.output.exitCode = 99;
|
|
16716
16941
|
ctx.output.reason = `runScheduledExecutableTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
16717
16942
|
return;
|
|
@@ -16742,8 +16967,8 @@ var init_runScheduledExecutableTick = __esm({
|
|
|
16742
16967
|
});
|
|
16743
16968
|
|
|
16744
16969
|
// src/scripts/runTickScript.ts
|
|
16745
|
-
import * as
|
|
16746
|
-
import * as
|
|
16970
|
+
import * as fs42 from "fs";
|
|
16971
|
+
import * as path41 from "path";
|
|
16747
16972
|
var runTickScript;
|
|
16748
16973
|
var init_runTickScript = __esm({
|
|
16749
16974
|
"src/scripts/runTickScript.ts"() {
|
|
@@ -16762,10 +16987,10 @@ var init_runTickScript = __esm({
|
|
|
16762
16987
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
16763
16988
|
return;
|
|
16764
16989
|
}
|
|
16765
|
-
const capability = readCapabilityFolder(
|
|
16990
|
+
const capability = readCapabilityFolder(path41.join(ctx.cwd, jobsDir), slug2);
|
|
16766
16991
|
if (!capability) {
|
|
16767
16992
|
ctx.output.exitCode = 99;
|
|
16768
|
-
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${
|
|
16993
|
+
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path41.join(ctx.cwd, jobsDir, slug2)}`;
|
|
16769
16994
|
return;
|
|
16770
16995
|
}
|
|
16771
16996
|
const tickScript = capability.config.tickScript;
|
|
@@ -16774,8 +16999,8 @@ var init_runTickScript = __esm({
|
|
|
16774
16999
|
ctx.output.reason = `runTickScript: capability ${slug2} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
|
|
16775
17000
|
return;
|
|
16776
17001
|
}
|
|
16777
|
-
const scriptPath =
|
|
16778
|
-
if (!
|
|
17002
|
+
const scriptPath = path41.isAbsolute(tickScript) ? tickScript : path41.join(ctx.cwd, tickScript);
|
|
17003
|
+
if (!fs42.existsSync(scriptPath)) {
|
|
16779
17004
|
ctx.output.exitCode = 99;
|
|
16780
17005
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
16781
17006
|
return;
|
|
@@ -17598,7 +17823,7 @@ var init_warmupMcp = __esm({
|
|
|
17598
17823
|
});
|
|
17599
17824
|
|
|
17600
17825
|
// src/scripts/writeAgentRunSummary.ts
|
|
17601
|
-
import * as
|
|
17826
|
+
import * as fs43 from "fs";
|
|
17602
17827
|
var writeAgentRunSummary;
|
|
17603
17828
|
var init_writeAgentRunSummary = __esm({
|
|
17604
17829
|
"src/scripts/writeAgentRunSummary.ts"() {
|
|
@@ -17624,7 +17849,7 @@ var init_writeAgentRunSummary = __esm({
|
|
|
17624
17849
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
17625
17850
|
lines.push("");
|
|
17626
17851
|
try {
|
|
17627
|
-
|
|
17852
|
+
fs43.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
17628
17853
|
`);
|
|
17629
17854
|
} catch {
|
|
17630
17855
|
}
|
|
@@ -17746,7 +17971,7 @@ var init_scripts = __esm({
|
|
|
17746
17971
|
init_appendCompanyActivity();
|
|
17747
17972
|
init_appendCompanyIntentDecision();
|
|
17748
17973
|
init_applyCapabilityReports();
|
|
17749
|
-
|
|
17974
|
+
init_applyAgencyArchitectDecision();
|
|
17750
17975
|
init_buildSyntheticPlugin();
|
|
17751
17976
|
init_checkCoverageWithRetry();
|
|
17752
17977
|
init_classifyByLabel();
|
|
@@ -17793,7 +18018,7 @@ var init_scripts = __esm({
|
|
|
17793
18018
|
init_openAgentFactoryStatePr();
|
|
17794
18019
|
init_openQaIssue();
|
|
17795
18020
|
init_parseAgentResult();
|
|
17796
|
-
|
|
18021
|
+
init_parseAgencyArchitectDecision();
|
|
17797
18022
|
init_parseIssueStateFromAgentResult();
|
|
17798
18023
|
init_parseJobStateFromAgentResult();
|
|
17799
18024
|
init_parseReproOutput();
|
|
@@ -17888,7 +18113,7 @@ var init_scripts = __esm({
|
|
|
17888
18113
|
};
|
|
17889
18114
|
postflightScripts = {
|
|
17890
18115
|
parseAgentResult: parseAgentResult2,
|
|
17891
|
-
|
|
18116
|
+
parseAgencyArchitectDecision,
|
|
17892
18117
|
parseIssueStateFromAgentResult,
|
|
17893
18118
|
parseJobStateFromAgentResult,
|
|
17894
18119
|
parseReproOutput,
|
|
@@ -17896,7 +18121,7 @@ var init_scripts = __esm({
|
|
|
17896
18121
|
writeJobStateFile,
|
|
17897
18122
|
appendCompanyActivity,
|
|
17898
18123
|
appendCompanyIntentDecision: appendCompanyIntentDecision2,
|
|
17899
|
-
|
|
18124
|
+
applyAgencyArchitectDecision,
|
|
17900
18125
|
requireFeedbackActions,
|
|
17901
18126
|
requirePlanDeviations,
|
|
17902
18127
|
verify,
|
|
@@ -17946,53 +18171,53 @@ var init_scripts = __esm({
|
|
|
17946
18171
|
// src/stateWorkspace.ts
|
|
17947
18172
|
import { execFileSync as execFileSync24 } from "child_process";
|
|
17948
18173
|
import * as crypto3 from "crypto";
|
|
17949
|
-
import * as
|
|
18174
|
+
import * as fs44 from "fs";
|
|
17950
18175
|
import * as os7 from "os";
|
|
17951
|
-
import * as
|
|
18176
|
+
import * as path42 from "path";
|
|
17952
18177
|
function writeLocalFile(cwd, relativePath, content) {
|
|
17953
|
-
const fullPath =
|
|
17954
|
-
|
|
17955
|
-
|
|
18178
|
+
const fullPath = path42.join(cwd, relativePath);
|
|
18179
|
+
fs44.mkdirSync(path42.dirname(fullPath), { recursive: true });
|
|
18180
|
+
fs44.writeFileSync(fullPath, content);
|
|
17956
18181
|
}
|
|
17957
18182
|
function copyPath(source, target) {
|
|
17958
|
-
const st =
|
|
17959
|
-
|
|
18183
|
+
const st = fs44.lstatSync(source);
|
|
18184
|
+
fs44.rmSync(target, { recursive: true, force: true });
|
|
17960
18185
|
if (st.isSymbolicLink()) return;
|
|
17961
|
-
|
|
17962
|
-
|
|
18186
|
+
fs44.mkdirSync(path42.dirname(target), { recursive: true });
|
|
18187
|
+
fs44.cpSync(source, target, { recursive: true, force: true });
|
|
17963
18188
|
}
|
|
17964
18189
|
function overlayDirectoryChildren(cwd, sourceDir, localDir) {
|
|
17965
|
-
if (!
|
|
17966
|
-
for (const entry of
|
|
17967
|
-
const source =
|
|
17968
|
-
const target =
|
|
18190
|
+
if (!fs44.existsSync(sourceDir)) return;
|
|
18191
|
+
for (const entry of fs44.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
18192
|
+
const source = path42.join(sourceDir, entry.name);
|
|
18193
|
+
const target = path42.join(cwd, localDir, entry.name);
|
|
17969
18194
|
copyPath(source, target);
|
|
17970
18195
|
}
|
|
17971
18196
|
}
|
|
17972
18197
|
function hydrateStateWorkspace(config, cwd) {
|
|
17973
18198
|
if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
|
|
17974
18199
|
const parsed = parseStateRepo(config);
|
|
17975
|
-
const hydrateKey = `${
|
|
18200
|
+
const hydrateKey = `${path42.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
|
|
17976
18201
|
if (hydratedWorkspaces.has(hydrateKey)) return;
|
|
17977
18202
|
const snapshotRoot = fetchStateSnapshot(parsed);
|
|
17978
18203
|
for (const mapping of DIR_MAPPINGS) {
|
|
17979
|
-
overlayDirectoryChildren(cwd,
|
|
18204
|
+
overlayDirectoryChildren(cwd, path42.join(snapshotRoot, mapping.stateDir), mapping.localDir);
|
|
17980
18205
|
}
|
|
17981
18206
|
for (const mapping of FILE_MAPPINGS) {
|
|
17982
|
-
const source =
|
|
17983
|
-
if (
|
|
17984
|
-
writeLocalFile(cwd, mapping.localPath,
|
|
18207
|
+
const source = path42.join(snapshotRoot, mapping.statePath);
|
|
18208
|
+
if (fs44.existsSync(source) && !fs44.lstatSync(source).isSymbolicLink() && fs44.statSync(source).isFile()) {
|
|
18209
|
+
writeLocalFile(cwd, mapping.localPath, fs44.readFileSync(source, "utf-8"));
|
|
17985
18210
|
}
|
|
17986
18211
|
}
|
|
17987
18212
|
hydratedWorkspaces.add(hydrateKey);
|
|
17988
18213
|
}
|
|
17989
18214
|
function fetchStateSnapshot(parsed) {
|
|
17990
|
-
const cacheDir =
|
|
18215
|
+
const cacheDir = path42.join(cacheRoot2(), cacheKey3(parsed));
|
|
17991
18216
|
const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
17992
18217
|
try {
|
|
17993
|
-
|
|
17994
|
-
if (!
|
|
17995
|
-
|
|
18218
|
+
fs44.mkdirSync(path42.dirname(cacheDir), { recursive: true });
|
|
18219
|
+
if (!fs44.existsSync(path42.join(cacheDir, ".git"))) {
|
|
18220
|
+
fs44.rmSync(cacheDir, { recursive: true, force: true });
|
|
17996
18221
|
runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
|
|
17997
18222
|
}
|
|
17998
18223
|
runGit3(["-C", cacheDir, "remote", "set-url", "origin", url]);
|
|
@@ -18007,12 +18232,12 @@ function fetchStateSnapshot(parsed) {
|
|
|
18007
18232
|
`stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
|
|
18008
18233
|
);
|
|
18009
18234
|
}
|
|
18010
|
-
return
|
|
18235
|
+
return path42.join(cacheDir, parsed.basePath);
|
|
18011
18236
|
}
|
|
18012
18237
|
function cacheRoot2() {
|
|
18013
|
-
return process.env[CACHE_ENV2]?.trim() ||
|
|
18238
|
+
return process.env[CACHE_ENV2]?.trim() || path42.join(os7.homedir(), ".cache", "kody", "state-repo");
|
|
18014
18239
|
}
|
|
18015
|
-
function
|
|
18240
|
+
function cacheKey3(parsed) {
|
|
18016
18241
|
return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
|
|
18017
18242
|
}
|
|
18018
18243
|
function runGit3(args) {
|
|
@@ -18050,15 +18275,15 @@ var init_stateWorkspace = __esm({
|
|
|
18050
18275
|
"use strict";
|
|
18051
18276
|
init_stateRepo();
|
|
18052
18277
|
DIR_MAPPINGS = [
|
|
18053
|
-
{ stateDir: "capabilities", localDir:
|
|
18054
|
-
{ stateDir: "agents", localDir:
|
|
18055
|
-
{ stateDir: "context", localDir:
|
|
18056
|
-
{ stateDir: "memory", localDir:
|
|
18278
|
+
{ stateDir: "capabilities", localDir: path42.join(".kody", "capabilities") },
|
|
18279
|
+
{ stateDir: "agents", localDir: path42.join(".kody", "agents") },
|
|
18280
|
+
{ stateDir: "context", localDir: path42.join(".kody", "context") },
|
|
18281
|
+
{ stateDir: "memory", localDir: path42.join(".kody", "memory") }
|
|
18057
18282
|
];
|
|
18058
18283
|
FILE_MAPPINGS = [
|
|
18059
|
-
{ statePath: "instructions.md", localPath:
|
|
18060
|
-
{ statePath: "variables.json", localPath:
|
|
18061
|
-
{ statePath: "secrets.enc", localPath:
|
|
18284
|
+
{ statePath: "instructions.md", localPath: path42.join(".kody", "instructions.md") },
|
|
18285
|
+
{ statePath: "variables.json", localPath: path42.join(".kody", "variables.json") },
|
|
18286
|
+
{ statePath: "secrets.enc", localPath: path42.join(".kody", "secrets.enc") }
|
|
18062
18287
|
];
|
|
18063
18288
|
CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
|
|
18064
18289
|
TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
|
|
@@ -18132,8 +18357,8 @@ var init_tools = __esm({
|
|
|
18132
18357
|
|
|
18133
18358
|
// src/executor.ts
|
|
18134
18359
|
import { spawn as spawn7 } from "child_process";
|
|
18135
|
-
import * as
|
|
18136
|
-
import * as
|
|
18360
|
+
import * as fs45 from "fs";
|
|
18361
|
+
import * as path43 from "path";
|
|
18137
18362
|
function isMutatingPostflight(scriptName) {
|
|
18138
18363
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
18139
18364
|
}
|
|
@@ -18314,7 +18539,7 @@ async function runExecutable(profileName, input) {
|
|
|
18314
18539
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
18315
18540
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
18316
18541
|
const invokeAgent = async (prompt) => {
|
|
18317
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
18542
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path43.isAbsolute(p) ? p : path43.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
18318
18543
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
18319
18544
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
18320
18545
|
const agents = loadSubagents(profile);
|
|
@@ -18707,17 +18932,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
18707
18932
|
function resolveProfilePath(profileName) {
|
|
18708
18933
|
const found = resolveExecutable(profileName);
|
|
18709
18934
|
if (found) return found;
|
|
18710
|
-
const here =
|
|
18935
|
+
const here = path43.dirname(new URL(import.meta.url).pathname);
|
|
18711
18936
|
const candidates = [
|
|
18712
|
-
|
|
18937
|
+
path43.join(here, "executables", profileName, "profile.json"),
|
|
18713
18938
|
// same-dir sibling (dev)
|
|
18714
|
-
|
|
18939
|
+
path43.join(here, "..", "executables", profileName, "profile.json"),
|
|
18715
18940
|
// up one (prod: dist/bin → dist/executables)
|
|
18716
|
-
|
|
18941
|
+
path43.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
18717
18942
|
// fallback
|
|
18718
18943
|
];
|
|
18719
18944
|
for (const c of candidates) {
|
|
18720
|
-
if (
|
|
18945
|
+
if (fs45.existsSync(c)) return c;
|
|
18721
18946
|
}
|
|
18722
18947
|
return candidates[0];
|
|
18723
18948
|
}
|
|
@@ -18832,8 +19057,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
18832
19057
|
}
|
|
18833
19058
|
async function runShellEntry(entry, ctx, profile) {
|
|
18834
19059
|
const shellName = entry.shell;
|
|
18835
|
-
const shellPath =
|
|
18836
|
-
if (!
|
|
19060
|
+
const shellPath = path43.join(profile.dir, shellName);
|
|
19061
|
+
if (!fs45.existsSync(shellPath)) {
|
|
18837
19062
|
ctx.skipAgent = true;
|
|
18838
19063
|
ctx.output.exitCode = 99;
|
|
18839
19064
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -18983,8 +19208,8 @@ var init_executor = __esm({
|
|
|
18983
19208
|
});
|
|
18984
19209
|
|
|
18985
19210
|
// src/workflowDefinitions.ts
|
|
18986
|
-
import * as
|
|
18987
|
-
import * as
|
|
19211
|
+
import * as fs46 from "fs";
|
|
19212
|
+
import * as path44 from "path";
|
|
18988
19213
|
function isWorkflowDefinitionId(value) {
|
|
18989
19214
|
return WORKFLOW_ID_PATTERN.test(value);
|
|
18990
19215
|
}
|
|
@@ -18998,13 +19223,11 @@ function normalizeWorkflowDefinition(value) {
|
|
|
18998
19223
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
18999
19224
|
const raw = value;
|
|
19000
19225
|
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
19001
|
-
const instructions = typeof raw.instructions === "string" ? raw.instructions.trim() : "";
|
|
19002
19226
|
const capabilities = normalizeWorkflowCapabilities(raw.capabilities);
|
|
19003
|
-
if (!name ||
|
|
19227
|
+
if (!name || capabilities.length === 0) return null;
|
|
19004
19228
|
return {
|
|
19005
19229
|
version: 1,
|
|
19006
19230
|
name,
|
|
19007
|
-
instructions,
|
|
19008
19231
|
capabilities,
|
|
19009
19232
|
...typeof raw.createdAt === "string" ? { createdAt: raw.createdAt } : {},
|
|
19010
19233
|
...typeof raw.updatedAt === "string" ? { updatedAt: raw.updatedAt } : {}
|
|
@@ -19018,17 +19241,17 @@ function readWorkflowDefinition(config, cwd, id) {
|
|
|
19018
19241
|
function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
|
|
19019
19242
|
return {
|
|
19020
19243
|
slug: id,
|
|
19021
|
-
dir:
|
|
19244
|
+
dir: path44.dirname(source),
|
|
19022
19245
|
profilePath: source,
|
|
19023
19246
|
bodyPath: source,
|
|
19024
19247
|
title: workflow.name,
|
|
19025
|
-
body:
|
|
19026
|
-
rawBody:
|
|
19248
|
+
body: "",
|
|
19249
|
+
rawBody: "",
|
|
19027
19250
|
rawProfile: { name: id, workflow },
|
|
19028
19251
|
config: {
|
|
19029
19252
|
action: id,
|
|
19030
19253
|
workflow: workflowDefinitionToConfig(workflow),
|
|
19031
|
-
describe: workflow.
|
|
19254
|
+
describe: workflow.name
|
|
19032
19255
|
}
|
|
19033
19256
|
};
|
|
19034
19257
|
}
|
|
@@ -19053,9 +19276,9 @@ function workflowDefinitionToConfig(workflow) {
|
|
|
19053
19276
|
function readCompanyStoreWorkflowDefinition(id) {
|
|
19054
19277
|
const root = getCompanyStoreAssetRoot("workflows");
|
|
19055
19278
|
if (!root) return null;
|
|
19056
|
-
const filePath =
|
|
19057
|
-
if (!
|
|
19058
|
-
return parseWorkflowDefinition(
|
|
19279
|
+
const filePath = path44.join(root, id, "workflow.json");
|
|
19280
|
+
if (!fs46.existsSync(filePath)) return null;
|
|
19281
|
+
return parseWorkflowDefinition(fs46.readFileSync(filePath, "utf8"));
|
|
19059
19282
|
}
|
|
19060
19283
|
function parseWorkflowDefinition(content) {
|
|
19061
19284
|
try {
|
|
@@ -19087,7 +19310,7 @@ __export(job_exports, {
|
|
|
19087
19310
|
stableJobKey: () => stableJobKey,
|
|
19088
19311
|
validateJob: () => validateJob
|
|
19089
19312
|
});
|
|
19090
|
-
import * as
|
|
19313
|
+
import * as path45 from "path";
|
|
19091
19314
|
function newJobId(flavor) {
|
|
19092
19315
|
localJobSeq += 1;
|
|
19093
19316
|
const runId = process.env.GITHUB_RUN_ID;
|
|
@@ -19139,7 +19362,7 @@ function parseCapabilityResultTarget(raw) {
|
|
|
19139
19362
|
async function runJob(job, base) {
|
|
19140
19363
|
const valid = validateJob(job);
|
|
19141
19364
|
const action = valid.action ?? valid.capability;
|
|
19142
|
-
const projectCapabilitiesRoot =
|
|
19365
|
+
const projectCapabilitiesRoot = path45.join(base.cwd, ".kody", "capabilities");
|
|
19143
19366
|
const resolvedCapability = !valid.workflow && action ? resolveCapabilityAction(action, projectCapabilitiesRoot) : null;
|
|
19144
19367
|
const capabilityIdentity = valid.capability ?? resolvedCapability?.capability;
|
|
19145
19368
|
const capabilityContext = valid.workflow ? null : loadCapabilityContext(capabilityIdentity, base.cwd);
|
|
@@ -19152,7 +19375,7 @@ async function runJob(job, base) {
|
|
|
19152
19375
|
}
|
|
19153
19376
|
const workflow = capabilityContext?.config.workflow ?? workflowContext?.config.workflow;
|
|
19154
19377
|
const workflowIdentity = valid.workflow ?? capabilityIdentity ?? workflowContext?.slug;
|
|
19155
|
-
const capabilitySelectedExecutable = resolvedCapability?.executable ?? capabilityContext?.config.executable ?? capabilityContext?.config.executables?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
|
|
19378
|
+
const capabilitySelectedExecutable = resolvedCapability?.executable ?? capabilityContext?.config.implementation ?? capabilityContext?.config.executable ?? capabilityContext?.config.executables?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
|
|
19156
19379
|
const profileName = valid.executable ?? capabilitySelectedExecutable;
|
|
19157
19380
|
if (workflow && shouldRunCapabilityWorkflow(valid, workflow, workflowIdentity, capabilitySelectedExecutable, base)) {
|
|
19158
19381
|
const workflowCapability = capabilityContext ?? workflowContext;
|
|
@@ -19325,7 +19548,7 @@ function shouldRunWorkflowStep(step, data) {
|
|
|
19325
19548
|
if (!step.runWhen) return true;
|
|
19326
19549
|
const context = workflowConditionContext(data);
|
|
19327
19550
|
return Object.entries(step.runWhen).every(
|
|
19328
|
-
([
|
|
19551
|
+
([path51, expected]) => valueMatches(resolveDottedPath2(context, path51), expected)
|
|
19329
19552
|
);
|
|
19330
19553
|
}
|
|
19331
19554
|
function canContinueWorkflow(step, outcome) {
|
|
@@ -19395,7 +19618,7 @@ function composeStepWhy(parentWhy, step) {
|
|
|
19395
19618
|
}
|
|
19396
19619
|
function loadCapabilityContext(slug2, cwd) {
|
|
19397
19620
|
if (!slug2) return null;
|
|
19398
|
-
return resolveCapabilityFolder(slug2,
|
|
19621
|
+
return resolveCapabilityFolder(slug2, path45.join(cwd, ".kody", "capabilities"));
|
|
19399
19622
|
}
|
|
19400
19623
|
function loadWorkflowContext(slug2, base) {
|
|
19401
19624
|
if (!slug2 || !base.config || !isWorkflowDefinitionId(slug2)) return null;
|
|
@@ -19553,9 +19776,9 @@ function translateOpenAISseToBrain(opts) {
|
|
|
19553
19776
|
}
|
|
19554
19777
|
|
|
19555
19778
|
// src/servers/brain-serve.ts
|
|
19556
|
-
import * as
|
|
19779
|
+
import * as fs49 from "fs";
|
|
19557
19780
|
import { createServer as createServer2 } from "http";
|
|
19558
|
-
import * as
|
|
19781
|
+
import * as path48 from "path";
|
|
19559
19782
|
|
|
19560
19783
|
// src/chat/loop.ts
|
|
19561
19784
|
init_agent();
|
|
@@ -20231,8 +20454,8 @@ init_config();
|
|
|
20231
20454
|
|
|
20232
20455
|
// src/kody-cli.ts
|
|
20233
20456
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
20234
|
-
import * as
|
|
20235
|
-
import * as
|
|
20457
|
+
import * as fs47 from "fs";
|
|
20458
|
+
import * as path46 from "path";
|
|
20236
20459
|
|
|
20237
20460
|
// src/app-auth.ts
|
|
20238
20461
|
import { createSign } from "crypto";
|
|
@@ -20735,6 +20958,7 @@ init_gha();
|
|
|
20735
20958
|
init_issue();
|
|
20736
20959
|
init_job();
|
|
20737
20960
|
init_lifecycleLabels();
|
|
20961
|
+
init_companyStore();
|
|
20738
20962
|
init_registry();
|
|
20739
20963
|
|
|
20740
20964
|
// src/run-request.ts
|
|
@@ -20805,6 +21029,9 @@ var FAILED_DISPATCH_LABEL = {
|
|
|
20805
21029
|
color: "e11d21",
|
|
20806
21030
|
description: "Kody failed or rejected the run"
|
|
20807
21031
|
};
|
|
21032
|
+
function objectValue2(value) {
|
|
21033
|
+
return value && typeof value === "object" ? value : void 0;
|
|
21034
|
+
}
|
|
20808
21035
|
var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
|
|
20809
21036
|
|
|
20810
21037
|
Usage:
|
|
@@ -20962,9 +21189,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
20962
21189
|
return void 0;
|
|
20963
21190
|
}
|
|
20964
21191
|
function detectPackageManager2(cwd) {
|
|
20965
|
-
if (
|
|
20966
|
-
if (
|
|
20967
|
-
if (
|
|
21192
|
+
if (fs47.existsSync(path46.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
21193
|
+
if (fs47.existsSync(path46.join(cwd, "yarn.lock"))) return "yarn";
|
|
21194
|
+
if (fs47.existsSync(path46.join(cwd, "bun.lockb"))) return "bun";
|
|
20968
21195
|
return "npm";
|
|
20969
21196
|
}
|
|
20970
21197
|
function shouldChainScheduledWatch(match) {
|
|
@@ -21057,8 +21284,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
21057
21284
|
const logPath = lastRunLogPath(cwd);
|
|
21058
21285
|
let tail = "";
|
|
21059
21286
|
try {
|
|
21060
|
-
if (
|
|
21061
|
-
const content =
|
|
21287
|
+
if (fs47.existsSync(logPath)) {
|
|
21288
|
+
const content = fs47.readFileSync(logPath, "utf-8");
|
|
21062
21289
|
tail = content.slice(-3e3);
|
|
21063
21290
|
}
|
|
21064
21291
|
} catch {
|
|
@@ -21087,7 +21314,7 @@ async function runCi(argv) {
|
|
|
21087
21314
|
return 0;
|
|
21088
21315
|
}
|
|
21089
21316
|
const args = parseCiArgs(argv);
|
|
21090
|
-
const cwd = args.cwd ?
|
|
21317
|
+
const cwd = args.cwd ? path46.resolve(args.cwd) : process.cwd();
|
|
21091
21318
|
let earlyConfig;
|
|
21092
21319
|
let earlyConfigError;
|
|
21093
21320
|
try {
|
|
@@ -21110,6 +21337,9 @@ async function runCi(argv) {
|
|
|
21110
21337
|
`);
|
|
21111
21338
|
return 64;
|
|
21112
21339
|
}
|
|
21340
|
+
if (parsedRunRequest && "request" in parsedRunRequest) {
|
|
21341
|
+
applyCompanyStoreRuntimeConfig(parsedRunRequest.request.input);
|
|
21342
|
+
}
|
|
21113
21343
|
if (!args.issueNumber && !autoFallback && parsedRunRequest && "request" in parsedRunRequest) {
|
|
21114
21344
|
const route = routeRunRequest(parsedRunRequest.request);
|
|
21115
21345
|
if (route.kind === "error") {
|
|
@@ -21133,13 +21363,15 @@ async function runCi(argv) {
|
|
|
21133
21363
|
forceRunCliArgs = { goal: envForceMessage };
|
|
21134
21364
|
}
|
|
21135
21365
|
}
|
|
21136
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
21366
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs47.existsSync(dispatchEventPath)) {
|
|
21137
21367
|
try {
|
|
21138
|
-
const evt = JSON.parse(
|
|
21139
|
-
const
|
|
21140
|
-
|
|
21141
|
-
const
|
|
21142
|
-
const
|
|
21368
|
+
const evt = JSON.parse(fs47.readFileSync(dispatchEventPath, "utf-8"));
|
|
21369
|
+
const inputs = objectValue2(evt.inputs);
|
|
21370
|
+
applyCompanyStoreRuntimeConfig(inputs);
|
|
21371
|
+
const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
|
|
21372
|
+
const sessionInput = String(inputs?.sessionId ?? "");
|
|
21373
|
+
const capabilityInput = String(inputs?.capability ?? inputs?.executable ?? "").trim();
|
|
21374
|
+
const messageInput = String(inputs?.message ?? "").trim();
|
|
21143
21375
|
const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
21144
21376
|
if (noTarget && capabilityInput) {
|
|
21145
21377
|
forceRunAction = capabilityInput;
|
|
@@ -21497,8 +21729,8 @@ init_repoWorkspace();
|
|
|
21497
21729
|
|
|
21498
21730
|
// src/scripts/brainTurnLog.ts
|
|
21499
21731
|
init_runtimePaths();
|
|
21500
|
-
import * as
|
|
21501
|
-
import * as
|
|
21732
|
+
import * as fs48 from "fs";
|
|
21733
|
+
import * as path47 from "path";
|
|
21502
21734
|
import posixPath4 from "path/posix";
|
|
21503
21735
|
var live = /* @__PURE__ */ new Map();
|
|
21504
21736
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -21509,8 +21741,8 @@ function brainEventsStatePath(chatId) {
|
|
|
21509
21741
|
}
|
|
21510
21742
|
function lastPersistedSeq(dir, chatId) {
|
|
21511
21743
|
const p = brainEventsFilePath(dir, chatId);
|
|
21512
|
-
if (!
|
|
21513
|
-
const lines =
|
|
21744
|
+
if (!fs48.existsSync(p)) return 0;
|
|
21745
|
+
const lines = fs48.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
21514
21746
|
if (lines.length === 0) return 0;
|
|
21515
21747
|
try {
|
|
21516
21748
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -21520,9 +21752,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
21520
21752
|
}
|
|
21521
21753
|
function readSince(dir, chatId, since) {
|
|
21522
21754
|
const p = brainEventsFilePath(dir, chatId);
|
|
21523
|
-
if (!
|
|
21755
|
+
if (!fs48.existsSync(p)) return [];
|
|
21524
21756
|
const out = [];
|
|
21525
|
-
for (const line of
|
|
21757
|
+
for (const line of fs48.readFileSync(p, "utf-8").split("\n")) {
|
|
21526
21758
|
if (!line) continue;
|
|
21527
21759
|
try {
|
|
21528
21760
|
const rec = JSON.parse(line);
|
|
@@ -21548,12 +21780,12 @@ function beginTurn(dir, chatId) {
|
|
|
21548
21780
|
};
|
|
21549
21781
|
live.set(chatId, state);
|
|
21550
21782
|
const p = brainEventsFilePath(dir, chatId);
|
|
21551
|
-
|
|
21783
|
+
fs48.mkdirSync(path47.dirname(p), { recursive: true });
|
|
21552
21784
|
return (event) => {
|
|
21553
21785
|
state.seq += 1;
|
|
21554
21786
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
21555
21787
|
try {
|
|
21556
|
-
|
|
21788
|
+
fs48.appendFileSync(p, `${JSON.stringify(rec)}
|
|
21557
21789
|
`);
|
|
21558
21790
|
} catch (err) {
|
|
21559
21791
|
process.stderr.write(
|
|
@@ -21592,7 +21824,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
21592
21824
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
21593
21825
|
};
|
|
21594
21826
|
try {
|
|
21595
|
-
|
|
21827
|
+
fs48.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
|
|
21596
21828
|
`);
|
|
21597
21829
|
} catch {
|
|
21598
21830
|
}
|
|
@@ -21916,7 +22148,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
21916
22148
|
);
|
|
21917
22149
|
}
|
|
21918
22150
|
}
|
|
21919
|
-
|
|
22151
|
+
fs49.mkdirSync(path48.dirname(sessionFile), { recursive: true });
|
|
21920
22152
|
appendTurn(sessionFile, {
|
|
21921
22153
|
role: "user",
|
|
21922
22154
|
content: message,
|
|
@@ -21991,7 +22223,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
21991
22223
|
function buildServer(opts) {
|
|
21992
22224
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
21993
22225
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
21994
|
-
const reposRoot = opts.reposRoot ??
|
|
22226
|
+
const reposRoot = opts.reposRoot ?? path48.join(path48.dirname(path48.resolve(opts.cwd)), "repos");
|
|
21995
22227
|
return createServer2(async (req, res) => {
|
|
21996
22228
|
if (!req.method || !req.url) {
|
|
21997
22229
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -22594,8 +22826,8 @@ async function loadConfigSafe() {
|
|
|
22594
22826
|
}
|
|
22595
22827
|
|
|
22596
22828
|
// src/chat-cli.ts
|
|
22597
|
-
import * as
|
|
22598
|
-
import * as
|
|
22829
|
+
import * as fs51 from "fs";
|
|
22830
|
+
import * as path50 from "path";
|
|
22599
22831
|
|
|
22600
22832
|
// src/chat/inbox.ts
|
|
22601
22833
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -22667,8 +22899,8 @@ function currentBranch(cwd) {
|
|
|
22667
22899
|
|
|
22668
22900
|
// src/chat/state-sync.ts
|
|
22669
22901
|
init_stateRepo();
|
|
22670
|
-
import * as
|
|
22671
|
-
import * as
|
|
22902
|
+
import * as fs50 from "fs";
|
|
22903
|
+
import * as path49 from "path";
|
|
22672
22904
|
function jsonlLines2(text) {
|
|
22673
22905
|
return text.split("\n").filter((line) => line.length > 0);
|
|
22674
22906
|
}
|
|
@@ -22685,15 +22917,15 @@ function mergeJsonl2(localText, remoteText) {
|
|
|
22685
22917
|
function syncJsonlFileFromState(opts) {
|
|
22686
22918
|
const remote = readStateText(opts.config, opts.cwd, opts.statePath);
|
|
22687
22919
|
if (!remote) return;
|
|
22688
|
-
const local =
|
|
22920
|
+
const local = fs50.existsSync(opts.localPath) ? fs50.readFileSync(opts.localPath, "utf-8") : "";
|
|
22689
22921
|
const next = mergeJsonl2(local, remote.content);
|
|
22690
22922
|
if (next === local) return;
|
|
22691
|
-
|
|
22692
|
-
|
|
22923
|
+
fs50.mkdirSync(path49.dirname(opts.localPath), { recursive: true });
|
|
22924
|
+
fs50.writeFileSync(opts.localPath, next);
|
|
22693
22925
|
}
|
|
22694
22926
|
function persistJsonlFileToState(opts) {
|
|
22695
|
-
if (!
|
|
22696
|
-
const localText =
|
|
22927
|
+
if (!fs50.existsSync(opts.localPath)) return;
|
|
22928
|
+
const localText = fs50.readFileSync(opts.localPath, "utf-8");
|
|
22697
22929
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
22698
22930
|
const remote = readStateText(opts.config, opts.cwd, opts.statePath);
|
|
22699
22931
|
const body = mergeJsonl2(localText, remote?.content ?? "");
|
|
@@ -22877,6 +23109,7 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
22877
23109
|
init_config();
|
|
22878
23110
|
init_litellm();
|
|
22879
23111
|
init_stateWorkspace();
|
|
23112
|
+
init_companyStore();
|
|
22880
23113
|
var DEFAULT_MODEL2 = "claude/claude-haiku-4-5-20251001";
|
|
22881
23114
|
var CHAT_HELP = `kody chat \u2014 dashboard-driven chat session
|
|
22882
23115
|
|
|
@@ -22957,8 +23190,12 @@ async function runChat(argv) {
|
|
|
22957
23190
|
${CHAT_HELP}`);
|
|
22958
23191
|
return 64;
|
|
22959
23192
|
}
|
|
22960
|
-
const cwd = args.cwd ?
|
|
23193
|
+
const cwd = args.cwd ? path50.resolve(args.cwd) : process.cwd();
|
|
22961
23194
|
const sessionId = args.sessionId;
|
|
23195
|
+
const runRequest = readRunRequestFromEnv();
|
|
23196
|
+
if (runRequest && "request" in runRequest) {
|
|
23197
|
+
applyCompanyStoreRuntimeConfig(runRequest.request.input);
|
|
23198
|
+
}
|
|
22962
23199
|
const unpackedSecrets = unpackAllSecrets();
|
|
22963
23200
|
if (unpackedSecrets > 0) {
|
|
22964
23201
|
process.stdout.write(`\u2192 kody: unpacked ${unpackedSecrets} secret(s) from ALL_SECRETS
|
|
@@ -23018,7 +23255,7 @@ ${CHAT_HELP}`);
|
|
|
23018
23255
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
23019
23256
|
const meta = readMeta(sessionFile);
|
|
23020
23257
|
process.stdout.write(
|
|
23021
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
23258
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs51.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
23022
23259
|
`
|
|
23023
23260
|
);
|
|
23024
23261
|
try {
|
|
@@ -23130,42 +23367,8 @@ async function runCapabilityFallbackTick(deps) {
|
|
|
23130
23367
|
return { ran: true, claimed };
|
|
23131
23368
|
}
|
|
23132
23369
|
|
|
23133
|
-
// src/pool
|
|
23134
|
-
|
|
23135
|
-
var POOL_API_KEY_INFO = "kody-pool-api:v1";
|
|
23136
|
-
var RUNNER_API_KEY_INFO = "kody-runner-api:v1";
|
|
23137
|
-
function masterKeyBytes(raw) {
|
|
23138
|
-
const v = raw.trim();
|
|
23139
|
-
if (!v) throw new Error("KODY_MASTER_KEY is empty");
|
|
23140
|
-
if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
|
|
23141
|
-
return Buffer.from(v, "hex");
|
|
23142
|
-
}
|
|
23143
|
-
return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
23144
|
-
}
|
|
23145
|
-
function deriveKey(master, info, length = 32) {
|
|
23146
|
-
return Buffer.from(hkdfSync2("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
|
|
23147
|
-
}
|
|
23148
|
-
function derivePoolApiKey(master) {
|
|
23149
|
-
return deriveKey(master, POOL_API_KEY_INFO);
|
|
23150
|
-
}
|
|
23151
|
-
function deriveRunnerApiKey(master) {
|
|
23152
|
-
return deriveKey(master, RUNNER_API_KEY_INFO);
|
|
23153
|
-
}
|
|
23154
|
-
function bearerOk(headerAuth, xApiKey, expected) {
|
|
23155
|
-
const x = (xApiKey ?? "").trim();
|
|
23156
|
-
if (x && timingEqual(x, expected)) return true;
|
|
23157
|
-
const a = (headerAuth ?? "").trim();
|
|
23158
|
-
if (a.toLowerCase().startsWith("bearer ")) {
|
|
23159
|
-
return timingEqual(a.slice(7).trim(), expected);
|
|
23160
|
-
}
|
|
23161
|
-
return false;
|
|
23162
|
-
}
|
|
23163
|
-
function timingEqual(a, b) {
|
|
23164
|
-
if (a.length !== b.length) return false;
|
|
23165
|
-
let diff = 0;
|
|
23166
|
-
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
23167
|
-
return diff === 0;
|
|
23168
|
-
}
|
|
23370
|
+
// src/servers/pool-serve.ts
|
|
23371
|
+
init_keys();
|
|
23169
23372
|
|
|
23170
23373
|
// src/pool/fly.ts
|
|
23171
23374
|
var FLY_API_BASE = "https://api.machines.dev/v1";
|
|
@@ -23182,8 +23385,8 @@ var FlyClient = class {
|
|
|
23182
23385
|
get fetch() {
|
|
23183
23386
|
return this.opts.fetchImpl ?? fetch;
|
|
23184
23387
|
}
|
|
23185
|
-
async call(
|
|
23186
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
23388
|
+
async call(path51, init = {}) {
|
|
23389
|
+
const res = await this.fetch(`${FLY_API_BASE}${path51}`, {
|
|
23187
23390
|
method: init.method ?? "GET",
|
|
23188
23391
|
headers: {
|
|
23189
23392
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -23194,7 +23397,7 @@ var FlyClient = class {
|
|
|
23194
23397
|
if (res.status === 404 && init.allow404) return null;
|
|
23195
23398
|
if (!res.ok) {
|
|
23196
23399
|
const text = await res.text().catch(() => "");
|
|
23197
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
23400
|
+
throw new Error(`Fly API ${res.status} on ${path51}: ${text.slice(0, 200) || res.statusText}`);
|
|
23198
23401
|
}
|
|
23199
23402
|
if (res.status === 204) return null;
|
|
23200
23403
|
const raw = await res.text();
|
|
@@ -23530,59 +23733,8 @@ function isSuspendedWithIp(m) {
|
|
|
23530
23733
|
return (m.state === "suspended" || m.state === "suspending") && !!m.private_ip;
|
|
23531
23734
|
}
|
|
23532
23735
|
|
|
23533
|
-
// src/pool/vault.ts
|
|
23534
|
-
init_stateRepoGithub();
|
|
23535
|
-
import { createDecipheriv as createDecipheriv2 } from "crypto";
|
|
23536
|
-
var VAULT_PATH = "secrets.enc";
|
|
23537
|
-
var CACHE_TTL_MS = 6e4;
|
|
23538
|
-
var cache = /* @__PURE__ */ new Map();
|
|
23539
|
-
function decryptVault(payload, masterKey) {
|
|
23540
|
-
const parts = payload.split(":");
|
|
23541
|
-
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
23542
|
-
throw new Error("invalid vault payload format");
|
|
23543
|
-
}
|
|
23544
|
-
const [, ivB64, ctB64, tagB64] = parts;
|
|
23545
|
-
const iv = Buffer.from(ivB64, "base64");
|
|
23546
|
-
const ct = Buffer.from(ctB64, "base64");
|
|
23547
|
-
const tag = Buffer.from(tagB64, "base64");
|
|
23548
|
-
const decipher = createDecipheriv2("aes-256-gcm", masterKey, iv);
|
|
23549
|
-
decipher.setAuthTag(tag);
|
|
23550
|
-
return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
|
|
23551
|
-
}
|
|
23552
|
-
async function readVaultSecrets(opts) {
|
|
23553
|
-
const key = `${opts.owner}/${opts.repo}`.toLowerCase();
|
|
23554
|
-
const hit = cache.get(key);
|
|
23555
|
-
if (hit && hit.expiresAt > Date.now()) return hit.secrets;
|
|
23556
|
-
const file = await readGithubStateText({
|
|
23557
|
-
owner: opts.owner,
|
|
23558
|
-
repo: opts.repo,
|
|
23559
|
-
filePath: VAULT_PATH,
|
|
23560
|
-
githubToken: opts.githubToken,
|
|
23561
|
-
fetchImpl: opts.fetchImpl
|
|
23562
|
-
});
|
|
23563
|
-
if (!file) {
|
|
23564
|
-
cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
23565
|
-
return {};
|
|
23566
|
-
}
|
|
23567
|
-
const ciphertext = file.content.trim();
|
|
23568
|
-
const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
|
|
23569
|
-
const flat = {};
|
|
23570
|
-
for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
|
|
23571
|
-
if (entry && typeof entry.value === "string") flat[name] = entry.value;
|
|
23572
|
-
}
|
|
23573
|
-
cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
23574
|
-
return flat;
|
|
23575
|
-
}
|
|
23576
|
-
async function readRepoSecret(opts) {
|
|
23577
|
-
const secrets = await readVaultSecrets(opts);
|
|
23578
|
-
const v = secrets[opts.name];
|
|
23579
|
-
return v?.trim() ? v : null;
|
|
23580
|
-
}
|
|
23581
|
-
async function readRepoSecrets(opts) {
|
|
23582
|
-
return readVaultSecrets(opts);
|
|
23583
|
-
}
|
|
23584
|
-
|
|
23585
23736
|
// src/pool/registry.ts
|
|
23737
|
+
init_stateRepoVault();
|
|
23586
23738
|
var POOL_MIN_VAULT_KEY = "POOL_MIN";
|
|
23587
23739
|
var POOL_MIN_MAX = 10;
|
|
23588
23740
|
function parsePoolMin(raw, dflt) {
|
|
@@ -23963,7 +24115,7 @@ async function poolServe() {
|
|
|
23963
24115
|
|
|
23964
24116
|
// src/servers/runner-serve.ts
|
|
23965
24117
|
import { spawn as spawn8 } from "child_process";
|
|
23966
|
-
import * as
|
|
24118
|
+
import * as fs52 from "fs";
|
|
23967
24119
|
import { createServer as createServer6 } from "http";
|
|
23968
24120
|
var DEFAULT_PORT2 = 8080;
|
|
23969
24121
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -24098,8 +24250,8 @@ async function defaultRunJob(job) {
|
|
|
24098
24250
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
24099
24251
|
const branch = job.ref ?? "main";
|
|
24100
24252
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
24101
|
-
|
|
24102
|
-
|
|
24253
|
+
fs52.rmSync(workdir, { recursive: true, force: true });
|
|
24254
|
+
fs52.mkdirSync(workdir, { recursive: true });
|
|
24103
24255
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
24104
24256
|
const target = job.runRequest.target;
|
|
24105
24257
|
const interactive = target.type === "chat";
|