@rallycry/conveyor-agent 10.13.8 → 10.13.10
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/chunk-2K6RRTQC.js +451 -0
- package/dist/chunk-2K6RRTQC.js.map +1 -0
- package/dist/chunk-4VUQ2NPF.js +25 -0
- package/dist/chunk-4VUQ2NPF.js.map +1 -0
- package/dist/chunk-IE7EOKMD.js +55 -0
- package/dist/chunk-IE7EOKMD.js.map +1 -0
- package/dist/{chunk-AJZIO5QI.js → chunk-UVR5XFE4.js} +371 -587
- package/dist/chunk-UVR5XFE4.js.map +1 -0
- package/dist/chunk-YRC57EYG.js +413 -0
- package/dist/chunk-YRC57EYG.js.map +1 -0
- package/dist/cli.js +53 -9
- package/dist/cli.js.map +1 -1
- package/dist/client-ICIWKSK2.js +17 -0
- package/dist/client-ICIWKSK2.js.map +1 -0
- package/dist/index.js +24 -8
- package/dist/index.js.map +1 -1
- package/dist/mode-6RL3SVMV.js +13 -0
- package/dist/mode-6RL3SVMV.js.map +1 -0
- package/dist/protocol-QJRHTTMQ.js +15 -0
- package/dist/protocol-QJRHTTMQ.js.map +1 -0
- package/dist/server-UGE67KIZ.js +313 -0
- package/dist/server-UGE67KIZ.js.map +1 -0
- package/package.json +1 -1
- package/runtime/entrypoint.sh +97 -11
- package/dist/chunk-AJZIO5QI.js.map +0 -1
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LoopLagMonitor
|
|
3
3
|
} from "./chunk-7TQO4ZF4.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import {
|
|
5
|
+
getWorkbenchClient
|
|
6
|
+
} from "./chunk-YRC57EYG.js";
|
|
7
|
+
import {
|
|
8
|
+
workbenchEnabled
|
|
9
|
+
} from "./chunk-4VUQ2NPF.js";
|
|
10
|
+
import {
|
|
11
|
+
MAX_BETWEEN_TURN_BUFFER,
|
|
12
|
+
MAX_DIAGNOSTIC_OUTPUT,
|
|
13
|
+
buildPromptBytes,
|
|
14
|
+
buildSnapshotTar,
|
|
15
|
+
extractSnapshotTar,
|
|
16
|
+
inheritedEnv,
|
|
17
|
+
loadPtySpawn,
|
|
18
|
+
parseUserQuestions,
|
|
19
|
+
renderPromptContentText,
|
|
20
|
+
resolvePlanDialogTiming,
|
|
21
|
+
resolvePtySpawn,
|
|
22
|
+
resolveSubmitNudgeTiming,
|
|
23
|
+
resolveSubmitSettleMs,
|
|
24
|
+
sessionTempBase,
|
|
25
|
+
sleep,
|
|
26
|
+
transcriptSize,
|
|
27
|
+
turnOptionsFrom
|
|
28
|
+
} from "./chunk-2K6RRTQC.js";
|
|
11
29
|
|
|
12
30
|
// src/setup/bootstrap.ts
|
|
13
31
|
var BOOTSTRAP_TIMEOUT_MS = 3e4;
|
|
@@ -1193,6 +1211,14 @@ var GIT_TIMEOUT_MS = 6e4;
|
|
|
1193
1211
|
var GIT_SLOW_TIMEOUT_MS = 12e4;
|
|
1194
1212
|
var GIT_MAX_BUFFER = 16 * 1024 * 1024;
|
|
1195
1213
|
async function git(cwd, args, timeoutMs = GIT_TIMEOUT_MS) {
|
|
1214
|
+
if (workbenchEnabled()) {
|
|
1215
|
+
const { stdout: stdout2 } = await getWorkbenchClient().execFile("git", args, {
|
|
1216
|
+
cwd,
|
|
1217
|
+
timeout: timeoutMs,
|
|
1218
|
+
maxBuffer: GIT_MAX_BUFFER
|
|
1219
|
+
});
|
|
1220
|
+
return stdout2.trim();
|
|
1221
|
+
}
|
|
1196
1222
|
const { stdout } = await execFileAsync("git", args, {
|
|
1197
1223
|
cwd,
|
|
1198
1224
|
timeout: timeoutMs,
|
|
@@ -1574,7 +1600,9 @@ async function backupOtherBranches(cwd, currentBranch) {
|
|
|
1574
1600
|
}
|
|
1575
1601
|
|
|
1576
1602
|
// src/runner/work-preservation/index.ts
|
|
1577
|
-
import { rm as
|
|
1603
|
+
import { mkdtemp, rm as rm2 } from "fs/promises";
|
|
1604
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1605
|
+
import { join } from "path";
|
|
1578
1606
|
|
|
1579
1607
|
// src/runner/work-preservation/restore-precedence.ts
|
|
1580
1608
|
function selectRestoreSource(probe) {
|
|
@@ -1583,193 +1611,19 @@ function selectRestoreSource(probe) {
|
|
|
1583
1611
|
return "clean";
|
|
1584
1612
|
}
|
|
1585
1613
|
|
|
1586
|
-
// src/runner/work-preservation/snapshot-
|
|
1587
|
-
import {
|
|
1614
|
+
// src/runner/work-preservation/snapshot-transfer.ts
|
|
1615
|
+
import { randomUUID } from "crypto";
|
|
1588
1616
|
import { createReadStream, createWriteStream } from "fs";
|
|
1589
|
-
import {
|
|
1617
|
+
import { rm } from "fs/promises";
|
|
1590
1618
|
import { tmpdir } from "os";
|
|
1591
1619
|
import path from "path";
|
|
1592
|
-
import { pipeline } from "stream/promises";
|
|
1593
|
-
import { promisify as promisify2 } from "util";
|
|
1594
|
-
import { createGzip } from "zlib";
|
|
1595
|
-
import * as tar from "tar";
|
|
1596
|
-
|
|
1597
|
-
// src/runner/work-preservation/snapshot-artifact.ts
|
|
1598
|
-
function planSnapshotFiles(git2) {
|
|
1599
|
-
const include = git2.trackedAndUntracked();
|
|
1600
|
-
const includeSet = new Set(include);
|
|
1601
|
-
const deletions = [...new Set(git2.deletedSinceHead())].filter((path4) => !includeSet.has(path4));
|
|
1602
|
-
return { include, deletions };
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
// src/runner/work-preservation/snapshot-tar.ts
|
|
1606
|
-
var SNAPSHOT_MANIFEST_NAME = ".conveyor-snapshot-manifest.json";
|
|
1607
|
-
var LEGACY_PG_DUMP_FILENAME = ".conveyor-pgdump.sql";
|
|
1608
|
-
var STATUS_MAX_BUFFER = 64 * 1024 * 1024;
|
|
1609
|
-
function parseStatusPorcelainZ(raw) {
|
|
1610
|
-
const includes = [];
|
|
1611
|
-
const deletions = [];
|
|
1612
|
-
const tokens = raw.split("\0");
|
|
1613
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
1614
|
-
const token = tokens[i];
|
|
1615
|
-
if (!token || token.length < 4) continue;
|
|
1616
|
-
const x = token[0];
|
|
1617
|
-
const y = token[1];
|
|
1618
|
-
const filePath = token.slice(3);
|
|
1619
|
-
if (x === "R" || x === "C") {
|
|
1620
|
-
const origPath = tokens[++i];
|
|
1621
|
-
if (x === "R" && origPath) deletions.push(origPath);
|
|
1622
|
-
}
|
|
1623
|
-
if (x === "!") continue;
|
|
1624
|
-
const missingFromWorktree = y === "D" || x === "D" && y === " ";
|
|
1625
|
-
if (missingFromWorktree) deletions.push(filePath);
|
|
1626
|
-
else includes.push(filePath);
|
|
1627
|
-
}
|
|
1628
|
-
return { includes, deletions };
|
|
1629
|
-
}
|
|
1630
|
-
var execFileAsync2 = promisify2(execFile2);
|
|
1631
|
-
var GIT_TIMEOUT_MS2 = 12e4;
|
|
1632
|
-
async function realGitSurface(cwd) {
|
|
1633
|
-
const { stdout } = await execFileAsync2("git", ["status", "--porcelain=v1", "-z", "-uall"], {
|
|
1634
|
-
cwd,
|
|
1635
|
-
timeout: GIT_TIMEOUT_MS2,
|
|
1636
|
-
maxBuffer: STATUS_MAX_BUFFER
|
|
1637
|
-
});
|
|
1638
|
-
const cached = parseStatusPorcelainZ(stdout.toString());
|
|
1639
|
-
return {
|
|
1640
|
-
trackedAndUntracked: () => cached.includes,
|
|
1641
|
-
deletedSinceHead: () => cached.deletions
|
|
1642
|
-
};
|
|
1643
|
-
}
|
|
1644
|
-
async function gitHead(cwd) {
|
|
1645
|
-
try {
|
|
1646
|
-
const { stdout } = await execFileAsync2("git", ["rev-parse", "HEAD"], {
|
|
1647
|
-
cwd,
|
|
1648
|
-
timeout: GIT_TIMEOUT_MS2
|
|
1649
|
-
});
|
|
1650
|
-
return stdout.toString().trim() || null;
|
|
1651
|
-
} catch {
|
|
1652
|
-
return null;
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
async function buildSnapshotTar(cwd) {
|
|
1656
|
-
const staging = await mkdtemp(path.join(tmpdir(), "conveyor-snapshot-"));
|
|
1657
|
-
const cleanup = async () => {
|
|
1658
|
-
await rm(staging, { recursive: true, force: true }).catch(() => {
|
|
1659
|
-
});
|
|
1660
|
-
};
|
|
1661
|
-
try {
|
|
1662
|
-
const plan = planSnapshotFiles(await realGitSurface(cwd));
|
|
1663
|
-
const head = await gitHead(cwd);
|
|
1664
|
-
if (!head) throw new Error("cannot snapshot a repo without a resolvable HEAD");
|
|
1665
|
-
const capturedAt = Date.now();
|
|
1666
|
-
const manifest = {
|
|
1667
|
-
head,
|
|
1668
|
-
deletions: plan.deletions,
|
|
1669
|
-
capturedAt
|
|
1670
|
-
};
|
|
1671
|
-
await writeFile(path.join(staging, SNAPSHOT_MANIFEST_NAME), JSON.stringify(manifest), "utf8");
|
|
1672
|
-
const stagedEntries = [SNAPSHOT_MANIFEST_NAME];
|
|
1673
|
-
const rawTarPath = path.join(staging, "snapshot.tar");
|
|
1674
|
-
await tar.create({ cwd: staging, file: rawTarPath, portable: true }, stagedEntries);
|
|
1675
|
-
if (plan.include.length > 0) {
|
|
1676
|
-
await tar.replace({ file: rawTarPath, cwd, portable: true }, plan.include);
|
|
1677
|
-
}
|
|
1678
|
-
const tarPath = path.join(staging, "snapshot.tar.gz");
|
|
1679
|
-
await pipeline(createReadStream(rawTarPath), createGzip(), createWriteStream(tarPath));
|
|
1680
|
-
await rm(rawTarPath, { force: true });
|
|
1681
|
-
const { size } = await stat(tarPath);
|
|
1682
|
-
return {
|
|
1683
|
-
tarPath,
|
|
1684
|
-
sizeBytes: size,
|
|
1685
|
-
fileCount: plan.include.length,
|
|
1686
|
-
deletionCount: plan.deletions.length,
|
|
1687
|
-
capturedAt,
|
|
1688
|
-
cleanup
|
|
1689
|
-
};
|
|
1690
|
-
} catch (err) {
|
|
1691
|
-
await cleanup();
|
|
1692
|
-
throw err;
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
function isWithinWorkspace(cwd, rel) {
|
|
1696
|
-
if (!rel || path.isAbsolute(rel)) return false;
|
|
1697
|
-
const root = path.resolve(cwd);
|
|
1698
|
-
const abs = path.resolve(root, rel);
|
|
1699
|
-
return abs !== root && abs.startsWith(root + path.sep);
|
|
1700
|
-
}
|
|
1701
|
-
async function readSnapshotArchive(tarPath) {
|
|
1702
|
-
let manifestRaw = null;
|
|
1703
|
-
try {
|
|
1704
|
-
await tar.list({
|
|
1705
|
-
file: tarPath,
|
|
1706
|
-
onReadEntry: (entry) => {
|
|
1707
|
-
if (entry.path === SNAPSHOT_MANIFEST_NAME) {
|
|
1708
|
-
const chunks = [];
|
|
1709
|
-
entry.on("data", (chunk) => chunks.push(chunk));
|
|
1710
|
-
entry.on("end", () => {
|
|
1711
|
-
manifestRaw = Buffer.concat(chunks);
|
|
1712
|
-
});
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
});
|
|
1716
|
-
} catch {
|
|
1717
|
-
return null;
|
|
1718
|
-
}
|
|
1719
|
-
if (!manifestRaw) return null;
|
|
1720
|
-
try {
|
|
1721
|
-
const parsed = JSON.parse(manifestRaw.toString("utf8"));
|
|
1722
|
-
if (typeof parsed.head !== "string" || !parsed.head) return null;
|
|
1723
|
-
return {
|
|
1724
|
-
head: parsed.head,
|
|
1725
|
-
deletions: Array.isArray(parsed.deletions) ? parsed.deletions : [],
|
|
1726
|
-
capturedAt: typeof parsed.capturedAt === "number" ? parsed.capturedAt : 0
|
|
1727
|
-
};
|
|
1728
|
-
} catch {
|
|
1729
|
-
return null;
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
async function extractSnapshotTar(cwd, tarPath) {
|
|
1733
|
-
const manifest = await readSnapshotArchive(tarPath);
|
|
1734
|
-
if (!manifest) return { status: "invalid" };
|
|
1735
|
-
const currentHead = await gitHead(cwd);
|
|
1736
|
-
if (manifest.head !== currentHead) {
|
|
1737
|
-
return { status: "stale-head", snapshotHead: manifest.head, currentHead };
|
|
1738
|
-
}
|
|
1739
|
-
let filesExtracted = 0;
|
|
1740
|
-
await tar.extract({
|
|
1741
|
-
file: tarPath,
|
|
1742
|
-
cwd,
|
|
1743
|
-
filter: (entryPath) => {
|
|
1744
|
-
if (entryPath === SNAPSHOT_MANIFEST_NAME || entryPath === LEGACY_PG_DUMP_FILENAME) {
|
|
1745
|
-
return false;
|
|
1746
|
-
}
|
|
1747
|
-
filesExtracted++;
|
|
1748
|
-
return true;
|
|
1749
|
-
}
|
|
1750
|
-
});
|
|
1751
|
-
let deletionsApplied = 0;
|
|
1752
|
-
for (const rel of manifest.deletions) {
|
|
1753
|
-
if (typeof rel !== "string" || !isWithinWorkspace(cwd, rel)) continue;
|
|
1754
|
-
await rm(path.resolve(cwd, rel), { recursive: true, force: true });
|
|
1755
|
-
deletionsApplied++;
|
|
1756
|
-
}
|
|
1757
|
-
return { status: "extracted", filesExtracted, deletionsApplied };
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
// src/runner/work-preservation/snapshot-transfer.ts
|
|
1761
|
-
import { randomUUID } from "crypto";
|
|
1762
|
-
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2 } from "fs";
|
|
1763
|
-
import { rm as rm2 } from "fs/promises";
|
|
1764
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
1765
|
-
import path2 from "path";
|
|
1766
1620
|
import { Readable } from "stream";
|
|
1767
|
-
import { pipeline
|
|
1621
|
+
import { pipeline } from "stream/promises";
|
|
1768
1622
|
var SNAPSHOT_HTTP_TIMEOUT_MS = 6e4;
|
|
1769
1623
|
var SNAPSHOT_CAPTURED_AT_HEADER = "x-snapshot-captured-at";
|
|
1770
1624
|
async function putSnapshotToGcs(url, tarResult) {
|
|
1771
1625
|
try {
|
|
1772
|
-
const body = Readable.toWeb(
|
|
1626
|
+
const body = Readable.toWeb(createReadStream(tarResult.tarPath));
|
|
1773
1627
|
const res = await fetch(url, {
|
|
1774
1628
|
method: "PUT",
|
|
1775
1629
|
headers: {
|
|
@@ -1787,26 +1641,51 @@ async function putSnapshotToGcs(url, tarResult) {
|
|
|
1787
1641
|
}
|
|
1788
1642
|
}
|
|
1789
1643
|
async function downloadSnapshotToTemp(url) {
|
|
1790
|
-
const tmpTar =
|
|
1644
|
+
const tmpTar = path.join(tmpdir(), `conveyor-snapshot-restore-${randomUUID()}.tar.gz`);
|
|
1791
1645
|
try {
|
|
1792
1646
|
const res = await fetch(url, {
|
|
1793
1647
|
method: "GET",
|
|
1794
1648
|
signal: AbortSignal.timeout(SNAPSHOT_HTTP_TIMEOUT_MS)
|
|
1795
1649
|
});
|
|
1796
1650
|
if (!res.ok || !res.body) return null;
|
|
1797
|
-
await
|
|
1651
|
+
await pipeline(
|
|
1798
1652
|
Readable.fromWeb(res.body),
|
|
1799
|
-
|
|
1653
|
+
createWriteStream(tmpTar)
|
|
1800
1654
|
);
|
|
1801
1655
|
return tmpTar;
|
|
1802
1656
|
} catch {
|
|
1803
|
-
await
|
|
1657
|
+
await rm(tmpTar, { force: true }).catch(() => {
|
|
1804
1658
|
});
|
|
1805
1659
|
return null;
|
|
1806
1660
|
}
|
|
1807
1661
|
}
|
|
1808
1662
|
|
|
1809
1663
|
// src/runner/work-preservation/index.ts
|
|
1664
|
+
async function captureSnapshotTar(cwd) {
|
|
1665
|
+
if (!workbenchEnabled()) return buildSnapshotTar(cwd);
|
|
1666
|
+
const staging = await mkdtemp(join(tmpdir2(), "conveyor-snapshot-remote-"));
|
|
1667
|
+
const cleanup = async () => {
|
|
1668
|
+
await rm2(staging, { recursive: true, force: true }).catch(() => {
|
|
1669
|
+
});
|
|
1670
|
+
};
|
|
1671
|
+
try {
|
|
1672
|
+
const meta = await getWorkbenchClient().buildSnapshot(cwd, join(staging, "snapshot.tar.gz"));
|
|
1673
|
+
return {
|
|
1674
|
+
tarPath: meta.tarPath,
|
|
1675
|
+
sizeBytes: meta.sizeBytes,
|
|
1676
|
+
fileCount: meta.fileCount,
|
|
1677
|
+
deletionCount: meta.deletionCount,
|
|
1678
|
+
capturedAt: meta.capturedAt,
|
|
1679
|
+
cleanup
|
|
1680
|
+
};
|
|
1681
|
+
} catch (err) {
|
|
1682
|
+
await cleanup();
|
|
1683
|
+
throw err;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
function applySnapshotTar(cwd, tarPath) {
|
|
1687
|
+
return workbenchEnabled() ? getWorkbenchClient().extractSnapshot(cwd, tarPath) : extractSnapshotTar(cwd, tarPath);
|
|
1688
|
+
}
|
|
1810
1689
|
var periodicTimer = null;
|
|
1811
1690
|
var inFlightCapture = null;
|
|
1812
1691
|
var inFlightGcsUpload = null;
|
|
@@ -1819,7 +1698,7 @@ var EMPTY_GCS_RESULT = {
|
|
|
1819
1698
|
async function doUploadSnapshotToGcs(cwd, uploadUrl, opts) {
|
|
1820
1699
|
let tarResult;
|
|
1821
1700
|
try {
|
|
1822
|
-
tarResult = await
|
|
1701
|
+
tarResult = await captureSnapshotTar(cwd);
|
|
1823
1702
|
} catch {
|
|
1824
1703
|
return EMPTY_GCS_RESULT;
|
|
1825
1704
|
}
|
|
@@ -1902,7 +1781,7 @@ async function restoreOnBoot(bundle, cwd) {
|
|
|
1902
1781
|
const tmpTar = await downloadSnapshotToTemp(bundle.snapshotUrl);
|
|
1903
1782
|
if (tmpTar) {
|
|
1904
1783
|
try {
|
|
1905
|
-
const result = await
|
|
1784
|
+
const result = await applySnapshotTar(cwd, tmpTar);
|
|
1906
1785
|
if (result.status === "extracted") {
|
|
1907
1786
|
gcsAvailable = true;
|
|
1908
1787
|
gcsFileCount = result.filesExtracted;
|
|
@@ -1910,7 +1789,7 @@ async function restoreOnBoot(bundle, cwd) {
|
|
|
1910
1789
|
} catch {
|
|
1911
1790
|
gcsAvailable = false;
|
|
1912
1791
|
} finally {
|
|
1913
|
-
await
|
|
1792
|
+
await rm2(tmpTar, { force: true }).catch(() => {
|
|
1914
1793
|
});
|
|
1915
1794
|
}
|
|
1916
1795
|
}
|
|
@@ -2318,7 +2197,10 @@ var UpdateTaskPropertiesRequestSchema = z3.object({
|
|
|
2318
2197
|
tagIds: z3.array(z3.string()).optional(),
|
|
2319
2198
|
tagNames: z3.array(z3.string()).optional(),
|
|
2320
2199
|
githubPRUrl: z3.string().url().optional(),
|
|
2321
|
-
githubBranch: z3.string().optional()
|
|
2200
|
+
githubBranch: z3.string().optional(),
|
|
2201
|
+
// Canonical risk level, or null to clear — same semantics as the headless
|
|
2202
|
+
// update_task boundary (resolved to the project's Risk row in the handler).
|
|
2203
|
+
risk: riskLevelSchema.nullable().optional()
|
|
2322
2204
|
});
|
|
2323
2205
|
var ListIconsRequestSchema = z3.object({
|
|
2324
2206
|
sessionId: z3.string()
|
|
@@ -2633,6 +2515,9 @@ var GetProjectSummaryRequestSchema = z4.object({
|
|
|
2633
2515
|
var GetProjectOnboardingStatusRequestSchema = z4.object({
|
|
2634
2516
|
projectId: z4.string()
|
|
2635
2517
|
});
|
|
2518
|
+
var GetProjectOnboardingStepRequestSchema = z4.object({
|
|
2519
|
+
projectId: z4.string()
|
|
2520
|
+
});
|
|
2636
2521
|
var GetProjectConnectUrlsRequestSchema = z4.object({
|
|
2637
2522
|
projectId: z4.string()
|
|
2638
2523
|
});
|
|
@@ -2650,7 +2535,13 @@ var UpdateProjectTaskRequestSchema = z4.object({
|
|
|
2650
2535
|
projectId: z4.string(),
|
|
2651
2536
|
taskId: z4.string(),
|
|
2652
2537
|
title: z4.string().optional(),
|
|
2538
|
+
description: z4.string().optional(),
|
|
2653
2539
|
plan: z4.string().optional(),
|
|
2540
|
+
// Enum validation lives at the MCP tool layer (mirrors createProjectTask);
|
|
2541
|
+
// the handler routes through the shared updateStatus core (InProgress
|
|
2542
|
+
// dependency check + cleanup/board/Slack side effects), not the stricter
|
|
2543
|
+
// card-type-validating path the Socket.IO updateTaskStatus mutation uses.
|
|
2544
|
+
status: z4.string().optional(),
|
|
2654
2545
|
// Canonical risk level, or null to clear. Resolved to the project's
|
|
2655
2546
|
// configured Risk row (by rank) in the handler.
|
|
2656
2547
|
risk: riskLevelSchema.nullable().optional(),
|
|
@@ -2660,9 +2551,9 @@ var UpdateProjectTaskRequestSchema = z4.object({
|
|
|
2660
2551
|
subProjectId: z4.string().nullable().optional(),
|
|
2661
2552
|
requestingUserId: z4.string().optional()
|
|
2662
2553
|
}).strict().refine(
|
|
2663
|
-
(v) => v.title !== void 0 || v.plan !== void 0 || v.risk !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0,
|
|
2554
|
+
(v) => v.title !== void 0 || v.description !== void 0 || v.plan !== void 0 || v.status !== void 0 || v.risk !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0,
|
|
2664
2555
|
{
|
|
2665
|
-
message: "update_task requires at least one field to change (title, plan, risk, assignedUserId, or subProjectId)"
|
|
2556
|
+
message: "update_task requires at least one field to change (title, description, plan, status, risk, assignedUserId, or subProjectId)"
|
|
2666
2557
|
}
|
|
2667
2558
|
);
|
|
2668
2559
|
var TransitionProjectTaskStatusRequestSchema = z4.object({
|
|
@@ -2814,6 +2705,9 @@ var UpdateProjectSubtaskRequestSchema = z4.object({
|
|
|
2814
2705
|
ordinal: z4.number().int().nonnegative().optional(),
|
|
2815
2706
|
storyPointValue: z4.number().int().positive().optional(),
|
|
2816
2707
|
followParentStatus: z4.boolean().optional(),
|
|
2708
|
+
/** Replace-set of sibling subtask ids/slugs this subtask blocks on ([] clears).
|
|
2709
|
+
* Mirrors the in-pod updateSubtask semantics. */
|
|
2710
|
+
dependsOn: z4.array(z4.string().min(1)).max(32).optional(),
|
|
2817
2711
|
requestingUserId: z4.string().optional()
|
|
2818
2712
|
});
|
|
2819
2713
|
var DeleteProjectSubtaskRequestSchema = z4.object({
|
|
@@ -3389,9 +3283,9 @@ var ClaudeCodeHarness = class {
|
|
|
3389
3283
|
};
|
|
3390
3284
|
|
|
3391
3285
|
// src/harness/pty/session.ts
|
|
3392
|
-
import {
|
|
3393
|
-
import {
|
|
3394
|
-
import { join as
|
|
3286
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3287
|
+
import { mkdtemp as mkdtemp2, mkdir as mkdir3, rm as rm4 } from "fs/promises";
|
|
3288
|
+
import { join as join5, dirname } from "path";
|
|
3395
3289
|
|
|
3396
3290
|
// src/harness/pty/event-queue.ts
|
|
3397
3291
|
var AsyncEventQueue = class {
|
|
@@ -3672,8 +3566,8 @@ function mapTranscriptRecord(raw) {
|
|
|
3672
3566
|
// src/harness/pty/jsonl-tailer.ts
|
|
3673
3567
|
var POLL_INTERVAL_MS = 25;
|
|
3674
3568
|
var JsonlTailer = class {
|
|
3675
|
-
constructor(
|
|
3676
|
-
this.path =
|
|
3569
|
+
constructor(path3, onEvent, onRawRecord) {
|
|
3570
|
+
this.path = path3;
|
|
3677
3571
|
this.onEvent = onEvent;
|
|
3678
3572
|
this.onRawRecord = onRawRecord;
|
|
3679
3573
|
}
|
|
@@ -3807,6 +3701,27 @@ function stringField2(record, ...keys) {
|
|
|
3807
3701
|
function truncate(text, max) {
|
|
3808
3702
|
return text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
3809
3703
|
}
|
|
3704
|
+
function compactQuestionsJson(questions) {
|
|
3705
|
+
const serialize = (qs) => JSON.stringify({ questions: qs });
|
|
3706
|
+
const withDescriptions = (max) => questions.map((q) => ({
|
|
3707
|
+
...q,
|
|
3708
|
+
options: q.options.map((o) => ({ ...o, description: truncate(o.description, max) }))
|
|
3709
|
+
}));
|
|
3710
|
+
const full = serialize(questions);
|
|
3711
|
+
if (full.length <= TOOL_INPUT_MAX) return full;
|
|
3712
|
+
const shortened = serialize(withDescriptions(80));
|
|
3713
|
+
if (shortened.length <= TOOL_INPUT_MAX) return shortened;
|
|
3714
|
+
const bare = serialize(withDescriptions(0));
|
|
3715
|
+
if (bare.length <= TOOL_INPUT_MAX) return bare;
|
|
3716
|
+
return bare.slice(0, TOOL_INPUT_MAX);
|
|
3717
|
+
}
|
|
3718
|
+
function compactToolInput(name, input) {
|
|
3719
|
+
if (name === "AskUserQuestion" && isRecord2(input)) {
|
|
3720
|
+
const questions = parseUserQuestions(input);
|
|
3721
|
+
if (questions.length > 0) return compactQuestionsJson(questions);
|
|
3722
|
+
}
|
|
3723
|
+
return JSON.stringify(input ?? {}).slice(0, TOOL_INPUT_MAX);
|
|
3724
|
+
}
|
|
3810
3725
|
function isNonConversationText(text) {
|
|
3811
3726
|
const trimmed = text.trimStart();
|
|
3812
3727
|
return trimmed.startsWith("<command-name>") || trimmed.startsWith("<local-command-") || trimmed.startsWith("<task-notification>");
|
|
@@ -3843,7 +3758,7 @@ function mapAssistant2(record) {
|
|
|
3843
3758
|
const event = {
|
|
3844
3759
|
kind: "tool_use",
|
|
3845
3760
|
name: truncate(name, 200),
|
|
3846
|
-
input:
|
|
3761
|
+
input: compactToolInput(name, input)
|
|
3847
3762
|
};
|
|
3848
3763
|
const id = stringField2(raw, "id");
|
|
3849
3764
|
if (id !== void 0) event.id = id;
|
|
@@ -3912,17 +3827,17 @@ function mapChatRecords(raw) {
|
|
|
3912
3827
|
}
|
|
3913
3828
|
|
|
3914
3829
|
// src/harness/pty/settings.ts
|
|
3915
|
-
import { mkdir, writeFile
|
|
3830
|
+
import { mkdir, writeFile, chmod } from "fs/promises";
|
|
3916
3831
|
import { homedir } from "os";
|
|
3917
|
-
import { join } from "path";
|
|
3832
|
+
import { join as join2 } from "path";
|
|
3918
3833
|
function claudeConfigHome() {
|
|
3919
|
-
return process.env.CLAUDE_CONFIG_DIR ??
|
|
3834
|
+
return process.env.CLAUDE_CONFIG_DIR ?? join2(homedir(), ".claude");
|
|
3920
3835
|
}
|
|
3921
3836
|
function projectSlug(cwd) {
|
|
3922
3837
|
return cwd.replace(/\//g, "-");
|
|
3923
3838
|
}
|
|
3924
3839
|
function sessionTranscriptPath(cwd, sessionId) {
|
|
3925
|
-
return
|
|
3840
|
+
return join2(claudeConfigHome(), "projects", projectSlug(cwd), `${sessionId}.jsonl`);
|
|
3926
3841
|
}
|
|
3927
3842
|
var ALLOW_RULES = [
|
|
3928
3843
|
"Bash",
|
|
@@ -4090,10 +4005,10 @@ function preToolUse(payload) {
|
|
|
4090
4005
|
}
|
|
4091
4006
|
`;
|
|
4092
4007
|
async function writeHookSettings(dir) {
|
|
4093
|
-
const helperPath =
|
|
4094
|
-
const settingsPath =
|
|
4008
|
+
const helperPath = join2(dir, "hook-helper.cjs");
|
|
4009
|
+
const settingsPath = join2(dir, "settings.json");
|
|
4095
4010
|
await mkdir(dir, { recursive: true });
|
|
4096
|
-
await
|
|
4011
|
+
await writeFile(helperPath, HOOK_HELPER_SOURCE, "utf8");
|
|
4097
4012
|
await chmod(helperPath, 493);
|
|
4098
4013
|
const settings = {
|
|
4099
4014
|
// Pre-accept Claude Code's "Bypass Permissions mode" disclaimer. Build-capable
|
|
@@ -4149,7 +4064,7 @@ async function writeHookSettings(dir) {
|
|
|
4149
4064
|
]
|
|
4150
4065
|
}
|
|
4151
4066
|
};
|
|
4152
|
-
await
|
|
4067
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf8");
|
|
4153
4068
|
return { settingsPath, helperPath };
|
|
4154
4069
|
}
|
|
4155
4070
|
|
|
@@ -4208,8 +4123,8 @@ var PtyOutputCoalescer = class {
|
|
|
4208
4123
|
// src/harness/pty/tool-server.ts
|
|
4209
4124
|
import { createServer as createServer2 } from "http";
|
|
4210
4125
|
import { z as z6 } from "zod";
|
|
4211
|
-
import { writeFile as
|
|
4212
|
-
import { join as
|
|
4126
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
4127
|
+
import { join as join3 } from "path";
|
|
4213
4128
|
import { randomBytes } from "crypto";
|
|
4214
4129
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4215
4130
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
@@ -4400,8 +4315,8 @@ async function startToolServers(mcpServers, tempDir) {
|
|
|
4400
4315
|
config[name] = { type: "http", url, headers: { Authorization: `Bearer ${token}` } };
|
|
4401
4316
|
}
|
|
4402
4317
|
if (Object.keys(config).length === 0) return { servers, mcpConfigPath: null };
|
|
4403
|
-
const mcpConfigPath =
|
|
4404
|
-
await
|
|
4318
|
+
const mcpConfigPath = join3(tempDir, "mcp-config.json");
|
|
4319
|
+
await writeFile2(mcpConfigPath, JSON.stringify({ mcpServers: config }, null, 2), "utf8");
|
|
4405
4320
|
return { servers, mcpConfigPath };
|
|
4406
4321
|
}
|
|
4407
4322
|
|
|
@@ -4476,13 +4391,13 @@ ${tail}`);
|
|
|
4476
4391
|
}
|
|
4477
4392
|
|
|
4478
4393
|
// src/harness/pty/credentials.ts
|
|
4479
|
-
import { chmod as chmod2, mkdir as mkdir2, readFile, rm as
|
|
4394
|
+
import { chmod as chmod2, mkdir as mkdir2, readFile, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
4480
4395
|
import { homedir as homedir2 } from "os";
|
|
4481
|
-
import { join as
|
|
4396
|
+
import { join as join4 } from "path";
|
|
4482
4397
|
var SYNTH_TOKEN_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
4483
4398
|
var REFRESH_SKEW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
4484
4399
|
function claudeCredentialsPath() {
|
|
4485
|
-
return
|
|
4400
|
+
return join4(claudeConfigHome(), ".credentials.json");
|
|
4486
4401
|
}
|
|
4487
4402
|
function isConveyorCloudEnv(env = process.env) {
|
|
4488
4403
|
return Boolean(env.CLAUDESPACE_NAME || env.CODESPACE_NAME || env.CODESPACES);
|
|
@@ -4527,9 +4442,9 @@ function planCredentialsWrite(input) {
|
|
|
4527
4442
|
if (fresh) return { action: "skip", reason: "current" };
|
|
4528
4443
|
return { action: "write", contents };
|
|
4529
4444
|
}
|
|
4530
|
-
async function readRaw(
|
|
4445
|
+
async function readRaw(path3) {
|
|
4531
4446
|
try {
|
|
4532
|
-
return await readFile(
|
|
4447
|
+
return await readFile(path3, "utf8");
|
|
4533
4448
|
} catch {
|
|
4534
4449
|
return null;
|
|
4535
4450
|
}
|
|
@@ -4555,10 +4470,10 @@ async function writeWithReadBackRetry(io2, contents, delaysMs = READ_BACK_DELAYS
|
|
|
4555
4470
|
await sleep2(delaysMs[attempt]);
|
|
4556
4471
|
}
|
|
4557
4472
|
}
|
|
4558
|
-
function fsWriteIo(
|
|
4473
|
+
function fsWriteIo(path3, mode) {
|
|
4559
4474
|
return {
|
|
4560
|
-
write: (contents) =>
|
|
4561
|
-
read: () => readRaw(
|
|
4475
|
+
write: (contents) => writeFile3(path3, contents, mode === void 0 ? "utf8" : { encoding: "utf8", mode }),
|
|
4476
|
+
read: () => readRaw(path3)
|
|
4562
4477
|
};
|
|
4563
4478
|
}
|
|
4564
4479
|
async function ensureClaudeCredentials(env = process.env) {
|
|
@@ -4568,23 +4483,23 @@ async function ensureClaudeCredentials(env = process.env) {
|
|
|
4568
4483
|
await sanitizeApprovedApiKeys(token);
|
|
4569
4484
|
}
|
|
4570
4485
|
try {
|
|
4571
|
-
const
|
|
4486
|
+
const path3 = claudeCredentialsPath();
|
|
4572
4487
|
const plan = planCredentialsWrite({
|
|
4573
4488
|
isCloud,
|
|
4574
4489
|
token,
|
|
4575
|
-
existingRaw: await readRaw(
|
|
4490
|
+
existingRaw: await readRaw(path3),
|
|
4576
4491
|
now: Date.now()
|
|
4577
4492
|
});
|
|
4578
4493
|
if (plan.action === "skip") return;
|
|
4579
4494
|
await mkdir2(claudeConfigHome(), { recursive: true });
|
|
4580
|
-
const verified = await writeWithReadBackRetry(fsWriteIo(
|
|
4495
|
+
const verified = await writeWithReadBackRetry(fsWriteIo(path3, 384), plan.contents);
|
|
4581
4496
|
if (!verified) {
|
|
4582
4497
|
process.stderr.write(
|
|
4583
|
-
`[conveyor-agent] claude credentials read-back still stale after retries at ${
|
|
4498
|
+
`[conveyor-agent] claude credentials read-back still stale after retries at ${path3} \u2014 TUI may land on the login picker
|
|
4584
4499
|
`
|
|
4585
4500
|
);
|
|
4586
4501
|
}
|
|
4587
|
-
await chmod2(
|
|
4502
|
+
await chmod2(path3, 384).catch(() => {
|
|
4588
4503
|
});
|
|
4589
4504
|
} catch (err) {
|
|
4590
4505
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -4613,12 +4528,12 @@ function planApprovedApiKeyCleanup(existingRaw, oauthToken) {
|
|
|
4613
4528
|
}
|
|
4614
4529
|
async function sanitizeApprovedApiKeys(oauthToken) {
|
|
4615
4530
|
try {
|
|
4616
|
-
const
|
|
4617
|
-
const cleaned = planApprovedApiKeyCleanup(await readRaw(
|
|
4531
|
+
const path3 = claudeJsonPath();
|
|
4532
|
+
const cleaned = planApprovedApiKeyCleanup(await readRaw(path3), oauthToken);
|
|
4618
4533
|
if (cleaned === null) return;
|
|
4619
|
-
const verified = await writeWithReadBackRetry(fsWriteIo(
|
|
4534
|
+
const verified = await writeWithReadBackRetry(fsWriteIo(path3), cleaned);
|
|
4620
4535
|
process.stderr.write(
|
|
4621
|
-
verified ? "[conveyor-agent] removed poisoned customApiKeyResponses.approved entry from .claude.json\n" : `[conveyor-agent] approved-key sanitize read-back still stale after retries at ${
|
|
4536
|
+
verified ? "[conveyor-agent] removed poisoned customApiKeyResponses.approved entry from .claude.json\n" : `[conveyor-agent] approved-key sanitize read-back still stale after retries at ${path3} \u2014 CLI may still see the poisoned entry
|
|
4622
4537
|
`
|
|
4623
4538
|
);
|
|
4624
4539
|
} catch (err) {
|
|
@@ -4629,7 +4544,7 @@ async function sanitizeApprovedApiKeys(oauthToken) {
|
|
|
4629
4544
|
}
|
|
4630
4545
|
function claudeJsonPath() {
|
|
4631
4546
|
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
4632
|
-
return configDir ?
|
|
4547
|
+
return configDir ? join4(configDir, ".claude.json") : join4(homedir2(), ".claude.json");
|
|
4633
4548
|
}
|
|
4634
4549
|
function asRecord(value) {
|
|
4635
4550
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
@@ -4722,7 +4637,7 @@ function planClaudeJsonSeed(existingRaw, trustCwd, oauthIdentity) {
|
|
|
4722
4637
|
return changed ? JSON.stringify(config) : null;
|
|
4723
4638
|
}
|
|
4724
4639
|
function conveyorOauthMarkerPath() {
|
|
4725
|
-
return
|
|
4640
|
+
return join4(claudeConfigHome(), "conveyor-oauth-account.json");
|
|
4726
4641
|
}
|
|
4727
4642
|
function parseOauthIdentity(raw) {
|
|
4728
4643
|
if (!raw || raw.trim() === "") return null;
|
|
@@ -4755,8 +4670,8 @@ async function persistOauthIdentityMarker(configIdentity, markerIdentity) {
|
|
|
4755
4670
|
async function ensureClaudeOnboarding(env = process.env, trustCwd) {
|
|
4756
4671
|
try {
|
|
4757
4672
|
if (!isConveyorCloudEnv(env)) return;
|
|
4758
|
-
const
|
|
4759
|
-
const existingRaw = await readRaw(
|
|
4673
|
+
const path3 = claudeJsonPath();
|
|
4674
|
+
const existingRaw = await readRaw(path3);
|
|
4760
4675
|
const markerIdentity = parseOauthIdentity(await readRaw(conveyorOauthMarkerPath()));
|
|
4761
4676
|
await persistOauthIdentityMarker(
|
|
4762
4677
|
extractOauthIdentity(parseClaudeJson(existingRaw)),
|
|
@@ -4764,16 +4679,16 @@ async function ensureClaudeOnboarding(env = process.env, trustCwd) {
|
|
|
4764
4679
|
);
|
|
4765
4680
|
const contents = planClaudeJsonSeed(existingRaw, trustCwd, markerIdentity);
|
|
4766
4681
|
if (contents === null) return;
|
|
4767
|
-
const verified = await writeWithReadBackRetry(fsWriteIo(
|
|
4682
|
+
const verified = await writeWithReadBackRetry(fsWriteIo(path3), contents);
|
|
4768
4683
|
if (verified) {
|
|
4769
4684
|
process.stderr.write(
|
|
4770
4685
|
`[conveyor-agent] claude onboarding seeded${trustCwd ? ` (trust: ${trustCwd})` : ""}
|
|
4771
4686
|
`
|
|
4772
4687
|
);
|
|
4773
4688
|
} else {
|
|
4774
|
-
const verify = await readRaw(
|
|
4689
|
+
const verify = await readRaw(path3);
|
|
4775
4690
|
process.stderr.write(
|
|
4776
|
-
`[conveyor-agent] claude onboarding seed read-back MISMATCH at ${
|
|
4691
|
+
`[conveyor-agent] claude onboarding seed read-back MISMATCH at ${path3} after retries: wrote ${contents.length}B, read ${verify?.length ?? 0}B \u2014 CLI may see stale config and park at a startup dialog
|
|
4777
4692
|
`
|
|
4778
4693
|
);
|
|
4779
4694
|
}
|
|
@@ -4786,12 +4701,12 @@ async function ensureClaudeOnboarding(env = process.env, trustCwd) {
|
|
|
4786
4701
|
async function removeConveyorCredentials(env = process.env) {
|
|
4787
4702
|
try {
|
|
4788
4703
|
if (!isConveyorCloudEnv(env)) return;
|
|
4789
|
-
const
|
|
4790
|
-
const existing = parseClaudeAiOauth(await readRaw(
|
|
4704
|
+
const path3 = claudeCredentialsPath();
|
|
4705
|
+
const existing = parseClaudeAiOauth(await readRaw(path3));
|
|
4791
4706
|
if (existing && typeof existing.refreshToken === "string" && existing.refreshToken.length > 0) {
|
|
4792
4707
|
return;
|
|
4793
4708
|
}
|
|
4794
|
-
await
|
|
4709
|
+
await rm3(path3, { force: true });
|
|
4795
4710
|
} catch (err) {
|
|
4796
4711
|
const message = err instanceof Error ? err.message : String(err);
|
|
4797
4712
|
process.stderr.write(`[conveyor-agent] claude credentials removal failed: ${message}
|
|
@@ -4799,126 +4714,6 @@ async function removeConveyorCredentials(env = process.env) {
|
|
|
4799
4714
|
}
|
|
4800
4715
|
}
|
|
4801
4716
|
|
|
4802
|
-
// src/harness/pty/pty-support.ts
|
|
4803
|
-
import { stat as stat2 } from "fs/promises";
|
|
4804
|
-
var MAX_DIAGNOSTIC_OUTPUT = 4e3;
|
|
4805
|
-
var MAX_BETWEEN_TURN_BUFFER = 500;
|
|
4806
|
-
var SUBMIT_SETTLE_MS = 300;
|
|
4807
|
-
var SUBMIT_NUDGE_INTERVAL_MS = 2e3;
|
|
4808
|
-
var SUBMIT_NUDGE_MAX_PRESSES = 5;
|
|
4809
|
-
var SUBMIT_NUDGE_SLOW_INTERVAL_MS = 5e3;
|
|
4810
|
-
var SUBMIT_NUDGE_WINDOW_MS = 9e4;
|
|
4811
|
-
var PLAN_DIALOG_FIRST_PRESS_MS = 700;
|
|
4812
|
-
var PLAN_DIALOG_INTERVAL_MS = 1500;
|
|
4813
|
-
var PLAN_DIALOG_SLOW_INTERVAL_MS = 5e3;
|
|
4814
|
-
var PLAN_DIALOG_FAST_WINDOW_MS = 1e4;
|
|
4815
|
-
var PLAN_DIALOG_WINDOW_MS = 9e4;
|
|
4816
|
-
function envMs(name, fallback) {
|
|
4817
|
-
const raw = Number(process.env[name]);
|
|
4818
|
-
return Number.isFinite(raw) && raw > 0 ? raw : fallback;
|
|
4819
|
-
}
|
|
4820
|
-
function resolveSubmitSettleMs() {
|
|
4821
|
-
return envMs("CONVEYOR_PTY_SUBMIT_SETTLE_MS", SUBMIT_SETTLE_MS);
|
|
4822
|
-
}
|
|
4823
|
-
function resolveSubmitNudgeTiming() {
|
|
4824
|
-
return {
|
|
4825
|
-
intervalMs: envMs("CONVEYOR_PTY_NUDGE_INTERVAL_MS", SUBMIT_NUDGE_INTERVAL_MS),
|
|
4826
|
-
slowIntervalMs: envMs("CONVEYOR_PTY_NUDGE_SLOW_INTERVAL_MS", SUBMIT_NUDGE_SLOW_INTERVAL_MS),
|
|
4827
|
-
maxPresses: SUBMIT_NUDGE_MAX_PRESSES,
|
|
4828
|
-
windowMs: envMs("CONVEYOR_PTY_NUDGE_WINDOW_MS", SUBMIT_NUDGE_WINDOW_MS)
|
|
4829
|
-
};
|
|
4830
|
-
}
|
|
4831
|
-
function resolvePlanDialogTiming() {
|
|
4832
|
-
return {
|
|
4833
|
-
firstPressMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FIRST_PRESS_MS", PLAN_DIALOG_FIRST_PRESS_MS),
|
|
4834
|
-
intervalMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_INTERVAL_MS", PLAN_DIALOG_INTERVAL_MS),
|
|
4835
|
-
slowIntervalMs: envMs(
|
|
4836
|
-
"CONVEYOR_PTY_PLAN_DIALOG_SLOW_INTERVAL_MS",
|
|
4837
|
-
PLAN_DIALOG_SLOW_INTERVAL_MS
|
|
4838
|
-
),
|
|
4839
|
-
fastWindowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FAST_WINDOW_MS", PLAN_DIALOG_FAST_WINDOW_MS),
|
|
4840
|
-
windowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_WINDOW_MS", PLAN_DIALOG_WINDOW_MS)
|
|
4841
|
-
};
|
|
4842
|
-
}
|
|
4843
|
-
function turnOptionsFrom(options) {
|
|
4844
|
-
return {
|
|
4845
|
-
canUseTool: options.canUseTool,
|
|
4846
|
-
promptDelivery: options.promptDelivery,
|
|
4847
|
-
planDialogAutoAccept: options.planDialogAutoAccept,
|
|
4848
|
-
abortController: options.abortController
|
|
4849
|
-
};
|
|
4850
|
-
}
|
|
4851
|
-
function isRecord3(value) {
|
|
4852
|
-
return typeof value === "object" && value !== null;
|
|
4853
|
-
}
|
|
4854
|
-
function extractSpawn(mod) {
|
|
4855
|
-
if (!isRecord3(mod)) return null;
|
|
4856
|
-
if (typeof mod.spawn === "function") return mod.spawn;
|
|
4857
|
-
const def = mod.default;
|
|
4858
|
-
if (isRecord3(def) && typeof def.spawn === "function") return def.spawn;
|
|
4859
|
-
return null;
|
|
4860
|
-
}
|
|
4861
|
-
async function loadPtySpawn() {
|
|
4862
|
-
const mod = await import("node-pty");
|
|
4863
|
-
const spawn2 = extractSpawn(mod);
|
|
4864
|
-
if (!spawn2) throw new Error("node-pty: spawn export not found");
|
|
4865
|
-
return spawn2;
|
|
4866
|
-
}
|
|
4867
|
-
function inheritedEnv(socketPath) {
|
|
4868
|
-
const env = {};
|
|
4869
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
4870
|
-
if (typeof value === "string") env[key] = value;
|
|
4871
|
-
}
|
|
4872
|
-
if (env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
4873
|
-
delete env.ANTHROPIC_API_KEY;
|
|
4874
|
-
}
|
|
4875
|
-
if (socketPath) {
|
|
4876
|
-
env.CONVEYOR_HOOK_SOCKET = socketPath;
|
|
4877
|
-
}
|
|
4878
|
-
env.MCP_TIMEOUT ??= "60000";
|
|
4879
|
-
env.MCP_TOOL_TIMEOUT ??= "180000";
|
|
4880
|
-
return env;
|
|
4881
|
-
}
|
|
4882
|
-
function buildPromptBytes(text) {
|
|
4883
|
-
return `\x1B[200~${text}\x1B[201~`;
|
|
4884
|
-
}
|
|
4885
|
-
function renderPromptContentText(content) {
|
|
4886
|
-
return content.map((block) => {
|
|
4887
|
-
const b = block;
|
|
4888
|
-
if (b?.type === "text" && typeof b.text === "string") return b.text;
|
|
4889
|
-
if (b?.type === "image") {
|
|
4890
|
-
return `[Image attachment \u2014 use list_task_files / get_attachment to view]`;
|
|
4891
|
-
}
|
|
4892
|
-
return JSON.stringify(block);
|
|
4893
|
-
}).join("\n\n");
|
|
4894
|
-
}
|
|
4895
|
-
async function transcriptSize(path4) {
|
|
4896
|
-
try {
|
|
4897
|
-
return (await stat2(path4)).size;
|
|
4898
|
-
} catch {
|
|
4899
|
-
return 0;
|
|
4900
|
-
}
|
|
4901
|
-
}
|
|
4902
|
-
function parseUserQuestions(input) {
|
|
4903
|
-
if (!Array.isArray(input.questions)) return [];
|
|
4904
|
-
const questions = [];
|
|
4905
|
-
for (const entry of input.questions) {
|
|
4906
|
-
if (!isRecord3(entry)) continue;
|
|
4907
|
-
if (typeof entry.question !== "string") continue;
|
|
4908
|
-
const options = Array.isArray(entry.options) ? entry.options.filter(isRecord3).filter((o) => typeof o.label === "string").map((o) => ({
|
|
4909
|
-
label: o.label,
|
|
4910
|
-
description: typeof o.description === "string" ? o.description : ""
|
|
4911
|
-
})) : [];
|
|
4912
|
-
questions.push({
|
|
4913
|
-
question: entry.question,
|
|
4914
|
-
header: typeof entry.header === "string" ? entry.header : "",
|
|
4915
|
-
options,
|
|
4916
|
-
...typeof entry.multiSelect === "boolean" ? { multiSelect: entry.multiSelect } : {}
|
|
4917
|
-
});
|
|
4918
|
-
}
|
|
4919
|
-
return questions;
|
|
4920
|
-
}
|
|
4921
|
-
|
|
4922
4717
|
// src/harness/pty/adapters/claude.ts
|
|
4923
4718
|
var ClaudeTuiAdapter = class {
|
|
4924
4719
|
id = "claude-code";
|
|
@@ -5029,6 +4824,18 @@ var PtySession = class {
|
|
|
5029
4824
|
// Submit-nudge state (see armSubmitNudge).
|
|
5030
4825
|
pendingSubmitNudge = false;
|
|
5031
4826
|
submitNudgeTimer = null;
|
|
4827
|
+
// Synthetic AskUserQuestion chat-card state. The CLI does NOT flush the
|
|
4828
|
+
// assistant record holding a pending AskUserQuestion tool_use to the
|
|
4829
|
+
// transcript until the questionnaire resolves (verified live on CLI 2.1.209:
|
|
4830
|
+
// dialog parked on screen, transcript untouched) — so a transcript-derived
|
|
4831
|
+
// question card could only ever render AFTER the human answered in the raw
|
|
4832
|
+
// terminal. Instead the PreToolUse hook (which fires at ask time and carries
|
|
4833
|
+
// the full questions input) emits a synthetic `tool_use` chat event under an
|
|
4834
|
+
// `aq-…` id. When the real records eventually flush, the duplicate tool_use
|
|
4835
|
+
// is dropped (FIFO match below) and its tool_result is re-pointed at the
|
|
4836
|
+
// synthetic id so the card flips to answered.
|
|
4837
|
+
pendingSyntheticQuestionIds = [];
|
|
4838
|
+
questionResultRemap = /* @__PURE__ */ new Map();
|
|
5032
4839
|
// Per-turn state: the prompt to feed and the per-turn options subset. Both
|
|
5033
4840
|
// start from the constructor args (turn 1) and are replaced by beginTurn.
|
|
5034
4841
|
turnPrompt;
|
|
@@ -5150,6 +4957,7 @@ var PtySession = class {
|
|
|
5150
4957
|
this.passiveSignaled = false;
|
|
5151
4958
|
this.disarmSubmitNudge();
|
|
5152
4959
|
this.disarmPlanDialogAutoAccept();
|
|
4960
|
+
this.closeSyntheticQuestionCards();
|
|
5153
4961
|
}
|
|
5154
4962
|
/**
|
|
5155
4963
|
* (Re)register the abort→teardown listener on the current turn's controller,
|
|
@@ -5181,6 +4989,7 @@ var PtySession = class {
|
|
|
5181
4989
|
*/
|
|
5182
4990
|
endTurn(clean) {
|
|
5183
4991
|
this.lastTurnCleanResult = clean;
|
|
4992
|
+
this.closeSyntheticQuestionCards();
|
|
5184
4993
|
this.activeQueue?.close();
|
|
5185
4994
|
this.activeQueue = null;
|
|
5186
4995
|
if (this.abortHandler && this.turn.abortController) {
|
|
@@ -5205,7 +5014,7 @@ var PtySession = class {
|
|
|
5205
5014
|
const { settingsPath, socketPath } = await this.startStructuredEventSources(sessionId);
|
|
5206
5015
|
await this.spawn(settingsPath, socketPath);
|
|
5207
5016
|
} else {
|
|
5208
|
-
this.tempDir = await mkdtemp2(
|
|
5017
|
+
this.tempDir = await mkdtemp2(join5(sessionTempBase(), "conveyor-pty-"));
|
|
5209
5018
|
await this.spawn();
|
|
5210
5019
|
this.pushEvent({
|
|
5211
5020
|
type: "system",
|
|
@@ -5242,8 +5051,8 @@ var PtySession = class {
|
|
|
5242
5051
|
* paths spawn() must wire into the child's argv/env.
|
|
5243
5052
|
*/
|
|
5244
5053
|
async startStructuredEventSources(sessionId) {
|
|
5245
|
-
this.tempDir = await mkdtemp2(
|
|
5246
|
-
const socketPath =
|
|
5054
|
+
this.tempDir = await mkdtemp2(join5(sessionTempBase(), "conveyor-pty-"));
|
|
5055
|
+
const socketPath = join5(this.tempDir, "hook.sock");
|
|
5247
5056
|
this.socket = new HookSocketServer(
|
|
5248
5057
|
socketPath,
|
|
5249
5058
|
(progress) => this.handleProgress(progress),
|
|
@@ -5255,17 +5064,72 @@ var PtySession = class {
|
|
|
5255
5064
|
const transcriptPath = sessionTranscriptPath(this.options.cwd, sessionId);
|
|
5256
5065
|
await mkdir3(dirname(transcriptPath), { recursive: true });
|
|
5257
5066
|
const startOffset = this.resume ? await transcriptSize(transcriptPath) : 0;
|
|
5258
|
-
const sendChat = this.bridge?.sendChatEvent?.bind(this.bridge);
|
|
5259
5067
|
this.tailer = new JsonlTailer(
|
|
5260
5068
|
transcriptPath,
|
|
5261
5069
|
(event) => this.handleTranscriptEvent(event),
|
|
5262
|
-
|
|
5263
|
-
for (const chatEvent of mapChatRecords(raw)) sendChat(chatEvent);
|
|
5264
|
-
} : void 0
|
|
5070
|
+
typeof this.bridge?.sendChatEvent === "function" ? (raw) => this.relayChatRecord(raw) : void 0
|
|
5265
5071
|
);
|
|
5266
5072
|
this.tailer.start(startOffset);
|
|
5267
5073
|
return { settingsPath, socketPath };
|
|
5268
5074
|
}
|
|
5075
|
+
/**
|
|
5076
|
+
* Project a tailed transcript record to chat events, reconciling them with
|
|
5077
|
+
* any synthetic question card already emitted at hook time: the flushed
|
|
5078
|
+
* AskUserQuestion `tool_use` duplicate is dropped (its real id remembered),
|
|
5079
|
+
* and the paired `tool_result` is re-pointed at the synthetic id so the
|
|
5080
|
+
* live-rendered card is the one that flips to answered.
|
|
5081
|
+
*/
|
|
5082
|
+
relayChatRecord(raw) {
|
|
5083
|
+
for (const event of mapChatRecords(raw)) {
|
|
5084
|
+
if (event.kind === "tool_use" && event.name === "AskUserQuestion") {
|
|
5085
|
+
const syntheticId = this.pendingSyntheticQuestionIds.shift();
|
|
5086
|
+
if (syntheticId) {
|
|
5087
|
+
if (event.id) this.questionResultRemap.set(event.id, syntheticId);
|
|
5088
|
+
continue;
|
|
5089
|
+
}
|
|
5090
|
+
} else if (event.kind === "tool_result" && event.toolUseId) {
|
|
5091
|
+
const syntheticId = this.questionResultRemap.get(event.toolUseId);
|
|
5092
|
+
if (syntheticId) {
|
|
5093
|
+
this.questionResultRemap.delete(event.toolUseId);
|
|
5094
|
+
this.sendChatEvent({ ...event, toolUseId: syntheticId });
|
|
5095
|
+
continue;
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
this.sendChatEvent(event);
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
sendChatEvent(event) {
|
|
5102
|
+
this.bridge?.sendChatEvent?.(event);
|
|
5103
|
+
}
|
|
5104
|
+
/** Render the question card in the web chat NOW — at hook time — instead of
|
|
5105
|
+
* whenever the CLI flushes the transcript records (which is only after the
|
|
5106
|
+
* questionnaire resolves; see the field comment). */
|
|
5107
|
+
emitSyntheticQuestionCard(questions) {
|
|
5108
|
+
if (questions.length === 0 || typeof this.bridge?.sendChatEvent !== "function") return;
|
|
5109
|
+
const id = `aq-${randomUUID2()}`;
|
|
5110
|
+
this.pendingSyntheticQuestionIds.push(id);
|
|
5111
|
+
this.sendChatEvent({
|
|
5112
|
+
kind: "tool_use",
|
|
5113
|
+
name: "AskUserQuestion",
|
|
5114
|
+
input: compactQuestionsJson(questions),
|
|
5115
|
+
id
|
|
5116
|
+
});
|
|
5117
|
+
}
|
|
5118
|
+
/**
|
|
5119
|
+
* Close any still-open synthetic question cards. The questionnaire can only
|
|
5120
|
+
* outlive its card via a path that never flushes the paired records — Esc /
|
|
5121
|
+
* interrupt, a superseding turn, or process teardown — so an answering
|
|
5122
|
+
* tool_result will never arrive for these ids; emit one so the web card
|
|
5123
|
+
* stops soliciting input for a dialog that no longer exists.
|
|
5124
|
+
*/
|
|
5125
|
+
closeSyntheticQuestionCards() {
|
|
5126
|
+
const orphaned = [...this.pendingSyntheticQuestionIds, ...this.questionResultRemap.values()];
|
|
5127
|
+
this.pendingSyntheticQuestionIds = [];
|
|
5128
|
+
this.questionResultRemap.clear();
|
|
5129
|
+
for (const toolUseId of orphaned) {
|
|
5130
|
+
this.sendChatEvent({ kind: "tool_result", toolUseId, output: "", isError: false });
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5269
5133
|
writeStdin(text) {
|
|
5270
5134
|
this.pty?.write(text);
|
|
5271
5135
|
}
|
|
@@ -5299,6 +5163,7 @@ var PtySession = class {
|
|
|
5299
5163
|
this._toreDown = true;
|
|
5300
5164
|
this.disarmPlanDialogAutoAccept();
|
|
5301
5165
|
this.disarmSubmitNudge();
|
|
5166
|
+
this.closeSyntheticQuestionCards();
|
|
5302
5167
|
this.unsubInput?.();
|
|
5303
5168
|
this.unsubInput = null;
|
|
5304
5169
|
this.unsubResize?.();
|
|
@@ -5331,7 +5196,7 @@ var PtySession = class {
|
|
|
5331
5196
|
this.activeQueue?.close();
|
|
5332
5197
|
this.activeQueue = null;
|
|
5333
5198
|
if (this.tempDir) {
|
|
5334
|
-
await
|
|
5199
|
+
await rm4(this.tempDir, { recursive: true, force: true });
|
|
5335
5200
|
this.tempDir = "";
|
|
5336
5201
|
}
|
|
5337
5202
|
}
|
|
@@ -5360,8 +5225,8 @@ var PtySession = class {
|
|
|
5360
5225
|
// server doesn't load).
|
|
5361
5226
|
...this.mcpConfigPath ? { mcpConfigPath: this.mcpConfigPath } : {}
|
|
5362
5227
|
});
|
|
5363
|
-
const
|
|
5364
|
-
const pty =
|
|
5228
|
+
const spawn = await resolvePtySpawn();
|
|
5229
|
+
const pty = spawn(spec.file, spec.args, {
|
|
5365
5230
|
name: "xterm-color",
|
|
5366
5231
|
cols: this.cols,
|
|
5367
5232
|
rows: this.rows,
|
|
@@ -5469,10 +5334,9 @@ var PtySession = class {
|
|
|
5469
5334
|
if (request.tool_name === "AskUserQuestion") {
|
|
5470
5335
|
this.disarmSubmitNudge();
|
|
5471
5336
|
this.disarmPlanDialogAutoAccept();
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
});
|
|
5337
|
+
const questions = parseUserQuestions(request.tool_input);
|
|
5338
|
+
this.pushEvent({ type: "user_question", questions });
|
|
5339
|
+
this.emitSyntheticQuestionCard(questions);
|
|
5476
5340
|
return { decision: "allow" };
|
|
5477
5341
|
}
|
|
5478
5342
|
const canUseTool = this.turn.canUseTool;
|
|
@@ -5608,7 +5472,7 @@ var PtySession = class {
|
|
|
5608
5472
|
// src/harness/pty/config-home-health.ts
|
|
5609
5473
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
5610
5474
|
import { homedir as homedir3 } from "os";
|
|
5611
|
-
import { join as
|
|
5475
|
+
import { join as join6 } from "path";
|
|
5612
5476
|
var MOUNT_DISCONNECT_CODES = /* @__PURE__ */ new Set(["ENOTCONN", "EIO", "ESTALE", "ENXIO"]);
|
|
5613
5477
|
var MOUNT_DISCONNECT_MESSAGES = [
|
|
5614
5478
|
"socket is not connected",
|
|
@@ -5624,12 +5488,12 @@ function isMountDisconnectError(err) {
|
|
|
5624
5488
|
return MOUNT_DISCONNECT_MESSAGES.some((needle) => lower.includes(needle));
|
|
5625
5489
|
}
|
|
5626
5490
|
function podLocalConfigHome() {
|
|
5627
|
-
return
|
|
5491
|
+
return join6(homedir3(), ".claude-local");
|
|
5628
5492
|
}
|
|
5629
5493
|
async function ensureUsableClaudeConfigHome(cwd, log) {
|
|
5630
5494
|
const configHome = claudeConfigHome();
|
|
5631
5495
|
try {
|
|
5632
|
-
await mkdir4(
|
|
5496
|
+
await mkdir4(join6(configHome, "projects", projectSlug(cwd)), { recursive: true });
|
|
5633
5497
|
return { configHome, fellBack: false };
|
|
5634
5498
|
} catch (err) {
|
|
5635
5499
|
if (!isMountDisconnectError(err)) throw err;
|
|
@@ -5643,7 +5507,7 @@ async function ensureUsableClaudeConfigHome(cwd, log) {
|
|
|
5643
5507
|
}
|
|
5644
5508
|
);
|
|
5645
5509
|
process.env.CLAUDE_CONFIG_DIR = fallback;
|
|
5646
|
-
await mkdir4(
|
|
5510
|
+
await mkdir4(join6(fallback, "projects", projectSlug(cwd)), { recursive: true });
|
|
5647
5511
|
return { configHome: fallback, fellBack: true };
|
|
5648
5512
|
}
|
|
5649
5513
|
}
|
|
@@ -6089,8 +5953,38 @@ function formatIncidents(incidents) {
|
|
|
6089
5953
|
return parts;
|
|
6090
5954
|
}
|
|
6091
5955
|
|
|
5956
|
+
// src/workbench/fs.ts
|
|
5957
|
+
import { readFile as localReadFile, readdir as localReaddir, stat as localStat } from "fs/promises";
|
|
5958
|
+
async function readWorkspaceFile(path3) {
|
|
5959
|
+
if (workbenchEnabled()) {
|
|
5960
|
+
return (await getWorkbenchClient().readFile(path3)).toString("utf8");
|
|
5961
|
+
}
|
|
5962
|
+
return localReadFile(path3, "utf-8");
|
|
5963
|
+
}
|
|
5964
|
+
function readWorkspaceDir(path3) {
|
|
5965
|
+
if (workbenchEnabled()) return getWorkbenchClient().readdir(path3);
|
|
5966
|
+
return localReaddir(path3);
|
|
5967
|
+
}
|
|
5968
|
+
async function statWorkspacePath(path3) {
|
|
5969
|
+
if (workbenchEnabled()) return getWorkbenchClient().stat(path3);
|
|
5970
|
+
try {
|
|
5971
|
+
const s = await localStat(path3);
|
|
5972
|
+
return {
|
|
5973
|
+
exists: true,
|
|
5974
|
+
isFile: s.isFile(),
|
|
5975
|
+
isDirectory: s.isDirectory(),
|
|
5976
|
+
size: s.size,
|
|
5977
|
+
mtimeMs: s.mtimeMs
|
|
5978
|
+
};
|
|
5979
|
+
} catch {
|
|
5980
|
+
return { exists: false, isFile: false, isDirectory: false, size: 0, mtimeMs: 0 };
|
|
5981
|
+
}
|
|
5982
|
+
}
|
|
5983
|
+
async function workspacePathExists(path3) {
|
|
5984
|
+
return (await statWorkspacePath(path3)).exists;
|
|
5985
|
+
}
|
|
5986
|
+
|
|
6092
5987
|
// src/execution/tag-context-resolver.ts
|
|
6093
|
-
import { readFile as readFile2, readdir, stat as stat3 } from "fs/promises";
|
|
6094
5988
|
var TYPE_PRIORITY = { rule: 0, file: 1, folder: 2, doc: 3 };
|
|
6095
5989
|
var SUMMARY_SCAN_CHARS = 4e3;
|
|
6096
5990
|
var SUMMARY_MAX_CHARS = 160;
|
|
@@ -6162,18 +6056,14 @@ function truncateSummary(text) {
|
|
|
6162
6056
|
async function readFileSummary(filePath) {
|
|
6163
6057
|
try {
|
|
6164
6058
|
if (isBinaryPath(filePath)) return null;
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
mtimeMs = st.mtimeMs;
|
|
6169
|
-
} catch {
|
|
6170
|
-
return null;
|
|
6171
|
-
}
|
|
6059
|
+
const st = await statWorkspacePath(filePath);
|
|
6060
|
+
if (!st.exists) return null;
|
|
6061
|
+
const mtimeMs = st.mtimeMs;
|
|
6172
6062
|
const cached = fileSummaryCache.get(filePath);
|
|
6173
6063
|
if (cached && cached.mtimeMs === mtimeMs) {
|
|
6174
6064
|
return cached.summary;
|
|
6175
6065
|
}
|
|
6176
|
-
const raw = await
|
|
6066
|
+
const raw = await readWorkspaceFile(filePath);
|
|
6177
6067
|
fileReadCount++;
|
|
6178
6068
|
const summary = deriveSummary(raw.slice(0, SUMMARY_SCAN_CHARS));
|
|
6179
6069
|
fileSummaryCache.set(filePath, { mtimeMs, summary });
|
|
@@ -6184,18 +6074,14 @@ async function readFileSummary(filePath) {
|
|
|
6184
6074
|
}
|
|
6185
6075
|
async function readFolderListing(folderPath) {
|
|
6186
6076
|
try {
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
mtimeMs = st.mtimeMs;
|
|
6191
|
-
} catch {
|
|
6192
|
-
return null;
|
|
6193
|
-
}
|
|
6077
|
+
const st = await statWorkspacePath(folderPath);
|
|
6078
|
+
if (!st.exists) return null;
|
|
6079
|
+
const mtimeMs = st.mtimeMs;
|
|
6194
6080
|
const cached = folderListingCache.get(folderPath);
|
|
6195
6081
|
if (cached && cached.mtimeMs === mtimeMs) {
|
|
6196
6082
|
return cached.listing;
|
|
6197
6083
|
}
|
|
6198
|
-
const entries = await
|
|
6084
|
+
const entries = await readWorkspaceDir(folderPath);
|
|
6199
6085
|
folderReadCount++;
|
|
6200
6086
|
const listing = `Files: ${entries.join(", ")}`;
|
|
6201
6087
|
folderListingCache.set(folderPath, { mtimeMs, listing });
|
|
@@ -7582,7 +7468,16 @@ function buildForceUpdateTaskStatusTool(connection) {
|
|
|
7582
7468
|
"force_update_task_status",
|
|
7583
7469
|
"EMERGENCY ONLY: force-override a task's Kanban status. Use when an automatic transition failed and the task is wedged. Normal flow transitions status automatically.",
|
|
7584
7470
|
{
|
|
7585
|
-
status: z9.enum([
|
|
7471
|
+
status: z9.enum([
|
|
7472
|
+
"Planning",
|
|
7473
|
+
"Open",
|
|
7474
|
+
"InProgress",
|
|
7475
|
+
"ReviewPR",
|
|
7476
|
+
"ReviewDev",
|
|
7477
|
+
"ReviewLive",
|
|
7478
|
+
"Complete",
|
|
7479
|
+
"Cancelled"
|
|
7480
|
+
]).describe("The new status for the task"),
|
|
7586
7481
|
task_id: z9.string().optional().describe("Child task ID to update. Omit to update the current task.")
|
|
7587
7482
|
},
|
|
7588
7483
|
async ({ status, task_id }) => {
|
|
@@ -7846,8 +7741,8 @@ function buildMutationTools(connection, config) {
|
|
|
7846
7741
|
}
|
|
7847
7742
|
|
|
7848
7743
|
// src/tools/attachment-tools.ts
|
|
7849
|
-
import { readFile as
|
|
7850
|
-
import { basename, extname, isAbsolute, join as
|
|
7744
|
+
import { readFile as readFile2, stat } from "fs/promises";
|
|
7745
|
+
import { basename, extname, isAbsolute, join as join7 } from "path";
|
|
7851
7746
|
import { z as z10 } from "zod";
|
|
7852
7747
|
var IMAGE_MIME_BY_EXT = {
|
|
7853
7748
|
".png": "image/png",
|
|
@@ -7864,16 +7759,16 @@ function buildUploadAttachmentTool(connection, config) {
|
|
|
7864
7759
|
path: z10.string().describe("Path to the image file \u2014 absolute, or relative to the workspace root"),
|
|
7865
7760
|
title: z10.string().optional().describe("Short caption posted with the image (defaults to the file name)")
|
|
7866
7761
|
},
|
|
7867
|
-
async ({ path:
|
|
7762
|
+
async ({ path: path3, title }) => {
|
|
7868
7763
|
try {
|
|
7869
|
-
const filePath = isAbsolute(
|
|
7764
|
+
const filePath = isAbsolute(path3) ? path3 : join7(config.workspaceDir, path3);
|
|
7870
7765
|
const mimeType = IMAGE_MIME_BY_EXT[extname(filePath).toLowerCase()];
|
|
7871
7766
|
if (!mimeType) {
|
|
7872
7767
|
return textResult(
|
|
7873
7768
|
`Unsupported file type "${extname(filePath) || "(none)"}". Supported: ${Object.keys(IMAGE_MIME_BY_EXT).join(", ")}`
|
|
7874
7769
|
);
|
|
7875
7770
|
}
|
|
7876
|
-
const info = await
|
|
7771
|
+
const info = await stat(filePath).catch(() => null);
|
|
7877
7772
|
if (!info?.isFile()) {
|
|
7878
7773
|
return textResult(`File not found: ${filePath}`);
|
|
7879
7774
|
}
|
|
@@ -7889,7 +7784,7 @@ function buildUploadAttachmentTool(connection, config) {
|
|
|
7889
7784
|
mimeType,
|
|
7890
7785
|
fileSize: info.size
|
|
7891
7786
|
});
|
|
7892
|
-
const body = await
|
|
7787
|
+
const body = await readFile2(filePath);
|
|
7893
7788
|
const res = await fetch(uploadUrl, {
|
|
7894
7789
|
method: "PUT",
|
|
7895
7790
|
headers: { "Content-Type": mimeType },
|
|
@@ -8399,42 +8294,52 @@ function buildPmTools(connection, options) {
|
|
|
8399
8294
|
// src/tools/discovery-tools.ts
|
|
8400
8295
|
import { z as z13 } from "zod";
|
|
8401
8296
|
var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique). The key is 'storyPointValue' \u2014 not 'storyPoints'.";
|
|
8402
|
-
var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch";
|
|
8297
|
+
var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk";
|
|
8298
|
+
function describeUpdatedFields(p) {
|
|
8299
|
+
const fields = [];
|
|
8300
|
+
if (p.title !== void 0) fields.push(`title to "${p.title}"`);
|
|
8301
|
+
if (p.storyPointValue !== void 0) fields.push(`story points to ${p.storyPointValue}`);
|
|
8302
|
+
if (p.tagNames !== void 0) fields.push(`tags (${p.tagNames.length} tag(s))`);
|
|
8303
|
+
if (p.githubPRUrl !== void 0) fields.push(`PR link to "${p.githubPRUrl}"`);
|
|
8304
|
+
if (p.githubBranch !== void 0) fields.push(`branch to "${p.githubBranch}"`);
|
|
8305
|
+
if (p.risk !== void 0) fields.push(`risk to ${p.risk ?? "cleared"}`);
|
|
8306
|
+
return fields;
|
|
8307
|
+
}
|
|
8403
8308
|
function buildDiscoveryTools(connection) {
|
|
8404
8309
|
return [
|
|
8405
8310
|
defineTool(
|
|
8406
8311
|
"update_task_properties",
|
|
8407
|
-
"Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
|
|
8312
|
+
"Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
|
|
8408
8313
|
{
|
|
8409
8314
|
title: z13.string().optional().describe("The new task title"),
|
|
8410
8315
|
storyPointValue: z13.number().optional().describe(SP_DESCRIPTION2),
|
|
8411
8316
|
tagNames: z13.array(z13.string()).optional().describe("Array of tag names to assign"),
|
|
8412
8317
|
githubPRUrl: z13.string().url().optional().describe("GitHub pull request URL to link to this task"),
|
|
8413
|
-
githubBranch: z13.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')")
|
|
8318
|
+
githubBranch: z13.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
|
|
8319
|
+
risk: z13.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
|
|
8320
|
+
"Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
|
|
8321
|
+
)
|
|
8414
8322
|
},
|
|
8415
|
-
async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch }) => {
|
|
8323
|
+
async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk }) => {
|
|
8416
8324
|
try {
|
|
8417
|
-
const
|
|
8418
|
-
|
|
8325
|
+
const params = {
|
|
8326
|
+
title,
|
|
8327
|
+
storyPointValue,
|
|
8328
|
+
tagNames,
|
|
8329
|
+
githubPRUrl,
|
|
8330
|
+
githubBranch,
|
|
8331
|
+
risk
|
|
8332
|
+
};
|
|
8333
|
+
const updatedFields = describeUpdatedFields(params);
|
|
8334
|
+
if (updatedFields.length === 0) {
|
|
8419
8335
|
return textResult(
|
|
8420
8336
|
`No task properties were updated: none of the recognized keys were provided. Valid keys: ${VALID_PROPERTY_KEYS}. (Story points are set via 'storyPointValue', not 'storyPoints'.)`
|
|
8421
8337
|
);
|
|
8422
8338
|
}
|
|
8423
8339
|
await connection.call("updateTaskProperties", {
|
|
8424
8340
|
sessionId: connection.sessionId,
|
|
8425
|
-
|
|
8426
|
-
storyPointValue,
|
|
8427
|
-
tagNames,
|
|
8428
|
-
githubPRUrl,
|
|
8429
|
-
githubBranch
|
|
8341
|
+
...params
|
|
8430
8342
|
});
|
|
8431
|
-
const updatedFields = [];
|
|
8432
|
-
if (title !== void 0) updatedFields.push(`title to "${title}"`);
|
|
8433
|
-
if (storyPointValue !== void 0)
|
|
8434
|
-
updatedFields.push(`story points to ${storyPointValue}`);
|
|
8435
|
-
if (tagNames !== void 0) updatedFields.push(`tags (${tagNames.length} tag(s))`);
|
|
8436
|
-
if (githubPRUrl !== void 0) updatedFields.push(`PR link to "${githubPRUrl}"`);
|
|
8437
|
-
if (githubBranch !== void 0) updatedFields.push(`branch to "${githubBranch}"`);
|
|
8438
8343
|
return textResult(`Task properties updated: ${updatedFields.join(", ")}`);
|
|
8439
8344
|
} catch (error) {
|
|
8440
8345
|
return textResult(
|
|
@@ -8618,7 +8523,7 @@ function createConveyorMcpServer(harness, connection, config, context, agentMode
|
|
|
8618
8523
|
|
|
8619
8524
|
// src/harness/pty/adapters/types.ts
|
|
8620
8525
|
import { accessSync, constants, statSync } from "fs";
|
|
8621
|
-
import { join as
|
|
8526
|
+
import { join as join8 } from "path";
|
|
8622
8527
|
var TuiUnavailableError = class extends Error {
|
|
8623
8528
|
constructor(tui, message) {
|
|
8624
8529
|
super(message);
|
|
@@ -8627,10 +8532,10 @@ var TuiUnavailableError = class extends Error {
|
|
|
8627
8532
|
}
|
|
8628
8533
|
tui;
|
|
8629
8534
|
};
|
|
8630
|
-
function isExecutable(
|
|
8535
|
+
function isExecutable(path3) {
|
|
8631
8536
|
try {
|
|
8632
|
-
if (!statSync(
|
|
8633
|
-
accessSync(
|
|
8537
|
+
if (!statSync(path3).isFile()) return false;
|
|
8538
|
+
accessSync(path3, constants.X_OK);
|
|
8634
8539
|
return true;
|
|
8635
8540
|
} catch {
|
|
8636
8541
|
return false;
|
|
@@ -8642,7 +8547,7 @@ function findOnPath(binary, env = process.env) {
|
|
|
8642
8547
|
}
|
|
8643
8548
|
for (const dir of (env.PATH ?? "").split(":")) {
|
|
8644
8549
|
if (!dir) continue;
|
|
8645
|
-
const candidate =
|
|
8550
|
+
const candidate = join8(dir, binary);
|
|
8646
8551
|
if (isExecutable(candidate)) return candidate;
|
|
8647
8552
|
}
|
|
8648
8553
|
return null;
|
|
@@ -8974,14 +8879,14 @@ function flushPendingToolCalls(host, turnToolCalls) {
|
|
|
8974
8879
|
}
|
|
8975
8880
|
const outputsByTool = /* @__PURE__ */ new Map();
|
|
8976
8881
|
for (const entry of host.pendingToolOutputs) {
|
|
8977
|
-
const
|
|
8978
|
-
|
|
8979
|
-
outputsByTool.set(entry.tool,
|
|
8882
|
+
const list = outputsByTool.get(entry.tool) ?? [];
|
|
8883
|
+
list.push(entry.output);
|
|
8884
|
+
outputsByTool.set(entry.tool, list);
|
|
8980
8885
|
}
|
|
8981
8886
|
for (const call of turnToolCalls) {
|
|
8982
|
-
const
|
|
8983
|
-
if (
|
|
8984
|
-
call.output =
|
|
8887
|
+
const list = outputsByTool.get(call.tool);
|
|
8888
|
+
if (list && list.length > 0) {
|
|
8889
|
+
call.output = list.shift();
|
|
8985
8890
|
}
|
|
8986
8891
|
}
|
|
8987
8892
|
host.connection.sendEvent({ type: "turn_end", toolCalls: [...turnToolCalls] });
|
|
@@ -9506,10 +9411,10 @@ function resolveSessionStart(lineageKey, cwd) {
|
|
|
9506
9411
|
}
|
|
9507
9412
|
return { sessionId: sessionUuid };
|
|
9508
9413
|
}
|
|
9509
|
-
function repairTornSessionFile(
|
|
9414
|
+
function repairTornSessionFile(path3) {
|
|
9510
9415
|
try {
|
|
9511
|
-
if (!existsSync2(
|
|
9512
|
-
const content = readFileSync(
|
|
9416
|
+
if (!existsSync2(path3)) return false;
|
|
9417
|
+
const content = readFileSync(path3, "utf8");
|
|
9513
9418
|
if (content.length === 0) return false;
|
|
9514
9419
|
let keepEnd = content.length;
|
|
9515
9420
|
if (!content.endsWith("\n")) {
|
|
@@ -9528,9 +9433,9 @@ function repairTornSessionFile(path4) {
|
|
|
9528
9433
|
keepEnd = prevNewline + 1;
|
|
9529
9434
|
}
|
|
9530
9435
|
if (keepEnd === content.length) return false;
|
|
9531
|
-
truncateSync(
|
|
9436
|
+
truncateSync(path3, Buffer.byteLength(content.slice(0, keepEnd), "utf8"));
|
|
9532
9437
|
logger2.warn("Repaired torn transcript before resume", {
|
|
9533
|
-
path:
|
|
9438
|
+
path: path3,
|
|
9534
9439
|
trimmedBytes: content.length - keepEnd
|
|
9535
9440
|
});
|
|
9536
9441
|
return true;
|
|
@@ -10328,7 +10233,7 @@ var QueryBridge = class {
|
|
|
10328
10233
|
|
|
10329
10234
|
// src/runner/session-runner-helpers.ts
|
|
10330
10235
|
import { readFileSync as readFileSync2 } from "fs";
|
|
10331
|
-
import { dirname as dirname2, join as
|
|
10236
|
+
import { dirname as dirname2, join as join9 } from "path";
|
|
10332
10237
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10333
10238
|
function mapChatHistory(messages) {
|
|
10334
10239
|
if (!messages) return [];
|
|
@@ -10357,7 +10262,7 @@ function readAgentVersion() {
|
|
|
10357
10262
|
const here = dirname2(fileURLToPath2(import.meta.url));
|
|
10358
10263
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
10359
10264
|
try {
|
|
10360
|
-
const pkg = JSON.parse(readFileSync2(
|
|
10265
|
+
const pkg = JSON.parse(readFileSync2(join9(here, rel), "utf-8"));
|
|
10361
10266
|
if (pkg.version) return pkg.version;
|
|
10362
10267
|
} catch {
|
|
10363
10268
|
}
|
|
@@ -10563,10 +10468,10 @@ var UsageProbeRun = class {
|
|
|
10563
10468
|
}
|
|
10564
10469
|
};
|
|
10565
10470
|
async function runUsageProbe(deps = {}) {
|
|
10566
|
-
let
|
|
10567
|
-
if (!
|
|
10471
|
+
let spawn = deps.spawn;
|
|
10472
|
+
if (!spawn) {
|
|
10568
10473
|
try {
|
|
10569
|
-
|
|
10474
|
+
spawn = await loadPtySpawn();
|
|
10570
10475
|
} catch {
|
|
10571
10476
|
return "";
|
|
10572
10477
|
}
|
|
@@ -10582,7 +10487,7 @@ async function runUsageProbe(deps = {}) {
|
|
|
10582
10487
|
return new Promise((resolve) => {
|
|
10583
10488
|
let child;
|
|
10584
10489
|
try {
|
|
10585
|
-
child =
|
|
10490
|
+
child = spawn(binary, [], {
|
|
10586
10491
|
name: "xterm-256color",
|
|
10587
10492
|
cols: 120,
|
|
10588
10493
|
rows: 45,
|
|
@@ -10656,14 +10561,14 @@ async function sampleKeyUsage(token, probe = () => runUsageProbe(), hasSubscript
|
|
|
10656
10561
|
}
|
|
10657
10562
|
|
|
10658
10563
|
// src/setup/git-ready.ts
|
|
10659
|
-
import { access, stat as
|
|
10564
|
+
import { access, stat as stat2 } from "fs/promises";
|
|
10660
10565
|
var DEFAULT_FAILED_PATH = "/workspaces/.conveyor-git-failed";
|
|
10661
10566
|
var DEFAULT_TIMEOUT_MS = 6e5;
|
|
10662
10567
|
var DEFAULT_POLL_MS = 200;
|
|
10663
|
-
async function fileExists(
|
|
10568
|
+
async function fileExists(path3) {
|
|
10664
10569
|
try {
|
|
10665
|
-
await access(
|
|
10666
|
-
const s = await
|
|
10570
|
+
await access(path3);
|
|
10571
|
+
const s = await stat2(path3);
|
|
10667
10572
|
return s.isFile();
|
|
10668
10573
|
} catch {
|
|
10669
10574
|
return false;
|
|
@@ -10726,8 +10631,8 @@ async function pollForMarkers(markerPath, failedPath, timeoutMs, pollMs, onLog,
|
|
|
10726
10631
|
}
|
|
10727
10632
|
|
|
10728
10633
|
// src/runner/port-discovery.ts
|
|
10729
|
-
import { readFile as
|
|
10730
|
-
import { execFile as
|
|
10634
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
10635
|
+
import { execFile as execFile2 } from "child_process";
|
|
10731
10636
|
var PROC_TCP_LISTEN_STATE = "0A";
|
|
10732
10637
|
function isLoopbackHexAddress(hex) {
|
|
10733
10638
|
const addr = hex.toUpperCase();
|
|
@@ -10778,9 +10683,9 @@ var DEFAULT_PROC_PATHS = ["/proc/net/tcp", "/proc/net/tcp6"];
|
|
|
10778
10683
|
async function readProcListeningPorts(procPaths = DEFAULT_PROC_PATHS) {
|
|
10779
10684
|
const sockets = [];
|
|
10780
10685
|
let readable = false;
|
|
10781
|
-
for (const
|
|
10686
|
+
for (const path3 of procPaths) {
|
|
10782
10687
|
try {
|
|
10783
|
-
const content = await
|
|
10688
|
+
const content = await readFile3(path3, "utf8");
|
|
10784
10689
|
readable = true;
|
|
10785
10690
|
sockets.push(...parseProcNetTcpListeners(content));
|
|
10786
10691
|
} catch {
|
|
@@ -10790,7 +10695,7 @@ async function readProcListeningPorts(procPaths = DEFAULT_PROC_PATHS) {
|
|
|
10790
10695
|
}
|
|
10791
10696
|
async function readNetstatListeningPorts() {
|
|
10792
10697
|
const output = await new Promise((resolve) => {
|
|
10793
|
-
|
|
10698
|
+
execFile2("netstat", ["-an", "-p", "tcp"], { timeout: 5e3 }, (err, stdout) => {
|
|
10794
10699
|
resolve(err ? null : stdout);
|
|
10795
10700
|
});
|
|
10796
10701
|
});
|
|
@@ -10963,9 +10868,9 @@ var PortDiscovery = class {
|
|
|
10963
10868
|
};
|
|
10964
10869
|
|
|
10965
10870
|
// src/runner/parent-pull-handler.ts
|
|
10966
|
-
import { execFile as
|
|
10967
|
-
import { promisify as
|
|
10968
|
-
var
|
|
10871
|
+
import { execFile as execFile3 } from "child_process";
|
|
10872
|
+
import { promisify as promisify2 } from "util";
|
|
10873
|
+
var execFileAsync2 = promisify2(execFile3);
|
|
10969
10874
|
async function handlePullBranch(workDir, branch) {
|
|
10970
10875
|
if (!branch) return;
|
|
10971
10876
|
const current = await getCurrentBranch(workDir);
|
|
@@ -10984,14 +10889,14 @@ async function handlePullBranch(workDir, branch) {
|
|
|
10984
10889
|
return;
|
|
10985
10890
|
}
|
|
10986
10891
|
try {
|
|
10987
|
-
await
|
|
10892
|
+
await execFileAsync2("git", ["fetch", "origin", branch], { cwd: workDir, timeout: 6e4 });
|
|
10988
10893
|
} catch {
|
|
10989
10894
|
process.stderr.write(`[conveyor-agent] pull_branch: fetch failed for ${branch}
|
|
10990
10895
|
`);
|
|
10991
10896
|
return;
|
|
10992
10897
|
}
|
|
10993
10898
|
try {
|
|
10994
|
-
await
|
|
10899
|
+
await execFileAsync2("git", ["pull", "--ff-only", "origin", branch], {
|
|
10995
10900
|
cwd: workDir,
|
|
10996
10901
|
timeout: 6e4
|
|
10997
10902
|
});
|
|
@@ -11007,7 +10912,7 @@ async function handlePullBranch(workDir, branch) {
|
|
|
11007
10912
|
|
|
11008
10913
|
// src/runner/heavy-gate.ts
|
|
11009
10914
|
import { readFileSync as readFileSync3 } from "fs";
|
|
11010
|
-
import
|
|
10915
|
+
import path2 from "path";
|
|
11011
10916
|
var GATE_KEYS = ["heavy", "test", "typecheck", "build"];
|
|
11012
10917
|
function runDir() {
|
|
11013
10918
|
return process.env.CONVEYOR_RUN_DIR ?? "/tmp/conveyor-run";
|
|
@@ -11023,7 +10928,7 @@ function pidAlive(pid) {
|
|
|
11023
10928
|
function isHeavyGateActive() {
|
|
11024
10929
|
for (const key of GATE_KEYS) {
|
|
11025
10930
|
try {
|
|
11026
|
-
const raw = readFileSync3(
|
|
10931
|
+
const raw = readFileSync3(path2.join(runDir(), `${key}.pid`), "utf8").trim();
|
|
11027
10932
|
const pid = Number.parseInt(raw, 10);
|
|
11028
10933
|
if (Number.isInteger(pid) && pid > 0 && pidAlive(pid)) return true;
|
|
11029
10934
|
} catch {
|
|
@@ -11924,13 +11829,12 @@ var SessionRunner = class _SessionRunner {
|
|
|
11924
11829
|
};
|
|
11925
11830
|
|
|
11926
11831
|
// src/setup/config.ts
|
|
11927
|
-
import {
|
|
11928
|
-
import { join as join9 } from "path";
|
|
11832
|
+
import { join as join10 } from "path";
|
|
11929
11833
|
var DEVCONTAINER_PATH = ".devcontainer/conveyor/devcontainer.json";
|
|
11930
11834
|
var DEVCONTAINER_PORT_DENY_LIST = /* @__PURE__ */ new Set([5432, 6379, 9200]);
|
|
11931
11835
|
async function loadForwardPorts(workspaceDir) {
|
|
11932
11836
|
try {
|
|
11933
|
-
const raw = await
|
|
11837
|
+
const raw = await readWorkspaceFile(join10(workspaceDir, DEVCONTAINER_PATH));
|
|
11934
11838
|
const parsed = JSON.parse(raw);
|
|
11935
11839
|
const ports = (parsed.forwardPorts ?? []).filter(
|
|
11936
11840
|
(p) => typeof p === "number" && !DEVCONTAINER_PORT_DENY_LIST.has(p)
|
|
@@ -11971,125 +11875,11 @@ function loadConveyorConfig() {
|
|
|
11971
11875
|
return null;
|
|
11972
11876
|
}
|
|
11973
11877
|
|
|
11974
|
-
// src/setup/commands.ts
|
|
11975
|
-
import { spawn, execSync } from "child_process";
|
|
11976
|
-
var PROCESS_TERMINATION_GRACE_MS = 5e3;
|
|
11977
|
-
function abortError2() {
|
|
11978
|
-
const error = new Error("Operation aborted");
|
|
11979
|
-
error.name = "AbortError";
|
|
11980
|
-
return error;
|
|
11981
|
-
}
|
|
11982
|
-
function signalProcessGroup(child, signal) {
|
|
11983
|
-
try {
|
|
11984
|
-
if (child.pid) process.kill(-child.pid, signal);
|
|
11985
|
-
else child.kill(signal);
|
|
11986
|
-
} catch {
|
|
11987
|
-
try {
|
|
11988
|
-
child.kill(signal);
|
|
11989
|
-
} catch {
|
|
11990
|
-
}
|
|
11991
|
-
}
|
|
11992
|
-
}
|
|
11993
|
-
function terminateProcessGroup(child, graceMs = PROCESS_TERMINATION_GRACE_MS) {
|
|
11994
|
-
if (child.exitCode !== null) return Promise.resolve();
|
|
11995
|
-
return new Promise((resolve) => {
|
|
11996
|
-
let settled = false;
|
|
11997
|
-
const finish = () => {
|
|
11998
|
-
if (settled) return;
|
|
11999
|
-
settled = true;
|
|
12000
|
-
clearTimeout(timer);
|
|
12001
|
-
child.removeListener("exit", finish);
|
|
12002
|
-
resolve();
|
|
12003
|
-
};
|
|
12004
|
-
const timer = setTimeout(() => {
|
|
12005
|
-
signalProcessGroup(child, "SIGKILL");
|
|
12006
|
-
finish();
|
|
12007
|
-
}, graceMs);
|
|
12008
|
-
timer.unref();
|
|
12009
|
-
child.once("exit", finish);
|
|
12010
|
-
signalProcessGroup(child, "SIGTERM");
|
|
12011
|
-
});
|
|
12012
|
-
}
|
|
12013
|
-
function runSetupCommand(cmd, cwd, onOutput, signal) {
|
|
12014
|
-
if (signal?.aborted) return Promise.reject(abortError2());
|
|
12015
|
-
return new Promise((resolve, reject) => {
|
|
12016
|
-
const child = spawn("sh", ["-c", cmd], {
|
|
12017
|
-
cwd,
|
|
12018
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
12019
|
-
detached: true,
|
|
12020
|
-
env: { ...process.env }
|
|
12021
|
-
});
|
|
12022
|
-
let settled = false;
|
|
12023
|
-
let aborting = false;
|
|
12024
|
-
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
12025
|
-
const settle = (error) => {
|
|
12026
|
-
if (settled) return;
|
|
12027
|
-
settled = true;
|
|
12028
|
-
cleanup();
|
|
12029
|
-
if (error) reject(error);
|
|
12030
|
-
else resolve();
|
|
12031
|
-
};
|
|
12032
|
-
const onAbort = () => {
|
|
12033
|
-
if (settled || aborting) return;
|
|
12034
|
-
aborting = true;
|
|
12035
|
-
void terminateProcessGroup(child).then(() => settle(abortError2()));
|
|
12036
|
-
};
|
|
12037
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
12038
|
-
if (signal?.aborted) onAbort();
|
|
12039
|
-
child.stdout.on("data", (chunk) => {
|
|
12040
|
-
if (aborting || signal?.aborted) return;
|
|
12041
|
-
onOutput("stdout", chunk.toString());
|
|
12042
|
-
});
|
|
12043
|
-
child.stderr.on("data", (chunk) => {
|
|
12044
|
-
if (aborting || signal?.aborted) return;
|
|
12045
|
-
onOutput("stderr", chunk.toString());
|
|
12046
|
-
});
|
|
12047
|
-
child.on("close", (code) => {
|
|
12048
|
-
if (aborting) return;
|
|
12049
|
-
settle(code === 0 ? void 0 : new Error(`Setup command exited with code ${code}`));
|
|
12050
|
-
});
|
|
12051
|
-
child.on("error", (err) => {
|
|
12052
|
-
if (!aborting) settle(err);
|
|
12053
|
-
});
|
|
12054
|
-
});
|
|
12055
|
-
}
|
|
12056
|
-
var AUTH_TOKEN_TIMEOUT_MS = 3e4;
|
|
12057
|
-
function runAuthTokenCommand(cmd, userEmail, cwd) {
|
|
12058
|
-
try {
|
|
12059
|
-
const output = execSync(`${cmd} ${JSON.stringify(userEmail)}`, {
|
|
12060
|
-
cwd,
|
|
12061
|
-
timeout: AUTH_TOKEN_TIMEOUT_MS,
|
|
12062
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
12063
|
-
env: { ...process.env }
|
|
12064
|
-
});
|
|
12065
|
-
const token = output.toString().trim();
|
|
12066
|
-
return token || null;
|
|
12067
|
-
} catch {
|
|
12068
|
-
return null;
|
|
12069
|
-
}
|
|
12070
|
-
}
|
|
12071
|
-
function runStartCommand(cmd, cwd, onOutput) {
|
|
12072
|
-
const child = spawn("sh", ["-c", cmd], {
|
|
12073
|
-
cwd,
|
|
12074
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
12075
|
-
detached: true,
|
|
12076
|
-
env: { ...process.env }
|
|
12077
|
-
});
|
|
12078
|
-
child.stdout.on("data", (chunk) => {
|
|
12079
|
-
onOutput("stdout", chunk.toString());
|
|
12080
|
-
});
|
|
12081
|
-
child.stderr.on("data", (chunk) => {
|
|
12082
|
-
onOutput("stderr", chunk.toString());
|
|
12083
|
-
});
|
|
12084
|
-
child.unref();
|
|
12085
|
-
return child;
|
|
12086
|
-
}
|
|
12087
|
-
|
|
12088
11878
|
// src/setup/codespace.ts
|
|
12089
|
-
import { execSync
|
|
11879
|
+
import { execSync } from "child_process";
|
|
12090
11880
|
function unshallowRepo(workspaceDir) {
|
|
12091
11881
|
try {
|
|
12092
|
-
|
|
11882
|
+
execSync("git fetch --unshallow", {
|
|
12093
11883
|
cwd: workspaceDir,
|
|
12094
11884
|
timeout: 6e4,
|
|
12095
11885
|
stdio: "ignore"
|
|
@@ -12110,12 +11900,10 @@ export {
|
|
|
12110
11900
|
cleanTerminalOutput,
|
|
12111
11901
|
buildSynthesizedCredentials,
|
|
12112
11902
|
claudeJsonPath,
|
|
12113
|
-
loadPtySpawn,
|
|
12114
|
-
inheritedEnv,
|
|
12115
|
-
buildPromptBytes,
|
|
12116
11903
|
ClaudeTuiAdapter,
|
|
12117
11904
|
createServiceLogger,
|
|
12118
11905
|
PtyHarness,
|
|
11906
|
+
workspacePathExists,
|
|
12119
11907
|
textResult,
|
|
12120
11908
|
GIT_TIMEOUT_MS,
|
|
12121
11909
|
hasUncommittedChanges,
|
|
@@ -12144,10 +11932,6 @@ export {
|
|
|
12144
11932
|
loadForwardPorts,
|
|
12145
11933
|
buildSessionPreviewPorts,
|
|
12146
11934
|
loadConveyorConfig,
|
|
12147
|
-
terminateProcessGroup,
|
|
12148
|
-
runSetupCommand,
|
|
12149
|
-
runAuthTokenCommand,
|
|
12150
|
-
runStartCommand,
|
|
12151
11935
|
unshallowRepo
|
|
12152
11936
|
};
|
|
12153
|
-
//# sourceMappingURL=chunk-
|
|
11937
|
+
//# sourceMappingURL=chunk-UVR5XFE4.js.map
|