@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 +100 -10
- package/dist/cli.js.map +3 -3
- package/dist/index.js +100 -10
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
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
|
|
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(
|
|
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:
|
|
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] &&
|
|
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);
|