@kody-ade/kody-engine 0.4.408 → 0.4.410

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +191 -114
  2. package/package.json +1 -1
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.408",
18
+ version: "0.4.410",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -2219,6 +2219,108 @@ var init_convex_client = __esm({
2219
2219
  }
2220
2220
  });
2221
2221
 
2222
+ // src/kody-api-client.ts
2223
+ import { getFunctionName } from "convex/server";
2224
+ function apiUrl(env) {
2225
+ return (env.KODY_API_URL?.trim() || DEFAULT_KODY_API_URL).replace(/\/$/, "");
2226
+ }
2227
+ function oidcRequestUrl(env) {
2228
+ const raw = env.ACTIONS_ID_TOKEN_REQUEST_URL?.trim();
2229
+ if (!raw) throw new Error("GitHub Actions OIDC request URL is unavailable");
2230
+ const url = new URL(raw);
2231
+ if (url.protocol !== "https:") throw new Error("GitHub Actions OIDC request URL must use HTTPS");
2232
+ url.searchParams.set("audience", OIDC_AUDIENCE);
2233
+ return url;
2234
+ }
2235
+ function tokenExpiry(token) {
2236
+ try {
2237
+ const payload = JSON.parse(Buffer.from(token.split(".")[1] ?? "", "base64url").toString("utf8"));
2238
+ return typeof payload.exp === "number" ? payload.exp * 1e3 : Date.now() + 4 * 6e4;
2239
+ } catch {
2240
+ return Date.now() + 4 * 6e4;
2241
+ }
2242
+ }
2243
+ async function githubOidcToken(env, force = false) {
2244
+ if (!force && cachedToken && cachedToken.expiresAt - 6e4 > Date.now()) return cachedToken.value;
2245
+ const requestToken = env.ACTIONS_ID_TOKEN_REQUEST_TOKEN?.trim();
2246
+ if (!requestToken) throw new Error("GitHub Actions OIDC request token is unavailable");
2247
+ const response = await fetch(oidcRequestUrl(env), {
2248
+ headers: { Authorization: `Bearer ${requestToken}` },
2249
+ signal: AbortSignal.timeout(15e3)
2250
+ });
2251
+ if (!response.ok) throw new Error(`GitHub Actions identity request failed (${response.status})`);
2252
+ const body = await response.json();
2253
+ if (typeof body.value !== "string" || !body.value) throw new Error("GitHub Actions identity response was invalid");
2254
+ cachedToken = { value: body.value, expiresAt: tokenExpiry(body.value) };
2255
+ return body.value;
2256
+ }
2257
+ function operationName(fn) {
2258
+ return getFunctionName(fn).replace(":", ".");
2259
+ }
2260
+ async function callKodyApi(kind, fn, args, env) {
2261
+ const call = async (forceToken) => {
2262
+ const token = await githubOidcToken(env, forceToken);
2263
+ return fetch(`${apiUrl(env)}/api/kody/engine/backend`, {
2264
+ method: "POST",
2265
+ headers: {
2266
+ Authorization: `Bearer ${token}`,
2267
+ "Content-Type": "application/json"
2268
+ },
2269
+ body: JSON.stringify({ kind, operation: operationName(fn), args }),
2270
+ signal: AbortSignal.timeout(3e4)
2271
+ });
2272
+ };
2273
+ let response = await call(false);
2274
+ if (response.status === 401) response = await call(true);
2275
+ if (!response.ok) throw new Error(`Kody backend request failed (${response.status})`);
2276
+ const body = await response.json();
2277
+ return body.result;
2278
+ }
2279
+ function hasGitHubActionsIdentity(env = process.env) {
2280
+ return Boolean(
2281
+ env.GITHUB_ACTIONS === "true" && env.ACTIONS_ID_TOKEN_REQUEST_URL?.trim() && env.ACTIONS_ID_TOKEN_REQUEST_TOKEN?.trim()
2282
+ );
2283
+ }
2284
+ function createKodyApiBackendClient(env = process.env) {
2285
+ if (!hasGitHubActionsIdentity(env)) throw new Error("GitHub Actions workflow identity is unavailable");
2286
+ return {
2287
+ query: (fn, args) => callKodyApi("query", fn, args, env),
2288
+ mutation: (fn, args) => callKodyApi("mutation", fn, args, env)
2289
+ };
2290
+ }
2291
+ async function readRuntimeSecretFromKody(name, env = process.env) {
2292
+ const token = await githubOidcToken(env);
2293
+ const response = await fetch(`${apiUrl(env)}/api/kody/engine/secret`, {
2294
+ method: "POST",
2295
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
2296
+ body: JSON.stringify({ name }),
2297
+ signal: AbortSignal.timeout(3e4)
2298
+ });
2299
+ if (response.status === 404) return null;
2300
+ if (!response.ok) throw new Error(`Kody secret request failed (${response.status})`);
2301
+ const body = await response.json();
2302
+ return typeof body.value === "string" ? body.value : null;
2303
+ }
2304
+ async function readPreviewContextFromKody(env = process.env) {
2305
+ const token = await githubOidcToken(env);
2306
+ const response = await fetch(`${apiUrl(env)}/api/kody/engine/preview-context`, {
2307
+ method: "POST",
2308
+ headers: { Authorization: `Bearer ${token}` },
2309
+ signal: AbortSignal.timeout(3e4)
2310
+ });
2311
+ if (!response.ok) throw new Error(`Kody preview context request failed (${response.status})`);
2312
+ return await response.json();
2313
+ }
2314
+ var DEFAULT_KODY_API_URL, OIDC_AUDIENCE, cachedToken;
2315
+ var init_kody_api_client = __esm({
2316
+ "src/kody-api-client.ts"() {
2317
+ "use strict";
2318
+ DEFAULT_KODY_API_URL = "https://kody-dashboard-khaki.vercel.app";
2319
+ OIDC_AUDIENCE = "kody-api";
2320
+ cachedToken = null;
2321
+ }
2322
+ });
2323
+
2222
2324
  // src/state-backend.ts
