@kynver-app/runtime 0.1.9 → 0.1.11

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/cli.js CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { mkdirSync as mkdirSync5 } from "node:fs";
5
- import path14 from "node:path";
4
+ import { mkdirSync as mkdirSync5, realpathSync } from "node:fs";
6
5
  import { fileURLToPath } from "node:url";
7
6
 
8
7
  // src/config.ts
@@ -381,12 +380,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
381
380
  var DEFAULT_MAX_USED_PERCENT = 80;
382
381
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
383
382
  function observeRunnerDiskGate(input = {}) {
384
- const path15 = input.diskPath?.trim() || "/";
383
+ const path14 = input.diskPath?.trim() || "/";
385
384
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
386
385
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
387
386
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
388
387
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
389
- const stats = statfsSync(path15);
388
+ const stats = statfsSync(path14);
390
389
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
391
390
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
392
391
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -406,7 +405,7 @@ function observeRunnerDiskGate(input = {}) {
406
405
  }
407
406
  return {
408
407
  ok,
409
- path: path15,
408
+ path: path14,
410
409
  freeBytes,
411
410
  totalBytes,
412
411
  usedPercent,
@@ -594,6 +593,92 @@ function ensureGitRepo(repo) {
594
593
  function gitStatusShort(worktreePath) {
595
594
  return git(worktreePath, ["status", "--short"], { allowFailure: true }).split("\n").map((line) => line.trim()).filter(Boolean);
596
595
  }
596
+ function gitCapture(cwd, args) {
597
+ try {
598
+ const res = spawnSync("git", args, { cwd, encoding: "utf8" });
599
+ return {
600
+ status: res.status,
601
+ stdout: res.stdout || "",
602
+ stderr: res.stderr || "",
603
+ error: res.error ? res.error.message : null
604
+ };
605
+ } catch (error) {
606
+ return {
607
+ status: null,
608
+ stdout: "",
609
+ stderr: "",
610
+ error: error.message
611
+ };
612
+ }
613
+ }
614
+ function gitIsAncestor(cwd, ancestor, descendant) {
615
+ const res = gitCapture(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]);
616
+ if (res.status === 0) return { isAncestor: true, error: null };
617
+ if (res.status === 1) return { isAncestor: false, error: null };
618
+ return { isAncestor: null, error: res.error || res.stderr || res.stdout || `git exited ${res.status}` };
619
+ }
620
+ function computeGitAncestry(worktreePath, base = "origin/main") {
621
+ if (!worktreePath) {
622
+ return unknownAncestry(base, "missing worktree path");
623
+ }
624
+ const head = gitCapture(worktreePath, ["rev-parse", "HEAD"]);
625
+ if (head.status !== 0) return unknownAncestry(base, head.error || head.stderr || head.stdout || "failed to resolve HEAD");
626
+ const baseHead = gitCapture(worktreePath, ["rev-parse", base]);
627
+ if (baseHead.status !== 0) {
628
+ return unknownAncestry(base, baseHead.error || baseHead.stderr || baseHead.stdout || `failed to resolve ${base}`, head.stdout.trim());
629
+ }
630
+ const headSha = head.stdout.trim();
631
+ const baseSha = baseHead.stdout.trim();
632
+ if (headSha === baseSha) {
633
+ return {
634
+ checked: true,
635
+ base,
636
+ head: headSha,
637
+ baseHead: baseSha,
638
+ baseIsAncestorOfHead: true,
639
+ headIsAncestorOfBase: true,
640
+ relation: "synced"
641
+ };
642
+ }
643
+ const baseIsAncestorOfHead = gitIsAncestor(worktreePath, baseSha, headSha);
644
+ const headIsAncestorOfBase = gitIsAncestor(worktreePath, headSha, baseSha);
645
+ const error = baseIsAncestorOfHead.error || headIsAncestorOfBase.error || void 0;
646
+ if (baseIsAncestorOfHead.isAncestor == null || headIsAncestorOfBase.isAncestor == null) {
647
+ return {
648
+ checked: false,
649
+ base,
650
+ head: headSha,
651
+ baseHead: baseSha,
652
+ baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
653
+ headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
654
+ relation: "unknown",
655
+ ...error ? { error } : {}
656
+ };
657
+ }
658
+ const relation = baseIsAncestorOfHead.isAncestor ? "ahead" : headIsAncestorOfBase.isAncestor ? "merged" : "diverged";
659
+ return {
660
+ checked: true,
661
+ base,
662
+ head: headSha,
663
+ baseHead: baseSha,
664
+ baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
665
+ headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
666
+ relation,
667
+ ...error ? { error } : {}
668
+ };
669
+ }
670
+ function unknownAncestry(base, error, head = null) {
671
+ return {
672
+ checked: false,
673
+ base,
674
+ head,
675
+ baseHead: null,
676
+ baseIsAncestorOfHead: null,
677
+ headIsAncestorOfBase: null,
678
+ relation: "unknown",
679
+ error
680
+ };
681
+ }
597
682
  function scrubClaudeEnv(env) {
598
683
  const next = { ...env };
599
684
  delete next.ANTHROPIC_API_KEY;
@@ -620,7 +705,7 @@ function computeAttention(input) {
620
705
  }
621
706
  return { state: "ok", reason: "recent activity" };
622
707
  }
623
- function computeWorkerStatus(worker) {
708
+ function computeWorkerStatus(worker, options = {}) {
624
709
  const parsed = parseClaudeStream(worker.stdoutPath);
625
710
  const heartbeat = parseHeartbeat(worker.heartbeatPath);
626
711
  const alive = isPidAlive(worker.pid);
@@ -628,6 +713,7 @@ function computeWorkerStatus(worker) {
628
713
  const stderrBytes = fileSize(worker.stderrPath);
629
714
  const heartbeatBytes = fileSize(worker.heartbeatPath);
630
715
  const changedFiles = gitStatusShort(worker.worktreePath);
716
+ const gitAncestry = computeGitAncestry(worker.worktreePath, options.base);
631
717
  const lastActivityAt = latestIso([
632
718
  parsed.lastEventAt,
633
719
  heartbeat.lastHeartbeatAt,
@@ -669,7 +755,8 @@ function computeWorkerStatus(worker) {
669
755
  heartbeatBlocker: heartbeat.heartbeatBlocker,
670
756
  finalResult: parsed.finalResult,
671
757
  error: parsed.error || (!alive && !parsed.finalResult ? tailFile(worker.stderrPath, 10).trim() || void 0 : void 0),
672
- changedFiles
758
+ changedFiles,
759
+ gitAncestry
673
760
  };
674
761
  }
675
762
  function isFinishedWorkerStatus(status) {
@@ -1431,7 +1518,7 @@ function runStatus(args) {
1431
1518
  if (!worker) {
1432
1519
  return { worker: name, status: "missing", attention: "needs_attention", attentionReason: "worker.json not found" };
1433
1520
  }
1434
- const status = computeWorkerStatus(worker);
1521
+ const status = computeWorkerStatus(worker, { base: run.base });
1435
1522
  return {
1436
1523
  worker: status.worker,
1437
1524
  status: status.status,
@@ -1445,7 +1532,9 @@ function runStatus(args) {
1445
1532
  lastHeartbeatSummary: status.lastHeartbeatSummary,
1446
1533
  heartbeatBlocker: status.heartbeatBlocker,
1447
1534
  changedFileCount: status.changedFiles.length,
1448
- branch: status.branch
1535
+ branch: status.branch,
1536
+ ancestry: status.gitAncestry.relation,
1537
+ ancestryChecked: status.gitAncestry.checked
1449
1538
  };
1450
1539
  });
1451
1540
  const board = {
@@ -1829,6 +1918,7 @@ async function main(argv = process.argv.slice(2)) {
1829
1918
  } else {
1830
1919
  rest = argv;
1831
1920
  }
1921
+ if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
1832
1922
  const args = parseArgs(rest);
1833
1923
  const { runsDir, worktreesDir } = getPaths();
1834
1924
  mkdirSync5(runsDir, { recursive: true });
@@ -1851,7 +1941,7 @@ async function main(argv = process.argv.slice(2)) {
1851
1941
  if (scope === "worker" && action === "complete") return void await completeWorker(args);
1852
1942
  unknownCommand(scope, action);
1853
1943
  }
1854
- var isCliEntry = process.argv[1] && path14.resolve(process.argv[1]) === path14.resolve(fileURLToPath(import.meta.url));
1944
+ var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(fileURLToPath(import.meta.url));
1855
1945
  if (isCliEntry) {
1856
1946
  void main().catch((error) => {
1857
1947
  console.error(error);