@kynver-app/runtime 0.1.2 → 0.1.3

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/index.js CHANGED
@@ -236,12 +236,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
236
236
  var DEFAULT_MAX_USED_PERCENT = 80;
237
237
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
238
238
  function observeRunnerDiskGate(input = {}) {
239
- const path13 = input.diskPath?.trim() || "/";
239
+ const path14 = input.diskPath?.trim() || "/";
240
240
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
241
241
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
242
242
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
243
243
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
244
- const stats = statfsSync(path13);
244
+ const stats = statfsSync(path14);
245
245
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
246
246
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
247
247
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -261,7 +261,7 @@ function observeRunnerDiskGate(input = {}) {
261
261
  }
262
262
  return {
263
263
  ok,
264
- path: path13,
264
+ path: path14,
265
265
  freeBytes,
266
266
  totalBytes,
267
267
  usedPercent,
@@ -372,7 +372,8 @@ function parseClaudeStream(file) {
372
372
  for (const line of lines) {
373
373
  const event = safeJson(line);
374
374
  if (!event) continue;
375
- const ts = event.timestamp || event.ts;
375
+ const tsMs = event.timestamp_ms;
376
+ const ts = event.timestamp || event.ts || (tsMs ? new Date(tsMs).toISOString() : void 0);
376
377
  if (ts) {
377
378
  result.firstEventAt ||= ts;
378
379
  result.lastEventAt = ts;
@@ -631,8 +632,8 @@ function observeRunnerResourceGate(input) {
631
632
  }
632
633
 
633
634
  // src/supervisor.ts
634
- import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "node:fs";
635
- import path6 from "node:path";
635
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3 } from "node:fs";
636
+ import path7 from "node:path";
636
637
 
637
638
  // src/prompt.ts
638
639
  function buildPrompt(input) {
@@ -692,11 +693,44 @@ var claudeProvider = {
692
693
  };
693
694
 
694
695
  // src/providers/cursor.ts
695
- import { closeSync as closeSync2, openSync as openSync2 } from "node:fs";
696
+ import { closeSync as closeSync2, existsSync as existsSync6, openSync as openSync2, readdirSync as readdirSync2 } from "node:fs";
696
697
  import { spawn as spawn2 } from "node:child_process";
698
+ import path6 from "node:path";
697
699
  var DEFAULT_CURSOR_MODEL = "composer-2.5";
700
+ function latestVersionDir(versionsRoot) {
701
+ if (!existsSync6(versionsRoot)) return null;
702
+ const versions = readdirSync2(versionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^\d{4}\.\d/.test(entry.name)).map((entry) => entry.name).sort((a, b) => b.localeCompare(a));
703
+ return versions[0] ? path6.join(versionsRoot, versions[0]) : null;
704
+ }
705
+ function resolveBundledCursor(versionDir) {
706
+ const nodeExe = path6.join(versionDir, "node.exe");
707
+ const indexJs = path6.join(versionDir, "index.js");
708
+ if (!existsSync6(nodeExe) || !existsSync6(indexJs)) return null;
709
+ return { executable: nodeExe, prefixArgs: [indexJs], shell: false, detached: true };
710
+ }
711
+ function resolveWindowsCursorSpawn(agentBin) {
712
+ const agentRoot = path6.dirname(agentBin);
713
+ const direct = resolveBundledCursor(agentRoot);
714
+ if (direct) return direct;
715
+ const versionDir = latestVersionDir(path6.join(agentRoot, "versions"));
716
+ return versionDir ? resolveBundledCursor(versionDir) : null;
717
+ }
718
+ function resolveCursorSpawn(agentBin) {
719
+ if (process.platform === "win32" && /\.(cmd|bat)$/i.test(agentBin)) {
720
+ const bundled = resolveWindowsCursorSpawn(agentBin);
721
+ if (bundled) return bundled;
722
+ return { executable: agentBin, prefixArgs: [], shell: true, detached: false };
723
+ }
724
+ return { executable: agentBin, prefixArgs: [], shell: false, detached: true };
725
+ }
698
726
  function resolveAgentBin() {
699
- return process.env.KYNVER_CURSOR_AGENT_BIN?.trim() || process.env.CURSOR_AGENT_BIN?.trim() || "agent";
727
+ const configured = process.env.KYNVER_CURSOR_AGENT_BIN?.trim() || process.env.CURSOR_AGENT_BIN?.trim();
728
+ if (configured) return configured;
729
+ if (process.platform === "win32") {
730
+ const localAgent = path6.join(process.env.LOCALAPPDATA || "", "cursor-agent", "agent.cmd");
731
+ if (existsSync6(localAgent)) return localAgent;
732
+ }
733
+ return "agent";
700
734
  }
701
735
  var cursorProvider = {
702
736
  name: "cursor",
@@ -705,9 +739,11 @@ var cursorProvider = {
705
739
  const stdoutFd = openSync2(opts.stdoutPath, "a");
706
740
  const stderrFd = openSync2(opts.stderrPath, "a");
707
741
  const agentBin = resolveAgentBin();
742
+ const spawnTarget = resolveCursorSpawn(agentBin);
708
743
  const child = spawn2(
709
- agentBin,
744
+ spawnTarget.executable,
710
745
  [
746
+ ...spawnTarget.prefixArgs,
711
747
  "-p",
712
748
  "--force",
713
749
  "--trust",
@@ -722,9 +758,13 @@ var cursorProvider = {
722
758
  ],
723
759
  {
724
760
  cwd: opts.worktreePath,
725
- detached: true,
761
+ detached: spawnTarget.detached,
762
+ shell: spawnTarget.shell,
726
763
  stdio: ["ignore", stdoutFd, stderrFd],
727
- env: process.env
764
+ env: {
765
+ ...process.env,
766
+ ...spawnTarget.prefixArgs.length > 0 ? { CURSOR_INVOKED_AS: path6.basename(agentBin) } : {}
767
+ }
728
768
  }
729
769
  );
730
770
  closeSync2(stdoutFd);
@@ -762,16 +802,16 @@ function spawnWorkerProcess(run, opts) {
762
802
  if (run.workers?.[name]) throw new Error(`worker already exists in run ${run.id}: ${name}`);
763
803
  if (!opts.task) throw new Error(`missing task text for worker ${name}`);
764
804
  const { worktreesDir } = getPaths();
765
- const workerDir = path6.join(runDirectory(run.id), "workers", name);
805
+ const workerDir = path7.join(runDirectory(run.id), "workers", name);
766
806
  mkdirSync3(workerDir, { recursive: true });
767
- const worktreePath = path6.join(worktreesDir, run.id, name);
807
+ const worktreePath = path7.join(worktreesDir, run.id, name);
768
808
  const branch = opts.branch || `agent/${run.id}/${name}`;
769
- if (existsSync6(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
809
+ if (existsSync7(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
770
810
  git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
771
811
  git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
772
- const stdoutPath = path6.join(workerDir, "stdout.jsonl");
773
- const stderrPath = path6.join(workerDir, "stderr.log");
774
- const heartbeatPath = path6.join(workerDir, "heartbeat.jsonl");
812
+ const stdoutPath = path7.join(workerDir, "stdout.jsonl");
813
+ const stderrPath = path7.join(workerDir, "stderr.log");
814
+ const heartbeatPath = path7.join(workerDir, "heartbeat.jsonl");
775
815
  const prompt = buildPrompt({
776
816
  task: opts.task,
777
817
  ownedPaths: opts.ownedPaths || [],
@@ -820,7 +860,7 @@ function spawnWorkerProcess(run, opts) {
820
860
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
821
861
  };
822
862
  saveWorker(run.id, worker);
823
- run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path6.join(workerDir, "worker.json") } };
863
+ run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path7.join(workerDir, "worker.json") } };
824
864
  run.status = "running";
825
865
  saveRun(run);
826
866
  return worker;
@@ -1025,7 +1065,7 @@ function redactHarness(text, secret) {
1025
1065
  }
1026
1066
 
1027
1067
  // src/validate.ts
1028
- import path7 from "node:path";
1068
+ import path8 from "node:path";
1029
1069
  var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
1030
1070
  var WORKER_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
1031
1071
  function validateRunId(runId) {
@@ -1039,15 +1079,15 @@ function validateWorkerName(name) {
1039
1079
  return trimmed;
1040
1080
  }
1041
1081
  function validateRepo(repo) {
1042
- const resolved = path7.resolve(repo);
1082
+ const resolved = path8.resolve(repo);
1043
1083
  if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
1044
1084
  return resolved;
1045
1085
  }
1046
1086
  function validateOwnedPaths(repoRoot, ownedPaths) {
1047
1087
  return ownedPaths.map((owned) => {
1048
- const resolved = path7.resolve(repoRoot, owned);
1049
- const rel = path7.relative(repoRoot, resolved);
1050
- if (rel.startsWith("..") || path7.isAbsolute(rel)) {
1088
+ const resolved = path8.resolve(repoRoot, owned);
1089
+ const rel = path8.relative(repoRoot, resolved);
1090
+ if (rel.startsWith("..") || path8.isAbsolute(rel)) {
1051
1091
  throw new Error(`owned path escapes repo: ${owned}`);
1052
1092
  }
1053
1093
  return resolved;
@@ -1059,14 +1099,14 @@ function validateTailLines(lines) {
1059
1099
  }
1060
1100
 
1061
1101
  // src/worktree.ts
1062
- import { existsSync as existsSync7, mkdirSync as mkdirSync4 } from "node:fs";
1063
- import path8 from "node:path";
1102
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4 } from "node:fs";
1103
+ import path9 from "node:path";
1064
1104
  function createRun(args) {
1065
1105
  const repo = validateRepo(required(String(args.repo || ""), "--repo"));
1066
1106
  ensureGitRepo(repo);
1067
1107
  const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
1068
1108
  const dir = runDirectory(id);
1069
- if (existsSync7(dir)) failExists(`run already exists: ${id}`);
1109
+ if (existsSync8(dir)) failExists(`run already exists: ${id}`);
1070
1110
  mkdirSync4(dir, { recursive: true });
1071
1111
  const base = String(args.base || "origin/main");
1072
1112
  const baseCommit = git(repo, ["rev-parse", base]).trim();
@@ -1080,12 +1120,12 @@ function createRun(args) {
1080
1120
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1081
1121
  workers: {}
1082
1122
  };
1083
- writeJson(path8.join(dir, "run.json"), run);
1123
+ writeJson(path9.join(dir, "run.json"), run);
1084
1124
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
1085
1125
  }
1086
1126
  function listRuns() {
1087
1127
  const { runsDir } = getPaths();
1088
- const rows = listRunIds(runsDir).map((id) => readJson(path8.join(runDirectory(id), "run.json"), null)).filter(Boolean).map((run) => ({
1128
+ const rows = listRunIds(runsDir).map((id) => readJson(path9.join(runDirectory(id), "run.json"), null)).filter(Boolean).map((run) => ({
1089
1129
  id: run.id,
1090
1130
  name: run.name,
1091
1131
  status: run.status,
@@ -1100,7 +1140,7 @@ function failExists(message) {
1100
1140
  }
1101
1141
 
1102
1142
  // src/sweep.ts
1103
- import path9 from "node:path";
1143
+ import path10 from "node:path";
1104
1144
  async function sweepRun(args) {
1105
1145
  const pipeline = args.pipeline === true || args.pipeline === "true";
1106
1146
  try {
@@ -1112,7 +1152,7 @@ async function sweepRun(args) {
1112
1152
  const releasedLocalOrphans = [];
1113
1153
  for (const name of Object.keys(run.workers || {})) {
1114
1154
  const worker = readJson(
1115
- path9.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1155
+ path10.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1116
1156
  null
1117
1157
  );
1118
1158
  if (!worker || !worker.dispatched || !worker.taskId) continue;
@@ -1155,7 +1195,7 @@ async function sweepRun(args) {
1155
1195
  }
1156
1196
 
1157
1197
  // src/worker-ops.ts
1158
- import path10 from "node:path";
1198
+ import path11 from "node:path";
1159
1199
  async function tryCompleteWorker(args) {
1160
1200
  const worker = loadWorker(String(args.run), String(args.name));
1161
1201
  const status = computeWorkerStatus(worker);
@@ -1248,7 +1288,7 @@ async function completeWorker(args) {
1248
1288
  function workerStatus(args) {
1249
1289
  const worker = loadWorker(String(args.run), String(args.name));
1250
1290
  const status = computeWorkerStatus(worker);
1251
- writeJson(path10.join(worker.workerDir, "last-status.json"), status);
1291
+ writeJson(path11.join(worker.workerDir, "last-status.json"), status);
1252
1292
  console.log(JSON.stringify(status, null, 2));
1253
1293
  }
1254
1294
  function runStatus(args) {
@@ -1256,7 +1296,7 @@ function runStatus(args) {
1256
1296
  const names = Object.keys(run.workers || {});
1257
1297
  const workers = names.map((name) => {
1258
1298
  const worker = readJson(
1259
- path10.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1299
+ path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1260
1300
  null
1261
1301
  );
1262
1302
  if (!worker) {
@@ -1288,7 +1328,7 @@ function runStatus(args) {
1288
1328
  needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
1289
1329
  workers
1290
1330
  };
1291
- writeJson(path10.join(runDirectory(run.id), "last-board.json"), board);
1331
+ writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
1292
1332
  console.log(JSON.stringify(board, null, 2));
1293
1333
  }
1294
1334
  function tailWorker(args) {
@@ -1322,11 +1362,11 @@ function stopWorker(args) {
1322
1362
 
1323
1363
  // src/cli.ts
1324
1364
  import { mkdirSync as mkdirSync5 } from "node:fs";
1325
- import path12 from "node:path";
1365
+ import path13 from "node:path";
1326
1366
  import { fileURLToPath } from "node:url";
1327
1367
 
1328
1368
  // src/pipeline-tick.ts
1329
- import path11 from "node:path";
1369
+ import path12 from "node:path";
1330
1370
 
1331
1371
  // src/workspace-runtime-config.ts
1332
1372
  async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
@@ -1355,7 +1395,7 @@ async function completeFinishedWorkers(runId, args) {
1355
1395
  const outcomes = [];
1356
1396
  for (const name of Object.keys(run.workers || {})) {
1357
1397
  const worker = readJson(
1358
- path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1398
+ path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1359
1399
  null
1360
1400
  );
1361
1401
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -1534,7 +1574,7 @@ async function main(argv = process.argv.slice(2)) {
1534
1574
  if (scope === "worker" && action === "complete") return void await completeWorker(args);
1535
1575
  unknownCommand(scope, action);
1536
1576
  }
1537
- var isCliEntry = process.argv[1] && path12.resolve(process.argv[1]) === path12.resolve(fileURLToPath(import.meta.url));
1577
+ var isCliEntry = process.argv[1] && path13.resolve(process.argv[1]) === path13.resolve(fileURLToPath(import.meta.url));
1538
1578
  if (isCliEntry) {
1539
1579
  void main().catch((error) => {
1540
1580
  console.error(error);