2223
2325
  import { anyApi } from "convex/server";
2224
2326
  function requireTenant(tenantId2) {
@@ -2236,10 +2338,16 @@ function requireNonEmpty(value, name) {
2236
2338
  return normalized;
2237
2339
  }
2238
2340
  function createStateBackendFromEnv(env = process.env, client) {
2239
- const url = env.CONVEX_URL?.trim();
2240
- const serviceKey = env.KODY_SERVICE_KEY?.trim();
2241
- if (!url || !serviceKey) throw new Error("CONVEX_URL and KODY_SERVICE_KEY are required");
2242
- const transport = client ?? createConvexClientFromEnv(env);
2341
+ let transport = client;
2342
+ if (!transport && hasGitHubActionsIdentity(env)) transport = createKodyApiBackendClient(env);
2343
+ if (!transport) {
2344
+ const url = env.CONVEX_URL?.trim();
2345
+ const serviceKey = env.KODY_SERVICE_KEY?.trim();
2346
+ if (!url || !serviceKey) {
2347
+ throw new Error("GitHub Actions identity or direct Kody backend credentials are required");
2348
+ }
2349
+ transport = createConvexClientFromEnv(env);
2350
+ }
2243
2351
  return {
2244
2352
  async get(tenantId2, taskKey, kind) {
2245
2353
  const result = await transport.query(anyApi.taskState.get, {
@@ -2412,10 +2520,14 @@ function createStateBackendFromEnv(env = process.env, client) {
2412
2520
  }
2413
2521
  };
2414
2522
  }
2523
+ function hasStateBackendConfig(env = process.env) {
2524
+ return hasGitHubActionsIdentity(env) || Boolean(env.CONVEX_URL?.trim() && env.KODY_SERVICE_KEY?.trim());
2525
+ }
2415
2526
  var init_state_backend = __esm({
2416
2527
  "src/state-backend.ts"() {
2417
2528
  "use strict";
2418
2529
  init_convex_client();
2530
+ init_kody_api_client();
2419
2531
  }
2420
2532
  });
2421
2533
 
@@ -2990,6 +3102,17 @@ var init_capabilityMcp = __esm({
2990
3102
  import { spawn as spawn2, spawnSync } from "child_process";
2991
3103
  import * as fs7 from "fs";
2992
3104
  import * as path8 from "path";
3105
+ function buildCloneProcess(repo, token, baseEnv = process.env) {
3106
+ const url = `https://github.com/${repo}.git`;
3107
+ const env = { ...baseEnv };
3108
+ if (!token) return { url, env };
3109
+ const parsedCount = Number.parseInt(env.GIT_CONFIG_COUNT ?? "0", 10);
3110
+ const count = Number.isInteger(parsedCount) && parsedCount >= 0 ? parsedCount : 0;
3111
+ env.GIT_CONFIG_COUNT = String(count + 1);
3112
+ env[`GIT_CONFIG_KEY_${count}`] = "http.https://github.com/.extraHeader";
3113
+ env[`GIT_CONFIG_VALUE_${count}`] = `Authorization: Basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`;
3114
+ return { url, env };
3115
+ }
2993
3116
  async function resolveAndClone(reposRoot, repo, repoToken, cloneRepo) {
2994
3117
  const name = repo?.trim();
2995
3118
  if (!name || !REPO_RE.test(name)) return null;
@@ -3020,17 +3143,19 @@ async function fetchRepo(opts) {
3020
3143
  }
3021
3144
  return dir;
3022
3145
  }
3023
- var REPO_RE, repoClones, defaultCloneRepo;
3146
+ var REPO_RE, repoClones, GIT_CREDENTIAL_HELPER, defaultCloneRepo;
3024
3147
  var init_repoWorkspace = __esm({
3025
3148
  "src/repoWorkspace.ts"() {
3026
3149
  "use strict";
3027
3150
  REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
3028
3151
  repoClones = /* @__PURE__ */ new Map();
3152
+ GIT_CREDENTIAL_HELPER = `!f() { if [ "$1" = get ] && [ -n "$GITHUB_TOKEN" ]; then printf '%s\\n' username=x-access-token "password=$GITHUB_TOKEN"; fi; }; f`;
3029
3153
  defaultCloneRepo = (repo, token, dir) => {
3030
3154
  fs7.mkdirSync(path8.dirname(dir), { recursive: true });
3031
- const authUrl = token ? `https://x-access-token:${token}@github.com/${repo}.git` : `https://github.com/${repo}.git`;
3155
+ const clone = buildCloneProcess(repo, token);
3032
3156
  return new Promise((resolve16, reject) => {
3033
- const child = spawn2("git", ["clone", "--depth=1", authUrl, dir], {
3157
+ const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
3158
+ env: clone.env,
3034
3159
  stdio: "inherit"
3035
3160
  });
3036
3161
  child.on("exit", (code) => {
@@ -3043,6 +3168,9 @@ var init_repoWorkspace = __esm({
3043
3168
  const email = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
3044
3169
  spawnSync("git", ["-C", dir, "config", "user.name", name]);
3045
3170
  spawnSync("git", ["-C", dir, "config", "user.email", email]);
3171
+ if (token) {
3172
+ spawnSync("git", ["-C", dir, "config", "credential.helper", GIT_CREDENTIAL_HELPER]);
3173
+ }
3046
3174
  } catch {
3047
3175
  }
3048
3176
  resolve16();
@@ -3169,24 +3297,26 @@ function stripAgentSecrets(env) {
3169
3297
  }
3170
3298
  return out;
3171
3299
  }
3172
- async function runAgent(opts) {
3173
- const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
3174
- fs8.mkdirSync(ndjsonDir, { recursive: true });
3175
- const ndjsonPath = path9.join(ndjsonDir, "last-run.jsonl");
3300
+ function buildAgentEnvironment(baseEnv, repoToken) {
3176
3301
  const env = stripAgentSecrets({
3177
- ...process.env,
3302
+ ...baseEnv,
3178
3303
  SKIP_HOOKS: "1",
3179
3304
  HUSKY: "0",
3180
- CI: process.env.CI ?? "1",
3181
- // MCP servers are spawned asynchronously by the SDK. With the default
3182
- // non-blocking behavior, the SDK announces its tool list at session
3183
- // init while servers are still in `pending`, so their tools never
3184
- // reach the model. Block until each MCP completes its handshake (or
3185
- // the timeout below elapses) so the tool list is complete on first
3186
- // turn.
3187
- MCP_CONNECTION_NONBLOCKING: process.env.MCP_CONNECTION_NONBLOCKING ?? "false",
3188
- MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "60000"
3305
+ CI: baseEnv.CI ?? "1",
3306
+ MCP_CONNECTION_NONBLOCKING: baseEnv.MCP_CONNECTION_NONBLOCKING ?? "false",
3307
+ MCP_TIMEOUT: baseEnv.MCP_TIMEOUT ?? "60000"
3189
3308
  });
3309
+ if (repoToken) {
3310
+ env.GITHUB_TOKEN = repoToken;
3311
+ env.GH_TOKEN = repoToken;
3312
+ }
3313
+ return env;
3314
+ }
3315
+ async function runAgent(opts) {
3316
+ const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
3317
+ fs8.mkdirSync(ndjsonDir, { recursive: true });
3318
+ const ndjsonPath = path9.join(ndjsonDir, "last-run.jsonl");
3319
+ const env = buildAgentEnvironment(process.env, opts.repoToken);
3190
3320
  if (opts.litellmUrl) {
3191
3321
  env.ANTHROPIC_BASE_URL = opts.litellmUrl;
3192
3322
  env.ANTHROPIC_API_KEY = getAnthropicApiKeyOrDummy();
@@ -3681,12 +3811,10 @@ function verifyTaskArtifacts(absDir) {
3681
3811
  }
3682
3812
  async function persistTaskArtifactsToState(config, _cwd, artifacts) {
3683
3813
  const tenantId2 = config.github?.owner && config.github.repo ? `${config.github.owner}/${config.github.repo}` : process.env.GITHUB_REPOSITORY?.trim();
3684
- if (process.env.GITHUB_ACTIONS === "true" && (!process.env.CONVEX_URL || !process.env.KODY_SERVICE_KEY || !tenantId2)) {
3685
- throw new Error(
3686
- "Convex artifact backend is required in GitHub Actions (CONVEX_URL, KODY_SERVICE_KEY, and repository identity)"
3687
- );
3814
+ if (process.env.GITHUB_ACTIONS === "true" && (!hasStateBackendConfig() || !tenantId2)) {
3815
+ throw new Error("Kody backend access and repository identity are required in GitHub Actions");
3688
3816
  }
3689
- if (process.env.CONVEX_URL && process.env.KODY_SERVICE_KEY && tenantId2) {
3817
+ if (hasStateBackendConfig() && tenantId2) {
3690
3818
  const backend = createStateBackendFromEnv();
3691
3819
  for (const file of TASK_ARTIFACT_FILES) {
3692
3820
  const full = path11.join(artifacts.absDir, file);
@@ -5816,7 +5944,7 @@ var init_loadMemoryContext = __esm({
5816
5944
  loadMemoryContext = async (ctx) => {
5817
5945
  if (typeof ctx.data.memoryContext === "string") return;
5818
5946
  const tenantId2 = ctx.config.github?.owner && ctx.config.github.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : process.env.GITHUB_REPOSITORY?.trim();
5819
- if (process.env.CONVEX_URL && process.env.KODY_SERVICE_KEY && tenantId2) {
5947
+ if (hasStateBackendConfig() && tenantId2) {
5820
5948
  try {
5821
5949
  const backend = createStateBackendFromEnv();
5822
5950
  const docs = await backend.listRepoDocs(tenantId2, "memory:");
@@ -9517,7 +9645,7 @@ function subjectCandidates(kind, id, state) {
9517
9645
  return [...ids].map((candidate) => ({ kind, id: candidate }));
9518
9646
  }
9519
9647
  async function firstTrustOverride(ctx, subjects) {
9520
- const backendConfigured = Boolean(process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim());
9648
+ const backendConfigured = hasStateBackendConfig();
9521
9649
  if (!backendConfigured) return null;
9522
9650
  const repoSlug = ctx.config.github?.owner && ctx.config.github?.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : "";
9523
9651
  for (const subject of subjects) {
@@ -9733,6 +9861,7 @@ var init_advanceManagedGoal = __esm({
9733
9861
  init_typeDefinitions();
9734
9862
  init_issue();
9735
9863
  init_registry();
9864
+ init_state_backend();
9736
9865
  init_trustPolicy();
9737
9866
  init_workflowDefinitions();
9738
9867
  init_goalCapabilityScheduling();
@@ -10030,12 +10159,12 @@ function resolveTrigger(force) {
10030
10159
  }
10031
10160
  async function appendActivity(ctx, record2) {
10032
10161
  const tenantId2 = ctx.config.github?.owner && ctx.config.github.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : process.env.GITHUB_REPOSITORY;
10033
- if (process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim() && tenantId2) {
10162
+ if (hasStateBackendConfig() && tenantId2) {
10034
10163
  await createStateBackendFromEnv().appendDailyLog(tenantId2, "activity", record2.ts.slice(0, 10), record2);
10035
10164
  return;
10036
10165
  }
10037
10166
  if (process.env.GITHUB_ACTIONS === "true") {
10038
- throw new Error("Convex backend is required for company activity in GitHub Actions");
10167
+ throw new Error("Kody backend access is required for company activity in GitHub Actions");
10039
10168
  }
10040
10169
  }
10041
10170
  var appendCompanyActivity;
@@ -14755,6 +14884,18 @@ function envSecret(name, env) {
14755
14884
  }
14756
14885
  async function resolveRuntimeSecret(name, ctx, opts = {}) {
14757
14886
  const env = opts.env ?? process.env;
14887
+ if (hasGitHubActionsIdentity(env)) {
14888
+ try {
14889
+ const value = await readRuntimeSecretFromKody(name, env);
14890
+ return value ? { value, source: "vault" } : envSecret(name, env);
14891
+ } catch (err) {
14892
+ const fallback = envSecret(name, env);
14893
+ return {
14894
+ ...fallback,
14895
+ warning: `Kody secret read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
14896
+ };
14897
+ }
14898
+ }
14758
14899
  const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
14759
14900
  if (!masterRaw || !env.CONVEX_URL?.trim() || !env.KODY_SERVICE_KEY?.trim()) return envSecret(name, env);
14760
14901
  try {
@@ -14782,6 +14923,7 @@ var init_runtimeSecrets = __esm({
14782
14923
  "src/scripts/runtimeSecrets.ts"() {
14783
14924
  "use strict";
14784
14925
  init_backendVault();
14926
+ init_kody_api_client();
14785
14927
  init_keys();
14786
14928
  }
14787
14929
  });
@@ -16600,7 +16742,7 @@ var init_publishReport = __esm({
16600
16742
  });
16601
16743
  const runId = generatedAt.replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-");
16602
16744
  const tenantId2 = ctx.config.github?.owner && ctx.config.github.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : process.env.GITHUB_REPOSITORY;
16603
- if (process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim() && tenantId2) {
16745
+ if (hasStateBackendConfig() && tenantId2) {
16604
16746
  await createStateBackendFromEnv().saveReport(
16605
16747
  tenantId2,
16606
16748
  slug,
@@ -16616,7 +16758,7 @@ var init_publishReport = __esm({
16616
16758
  generatedAt
16617
16759
  );
16618
16760
  } else if (process.env.GITHUB_ACTIONS === "true") {
16619
- throw new Error("Convex backend is required for reports in GitHub Actions");
16761
+ throw new Error("Kody backend access is required for reports in GitHub Actions");
16620
16762
  }
16621
16763
  };
16622
16764
  }
@@ -17429,53 +17571,14 @@ function basePreviewAppName(repo) {
17429
17571
  }
17430
17572
  return `kp-${shortHash(owner)}-${shortHash(name)}-base`;
17431
17573
  }
17432
- function decryptVaultPayload(payload, keyRaw) {
17433
- const parts = payload.split(":");
17434
- if (parts.length !== 4 || parts[0] !== "v1") {
17435
- throw new Error("invalid vault payload format");
17436
- }
17437
- const [, ivB64, ctB64, tagB64] = parts;
17438
- const key = decodeMasterKey(keyRaw);
17439
- if (key.length !== 32) {
17440
- throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
17441
- }
17442
- const iv = Buffer.from(ivB64, "base64");
17443
- const ct = Buffer.from(ctB64, "base64");
17444
- const tag = Buffer.from(tagB64, "base64");
17445
- const decipher = createDecipheriv2("aes-256-gcm", key, iv);
17446
- decipher.setAuthTag(tag);
17447
- return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
17448
- }
17449
- function decodeMasterKey(keyRaw) {
17450
- if (/^[0-9a-fA-F]{64}$/.test(keyRaw)) return Buffer.from(keyRaw, "hex");
17451
- return Buffer.from(keyRaw.replace(/-/g, "+").replace(/_/g, "/"), "base64");
17452
- }
17453
- function derivePreviewVerifyKey(masterKeyRaw) {
17454
- const masterKey = decodeMasterKey(masterKeyRaw);
17455
- if (masterKey.length !== 32) {
17456
- throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
17457
- }
17458
- return Buffer.from(hkdfSync2("sha256", masterKey, Buffer.alloc(0), PREVIEW_KEY_INFO, 32)).toString("hex");
17459
- }
17460
17574
  function previewRuntimeEnv(args) {
17461
17575
  return {
17462
17576
  ...args.buildEnv,
17463
- KODY_PREVIEW_VERIFY_KEY: derivePreviewVerifyKey(args.masterKey),
17577
+ KODY_PREVIEW_VERIFY_KEY: args.previewVerifyKey,
17464
17578
  KODY_REPO_CONTEXT: args.repo,
17465
17579
  KODY_PR: String(args.pr)
17466
17580
  };
17467
17581
  }
17468
- function buildEnvFromVault(doc) {
17469
- const buildEnv = {};
17470
- for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
17471
- if (!entry?.value) continue;
17472
- if (NEVER_PASS_TO_BUILD.has(name)) continue;
17473
- buildEnv[name] = entry.value;
17474
- }
17475
- const raw = doc.secrets?.KODY_PREVIEW_BUILD_MODE?.value;
17476
- const buildMode = raw?.toLowerCase().trim() === "dev" ? "dev" : "prod";
17477
- return { buildEnv, buildMode };
17478
- }
17479
17582
  function formatPreviewComment(args) {
17480
17583
  return [
17481
17584
  "<!-- kody-fly-preview -->",
@@ -17487,23 +17590,9 @@ function formatPreviewComment(args) {
17487
17590
  function defaultImageTag(repo, ref) {
17488
17591
  return createHash4("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
17489
17592
  }
17490
- var NEVER_PASS_TO_BUILD, PREVIEW_KEY_INFO;
17491
17593
  var init_previewBuildHelpers = __esm({
17492
17594
  "src/scripts/previewBuildHelpers.ts"() {
17493
17595
  "use strict";
17494
- NEVER_PASS_TO_BUILD = /* @__PURE__ */ new Set([
17495
- "FLY_API_TOKEN",
17496
- "FLY_ORG_SLUG",
17497
- "FLY_DEFAULT_REGION",
17498
- "KODY_MASTER_KEY",
17499
- // Preview-config knob; consumed by the dispatcher before spawn.
17500
- "KODY_PREVIEW_BUILD_MODE",
17501
- "KODY_PREVIEW_VERIFY_KEY",
17502
- "KODY_REPO_CONTEXT",
17503
- "KODY_PR",
17504
- "KODY_BRANCH"
17505
- ]);
17506
- PREVIEW_KEY_INFO = "kody-preview:v1";
17507
17596
  }
17508
17597
  });
17509
17598
 
@@ -17612,16 +17701,6 @@ function flyHeaders(token) {
17612
17701
  "Content-Type": "application/json"
17613
17702
  };
17614
17703
  }
17615
- async function fetchVaultDoc(repo, masterKey) {
17616
- const [owner, name] = repo.split("/", 2);
17617
- if (!owner || !name) throw new Error(`invalid GITHUB_REPOSITORY "${repo}"`);
17618
- const record2 = await createStateBackendFromEnv().getRepoDoc(`${owner}/${name}`, "secrets.enc");
17619
- const raw = record2?.doc;
17620
- const payload = raw && typeof raw === "object" && !Array.isArray(raw) && typeof raw.ciphertext === "string" ? raw.ciphertext : "";
17621
- if (!payload) throw new Error("backend vault is empty \u2014 save secrets from the dashboard first");
17622
- const plaintext = decryptVaultPayload(payload, masterKey);
17623
- return JSON.parse(plaintext);
17624
- }
17625
17704
  async function flyAppExists(name, token) {
17626
17705
  const res = await fetch(`${FLY_MACHINES}/apps/${encodeURIComponent(name)}`, {
17627
17706
  headers: flyHeaders(token),
@@ -17796,7 +17875,7 @@ var FLY_MACHINES, FLY_GRAPHQL, REQ_TIMEOUT_MS2, runPreviewBuild;
17796
17875
  var init_runPreviewBuild = __esm({
17797
17876
  "src/scripts/runPreviewBuild.ts"() {
17798
17877
  "use strict";
17799
- init_state_backend();
17878
+ init_kody_api_client();
17800
17879
  init_previewBuildHelpers();
17801
17880
  init_previewBuildNamespace();
17802
17881
  init_previewBuildRun();
@@ -17813,12 +17892,10 @@ var init_runPreviewBuild = __esm({
17813
17892
  }
17814
17893
  let repo;
17815
17894
  let ref;
17816
- let masterKey;
17817
17895
  let ghToken3;
17818
17896
  try {
17819
17897
  repo = required("GITHUB_REPOSITORY");
17820
17898
  ref = required("GITHUB_SHA");
17821
- masterKey = required("KODY_MASTER_KEY");
17822
17899
  ghToken3 = (process.env.KODY_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_PAT ?? "").trim();
17823
17900
  if (!ghToken3) {
17824
17901
  throw new Error("GitHub auth token missing (KODY_TOKEN / GH_TOKEN / GITHUB_TOKEN / GH_PAT all empty)");
@@ -17832,17 +17909,17 @@ var init_runPreviewBuild = __esm({
17832
17909
  const appName = previewAppName(repo, pr);
17833
17910
  const tag = defaultImageTag(repo, ref);
17834
17911
  try {
17835
- const doc = await fetchVaultDoc(repo, masterKey);
17836
- const { buildEnv, buildMode } = buildEnvFromVault(doc);
17837
- const flyToken = doc.secrets?.FLY_API_TOKEN?.value?.trim();
17912
+ const previewContext = await readPreviewContextFromKody();
17913
+ const { buildEnv, buildMode } = previewContext;
17914
+ const flyToken = previewContext.flyApiToken?.trim();
17838
17915
  if (!flyToken) {
17839
17916
  ctx.output.exitCode = 99;
17840
17917
  ctx.output.reason = "runPreviewBuild: vault has no FLY_API_TOKEN \u2014 add it via the dashboard's /secrets page";
17841
17918
  return;
17842
17919
  }
17843
- const orgSlug = doc.secrets?.FLY_ORG_SLUG?.value?.trim() || (process.env.FLY_ORG_SLUG ?? "personal").trim();
17844
- const region = doc.secrets?.FLY_DEFAULT_REGION?.value?.trim() || (process.env.FLY_REGION ?? "fra").trim();
17845
- const nscTenantId = doc.secrets?.NSC_TENANT_ID?.value?.trim() || "";
17920
+ const orgSlug = previewContext.flyOrgSlug?.trim() || (process.env.FLY_ORG_SLUG ?? "personal").trim();
17921
+ const region = previewContext.flyRegion?.trim() || (process.env.FLY_REGION ?? "fra").trim();
17922
+ const nscTenantId = previewContext.namespaceTenantId?.trim() || "";
17846
17923
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
17847
17924
  if (Object.keys(buildEnv).length > 0) {
17848
17925
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
@@ -17924,7 +18001,7 @@ var init_runPreviewBuild = __esm({
17924
18001
  appName,
17925
18002
  region,
17926
18003
  image: `registry.fly.io/${appName}:${tag}`,
17927
- env: previewRuntimeEnv({ buildEnv, masterKey, pr, repo })
18004
+ env: previewRuntimeEnv({ buildEnv, previewVerifyKey: previewContext.previewVerifyKey, pr, repo })
17928
18005
  },
17929
18006
  flyToken
17930
18007
  );
@@ -19707,10 +19784,10 @@ async function hydrateWorkflows(backend, tenant, cwd) {
19707
19784
  }
19708
19785
  async function hydrateStateWorkspace(config, cwd, backendOverride) {
19709
19786
  const tenant = tenantId(config);
19710
- const configured = Boolean(process.env.CONVEX_URL?.trim()) && Boolean(process.env.KODY_SERVICE_KEY?.trim());
19787
+ const configured = hasStateBackendConfig();
19711
19788
  if (!tenant || !configured) {
19712
19789
  if (process.env.GITHUB_ACTIONS === "true")
19713
- throw new Error("Convex backend is required for runtime workspace documents");
19790
+ throw new Error("Kody backend access is required for runtime workspace documents");
19714
19791
  return;
19715
19792
  }
19716
19793
  const key = `${path43.resolve(cwd)}|${tenant}`;
@@ -22371,6 +22448,7 @@ async function runChatTurn(opts) {
22371
22448
  ],
22372
22449
  systemPromptAppend: systemPrompt,
22373
22450
  ...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
22451
+ ...opts.repoToken ? { repoToken: opts.repoToken } : {},
22374
22452
  // Cross-repo work is opt-in. Repo Brain's default path remains focused
22375
22453
  // on the selected repo even though the server stores clones under
22376
22454
  // reposRoot.
@@ -24334,12 +24412,12 @@ var BrokerSink = class {
24334
24412
  const be = translateChatEvent(event, this.chatId);
24335
24413
  if (!be) return;
24336
24414
  this.emitToLog(be);
24337
- if (this.tenantId && hasStateBackendConfig()) {
24415
+ if (this.tenantId && hasStateBackendConfig2()) {
24338
24416
  await createStateBackendFromEnv().appendChatEvent(this.tenantId, this.chatId, be);
24339
24417
  }
24340
24418
  }
24341
24419
  };
24342
- function hasStateBackendConfig(env = process.env) {
24420
+ function hasStateBackendConfig2(env = process.env) {
24343
24421
  return Boolean(env.CONVEX_URL?.trim() && env.KODY_SERVICE_KEY?.trim());
24344
24422
  }
24345
24423
  var chatQueues = /* @__PURE__ */ new Map();
@@ -25604,10 +25682,9 @@ async function hydrateDefinitions(options) {
25604
25682
  }
25605
25683
  async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env) {
25606
25684
  const tenantId2 = env.GITHUB_REPOSITORY?.trim();
25607
- const hasCredentials = Boolean(env.CONVEX_URL?.trim()) && Boolean(env.KODY_SERVICE_KEY?.trim());
25608
- if (!hasCredentials) {
25685
+ if (!hasStateBackendConfig(env)) {
25609
25686
  if (env.GITHUB_ACTIONS === "true") {
25610
- throw new Error("CONVEX_URL and KODY_SERVICE_KEY are required for backend definitions in GitHub Actions");
25687
+ throw new Error("GitHub Actions workflow identity is required for backend definitions");
25611
25688
  }
25612
25689
  return null;
25613
25690
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.408",
3
+ "version": "0.4.410",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",