runwork 0.25.0 → 0.25.2
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/bundled-types/core-routes.d.ts +25 -0
- package/dist/index.js +623 -301
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1020,26 +1020,36 @@ function buildHelperValue(execPath, scriptPath) {
|
|
|
1020
1020
|
return `!"${normalised}" git-credential-helper`;
|
|
1021
1021
|
}
|
|
1022
1022
|
async function configureGitCredentials(remoteUrl) {
|
|
1023
|
-
|
|
1023
|
+
let origin;
|
|
1024
|
+
try {
|
|
1025
|
+
origin = new URL(remoteUrl).origin;
|
|
1026
|
+
} catch {
|
|
1027
|
+
const message = `"${remoteUrl}" is not a valid URL.`;
|
|
1028
|
+
console.warn(`Note: ${message}`);
|
|
1029
|
+
return { ok: false, reason: "invalid-url", message };
|
|
1030
|
+
}
|
|
1024
1031
|
const key = `credential.${origin}.helper`;
|
|
1025
1032
|
const helperValue = buildHelperValue(process.execPath, process.argv[1]);
|
|
1026
1033
|
try {
|
|
1027
1034
|
try {
|
|
1028
1035
|
execFileSync("git", ["config", "--global", "--unset-all", key], { stdio: "pipe" });
|
|
1029
|
-
} catch
|
|
1030
|
-
if (unsetErr?.code === "ENOENT")
|
|
1031
|
-
throw unsetErr;
|
|
1032
|
-
}
|
|
1036
|
+
} catch {}
|
|
1033
1037
|
execFileSync("git", ["config", "--global", "--add", key, ""], { stdio: "pipe" });
|
|
1034
1038
|
execFileSync("git", ["config", "--global", "--add", key, helperValue], { stdio: "pipe" });
|
|
1039
|
+
return { ok: true };
|
|
1035
1040
|
} catch (err) {
|
|
1036
1041
|
const code = err?.code;
|
|
1037
1042
|
if (code === "ENOENT") {
|
|
1038
|
-
|
|
1039
|
-
console.warn(
|
|
1040
|
-
return;
|
|
1043
|
+
const message2 = "git is not installed. Install git before running `runwork init`, `clone`, `dev`, or `deploy`.";
|
|
1044
|
+
console.warn(`Note: ${message2}`);
|
|
1045
|
+
return { ok: false, reason: "missing", message: message2 };
|
|
1041
1046
|
}
|
|
1042
|
-
|
|
1047
|
+
const detail = err instanceof Error ? err.message.split(`
|
|
1048
|
+
`)[0] : String(err);
|
|
1049
|
+
const xcodeHint = process.platform === "darwin" ? " On macOS this usually means the Xcode Command Line Tools are missing; run `xcode-select --install`." : "";
|
|
1050
|
+
const message = `git is installed but could not be run (${detail}).${xcodeHint}`;
|
|
1051
|
+
console.warn(`Note: ${message}`);
|
|
1052
|
+
return { ok: false, reason: "unusable", message };
|
|
1043
1053
|
}
|
|
1044
1054
|
}
|
|
1045
1055
|
function lookupCredentialHelper(origin) {
|
|
@@ -1097,13 +1107,13 @@ async function ensureGitCredentialHelper(baseUrl) {
|
|
|
1097
1107
|
try {
|
|
1098
1108
|
origin = new URL(baseUrl).origin;
|
|
1099
1109
|
} catch {
|
|
1100
|
-
return;
|
|
1110
|
+
return { ok: false, reason: "invalid-url", message: `"${baseUrl}" is not a valid URL.` };
|
|
1101
1111
|
}
|
|
1102
1112
|
const lookup = lookupCredentialHelper(origin);
|
|
1103
1113
|
if (lookup.status === "registered" && helperBinaryStatus(lookup.value).ok && lookup.hasReset) {
|
|
1104
|
-
return;
|
|
1114
|
+
return { ok: true };
|
|
1105
1115
|
}
|
|
1106
|
-
|
|
1116
|
+
return configureGitCredentials(baseUrl);
|
|
1107
1117
|
}
|
|
1108
1118
|
async function removeGitCredentials(baseUrl) {
|
|
1109
1119
|
const origin = new URL(baseUrl).origin;
|
|
@@ -1380,8 +1390,154 @@ var init_identity = __esm(() => {
|
|
|
1380
1390
|
init_subprocess();
|
|
1381
1391
|
});
|
|
1382
1392
|
|
|
1393
|
+
// src/utils/ignore-matcher.ts
|
|
1394
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
1395
|
+
import { basename, join as join4 } from "path";
|
|
1396
|
+
function defaultIgnoreSets() {
|
|
1397
|
+
return {
|
|
1398
|
+
dirs: new Set(DEFAULT_DIR_NAMES),
|
|
1399
|
+
files: new Set(DEFAULT_FILE_NAMES),
|
|
1400
|
+
extensions: new Set(DEFAULT_EXTENSIONS)
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function parseGitignoreContent(content) {
|
|
1404
|
+
const dirs = new Set;
|
|
1405
|
+
const files = new Set;
|
|
1406
|
+
for (const rawLine of content.split(`
|
|
1407
|
+
`)) {
|
|
1408
|
+
const line = rawLine.trim();
|
|
1409
|
+
if (!line || line.startsWith("#"))
|
|
1410
|
+
continue;
|
|
1411
|
+
if (line.startsWith("!"))
|
|
1412
|
+
continue;
|
|
1413
|
+
if (GLOB_CHARS.test(line))
|
|
1414
|
+
continue;
|
|
1415
|
+
let pattern = line;
|
|
1416
|
+
const isDirOnly = pattern.endsWith("/");
|
|
1417
|
+
if (isDirOnly)
|
|
1418
|
+
pattern = pattern.slice(0, -1);
|
|
1419
|
+
if (pattern.startsWith("/"))
|
|
1420
|
+
pattern = pattern.slice(1);
|
|
1421
|
+
if (!pattern)
|
|
1422
|
+
continue;
|
|
1423
|
+
if (pattern.includes("/"))
|
|
1424
|
+
continue;
|
|
1425
|
+
if (isDirOnly) {
|
|
1426
|
+
dirs.add(pattern);
|
|
1427
|
+
} else {
|
|
1428
|
+
dirs.add(pattern);
|
|
1429
|
+
files.add(pattern);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return { dirs, files };
|
|
1433
|
+
}
|
|
1434
|
+
function loadGitignoreFromDir(dir) {
|
|
1435
|
+
const path = join4(dir, ".gitignore");
|
|
1436
|
+
if (!existsSync4(path))
|
|
1437
|
+
return { dirs: new Set, files: new Set };
|
|
1438
|
+
try {
|
|
1439
|
+
return parseGitignoreContent(readFileSync4(path, "utf-8"));
|
|
1440
|
+
} catch {
|
|
1441
|
+
return { dirs: new Set, files: new Set };
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
function buildIgnoreSets(dir) {
|
|
1445
|
+
const sets = defaultIgnoreSets();
|
|
1446
|
+
const parsed = loadGitignoreFromDir(dir);
|
|
1447
|
+
for (const name of parsed.dirs)
|
|
1448
|
+
sets.dirs.add(name);
|
|
1449
|
+
for (const name of parsed.files)
|
|
1450
|
+
sets.files.add(name);
|
|
1451
|
+
return sets;
|
|
1452
|
+
}
|
|
1453
|
+
function isPathIgnored(filePath, sets) {
|
|
1454
|
+
const name = basename(filePath);
|
|
1455
|
+
if (sets.dirs.has(name))
|
|
1456
|
+
return true;
|
|
1457
|
+
if (sets.files.has(name))
|
|
1458
|
+
return true;
|
|
1459
|
+
const dotIndex = name.lastIndexOf(".");
|
|
1460
|
+
if (dotIndex >= 0) {
|
|
1461
|
+
const ext = name.slice(dotIndex);
|
|
1462
|
+
if (sets.extensions.has(ext))
|
|
1463
|
+
return true;
|
|
1464
|
+
}
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1467
|
+
function isRelPathIgnored(relPath, sets) {
|
|
1468
|
+
const segments = relPath.split("/");
|
|
1469
|
+
for (let i = 0;i < segments.length - 1; i++) {
|
|
1470
|
+
if (sets.dirs.has(segments[i]))
|
|
1471
|
+
return true;
|
|
1472
|
+
}
|
|
1473
|
+
return isPathIgnored(relPath, sets);
|
|
1474
|
+
}
|
|
1475
|
+
var ALWAYS_IGNORED_DIRS, DEFAULT_DIR_NAMES, DEFAULT_FILE_NAMES, DEFAULT_EXTENSIONS, GLOB_CHARS;
|
|
1476
|
+
var init_ignore_matcher = __esm(() => {
|
|
1477
|
+
ALWAYS_IGNORED_DIRS = [
|
|
1478
|
+
".git",
|
|
1479
|
+
"node_modules",
|
|
1480
|
+
".runwork",
|
|
1481
|
+
".bun-cache",
|
|
1482
|
+
".npm-cache",
|
|
1483
|
+
".pnpm-store",
|
|
1484
|
+
".turbo",
|
|
1485
|
+
".vite",
|
|
1486
|
+
".cache",
|
|
1487
|
+
"coverage",
|
|
1488
|
+
"dist"
|
|
1489
|
+
];
|
|
1490
|
+
DEFAULT_DIR_NAMES = ALWAYS_IGNORED_DIRS;
|
|
1491
|
+
DEFAULT_FILE_NAMES = [
|
|
1492
|
+
".dev.vars",
|
|
1493
|
+
".env"
|
|
1494
|
+
];
|
|
1495
|
+
DEFAULT_EXTENSIONS = [
|
|
1496
|
+
".log"
|
|
1497
|
+
];
|
|
1498
|
+
GLOB_CHARS = /[*?\[\]]/;
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
// src/git/repo-config.ts
|
|
1502
|
+
function hardenRepoForRestrictedFs(cwd) {
|
|
1503
|
+
for (const [key, value] of HARDENING) {
|
|
1504
|
+
try {
|
|
1505
|
+
execFileSync("git", ["config", key, value], { cwd, stdio: "pipe" });
|
|
1506
|
+
} catch {}
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function buildInitialGitignore() {
|
|
1510
|
+
const dirs = ALWAYS_IGNORED_DIRS.filter((dir) => dir !== ".git").map((dir) => `${dir}/`);
|
|
1511
|
+
return [
|
|
1512
|
+
"# Dependencies, caches and build output",
|
|
1513
|
+
...dirs,
|
|
1514
|
+
"",
|
|
1515
|
+
"# Local secrets. Never commit these.",
|
|
1516
|
+
".dev.vars",
|
|
1517
|
+
".dev.vars*",
|
|
1518
|
+
".env",
|
|
1519
|
+
".env.*",
|
|
1520
|
+
"",
|
|
1521
|
+
"# Logs and tool state",
|
|
1522
|
+
"*.log",
|
|
1523
|
+
"*.tsbuildinfo",
|
|
1524
|
+
".eslintcache",
|
|
1525
|
+
""
|
|
1526
|
+
].join(`
|
|
1527
|
+
`);
|
|
1528
|
+
}
|
|
1529
|
+
var HARDENING;
|
|
1530
|
+
var init_repo_config = __esm(() => {
|
|
1531
|
+
init_subprocess();
|
|
1532
|
+
init_ignore_matcher();
|
|
1533
|
+
HARDENING = [
|
|
1534
|
+
["gc.auto", "0"],
|
|
1535
|
+
["maintenance.auto", "false"]
|
|
1536
|
+
];
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1383
1539
|
// src/git/preflight.ts
|
|
1384
|
-
import { existsSync as
|
|
1540
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1385
1541
|
import { win32 as winPath } from "path";
|
|
1386
1542
|
import { homedir as homedir3 } from "os";
|
|
1387
1543
|
function tryRun(bin) {
|
|
@@ -1403,9 +1559,9 @@ function whereGit() {
|
|
|
1403
1559
|
const out = buf.toString("utf-8");
|
|
1404
1560
|
const lines = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1405
1561
|
const exe = lines.find((line) => /\.exe$/i.test(line));
|
|
1406
|
-
if (exe &&
|
|
1562
|
+
if (exe && existsSync5(exe))
|
|
1407
1563
|
return exe;
|
|
1408
|
-
const fallback = lines.find((line) =>
|
|
1564
|
+
const fallback = lines.find((line) => existsSync5(line));
|
|
1409
1565
|
return fallback ?? null;
|
|
1410
1566
|
} catch {
|
|
1411
1567
|
return null;
|
|
@@ -1430,7 +1586,7 @@ function registryGit() {
|
|
|
1430
1586
|
continue;
|
|
1431
1587
|
const installRoot = match[1].trim();
|
|
1432
1588
|
const gitExe = winPath.join(installRoot, "cmd", "git.exe");
|
|
1433
|
-
if (
|
|
1589
|
+
if (existsSync5(gitExe))
|
|
1434
1590
|
return gitExe;
|
|
1435
1591
|
} catch {}
|
|
1436
1592
|
}
|
|
@@ -1498,7 +1654,7 @@ function probeGit() {
|
|
|
1498
1654
|
}
|
|
1499
1655
|
}
|
|
1500
1656
|
for (const candidate of canonicalGitCandidates()) {
|
|
1501
|
-
if (!
|
|
1657
|
+
if (!existsSync5(candidate))
|
|
1502
1658
|
continue;
|
|
1503
1659
|
const verify = tryRun(candidate);
|
|
1504
1660
|
if (verify.ok) {
|
|
@@ -1636,106 +1792,6 @@ async function resolveApp(client, nameOrId, workspaceId) {
|
|
|
1636
1792
|
process.exit(1);
|
|
1637
1793
|
}
|
|
1638
1794
|
|
|
1639
|
-
// src/utils/ignore-matcher.ts
|
|
1640
|
-
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1641
|
-
import { basename, join as join4 } from "path";
|
|
1642
|
-
function defaultIgnoreSets() {
|
|
1643
|
-
return {
|
|
1644
|
-
dirs: new Set(DEFAULT_DIR_NAMES),
|
|
1645
|
-
files: new Set(DEFAULT_FILE_NAMES),
|
|
1646
|
-
extensions: new Set(DEFAULT_EXTENSIONS)
|
|
1647
|
-
};
|
|
1648
|
-
}
|
|
1649
|
-
function parseGitignoreContent(content) {
|
|
1650
|
-
const dirs = new Set;
|
|
1651
|
-
const files = new Set;
|
|
1652
|
-
for (const rawLine of content.split(`
|
|
1653
|
-
`)) {
|
|
1654
|
-
const line = rawLine.trim();
|
|
1655
|
-
if (!line || line.startsWith("#"))
|
|
1656
|
-
continue;
|
|
1657
|
-
if (line.startsWith("!"))
|
|
1658
|
-
continue;
|
|
1659
|
-
if (GLOB_CHARS.test(line))
|
|
1660
|
-
continue;
|
|
1661
|
-
let pattern = line;
|
|
1662
|
-
const isDirOnly = pattern.endsWith("/");
|
|
1663
|
-
if (isDirOnly)
|
|
1664
|
-
pattern = pattern.slice(0, -1);
|
|
1665
|
-
if (pattern.startsWith("/"))
|
|
1666
|
-
pattern = pattern.slice(1);
|
|
1667
|
-
if (!pattern)
|
|
1668
|
-
continue;
|
|
1669
|
-
if (pattern.includes("/"))
|
|
1670
|
-
continue;
|
|
1671
|
-
if (isDirOnly) {
|
|
1672
|
-
dirs.add(pattern);
|
|
1673
|
-
} else {
|
|
1674
|
-
dirs.add(pattern);
|
|
1675
|
-
files.add(pattern);
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
return { dirs, files };
|
|
1679
|
-
}
|
|
1680
|
-
function loadGitignoreFromDir(dir) {
|
|
1681
|
-
const path = join4(dir, ".gitignore");
|
|
1682
|
-
if (!existsSync5(path))
|
|
1683
|
-
return { dirs: new Set, files: new Set };
|
|
1684
|
-
try {
|
|
1685
|
-
return parseGitignoreContent(readFileSync4(path, "utf-8"));
|
|
1686
|
-
} catch {
|
|
1687
|
-
return { dirs: new Set, files: new Set };
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
function buildIgnoreSets(dir) {
|
|
1691
|
-
const sets = defaultIgnoreSets();
|
|
1692
|
-
const parsed = loadGitignoreFromDir(dir);
|
|
1693
|
-
for (const name of parsed.dirs)
|
|
1694
|
-
sets.dirs.add(name);
|
|
1695
|
-
for (const name of parsed.files)
|
|
1696
|
-
sets.files.add(name);
|
|
1697
|
-
return sets;
|
|
1698
|
-
}
|
|
1699
|
-
function isPathIgnored(filePath, sets) {
|
|
1700
|
-
const name = basename(filePath);
|
|
1701
|
-
if (sets.dirs.has(name))
|
|
1702
|
-
return true;
|
|
1703
|
-
if (sets.files.has(name))
|
|
1704
|
-
return true;
|
|
1705
|
-
const dotIndex = name.lastIndexOf(".");
|
|
1706
|
-
if (dotIndex >= 0) {
|
|
1707
|
-
const ext = name.slice(dotIndex);
|
|
1708
|
-
if (sets.extensions.has(ext))
|
|
1709
|
-
return true;
|
|
1710
|
-
}
|
|
1711
|
-
return false;
|
|
1712
|
-
}
|
|
1713
|
-
function isRelPathIgnored(relPath, sets) {
|
|
1714
|
-
const segments = relPath.split("/");
|
|
1715
|
-
for (let i = 0;i < segments.length - 1; i++) {
|
|
1716
|
-
if (sets.dirs.has(segments[i]))
|
|
1717
|
-
return true;
|
|
1718
|
-
}
|
|
1719
|
-
return isPathIgnored(relPath, sets);
|
|
1720
|
-
}
|
|
1721
|
-
var DEFAULT_DIR_NAMES, DEFAULT_FILE_NAMES, DEFAULT_EXTENSIONS, GLOB_CHARS;
|
|
1722
|
-
var init_ignore_matcher = __esm(() => {
|
|
1723
|
-
DEFAULT_DIR_NAMES = [
|
|
1724
|
-
".git",
|
|
1725
|
-
"node_modules",
|
|
1726
|
-
".runwork",
|
|
1727
|
-
".bun-cache"
|
|
1728
|
-
];
|
|
1729
|
-
DEFAULT_FILE_NAMES = [
|
|
1730
|
-
".dev.vars",
|
|
1731
|
-
".env"
|
|
1732
|
-
];
|
|
1733
|
-
DEFAULT_EXTENSIONS = [
|
|
1734
|
-
".log"
|
|
1735
|
-
];
|
|
1736
|
-
GLOB_CHARS = /[*?\[\]]/;
|
|
1737
|
-
});
|
|
1738
|
-
|
|
1739
1795
|
// src/template/manifest.ts
|
|
1740
1796
|
import { createHash } from "crypto";
|
|
1741
1797
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2 } from "fs";
|
|
@@ -1782,17 +1838,39 @@ async function saveManifest(dir, manifest) {
|
|
|
1782
1838
|
}
|
|
1783
1839
|
writeFileSync2(join5(manifestDir, "template-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
1784
1840
|
}
|
|
1841
|
+
function loadManifestSync(dir) {
|
|
1842
|
+
const manifestPath = join5(dir, ".runwork", "template-manifest.json");
|
|
1843
|
+
if (!existsSync6(manifestPath))
|
|
1844
|
+
return null;
|
|
1845
|
+
try {
|
|
1846
|
+
return normalizeManifest(JSON.parse(readFileSync5(manifestPath, "utf-8")));
|
|
1847
|
+
} catch {
|
|
1848
|
+
return null;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
function isPristineTemplateFile(dir, relPath, manifest) {
|
|
1852
|
+
const expectedHash = manifest?.files[relPath];
|
|
1853
|
+
if (!expectedHash)
|
|
1854
|
+
return false;
|
|
1855
|
+
try {
|
|
1856
|
+
return sha256(readFileSync5(join5(dir, relPath))) === expectedHash;
|
|
1857
|
+
} catch {
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
function normalizeManifest(manifest) {
|
|
1862
|
+
const files = {};
|
|
1863
|
+
for (const [relPath, hash] of Object.entries(manifest.files)) {
|
|
1864
|
+
files[relPath.split("\\").join("/")] = hash;
|
|
1865
|
+
}
|
|
1866
|
+
return { ...manifest, files };
|
|
1867
|
+
}
|
|
1785
1868
|
async function loadManifest(dir) {
|
|
1786
1869
|
const manifestPath = join5(dir, ".runwork", "template-manifest.json");
|
|
1787
1870
|
if (!existsSync6(manifestPath))
|
|
1788
1871
|
return null;
|
|
1789
1872
|
try {
|
|
1790
|
-
|
|
1791
|
-
const files = {};
|
|
1792
|
-
for (const [relPath, hash] of Object.entries(manifest.files)) {
|
|
1793
|
-
files[relPath.split("\\").join("/")] = hash;
|
|
1794
|
-
}
|
|
1795
|
-
return { ...manifest, files };
|
|
1873
|
+
return normalizeManifest(JSON.parse(readFileSync5(manifestPath, "utf-8")));
|
|
1796
1874
|
} catch {
|
|
1797
1875
|
return null;
|
|
1798
1876
|
}
|
|
@@ -2251,10 +2329,7 @@ async function execInit(client, appName, workspace, options = {}, creds) {
|
|
|
2251
2329
|
execFileSync("git", ["remote", "set-url", "runwork", remoteUrl], { cwd: dir, stdio: "pipe" });
|
|
2252
2330
|
}
|
|
2253
2331
|
if (!existsSync7(join8(dir, ".gitignore"))) {
|
|
2254
|
-
writeFileSync4(join8(dir, ".gitignore"),
|
|
2255
|
-
.runwork/
|
|
2256
|
-
.dev.vars
|
|
2257
|
-
`);
|
|
2332
|
+
writeFileSync4(join8(dir, ".gitignore"), buildInitialGitignore());
|
|
2258
2333
|
}
|
|
2259
2334
|
execFileSync("git", ["add", "--", ".runwork.json", ".gitignore"], { cwd: dir, stdio: "pipe" });
|
|
2260
2335
|
try {
|
|
@@ -2335,6 +2410,7 @@ var init_init = __esm(() => {
|
|
|
2335
2410
|
init_store();
|
|
2336
2411
|
init_client();
|
|
2337
2412
|
init_identity();
|
|
2413
|
+
init_repo_config();
|
|
2338
2414
|
init_preflight();
|
|
2339
2415
|
init_prompt();
|
|
2340
2416
|
init_manifest();
|
|
@@ -2396,23 +2472,6 @@ var init_remote = __esm(() => {
|
|
|
2396
2472
|
init_subprocess();
|
|
2397
2473
|
});
|
|
2398
2474
|
|
|
2399
|
-
// src/git/repo-config.ts
|
|
2400
|
-
function hardenRepoForRestrictedFs(cwd) {
|
|
2401
|
-
for (const [key, value] of HARDENING) {
|
|
2402
|
-
try {
|
|
2403
|
-
execFileSync("git", ["config", key, value], { cwd, stdio: "pipe" });
|
|
2404
|
-
} catch {}
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
var HARDENING;
|
|
2408
|
-
var init_repo_config = __esm(() => {
|
|
2409
|
-
init_subprocess();
|
|
2410
|
-
HARDENING = [
|
|
2411
|
-
["gc.auto", "0"],
|
|
2412
|
-
["maintenance.auto", "false"]
|
|
2413
|
-
];
|
|
2414
|
-
});
|
|
2415
|
-
|
|
2416
2475
|
// src/git/classify-sync-error.ts
|
|
2417
2476
|
function classifySyncError(raw) {
|
|
2418
2477
|
if (!raw)
|
|
@@ -2420,6 +2479,9 @@ function classifySyncError(raw) {
|
|
|
2420
2479
|
const s = raw.toLowerCase();
|
|
2421
2480
|
if (raw === STASH_CONFLICT)
|
|
2422
2481
|
return "conflict";
|
|
2482
|
+
if (s.includes("[remote rejected]") || s.includes("[rejected]") || s.includes("pre-receive hook declined") || s.includes("push declined")) {
|
|
2483
|
+
return "rejected";
|
|
2484
|
+
}
|
|
2423
2485
|
if (s.includes("does not appear to be a git repository") || s.includes("'runwork' does not appear") || s.includes("no such remote") || s.includes("remote") && s.includes("not found")) {
|
|
2424
2486
|
return "no-remote";
|
|
2425
2487
|
}
|
|
@@ -2467,6 +2529,19 @@ function diagnoseSyncError(raw) {
|
|
|
2467
2529
|
"Confirm outbound access to runwork.ai, then run `runwork dev` again"
|
|
2468
2530
|
]
|
|
2469
2531
|
};
|
|
2532
|
+
case "rejected":
|
|
2533
|
+
return {
|
|
2534
|
+
reason,
|
|
2535
|
+
message: "the server refused the push",
|
|
2536
|
+
diagnosis: `The runwork remote rejected this push, so nothing was updated on the server. It said:
|
|
2537
|
+
${(raw ?? "").trim()}`,
|
|
2538
|
+
suggestions: [
|
|
2539
|
+
'Read the "remote:" lines above: they name the exact reason and the fix',
|
|
2540
|
+
"If an oversized file was rejected, remove it from the commit and add it to .gitignore",
|
|
2541
|
+
"If the history is reported as damaged, contact support to rebuild it",
|
|
2542
|
+
"Retrying without changing anything will fail the same way"
|
|
2543
|
+
]
|
|
2544
|
+
};
|
|
2470
2545
|
case "conflict":
|
|
2471
2546
|
return {
|
|
2472
2547
|
reason,
|
|
@@ -2942,26 +3017,61 @@ function hasTrackedChanges(cwd) {
|
|
|
2942
3017
|
}
|
|
2943
3018
|
}
|
|
2944
3019
|
function removeConflictingUntrackedFiles(cwd) {
|
|
3020
|
+
const kept = [];
|
|
2945
3021
|
try {
|
|
2946
3022
|
const remoteFiles = execFileSync("git", ["ls-tree", "-r", "--name-only", "runwork/main"], {
|
|
2947
3023
|
cwd,
|
|
2948
3024
|
encoding: "utf-8"
|
|
2949
3025
|
}).trim().split(`
|
|
2950
3026
|
`);
|
|
2951
|
-
const untrackedOutput = execFileSync("git", ["ls-files", "--others", "--exclude-standard"], {
|
|
3027
|
+
const untrackedOutput = execFileSync("git", ["-c", "core.quotePath=false", "ls-files", "--others", "--exclude-standard"], {
|
|
2952
3028
|
cwd,
|
|
2953
3029
|
encoding: "utf-8"
|
|
2954
3030
|
}).trim();
|
|
2955
3031
|
const untracked = new Set(untrackedOutput.split(`
|
|
2956
3032
|
`).filter(Boolean));
|
|
3033
|
+
const manifest = loadManifestSync(cwd);
|
|
2957
3034
|
for (const file of remoteFiles) {
|
|
2958
|
-
if (untracked.has(file))
|
|
3035
|
+
if (!untracked.has(file))
|
|
3036
|
+
continue;
|
|
3037
|
+
if (matchesRemote(cwd, file) || isPristineTemplateFile(cwd, file, manifest)) {
|
|
2959
3038
|
try {
|
|
2960
3039
|
unlinkSync2(join11(cwd, file));
|
|
2961
3040
|
} catch {}
|
|
3041
|
+
} else {
|
|
3042
|
+
kept.push(file);
|
|
2962
3043
|
}
|
|
2963
3044
|
}
|
|
2964
3045
|
} catch {}
|
|
3046
|
+
return { kept };
|
|
3047
|
+
}
|
|
3048
|
+
function matchesRemote(cwd, file) {
|
|
3049
|
+
try {
|
|
3050
|
+
const remoteOid = execFileSync("git", ["rev-parse", `runwork/main:${file}`], {
|
|
3051
|
+
cwd,
|
|
3052
|
+
encoding: "utf-8",
|
|
3053
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3054
|
+
}).trim();
|
|
3055
|
+
const localOid = execFileSync("git", ["hash-object", "--", file], {
|
|
3056
|
+
cwd,
|
|
3057
|
+
encoding: "utf-8",
|
|
3058
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3059
|
+
}).trim();
|
|
3060
|
+
return Boolean(remoteOid) && remoteOid === localOid;
|
|
3061
|
+
} catch {
|
|
3062
|
+
return false;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
function conflictedPaths(cwd) {
|
|
3066
|
+
try {
|
|
3067
|
+
return execFileSync("git", ["-c", "core.quotePath=false", "diff", "--name-only", "--diff-filter=U"], {
|
|
3068
|
+
cwd,
|
|
3069
|
+
encoding: "utf-8"
|
|
3070
|
+
}).trim().split(`
|
|
3071
|
+
`).filter(Boolean);
|
|
3072
|
+
} catch {
|
|
3073
|
+
return [];
|
|
3074
|
+
}
|
|
2965
3075
|
}
|
|
2966
3076
|
function extractGitError(err) {
|
|
2967
3077
|
if (err && typeof err === "object") {
|
|
@@ -2996,9 +3106,11 @@ function syncWithRemote(cwd) {
|
|
|
2996
3106
|
}
|
|
2997
3107
|
let status = "synced";
|
|
2998
3108
|
let syncError;
|
|
3109
|
+
let keptUntracked = [];
|
|
3110
|
+
let remoteOverwrote = [];
|
|
2999
3111
|
try {
|
|
3000
3112
|
execFileSync("git", ["fetch", "runwork", "main"], { cwd, stdio: "pipe" });
|
|
3001
|
-
removeConflictingUntrackedFiles(cwd);
|
|
3113
|
+
keptUntracked = removeConflictingUntrackedFiles(cwd).kept;
|
|
3002
3114
|
try {
|
|
3003
3115
|
execFileSync("git", ["rebase", "runwork/main"], { cwd, stdio: "pipe" });
|
|
3004
3116
|
} catch (rebaseErr) {
|
|
@@ -3009,6 +3121,7 @@ function syncWithRemote(cwd) {
|
|
|
3009
3121
|
execFileSync("git", ["merge", "runwork/main", "--allow-unrelated-histories", "--no-edit"], { cwd, stdio: "pipe" });
|
|
3010
3122
|
status = "merged";
|
|
3011
3123
|
} catch {
|
|
3124
|
+
remoteOverwrote = conflictedPaths(cwd);
|
|
3012
3125
|
try {
|
|
3013
3126
|
execFileSync("git", ["merge", "--abort"], { cwd, stdio: "pipe" });
|
|
3014
3127
|
} catch {}
|
|
@@ -3019,6 +3132,7 @@ function syncWithRemote(cwd) {
|
|
|
3019
3132
|
try {
|
|
3020
3133
|
execFileSync("git", ["merge", "--abort"], { cwd, stdio: "pipe" });
|
|
3021
3134
|
} catch {}
|
|
3135
|
+
remoteOverwrote = [];
|
|
3022
3136
|
status = "sync-failed";
|
|
3023
3137
|
syncError = extractGitError(mergeErr);
|
|
3024
3138
|
}
|
|
@@ -3032,21 +3146,25 @@ function syncWithRemote(cwd) {
|
|
|
3032
3146
|
try {
|
|
3033
3147
|
execFileSync("git", ["stash", "pop"], { cwd, stdio: "pipe" });
|
|
3034
3148
|
} catch {
|
|
3035
|
-
return { status, pushed: false, error: "stash-conflict" };
|
|
3149
|
+
return { status, pushed: false, error: "stash-conflict", keptUntracked, remoteOverwrote };
|
|
3036
3150
|
}
|
|
3037
3151
|
}
|
|
3038
3152
|
if (status === "sync-failed") {
|
|
3039
|
-
return { status, pushed: false, error: syncError };
|
|
3153
|
+
return { status, pushed: false, error: syncError, keptUntracked, remoteOverwrote };
|
|
3040
3154
|
}
|
|
3041
3155
|
let pushed = false;
|
|
3156
|
+
let pushError;
|
|
3042
3157
|
try {
|
|
3043
3158
|
execFileSync("git", ["push", "runwork", "HEAD:main"], { cwd, stdio: "pipe" });
|
|
3044
3159
|
pushed = true;
|
|
3045
|
-
} catch {
|
|
3046
|
-
|
|
3160
|
+
} catch (pushErr) {
|
|
3161
|
+
pushError = extractGitError(pushErr);
|
|
3162
|
+
}
|
|
3163
|
+
return { status, pushed, pushError, keptUntracked, remoteOverwrote };
|
|
3047
3164
|
}
|
|
3048
3165
|
var init_sync = __esm(() => {
|
|
3049
3166
|
init_subprocess();
|
|
3167
|
+
init_manifest();
|
|
3050
3168
|
});
|
|
3051
3169
|
|
|
3052
3170
|
// src/git/critical-files.ts
|
|
@@ -4982,6 +5100,31 @@ export interface ClientErrorReport {
|
|
|
4982
5100
|
level?: 'error' | 'warning' | 'info';
|
|
4983
5101
|
category?: string;
|
|
4984
5102
|
}
|
|
5103
|
+
/**
|
|
5104
|
+
* Decide whether to send the permissive \`Permissions-Policy\` header for an app
|
|
5105
|
+
* that is being embedded in an iframe.
|
|
5106
|
+
*
|
|
5107
|
+
* A document's own Permissions-Policy can only restrict, never grant: the grant
|
|
5108
|
+
* comes from the embedding page's policy plus the iframe \`allow\` attribute. This
|
|
5109
|
+
* header's job is therefore to stop the app self-restricting the features the
|
|
5110
|
+
* platform preview delegates to it, and this predicate decides who is trusted
|
|
5111
|
+
* enough to be handed that.
|
|
5112
|
+
*
|
|
5113
|
+
* Hostnames are parsed and compared exactly, never substring-matched against the
|
|
5114
|
+
* raw header. The difference is not cosmetic: \`origin.endsWith('runwork.ai')\`
|
|
5115
|
+
* also matches \`https://notrunwork.ai\`, and \`origin.includes(domain)\` matches
|
|
5116
|
+
* \`https://evil-acme.com.attacker.net\` against a tenant's \`acme.com\`, which would
|
|
5117
|
+
* hand an attacker's page camera, microphone, geolocation and screen capture over
|
|
5118
|
+
* an embedded app.
|
|
5119
|
+
*
|
|
5120
|
+
* @param origin The \`Origin\` header, or the \`Referer\` as a fallback. Origin is a
|
|
5121
|
+
* bare origin and Referer is a full URL; both parse the same way.
|
|
5122
|
+
* Empty means direct (non-embedded) access.
|
|
5123
|
+
* @param allowedOrigins Comma-separated exact hostnames from \`ALLOWED_ORIGINS\`,
|
|
5124
|
+
* injected by the platform as the workspace's active custom
|
|
5125
|
+
* domains. Matched exactly; subdomains are deliberately excluded.
|
|
5126
|
+
*/
|
|
5127
|
+
export declare function shouldGrantIframePermissions(origin: string, allowedOrigins?: string): boolean;
|
|
4985
5128
|
/**
|
|
4986
5129
|
* Mount all core platform routes on the Hono app
|
|
4987
5130
|
* Called from index.ts before user-defined routes
|
|
@@ -7840,7 +7983,7 @@ function createKeyboardListener() {
|
|
|
7840
7983
|
}
|
|
7841
7984
|
|
|
7842
7985
|
// src/generated/version.ts
|
|
7843
|
-
var VERSION = "0.25.
|
|
7986
|
+
var VERSION = "0.25.2";
|
|
7844
7987
|
|
|
7845
7988
|
// src/commands/dev.ts
|
|
7846
7989
|
var exports_dev = {};
|
|
@@ -8068,6 +8211,36 @@ async function execDev(options) {
|
|
|
8068
8211
|
}
|
|
8069
8212
|
}
|
|
8070
8213
|
}
|
|
8214
|
+
if (syncResult.remoteOverwrote && syncResult.remoteOverwrote.length > 0) {
|
|
8215
|
+
if (useJson) {
|
|
8216
|
+
jsonLine({ event: "sync_remote_overwrote", files: syncResult.remoteOverwrote, timestamp: ts() });
|
|
8217
|
+
} else {
|
|
8218
|
+
console.warn(yellow(` Sync resolved conflicts in the remote's favour; local changes were replaced in: ${syncResult.remoteOverwrote.join(", ")}`));
|
|
8219
|
+
console.warn(dim(" Recover them with `git reflog` / `git diff ORIG_HEAD` if that was wrong."));
|
|
8220
|
+
}
|
|
8221
|
+
}
|
|
8222
|
+
if (syncResult.keptUntracked && syncResult.keptUntracked.length > 0) {
|
|
8223
|
+
if (useJson) {
|
|
8224
|
+
jsonLine({ event: "sync_kept_untracked", files: syncResult.keptUntracked, timestamp: ts() });
|
|
8225
|
+
} else {
|
|
8226
|
+
console.warn(yellow(` Kept your untracked file(s) over the remote's copy: ${syncResult.keptUntracked.join(", ")}`));
|
|
8227
|
+
console.warn(dim(" Commit or remove them so the sync can reconcile that path."));
|
|
8228
|
+
}
|
|
8229
|
+
}
|
|
8230
|
+
if (!syncResult.pushed && syncResult.pushError) {
|
|
8231
|
+
const diag = diagnoseSyncError(syncResult.pushError);
|
|
8232
|
+
if (useJson) {
|
|
8233
|
+
jsonLine({
|
|
8234
|
+
event: "error",
|
|
8235
|
+
phase: "push",
|
|
8236
|
+
timestamp: ts(),
|
|
8237
|
+
error: { reason: diag.reason, message: diag.message, diagnosis: diag.diagnosis, suggestions: diag.suggestions }
|
|
8238
|
+
});
|
|
8239
|
+
} else {
|
|
8240
|
+
console.warn(yellow(` Push failed: ${diag.message}`));
|
|
8241
|
+
console.warn(dim(` ${diag.diagnosis}`));
|
|
8242
|
+
}
|
|
8243
|
+
}
|
|
8071
8244
|
if (useJson) {
|
|
8072
8245
|
jsonLine({ event: "startup", phase: "sync", status: syncResult.status, pushed: syncResult.pushed, timestamp: ts() });
|
|
8073
8246
|
if (syncResult.status === "sync-failed") {
|
|
@@ -8942,7 +9115,10 @@ var init_resolve = __esm(() => {
|
|
|
8942
9115
|
|
|
8943
9116
|
// ../../shared/skill/skill-canonical.ts
|
|
8944
9117
|
function toSkillSlug(value) {
|
|
8945
|
-
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9118
|
+
return transliterateLatin(value.trim()).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9119
|
+
}
|
|
9120
|
+
function transliterateLatin(value) {
|
|
9121
|
+
return value.replace(/[ıßøØłŁđĐæÆœŒþÞðÐ]/g, (ch) => NON_DECOMPOSABLE_LATIN[ch] ?? ch).normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
8946
9122
|
}
|
|
8947
9123
|
function isStructuredYamlValue(value) {
|
|
8948
9124
|
if (value.includes(`
|
|
@@ -8987,11 +9163,13 @@ function parseSkillMd(content) {
|
|
|
8987
9163
|
const lines = content.split(`
|
|
8988
9164
|
`);
|
|
8989
9165
|
if (lines[0]?.trim() !== "---") {
|
|
8990
|
-
const
|
|
9166
|
+
const firstLine = lines[0]?.trim() ?? "";
|
|
9167
|
+
const isHeading = /^#+\s/.test(firstLine);
|
|
9168
|
+
const rawName = firstLine.replace(/^#+\s*/, "").trim() || "Untitled Skill";
|
|
8991
9169
|
return {
|
|
8992
9170
|
frontmatter: {
|
|
8993
9171
|
name: toSkillSlug(rawName) || "untitled-skill",
|
|
8994
|
-
description: ""
|
|
9172
|
+
description: isHeading ? rawName : ""
|
|
8995
9173
|
},
|
|
8996
9174
|
orderedKeys: [],
|
|
8997
9175
|
body: content,
|
|
@@ -9078,6 +9256,27 @@ function buildSkillMd(parts) {
|
|
|
9078
9256
|
return lines.join(`
|
|
9079
9257
|
`);
|
|
9080
9258
|
}
|
|
9259
|
+
var NON_DECOMPOSABLE_LATIN;
|
|
9260
|
+
var init_skill_canonical = __esm(() => {
|
|
9261
|
+
NON_DECOMPOSABLE_LATIN = {
|
|
9262
|
+
"ı": "i",
|
|
9263
|
+
"ß": "ss",
|
|
9264
|
+
"ø": "o",
|
|
9265
|
+
"Ø": "O",
|
|
9266
|
+
"ł": "l",
|
|
9267
|
+
"Ł": "L",
|
|
9268
|
+
"đ": "d",
|
|
9269
|
+
"Đ": "D",
|
|
9270
|
+
"æ": "ae",
|
|
9271
|
+
"Æ": "AE",
|
|
9272
|
+
"œ": "oe",
|
|
9273
|
+
"Œ": "OE",
|
|
9274
|
+
"þ": "th",
|
|
9275
|
+
"Þ": "TH",
|
|
9276
|
+
"ð": "d",
|
|
9277
|
+
"Ð": "D"
|
|
9278
|
+
};
|
|
9279
|
+
});
|
|
9081
9280
|
|
|
9082
9281
|
// src/agents/registry-data.ts
|
|
9083
9282
|
function chatgptConnectorSteps(confirmLead) {
|
|
@@ -10716,6 +10915,7 @@ function buildSkillMd2(skill) {
|
|
|
10716
10915
|
}
|
|
10717
10916
|
var RUNWORK_MCP_PREFIX = "Runwork: ", RUNWORK_MCP_PREFIX_LEGACY = "runwork-", RUNWORK_WORKSPACE_MCP_NAME = "Runwork", RUNWORK_PLUGIN_MARKETPLACE = "runwork", toSlug;
|
|
10718
10917
|
var init_types = __esm(() => {
|
|
10918
|
+
init_skill_canonical();
|
|
10719
10919
|
toSlug = toSkillSlug;
|
|
10720
10920
|
});
|
|
10721
10921
|
|
|
@@ -17964,12 +18164,28 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
17964
18164
|
console.error(` - ${s}`);
|
|
17965
18165
|
process.exit(1);
|
|
17966
18166
|
}
|
|
18167
|
+
if (syncResult.remoteOverwrote && syncResult.remoteOverwrote.length > 0) {
|
|
18168
|
+
const files = syncResult.remoteOverwrote.join(", ");
|
|
18169
|
+
if (useJson) {
|
|
18170
|
+
jsonLine({ event: "sync_remote_overwrote", files: syncResult.remoteOverwrote, timestamp: new Date().toISOString() });
|
|
18171
|
+
} else {
|
|
18172
|
+
console.warn(`Sync resolved conflicts in the remote's favour; local changes were replaced in: ${files}`);
|
|
18173
|
+
console.warn(" Recover them with `git reflog` / `git diff ORIG_HEAD` before deploying again if that was wrong.");
|
|
18174
|
+
}
|
|
18175
|
+
}
|
|
18176
|
+
if (syncResult.keptUntracked && syncResult.keptUntracked.length > 0 && !useJson) {
|
|
18177
|
+
console.warn(`Kept your untracked file(s) over the remote's copy: ${syncResult.keptUntracked.join(", ")}`);
|
|
18178
|
+
}
|
|
17967
18179
|
if (!syncResult.pushed) {
|
|
18180
|
+
const diag = diagnoseSyncError(syncResult.pushError ?? syncResult.error);
|
|
17968
18181
|
if (useJson) {
|
|
17969
|
-
jsonOut(buildErrorResponse("deploy",
|
|
18182
|
+
jsonOut(buildErrorResponse("deploy", `Failed to push before deployment: ${diag.message}`, diag.diagnosis, [...diag.suggestions, "Then retry runwork deploy"]));
|
|
17970
18183
|
process.exit(1);
|
|
17971
18184
|
}
|
|
17972
|
-
console.error(
|
|
18185
|
+
console.error(`Push failed: ${diag.message}`);
|
|
18186
|
+
console.error(diag.diagnosis);
|
|
18187
|
+
for (const suggestion of diag.suggestions)
|
|
18188
|
+
console.error(` - ${suggestion}`);
|
|
17973
18189
|
process.exit(1);
|
|
17974
18190
|
}
|
|
17975
18191
|
if (!useJson)
|
|
@@ -19646,6 +19862,7 @@ var infoCommand = new Command12("info").description("Show app context, registrie
|
|
|
19646
19862
|
init_store();
|
|
19647
19863
|
init_client();
|
|
19648
19864
|
init_resolve();
|
|
19865
|
+
init_skill_canonical();
|
|
19649
19866
|
import { Command as Command13 } from "commander";
|
|
19650
19867
|
import { readFileSync as readFileSync22, existsSync as existsSync25 } from "fs";
|
|
19651
19868
|
function truncate(text2, max) {
|
|
@@ -19720,7 +19937,7 @@ function buildSkillPushPayload(fileContent, nameArg) {
|
|
|
19720
19937
|
const name = toSkillSlug(nameArg || "") || docName;
|
|
19721
19938
|
if (!name)
|
|
19722
19939
|
return null;
|
|
19723
|
-
const description = parsed.
|
|
19940
|
+
const description = parsed.frontmatter.description;
|
|
19724
19941
|
const extra = {};
|
|
19725
19942
|
for (const key of parsed.orderedKeys) {
|
|
19726
19943
|
if (key === "name" || key === "description")
|
|
@@ -22392,8 +22609,8 @@ init_resolve();
|
|
|
22392
22609
|
init_prompt();
|
|
22393
22610
|
await init_detect();
|
|
22394
22611
|
import { Command as Command27 } from "commander";
|
|
22395
|
-
import { join as
|
|
22396
|
-
import { homedir as
|
|
22612
|
+
import { join as join49 } from "path";
|
|
22613
|
+
import { homedir as homedir28 } from "os";
|
|
22397
22614
|
|
|
22398
22615
|
// src/commands/sync.ts
|
|
22399
22616
|
init_store();
|
|
@@ -22404,9 +22621,9 @@ await __promiseAll([
|
|
|
22404
22621
|
init_codex()
|
|
22405
22622
|
]);
|
|
22406
22623
|
import { Command as Command26 } from "commander";
|
|
22407
|
-
import { readFileSync as
|
|
22408
|
-
import { join as
|
|
22409
|
-
import { homedir as
|
|
22624
|
+
import { readFileSync as readFileSync41, existsSync as existsSync52 } from "fs";
|
|
22625
|
+
import { join as join48 } from "path";
|
|
22626
|
+
import { homedir as homedir27 } from "os";
|
|
22410
22627
|
|
|
22411
22628
|
// src/commands/mcp-entries.ts
|
|
22412
22629
|
init_types();
|
|
@@ -23265,6 +23482,91 @@ function sameStringSet(a, b) {
|
|
|
23265
23482
|
return true;
|
|
23266
23483
|
}
|
|
23267
23484
|
|
|
23485
|
+
// src/utils/sync-lock.ts
|
|
23486
|
+
import { existsSync as existsSync51, mkdirSync as mkdirSync28, readFileSync as readFileSync40, unlinkSync as unlinkSync8, writeFileSync as writeFileSync30 } from "fs";
|
|
23487
|
+
import { join as join47 } from "path";
|
|
23488
|
+
import { homedir as homedir26 } from "os";
|
|
23489
|
+
var LOCK_PATH = join47(homedir26(), ".runwork", "sync.lock");
|
|
23490
|
+
var STALE_LOCK_MS = 5 * 60 * 1000;
|
|
23491
|
+
var DEFAULT_WAIT_MS = 30000;
|
|
23492
|
+
var exitHandlerRegistered = false;
|
|
23493
|
+
function ensureExitHandler() {
|
|
23494
|
+
if (exitHandlerRegistered)
|
|
23495
|
+
return;
|
|
23496
|
+
exitHandlerRegistered = true;
|
|
23497
|
+
process.once("exit", releaseSyncLock);
|
|
23498
|
+
}
|
|
23499
|
+
function isProcessAlive(pid) {
|
|
23500
|
+
try {
|
|
23501
|
+
process.kill(pid, 0);
|
|
23502
|
+
return true;
|
|
23503
|
+
} catch (err) {
|
|
23504
|
+
return err?.code === "EPERM";
|
|
23505
|
+
}
|
|
23506
|
+
}
|
|
23507
|
+
function readLock() {
|
|
23508
|
+
try {
|
|
23509
|
+
return JSON.parse(readFileSync40(LOCK_PATH, "utf-8"));
|
|
23510
|
+
} catch {
|
|
23511
|
+
return null;
|
|
23512
|
+
}
|
|
23513
|
+
}
|
|
23514
|
+
function writeLockExclusive() {
|
|
23515
|
+
try {
|
|
23516
|
+
if (!existsSync51(join47(homedir26(), ".runwork"))) {
|
|
23517
|
+
mkdirSync28(join47(homedir26(), ".runwork"), { recursive: true });
|
|
23518
|
+
}
|
|
23519
|
+
writeFileSync30(LOCK_PATH, JSON.stringify({ pid: process.pid, startedAt: Date.now() }), {
|
|
23520
|
+
flag: "wx"
|
|
23521
|
+
});
|
|
23522
|
+
return true;
|
|
23523
|
+
} catch {
|
|
23524
|
+
return false;
|
|
23525
|
+
}
|
|
23526
|
+
}
|
|
23527
|
+
function tryAcquireOnce() {
|
|
23528
|
+
if (writeLockExclusive()) {
|
|
23529
|
+
ensureExitHandler();
|
|
23530
|
+
return true;
|
|
23531
|
+
}
|
|
23532
|
+
const existing = readLock();
|
|
23533
|
+
const stale = !existing || Date.now() - existing.startedAt > STALE_LOCK_MS || !isProcessAlive(existing.pid);
|
|
23534
|
+
if (!stale)
|
|
23535
|
+
return false;
|
|
23536
|
+
try {
|
|
23537
|
+
unlinkSync8(LOCK_PATH);
|
|
23538
|
+
} catch {}
|
|
23539
|
+
if (writeLockExclusive()) {
|
|
23540
|
+
ensureExitHandler();
|
|
23541
|
+
return true;
|
|
23542
|
+
}
|
|
23543
|
+
return false;
|
|
23544
|
+
}
|
|
23545
|
+
function sleep2(ms) {
|
|
23546
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
23547
|
+
}
|
|
23548
|
+
async function acquireSyncLock(waitMs = DEFAULT_WAIT_MS) {
|
|
23549
|
+
const deadline = Date.now() + waitMs;
|
|
23550
|
+
let delay = 250;
|
|
23551
|
+
for (;; ) {
|
|
23552
|
+
if (tryAcquireOnce())
|
|
23553
|
+
return true;
|
|
23554
|
+
const remaining = deadline - Date.now();
|
|
23555
|
+
if (remaining <= 0)
|
|
23556
|
+
return false;
|
|
23557
|
+
await sleep2(Math.min(delay, remaining));
|
|
23558
|
+
delay = Math.min(delay * 2, 5000);
|
|
23559
|
+
}
|
|
23560
|
+
}
|
|
23561
|
+
function releaseSyncLock() {
|
|
23562
|
+
const existing = readLock();
|
|
23563
|
+
if (existing?.pid === process.pid) {
|
|
23564
|
+
try {
|
|
23565
|
+
unlinkSync8(LOCK_PATH);
|
|
23566
|
+
} catch {}
|
|
23567
|
+
}
|
|
23568
|
+
}
|
|
23569
|
+
|
|
23268
23570
|
// src/commands/sync.ts
|
|
23269
23571
|
async function printAdoptionHint(credentials, workspaceId) {
|
|
23270
23572
|
if (!workspaceId)
|
|
@@ -23281,10 +23583,10 @@ Tip: ${hint.title}`);
|
|
|
23281
23583
|
} catch {}
|
|
23282
23584
|
}
|
|
23283
23585
|
function loadSetupState(filePath) {
|
|
23284
|
-
if (!
|
|
23586
|
+
if (!existsSync52(filePath))
|
|
23285
23587
|
return null;
|
|
23286
23588
|
try {
|
|
23287
|
-
return JSON.parse(
|
|
23589
|
+
return JSON.parse(readFileSync41(filePath, "utf-8"));
|
|
23288
23590
|
} catch {
|
|
23289
23591
|
return null;
|
|
23290
23592
|
}
|
|
@@ -23305,15 +23607,15 @@ function readLocalSkills(state) {
|
|
|
23305
23607
|
if (!baseDir)
|
|
23306
23608
|
continue;
|
|
23307
23609
|
for (const skillName of state.skills) {
|
|
23308
|
-
const skillMdPath =
|
|
23309
|
-
if (
|
|
23310
|
-
results.push({ name: skillName, content:
|
|
23610
|
+
const skillMdPath = join48(baseDir, skillName, "SKILL.md");
|
|
23611
|
+
if (existsSync52(skillMdPath)) {
|
|
23612
|
+
results.push({ name: skillName, content: readFileSync41(skillMdPath, "utf-8") });
|
|
23311
23613
|
continue;
|
|
23312
23614
|
}
|
|
23313
23615
|
const filename = skillName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
23314
|
-
const flatPath =
|
|
23315
|
-
if (
|
|
23316
|
-
results.push({ name: skillName, content:
|
|
23616
|
+
const flatPath = join48(baseDir, `${filename}.md`);
|
|
23617
|
+
if (existsSync52(flatPath)) {
|
|
23618
|
+
results.push({ name: skillName, content: readFileSync41(flatPath, "utf-8") });
|
|
23317
23619
|
}
|
|
23318
23620
|
}
|
|
23319
23621
|
if (results.length > 0)
|
|
@@ -23367,6 +23669,19 @@ function ensureWorkspacePointer(state, statePath2, credentials) {
|
|
|
23367
23669
|
return true;
|
|
23368
23670
|
}
|
|
23369
23671
|
async function syncFromState(state, statePath2, credentials, opts) {
|
|
23672
|
+
const acquired = await acquireSyncLock();
|
|
23673
|
+
if (!acquired) {
|
|
23674
|
+
console.log(" Another sync appears to be in progress on this machine; proceeding anyway.");
|
|
23675
|
+
await runSyncFromState(state, statePath2, credentials, opts);
|
|
23676
|
+
return;
|
|
23677
|
+
}
|
|
23678
|
+
try {
|
|
23679
|
+
await runSyncFromState(state, statePath2, credentials, opts);
|
|
23680
|
+
} finally {
|
|
23681
|
+
releaseSyncLock();
|
|
23682
|
+
}
|
|
23683
|
+
}
|
|
23684
|
+
async function runSyncFromState(state, statePath2, credentials, opts) {
|
|
23370
23685
|
const client = new ApiClient(credentials);
|
|
23371
23686
|
setVerbose(!!opts.verbose);
|
|
23372
23687
|
if (!ensureWorkspacePointer(state, statePath2, credentials)) {
|
|
@@ -23554,9 +23869,9 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23554
23869
|
persona: state.persona
|
|
23555
23870
|
});
|
|
23556
23871
|
let projectAppSkillFilter = null;
|
|
23557
|
-
if (
|
|
23872
|
+
if (existsSync52(".runwork.json")) {
|
|
23558
23873
|
try {
|
|
23559
|
-
const config = JSON.parse(
|
|
23874
|
+
const config = JSON.parse(readFileSync41(".runwork.json", "utf-8"));
|
|
23560
23875
|
if (config.appName) {
|
|
23561
23876
|
projectAppSkillFilter = config.appName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
23562
23877
|
}
|
|
@@ -23793,7 +24108,7 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23793
24108
|
}
|
|
23794
24109
|
for (const adapter2 of adapters) {
|
|
23795
24110
|
if (adapter2 instanceof CodexAdapter) {
|
|
23796
|
-
const runworkDir =
|
|
24111
|
+
const runworkDir = join48(homedir27(), ".runwork");
|
|
23797
24112
|
const result = adapter2.registerDesktopWorkspace(runworkDir, "Runwork");
|
|
23798
24113
|
if (result === "written") {
|
|
23799
24114
|
vlog(` [${adapter2.name}] Registered workspace in Codex desktop app`);
|
|
@@ -23935,8 +24250,8 @@ var syncCommand = new Command26("sync").description("Sync skills bidirectionally
|
|
|
23935
24250
|
verbose: !!opts.verbose,
|
|
23936
24251
|
redetect: !!opts.redetect
|
|
23937
24252
|
};
|
|
23938
|
-
const projectStatePath =
|
|
23939
|
-
const userStatePath =
|
|
24253
|
+
const projectStatePath = join48(process.cwd(), ".runwork", "setup.json");
|
|
24254
|
+
const userStatePath = join48(homedir27(), ".runwork", "setup.json");
|
|
23940
24255
|
const projectState = loadSetupState(projectStatePath);
|
|
23941
24256
|
const userState = loadSetupState(userStatePath);
|
|
23942
24257
|
if (!projectState && !userState) {
|
|
@@ -24005,7 +24320,7 @@ function toSkillFilename(name) {
|
|
|
24005
24320
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
24006
24321
|
}
|
|
24007
24322
|
function loadSetupStateForScope(scope) {
|
|
24008
|
-
const path4 = scope === "project" ?
|
|
24323
|
+
const path4 = scope === "project" ? join49(process.cwd(), ".runwork", "setup.json") : join49(homedir28(), ".runwork", "setup.json");
|
|
24009
24324
|
return readJsonOrNull(path4);
|
|
24010
24325
|
}
|
|
24011
24326
|
async function parkAndTeardownWorkspace(previous, scopes) {
|
|
@@ -24174,8 +24489,8 @@ Re-run without --dry-run to sync workspace data.`);
|
|
|
24174
24489
|
}
|
|
24175
24490
|
persistDefaultWorkspace(workspaceId, workspaceName);
|
|
24176
24491
|
for (const s of scopes) {
|
|
24177
|
-
const dir = s === "project" ? ".runwork" :
|
|
24178
|
-
writeJsonAtomic(
|
|
24492
|
+
const dir = s === "project" ? ".runwork" : join49(homedir28(), ".runwork");
|
|
24493
|
+
writeJsonAtomic(join49(dir, "setup.json"), state);
|
|
24179
24494
|
}
|
|
24180
24495
|
if (restored)
|
|
24181
24496
|
clearParkedState(workspaceId);
|
|
@@ -24183,7 +24498,7 @@ Re-run without --dry-run to sync workspace data.`);
|
|
|
24183
24498
|
Syncing workspace data...
|
|
24184
24499
|
`);
|
|
24185
24500
|
for (const s of scopes) {
|
|
24186
|
-
const statePath2 = s === "project" ?
|
|
24501
|
+
const statePath2 = s === "project" ? join49(process.cwd(), ".runwork", "setup.json") : join49(homedir28(), ".runwork", "setup.json");
|
|
24187
24502
|
await syncFromState(state, statePath2, credentials, {
|
|
24188
24503
|
dryRun: false,
|
|
24189
24504
|
pullOnly: true,
|
|
@@ -24203,16 +24518,16 @@ init_client();
|
|
|
24203
24518
|
import { Command as Command28 } from "commander";
|
|
24204
24519
|
|
|
24205
24520
|
// src/utils/setup-state.ts
|
|
24206
|
-
import { existsSync as
|
|
24207
|
-
import { join as
|
|
24208
|
-
import { homedir as
|
|
24521
|
+
import { existsSync as existsSync53, readFileSync as readFileSync42 } from "fs";
|
|
24522
|
+
import { join as join50 } from "path";
|
|
24523
|
+
import { homedir as homedir29 } from "os";
|
|
24209
24524
|
function loadSetupState2() {
|
|
24210
|
-
const projectPath =
|
|
24211
|
-
const userPath =
|
|
24525
|
+
const projectPath = join50(process.cwd(), ".runwork", "setup.json");
|
|
24526
|
+
const userPath = join50(homedir29(), ".runwork", "setup.json");
|
|
24212
24527
|
for (const p of [projectPath, userPath]) {
|
|
24213
|
-
if (
|
|
24528
|
+
if (existsSync53(p)) {
|
|
24214
24529
|
try {
|
|
24215
|
-
return JSON.parse(
|
|
24530
|
+
return JSON.parse(readFileSync42(p, "utf-8"));
|
|
24216
24531
|
} catch {
|
|
24217
24532
|
continue;
|
|
24218
24533
|
}
|
|
@@ -24271,14 +24586,14 @@ init_client();
|
|
|
24271
24586
|
init_types();
|
|
24272
24587
|
await init_detect();
|
|
24273
24588
|
import { Command as Command29 } from "commander";
|
|
24274
|
-
import { existsSync as
|
|
24275
|
-
import { resolve as resolve3, join as
|
|
24276
|
-
import { homedir as
|
|
24589
|
+
import { existsSync as existsSync54, readFileSync as readFileSync43 } from "fs";
|
|
24590
|
+
import { resolve as resolve3, join as join51 } from "path";
|
|
24591
|
+
import { homedir as homedir30 } from "os";
|
|
24277
24592
|
function loadSetupState3(filePath) {
|
|
24278
|
-
if (!
|
|
24593
|
+
if (!existsSync54(filePath))
|
|
24279
24594
|
return null;
|
|
24280
24595
|
try {
|
|
24281
|
-
return JSON.parse(
|
|
24596
|
+
return JSON.parse(readFileSync43(filePath, "utf-8"));
|
|
24282
24597
|
} catch {
|
|
24283
24598
|
return null;
|
|
24284
24599
|
}
|
|
@@ -24294,8 +24609,8 @@ var buildPluginCommand = new Command29("build-plugin").description("Build an ins
|
|
|
24294
24609
|
process.exit(1);
|
|
24295
24610
|
}
|
|
24296
24611
|
const credentials = requireAuth();
|
|
24297
|
-
const projectStatePath =
|
|
24298
|
-
const userStatePath =
|
|
24612
|
+
const projectStatePath = join51(process.cwd(), ".runwork", "setup.json");
|
|
24613
|
+
const userStatePath = join51(homedir30(), ".runwork", "setup.json");
|
|
24299
24614
|
const state = loadSetupState3(projectStatePath) ?? loadSetupState3(userStatePath);
|
|
24300
24615
|
if (!state) {
|
|
24301
24616
|
console.error("No setup state found. Run `runwork setup` first.");
|
|
@@ -24387,21 +24702,21 @@ var buildPluginCommand = new Command29("build-plugin").description("Build an ins
|
|
|
24387
24702
|
init_prompt();
|
|
24388
24703
|
await init_detect();
|
|
24389
24704
|
import { Command as Command30 } from "commander";
|
|
24390
|
-
import { existsSync as
|
|
24391
|
-
import { join as
|
|
24392
|
-
import { homedir as
|
|
24705
|
+
import { existsSync as existsSync55, readFileSync as readFileSync44, rmSync as rmSync13, unlinkSync as unlinkSync9 } from "fs";
|
|
24706
|
+
import { join as join52 } from "path";
|
|
24707
|
+
import { homedir as homedir31 } from "os";
|
|
24393
24708
|
function loadSetupState4(filePath) {
|
|
24394
|
-
if (!
|
|
24709
|
+
if (!existsSync55(filePath))
|
|
24395
24710
|
return null;
|
|
24396
24711
|
try {
|
|
24397
|
-
return JSON.parse(
|
|
24712
|
+
return JSON.parse(readFileSync44(filePath, "utf-8"));
|
|
24398
24713
|
} catch {
|
|
24399
24714
|
return null;
|
|
24400
24715
|
}
|
|
24401
24716
|
}
|
|
24402
24717
|
var uninstallCommand = new Command30("uninstall").description("Remove all Runwork configuration from local agents (MCP servers, skills, instructions)").option("-y, --yes", "Skip confirmation prompt").option("--keep-auth", "Keep authentication credentials (only remove agent configs)").action(async (opts) => {
|
|
24403
|
-
const projectStatePath =
|
|
24404
|
-
const userStatePath =
|
|
24718
|
+
const projectStatePath = join52(process.cwd(), ".runwork", "setup.json");
|
|
24719
|
+
const userStatePath = join52(homedir31(), ".runwork", "setup.json");
|
|
24405
24720
|
const projectState = loadSetupState4(projectStatePath);
|
|
24406
24721
|
const userState = loadSetupState4(userStatePath);
|
|
24407
24722
|
if (!projectState && !userState) {
|
|
@@ -24481,19 +24796,19 @@ This will remove all Runwork configuration from your local agents:
|
|
|
24481
24796
|
}
|
|
24482
24797
|
}
|
|
24483
24798
|
}
|
|
24484
|
-
const stateDir = label === "project" ?
|
|
24799
|
+
const stateDir = label === "project" ? join52(process.cwd(), ".runwork") : join52(homedir31(), ".runwork");
|
|
24485
24800
|
if (opts.keepAuth && label === "user") {
|
|
24486
|
-
const setupFile =
|
|
24487
|
-
if (
|
|
24801
|
+
const setupFile = join52(stateDir, "setup.json");
|
|
24802
|
+
if (existsSync55(setupFile)) {
|
|
24488
24803
|
try {
|
|
24489
|
-
|
|
24804
|
+
unlinkSync9(setupFile);
|
|
24490
24805
|
console.log(` Removed ${setupFile} (kept credentials)`);
|
|
24491
24806
|
} catch (err) {
|
|
24492
24807
|
console.warn(` Failed to remove ${setupFile}: ${err instanceof Error ? err.message : err}`);
|
|
24493
24808
|
errors++;
|
|
24494
24809
|
}
|
|
24495
24810
|
}
|
|
24496
|
-
} else if (
|
|
24811
|
+
} else if (existsSync55(stateDir)) {
|
|
24497
24812
|
try {
|
|
24498
24813
|
rmSync13(stateDir, { recursive: true, force: true });
|
|
24499
24814
|
console.log(` Removed ${stateDir}`);
|
|
@@ -24627,7 +24942,7 @@ var membersCommand = new Command32("members").description("List workspace member
|
|
|
24627
24942
|
init_store();
|
|
24628
24943
|
init_client();
|
|
24629
24944
|
import { Command as Command33 } from "commander";
|
|
24630
|
-
import { readFileSync as
|
|
24945
|
+
import { readFileSync as readFileSync45 } from "fs";
|
|
24631
24946
|
function normalizeApiPath(rawPath, baseUrl) {
|
|
24632
24947
|
if (/^https?:\/\//i.test(rawPath)) {
|
|
24633
24948
|
const target = new URL(rawPath);
|
|
@@ -24660,7 +24975,7 @@ to be read or pasted manually. Prefer a dedicated command when one exists
|
|
|
24660
24975
|
let curlStr = opts.curl;
|
|
24661
24976
|
if (opts.curlFile) {
|
|
24662
24977
|
try {
|
|
24663
|
-
curlStr =
|
|
24978
|
+
curlStr = readFileSync45(opts.curlFile, "utf-8");
|
|
24664
24979
|
} catch (err) {
|
|
24665
24980
|
console.error(`Could not read --curl-file: ${err instanceof Error ? err.message : err}`);
|
|
24666
24981
|
process.exit(1);
|
|
@@ -24680,7 +24995,7 @@ to be read or pasted manually. Prefer a dedicated command when one exists
|
|
|
24680
24995
|
let raw = opts.body;
|
|
24681
24996
|
if (raw.startsWith("@")) {
|
|
24682
24997
|
try {
|
|
24683
|
-
raw =
|
|
24998
|
+
raw = readFileSync45(raw.slice(1), "utf-8");
|
|
24684
24999
|
} catch (err) {
|
|
24685
25000
|
console.error(`Could not read body file: ${err instanceof Error ? err.message : err}`);
|
|
24686
25001
|
process.exit(1);
|
|
@@ -24733,9 +25048,9 @@ init_preflight();
|
|
|
24733
25048
|
init_credentials();
|
|
24734
25049
|
await init_detect();
|
|
24735
25050
|
import { parse as parse2 } from "smol-toml";
|
|
24736
|
-
import { existsSync as
|
|
24737
|
-
import { join as
|
|
24738
|
-
import { homedir as
|
|
25051
|
+
import { existsSync as existsSync56, readFileSync as readFileSync46 } from "fs";
|
|
25052
|
+
import { join as join53, sep as sep4 } from "path";
|
|
25053
|
+
import { homedir as homedir32, platform as osPlatform2, arch as osArch } from "os";
|
|
24739
25054
|
var BASE_URL2 = process.env.RUNWORK_DOWNLOAD_BASE_URL || "https://runwork.ai";
|
|
24740
25055
|
var LATEST_JSON_URL2 = `${BASE_URL2}/cli/latest.json`;
|
|
24741
25056
|
function detectPlatform() {
|
|
@@ -24763,10 +25078,10 @@ function buildContext() {
|
|
|
24763
25078
|
const credentials = getCredentials();
|
|
24764
25079
|
const client = credentials ? new ApiClient(credentials) : null;
|
|
24765
25080
|
let config = null;
|
|
24766
|
-
const configPath =
|
|
24767
|
-
if (
|
|
25081
|
+
const configPath = join53(process.cwd(), ".runwork.json");
|
|
25082
|
+
if (existsSync56(configPath)) {
|
|
24768
25083
|
try {
|
|
24769
|
-
config = JSON.parse(
|
|
25084
|
+
config = JSON.parse(readFileSync46(configPath, "utf-8"));
|
|
24770
25085
|
} catch {}
|
|
24771
25086
|
}
|
|
24772
25087
|
return { credentials, client, config, cwd: process.cwd() };
|
|
@@ -24867,9 +25182,9 @@ async function checkCliArtifactReachable() {
|
|
|
24867
25182
|
}
|
|
24868
25183
|
async function checkCliInstallLocation() {
|
|
24869
25184
|
const isWindows2 = osPlatform2() === "win32";
|
|
24870
|
-
const home =
|
|
24871
|
-
const canonicalDir =
|
|
24872
|
-
const canonicalBinary = isWindows2 ?
|
|
25185
|
+
const home = homedir32();
|
|
25186
|
+
const canonicalDir = join53(home, ".runwork", "bin");
|
|
25187
|
+
const canonicalBinary = isWindows2 ? join53(canonicalDir, "runwork.exe") : join53(canonicalDir, "runwork");
|
|
24873
25188
|
const candidates = [process.execPath, process.argv[1] || ""].filter(Boolean);
|
|
24874
25189
|
const runsFromCanonical = candidates.some((p) => normalizePath(p) === normalizePath(canonicalBinary));
|
|
24875
25190
|
if (runsFromCanonical) {
|
|
@@ -24879,7 +25194,7 @@ async function checkCliInstallLocation() {
|
|
|
24879
25194
|
message: `canonical (${canonicalBinary})`
|
|
24880
25195
|
};
|
|
24881
25196
|
}
|
|
24882
|
-
if (
|
|
25197
|
+
if (existsSync56(canonicalBinary)) {
|
|
24883
25198
|
return {
|
|
24884
25199
|
name: "cli-install-location",
|
|
24885
25200
|
status: "warn",
|
|
@@ -25001,8 +25316,8 @@ async function checkGitCredentialHelper(ctx) {
|
|
|
25001
25316
|
};
|
|
25002
25317
|
}
|
|
25003
25318
|
async function checkProjectConfig(ctx) {
|
|
25004
|
-
const configPath =
|
|
25005
|
-
if (!
|
|
25319
|
+
const configPath = join53(ctx.cwd, ".runwork.json");
|
|
25320
|
+
if (!existsSync56(configPath)) {
|
|
25006
25321
|
if (!ctx.credentials) {
|
|
25007
25322
|
return { name: "project-config", status: "skip", message: "no project (not logged in)" };
|
|
25008
25323
|
}
|
|
@@ -25064,7 +25379,7 @@ async function checkGitRemote(ctx) {
|
|
|
25064
25379
|
if (!ctx.config) {
|
|
25065
25380
|
return { name: "git-remote", status: "skip", message: "skipped (no project)" };
|
|
25066
25381
|
}
|
|
25067
|
-
if (!
|
|
25382
|
+
if (!existsSync56(join53(ctx.cwd, ".git"))) {
|
|
25068
25383
|
return {
|
|
25069
25384
|
name: "git-remote",
|
|
25070
25385
|
status: "fail",
|
|
@@ -25118,12 +25433,12 @@ async function checkDeployFreshness(ctx) {
|
|
|
25118
25433
|
return { name: "deploy-freshness", status: "skip", message: "local HEAD unknown" };
|
|
25119
25434
|
}
|
|
25120
25435
|
function loadSetupState5() {
|
|
25121
|
-
const projectPath =
|
|
25122
|
-
const userPath =
|
|
25436
|
+
const projectPath = join53(process.cwd(), ".runwork", "setup.json");
|
|
25437
|
+
const userPath = join53(homedir32(), ".runwork", "setup.json");
|
|
25123
25438
|
for (const p of [projectPath, userPath]) {
|
|
25124
|
-
if (
|
|
25439
|
+
if (existsSync56(p)) {
|
|
25125
25440
|
try {
|
|
25126
|
-
return JSON.parse(
|
|
25441
|
+
return JSON.parse(readFileSync46(p, "utf-8"));
|
|
25127
25442
|
} catch {
|
|
25128
25443
|
continue;
|
|
25129
25444
|
}
|
|
@@ -25138,13 +25453,13 @@ async function checkCodexNetwork() {
|
|
|
25138
25453
|
if (!state || !state.configuredAgents.includes("codex")) {
|
|
25139
25454
|
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
25140
25455
|
}
|
|
25141
|
-
const configPath =
|
|
25142
|
-
if (!
|
|
25456
|
+
const configPath = join53(homedir32(), ".codex", "config.toml");
|
|
25457
|
+
if (!existsSync56(configPath)) {
|
|
25143
25458
|
return { name, status: "skip", message: "no Codex config found" };
|
|
25144
25459
|
}
|
|
25145
25460
|
let parsed;
|
|
25146
25461
|
try {
|
|
25147
|
-
parsed = parse2(
|
|
25462
|
+
parsed = parse2(readFileSync46(configPath, "utf-8"));
|
|
25148
25463
|
} catch {
|
|
25149
25464
|
return { name, status: "warn", message: "could not parse ~/.codex/config.toml" };
|
|
25150
25465
|
}
|
|
@@ -25197,19 +25512,19 @@ async function checkCodexDesktopProject() {
|
|
|
25197
25512
|
if (!usesCodex) {
|
|
25198
25513
|
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
25199
25514
|
}
|
|
25200
|
-
const statePath2 =
|
|
25201
|
-
if (!
|
|
25515
|
+
const statePath2 = join53(homedir32(), ".codex", ".codex-global-state.json");
|
|
25516
|
+
if (!existsSync56(statePath2)) {
|
|
25202
25517
|
return { name, status: "skip", message: "Codex desktop app not detected" };
|
|
25203
25518
|
}
|
|
25204
25519
|
let savedRoots = [];
|
|
25205
25520
|
try {
|
|
25206
|
-
const parsed = JSON.parse(
|
|
25521
|
+
const parsed = JSON.parse(readFileSync46(statePath2, "utf-8"));
|
|
25207
25522
|
const roots = parsed["electron-saved-workspace-roots"];
|
|
25208
25523
|
savedRoots = Array.isArray(roots) ? roots.filter((r) => typeof r === "string") : [];
|
|
25209
25524
|
} catch {
|
|
25210
25525
|
return { name, status: "warn", message: "could not read Codex desktop state" };
|
|
25211
25526
|
}
|
|
25212
|
-
const runworkDir =
|
|
25527
|
+
const runworkDir = join53(homedir32(), ".runwork");
|
|
25213
25528
|
if (savedRoots.includes(runworkDir)) {
|
|
25214
25529
|
return { name, status: "pass", message: "Runwork project added to Codex desktop sidebar" };
|
|
25215
25530
|
}
|
|
@@ -25262,9 +25577,9 @@ async function checkAgentSetup() {
|
|
|
25262
25577
|
if (!adapter2 || !adapter2.supportsMcpScope("user"))
|
|
25263
25578
|
continue;
|
|
25264
25579
|
const mcpConfigPath = getMcpConfigPath2(slug, "user");
|
|
25265
|
-
if (mcpConfigPath &&
|
|
25580
|
+
if (mcpConfigPath && existsSync56(mcpConfigPath)) {
|
|
25266
25581
|
try {
|
|
25267
|
-
const content =
|
|
25582
|
+
const content = readFileSync46(mcpConfigPath, "utf-8");
|
|
25268
25583
|
const missingMcp = state.mcpServers.filter((name) => !content.includes(name));
|
|
25269
25584
|
if (missingMcp.length > 0) {
|
|
25270
25585
|
details.push(`${missingMcp.length} MCP server(s) missing from ${slug} config`);
|
|
@@ -25286,15 +25601,22 @@ async function checkAgentSetup() {
|
|
|
25286
25601
|
const skillsDir = getSkillsDir(slug, "user");
|
|
25287
25602
|
if (!skillsDir)
|
|
25288
25603
|
continue;
|
|
25604
|
+
const adapter2 = getAdapterBySlug(slug);
|
|
25605
|
+
const mcpCoversAppSkills = !!adapter2?.mcpProvidesSkills && state.mcpServers.length > 0;
|
|
25606
|
+
const isCoveredByMcp = (name) => mcpCoversAppSkills && state.skillHashes?.[name]?.source === "app";
|
|
25289
25607
|
const missingSkills = state.skills.filter((name) => {
|
|
25290
|
-
|
|
25291
|
-
|
|
25608
|
+
if (isCoveredByMcp(name))
|
|
25609
|
+
return false;
|
|
25610
|
+
const skillPath = join53(skillsDir, name, "SKILL.md");
|
|
25611
|
+
return !existsSync56(skillPath);
|
|
25292
25612
|
});
|
|
25293
25613
|
if (missingSkills.length > 0) {
|
|
25294
25614
|
details.push(`${missingSkills.length} skill(s) missing from ${slug}`);
|
|
25295
25615
|
upgrade("warn");
|
|
25296
25616
|
} else if (state.skills.length > 0) {
|
|
25297
|
-
|
|
25617
|
+
const mcpCoveredCount = state.skills.filter(isCoveredByMcp).length;
|
|
25618
|
+
const onDiskCount = state.skills.length - mcpCoveredCount;
|
|
25619
|
+
details.push(mcpCoveredCount > 0 ? `${state.skills.length} skill(s) installed (${onDiskCount} on disk, ${mcpCoveredCount} via MCP)` : `${state.skills.length} skill(s) installed`);
|
|
25298
25620
|
}
|
|
25299
25621
|
skillsChecked = true;
|
|
25300
25622
|
break;
|
|
@@ -25313,28 +25635,28 @@ async function checkAgentSetup() {
|
|
|
25313
25635
|
};
|
|
25314
25636
|
}
|
|
25315
25637
|
function getMcpConfigPath2(slug, scope) {
|
|
25316
|
-
const home =
|
|
25638
|
+
const home = homedir32();
|
|
25317
25639
|
switch (slug) {
|
|
25318
25640
|
case "claude-code":
|
|
25319
|
-
return scope === "project" ?
|
|
25641
|
+
return scope === "project" ? join53(process.cwd(), ".mcp.json") : join53(home, ".claude", "settings.json");
|
|
25320
25642
|
case "cursor":
|
|
25321
|
-
return scope === "project" ?
|
|
25643
|
+
return scope === "project" ? join53(process.cwd(), ".cursor", "mcp.json") : join53(home, ".cursor", "mcp.json");
|
|
25322
25644
|
case "windsurf":
|
|
25323
|
-
return scope === "project" ?
|
|
25645
|
+
return scope === "project" ? join53(process.cwd(), ".windsurf", "mcp.json") : join53(home, ".windsurf", "mcp.json");
|
|
25324
25646
|
case "codex":
|
|
25325
25647
|
case "codex-app":
|
|
25326
|
-
return scope === "user" ?
|
|
25648
|
+
return scope === "user" ? join53(home, ".codex", "config.toml") : null;
|
|
25327
25649
|
case "gemini":
|
|
25328
|
-
return scope === "user" ?
|
|
25650
|
+
return scope === "user" ? join53(home, ".gemini", "settings.json") : null;
|
|
25329
25651
|
default:
|
|
25330
25652
|
return null;
|
|
25331
25653
|
}
|
|
25332
25654
|
}
|
|
25333
25655
|
async function checkWorkspacePointers() {
|
|
25334
|
-
const userStatePath =
|
|
25335
|
-
const state =
|
|
25656
|
+
const userStatePath = join53(homedir32(), ".runwork", "setup.json");
|
|
25657
|
+
const state = existsSync56(userStatePath) ? (() => {
|
|
25336
25658
|
try {
|
|
25337
|
-
return JSON.parse(
|
|
25659
|
+
return JSON.parse(readFileSync46(userStatePath, "utf-8"));
|
|
25338
25660
|
} catch {
|
|
25339
25661
|
return null;
|
|
25340
25662
|
}
|
|
@@ -25364,15 +25686,15 @@ async function checkWorkspacePointers() {
|
|
|
25364
25686
|
};
|
|
25365
25687
|
}
|
|
25366
25688
|
function getSkillsDir(slug, scope) {
|
|
25367
|
-
const home =
|
|
25689
|
+
const home = homedir32();
|
|
25368
25690
|
switch (slug) {
|
|
25369
25691
|
case "claude-code":
|
|
25370
|
-
return scope === "project" ?
|
|
25692
|
+
return scope === "project" ? join53(process.cwd(), ".claude", "skills") : join53(home, ".claude", "skills");
|
|
25371
25693
|
case "codex":
|
|
25372
25694
|
case "codex-app":
|
|
25373
|
-
return scope === "project" ?
|
|
25695
|
+
return scope === "project" ? join53(process.cwd(), ".agents", "skills") : join53(home, ".agents", "skills");
|
|
25374
25696
|
case "gemini":
|
|
25375
|
-
return scope === "project" ?
|
|
25697
|
+
return scope === "project" ? join53(process.cwd(), ".gemini", "skills") : join53(home, ".gemini", "skills");
|
|
25376
25698
|
default:
|
|
25377
25699
|
return null;
|
|
25378
25700
|
}
|
|
@@ -25425,18 +25747,18 @@ async function runAllChecks(options) {
|
|
|
25425
25747
|
// src/health/fix.ts
|
|
25426
25748
|
init_credentials();
|
|
25427
25749
|
init_remote();
|
|
25428
|
-
import { existsSync as
|
|
25429
|
-
import { join as
|
|
25750
|
+
import { existsSync as existsSync57 } from "fs";
|
|
25751
|
+
import { join as join54 } from "path";
|
|
25430
25752
|
async function applyDoctorFixes(ctx, failingNames) {
|
|
25431
25753
|
const failing = new Set(failingNames);
|
|
25432
25754
|
const outcomes = [];
|
|
25433
25755
|
if (failing.has("git-credential-helper")) {
|
|
25434
25756
|
if (ctx.credentials?.baseUrl) {
|
|
25435
|
-
await ensureGitCredentialHelper(ctx.credentials.baseUrl);
|
|
25757
|
+
const result = await ensureGitCredentialHelper(ctx.credentials.baseUrl);
|
|
25436
25758
|
outcomes.push({
|
|
25437
25759
|
name: "git-credential-helper",
|
|
25438
|
-
applied:
|
|
25439
|
-
message: "registered the runwork git credential helper"
|
|
25760
|
+
applied: result.ok,
|
|
25761
|
+
message: result.ok ? "registered the runwork git credential helper" : result.message
|
|
25440
25762
|
});
|
|
25441
25763
|
} else {
|
|
25442
25764
|
outcomes.push({
|
|
@@ -25453,7 +25775,7 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
25453
25775
|
applied: false,
|
|
25454
25776
|
message: "no project config -- run inside an app directory"
|
|
25455
25777
|
});
|
|
25456
|
-
} else if (!
|
|
25778
|
+
} else if (!existsSync57(join54(ctx.cwd, ".git"))) {
|
|
25457
25779
|
outcomes.push({
|
|
25458
25780
|
name: "git-remote",
|
|
25459
25781
|
applied: false,
|
|
@@ -25472,10 +25794,10 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
25472
25794
|
}
|
|
25473
25795
|
|
|
25474
25796
|
// src/agents/runtime-detection.ts
|
|
25475
|
-
import { existsSync as
|
|
25476
|
-
import { homedir as
|
|
25477
|
-
import { join as
|
|
25478
|
-
var RUNWORK_SESSIONS_DIR =
|
|
25797
|
+
import { existsSync as existsSync58, readFileSync as readFileSync47, statSync as statSync10, readdirSync as readdirSync16 } from "fs";
|
|
25798
|
+
import { homedir as homedir33 } from "os";
|
|
25799
|
+
import { join as join55 } from "path";
|
|
25800
|
+
var RUNWORK_SESSIONS_DIR = join55(homedir33(), ".runwork", "sessions");
|
|
25479
25801
|
function detectCurrentAgent() {
|
|
25480
25802
|
const claudeCodeSessionId = process.env.CLAUDE_CODE_SESSION_ID;
|
|
25481
25803
|
if (claudeCodeSessionId) {
|
|
@@ -25538,11 +25860,11 @@ function detectCurrentAgent() {
|
|
|
25538
25860
|
return null;
|
|
25539
25861
|
}
|
|
25540
25862
|
function readHookSessionInfo(sessionId) {
|
|
25541
|
-
const path4 =
|
|
25542
|
-
if (!
|
|
25863
|
+
const path4 = join55(RUNWORK_SESSIONS_DIR, `${sessionId}.json`);
|
|
25864
|
+
if (!existsSync58(path4))
|
|
25543
25865
|
return null;
|
|
25544
25866
|
try {
|
|
25545
|
-
const raw =
|
|
25867
|
+
const raw = readFileSync47(path4, "utf8");
|
|
25546
25868
|
const parsed = JSON.parse(raw);
|
|
25547
25869
|
return parsed;
|
|
25548
25870
|
} catch {
|
|
@@ -25550,8 +25872,8 @@ function readHookSessionInfo(sessionId) {
|
|
|
25550
25872
|
}
|
|
25551
25873
|
}
|
|
25552
25874
|
function findClaudeCodeSessionFile(sessionId) {
|
|
25553
|
-
const root =
|
|
25554
|
-
if (!
|
|
25875
|
+
const root = join55(homedir33(), ".claude", "projects");
|
|
25876
|
+
if (!existsSync58(root))
|
|
25555
25877
|
return null;
|
|
25556
25878
|
let projectDirs;
|
|
25557
25879
|
try {
|
|
@@ -25560,15 +25882,15 @@ function findClaudeCodeSessionFile(sessionId) {
|
|
|
25560
25882
|
return null;
|
|
25561
25883
|
}
|
|
25562
25884
|
for (const dir of projectDirs) {
|
|
25563
|
-
const candidate =
|
|
25564
|
-
if (
|
|
25885
|
+
const candidate = join55(root, dir, `${sessionId}.jsonl`);
|
|
25886
|
+
if (existsSync58(candidate))
|
|
25565
25887
|
return candidate;
|
|
25566
25888
|
}
|
|
25567
25889
|
return null;
|
|
25568
25890
|
}
|
|
25569
25891
|
function findCodexRolloutFile(threadId) {
|
|
25570
|
-
const root =
|
|
25571
|
-
if (!
|
|
25892
|
+
const root = join55(homedir33(), ".codex", "sessions");
|
|
25893
|
+
if (!existsSync58(root))
|
|
25572
25894
|
return null;
|
|
25573
25895
|
const stack = [root];
|
|
25574
25896
|
while (stack.length > 0) {
|
|
@@ -25580,7 +25902,7 @@ function findCodexRolloutFile(threadId) {
|
|
|
25580
25902
|
continue;
|
|
25581
25903
|
}
|
|
25582
25904
|
for (const entry of entries) {
|
|
25583
|
-
const full =
|
|
25905
|
+
const full = join55(dir, entry);
|
|
25584
25906
|
let s;
|
|
25585
25907
|
try {
|
|
25586
25908
|
s = statSync10(full);
|
|
@@ -25597,8 +25919,8 @@ function findCodexRolloutFile(threadId) {
|
|
|
25597
25919
|
return null;
|
|
25598
25920
|
}
|
|
25599
25921
|
function findNewestClaudeCodeSession() {
|
|
25600
|
-
const root =
|
|
25601
|
-
if (!
|
|
25922
|
+
const root = join55(homedir33(), ".claude", "projects");
|
|
25923
|
+
if (!existsSync58(root))
|
|
25602
25924
|
return null;
|
|
25603
25925
|
let projectDirs;
|
|
25604
25926
|
try {
|
|
@@ -25608,7 +25930,7 @@ function findNewestClaudeCodeSession() {
|
|
|
25608
25930
|
}
|
|
25609
25931
|
let best = null;
|
|
25610
25932
|
for (const dir of projectDirs) {
|
|
25611
|
-
const projectPath =
|
|
25933
|
+
const projectPath = join55(root, dir);
|
|
25612
25934
|
let files;
|
|
25613
25935
|
try {
|
|
25614
25936
|
files = readdirSync16(projectPath);
|
|
@@ -25618,7 +25940,7 @@ function findNewestClaudeCodeSession() {
|
|
|
25618
25940
|
for (const file of files) {
|
|
25619
25941
|
if (!file.endsWith(".jsonl"))
|
|
25620
25942
|
continue;
|
|
25621
|
-
const full =
|
|
25943
|
+
const full = join55(projectPath, file);
|
|
25622
25944
|
try {
|
|
25623
25945
|
const s = statSync10(full);
|
|
25624
25946
|
if (!best || s.mtimeMs > best.mtime) {
|
|
@@ -25636,8 +25958,8 @@ function findNewestClaudeCodeSession() {
|
|
|
25636
25958
|
return best ? { sessionId: best.sessionId, path: best.path } : null;
|
|
25637
25959
|
}
|
|
25638
25960
|
function findNewestCodexRollout() {
|
|
25639
|
-
const root =
|
|
25640
|
-
if (!
|
|
25961
|
+
const root = join55(homedir33(), ".codex", "sessions");
|
|
25962
|
+
if (!existsSync58(root))
|
|
25641
25963
|
return null;
|
|
25642
25964
|
const stack = [root];
|
|
25643
25965
|
let best = null;
|
|
@@ -25650,7 +25972,7 @@ function findNewestCodexRollout() {
|
|
|
25650
25972
|
continue;
|
|
25651
25973
|
}
|
|
25652
25974
|
for (const entry of entries) {
|
|
25653
|
-
const full =
|
|
25975
|
+
const full = join55(dir, entry);
|
|
25654
25976
|
let s;
|
|
25655
25977
|
try {
|
|
25656
25978
|
s = statSync10(full);
|
|
@@ -25882,8 +26204,8 @@ init_store();
|
|
|
25882
26204
|
init_client();
|
|
25883
26205
|
init_resolve();
|
|
25884
26206
|
import { Command as Command35 } from "commander";
|
|
25885
|
-
import { readFileSync as
|
|
25886
|
-
import { join as
|
|
26207
|
+
import { readFileSync as readFileSync48, writeFileSync as writeFileSync32, existsSync as existsSync59, mkdtempSync as mkdtempSync4 } from "fs";
|
|
26208
|
+
import { join as join56 } from "path";
|
|
25887
26209
|
import { tmpdir as tmpdir4 } from "os";
|
|
25888
26210
|
import { createHash as createHash6 } from "crypto";
|
|
25889
26211
|
|
|
@@ -26005,14 +26327,14 @@ function resolveLocalSessionShare(opts, conversation) {
|
|
|
26005
26327
|
process.exit(1);
|
|
26006
26328
|
}
|
|
26007
26329
|
const title = opts.title ?? conversation.title ?? conversation.project;
|
|
26008
|
-
const markdown = renderTranscriptMarkdown(
|
|
26330
|
+
const markdown = renderTranscriptMarkdown(readFileSync48(conversation.transcriptPath, "utf8"), family, title);
|
|
26009
26331
|
if (!markdown) {
|
|
26010
26332
|
console.error("Error: this conversation has no shareable content.");
|
|
26011
26333
|
process.exit(1);
|
|
26012
26334
|
}
|
|
26013
|
-
const tempDir = mkdtempSync4(
|
|
26014
|
-
const transcriptFile =
|
|
26015
|
-
|
|
26335
|
+
const tempDir = mkdtempSync4(join56(tmpdir4(), "runwork-share-"));
|
|
26336
|
+
const transcriptFile = join56(tempDir, "transcript.md");
|
|
26337
|
+
writeFileSync32(transcriptFile, markdown);
|
|
26016
26338
|
opts.transcriptFile = transcriptFile;
|
|
26017
26339
|
opts.nativeFile = opts.nativeFile ?? conversation.transcriptPath;
|
|
26018
26340
|
opts.sourceAgent = opts.sourceAgent ?? conversation.agentSlug;
|
|
@@ -26049,7 +26371,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26049
26371
|
console.error("Error: --transcript-file is required. Pass the path to the LLM-emitted markdown transcript.");
|
|
26050
26372
|
process.exit(1);
|
|
26051
26373
|
}
|
|
26052
|
-
if (!
|
|
26374
|
+
if (!existsSync59(opts.transcriptFile)) {
|
|
26053
26375
|
console.error(`Error: transcript file does not exist: ${opts.transcriptFile}`);
|
|
26054
26376
|
process.exit(1);
|
|
26055
26377
|
}
|
|
@@ -26070,7 +26392,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26070
26392
|
const credentials = requireAuth();
|
|
26071
26393
|
const client = new ApiClient(credentials);
|
|
26072
26394
|
const { workspaceId } = await resolveWorkspace2(client, { workspace: opts.workspace });
|
|
26073
|
-
const transcriptContent =
|
|
26395
|
+
const transcriptContent = readFileSync48(opts.transcriptFile, "utf8");
|
|
26074
26396
|
const bundles = [
|
|
26075
26397
|
{
|
|
26076
26398
|
format: "transcript",
|
|
@@ -26083,19 +26405,19 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26083
26405
|
const sourceAgent = opts.sourceAgent ?? detected?.slug ?? "generic";
|
|
26084
26406
|
let nativeFilePath = null;
|
|
26085
26407
|
if (opts.nativeFile) {
|
|
26086
|
-
if (!
|
|
26408
|
+
if (!existsSync59(opts.nativeFile)) {
|
|
26087
26409
|
console.error(`Error: --native-file path does not exist: ${opts.nativeFile}`);
|
|
26088
26410
|
process.exit(1);
|
|
26089
26411
|
}
|
|
26090
26412
|
nativeFilePath = opts.nativeFile;
|
|
26091
|
-
} else if (detected?.sessionFilePath &&
|
|
26413
|
+
} else if (detected?.sessionFilePath && existsSync59(detected.sessionFilePath)) {
|
|
26092
26414
|
nativeFilePath = detected.sessionFilePath;
|
|
26093
26415
|
}
|
|
26094
26416
|
if (nativeFilePath) {
|
|
26095
26417
|
const nativeFormat = nativeBundleFormatForAgent(sourceAgent);
|
|
26096
26418
|
if (nativeFormat) {
|
|
26097
26419
|
try {
|
|
26098
|
-
const content =
|
|
26420
|
+
const content = readFileSync48(nativeFilePath, "utf8");
|
|
26099
26421
|
bundles.push({
|
|
26100
26422
|
format: nativeFormat,
|
|
26101
26423
|
content,
|
|
@@ -26111,7 +26433,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26111
26433
|
let metadata = {};
|
|
26112
26434
|
if (opts.metadataFile) {
|
|
26113
26435
|
try {
|
|
26114
|
-
metadata = JSON.parse(
|
|
26436
|
+
metadata = JSON.parse(readFileSync48(opts.metadataFile, "utf8"));
|
|
26115
26437
|
} catch (err) {
|
|
26116
26438
|
console.error(`Error: --metadata-file is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
26117
26439
|
process.exit(1);
|
|
@@ -26220,9 +26542,9 @@ init_client();
|
|
|
26220
26542
|
init_resolve();
|
|
26221
26543
|
init_registry_data();
|
|
26222
26544
|
import { Command as Command38 } from "commander";
|
|
26223
|
-
import { writeFileSync as
|
|
26224
|
-
import { homedir as
|
|
26225
|
-
import { join as
|
|
26545
|
+
import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync29, realpathSync } from "fs";
|
|
26546
|
+
import { homedir as homedir34 } from "os";
|
|
26547
|
+
import { join as join57 } from "path";
|
|
26226
26548
|
import { spawn as spawn5 } from "child_process";
|
|
26227
26549
|
init_registry();
|
|
26228
26550
|
init_which();
|
|
@@ -26261,10 +26583,10 @@ function extractCodexUuid(rolloutContent) {
|
|
|
26261
26583
|
}
|
|
26262
26584
|
function placeClaudeJsonl(uuid, content, recipientCwd) {
|
|
26263
26585
|
const encoded = encodeClaudeCodeCwd(recipientCwd);
|
|
26264
|
-
const projectDir =
|
|
26265
|
-
|
|
26266
|
-
const placedAt =
|
|
26267
|
-
|
|
26586
|
+
const projectDir = join57(homedir34(), ".claude", "projects", encoded);
|
|
26587
|
+
mkdirSync29(projectDir, { recursive: true });
|
|
26588
|
+
const placedAt = join57(projectDir, `${uuid}.jsonl`);
|
|
26589
|
+
writeFileSync33(placedAt, content);
|
|
26268
26590
|
return { placedAt, runFromCwd: recipientCwd };
|
|
26269
26591
|
}
|
|
26270
26592
|
function placeCodexRollout(uuid, content) {
|
|
@@ -26272,11 +26594,11 @@ function placeCodexRollout(uuid, content) {
|
|
|
26272
26594
|
const yyyy = String(now.getUTCFullYear());
|
|
26273
26595
|
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
26274
26596
|
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
26275
|
-
const dir =
|
|
26276
|
-
|
|
26597
|
+
const dir = join57(homedir34(), ".codex", "sessions", yyyy, mm, dd);
|
|
26598
|
+
mkdirSync29(dir, { recursive: true });
|
|
26277
26599
|
const ts = now.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
26278
|
-
const placedAt =
|
|
26279
|
-
|
|
26600
|
+
const placedAt = join57(dir, `rollout-${ts}-${uuid}.jsonl`);
|
|
26601
|
+
writeFileSync33(placedAt, content);
|
|
26280
26602
|
return { placedAt };
|
|
26281
26603
|
}
|
|
26282
26604
|
function pickTargetAgent(opts, sourceAgent) {
|