replicas-engine 0.1.155 → 0.1.157
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/src/index.js +264 -101
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -205,9 +205,13 @@ function parseWarmHookConfig(value, filename = "replicas.json") {
|
|
|
205
205
|
if (value.timeout !== void 0 && (typeof value.timeout !== "number" || value.timeout <= 0)) {
|
|
206
206
|
throw new Error(`Invalid ${filename}: "warmHook.timeout" must be a positive number`);
|
|
207
207
|
}
|
|
208
|
+
if (value.separate !== void 0 && typeof value.separate !== "boolean") {
|
|
209
|
+
throw new Error(`Invalid ${filename}: "warmHook.separate" must be a boolean`);
|
|
210
|
+
}
|
|
208
211
|
return {
|
|
209
212
|
commands: value.commands,
|
|
210
|
-
timeout: value.timeout
|
|
213
|
+
timeout: value.timeout,
|
|
214
|
+
separate: value.separate
|
|
211
215
|
};
|
|
212
216
|
}
|
|
213
217
|
function resolveWarmHookConfig(value) {
|
|
@@ -221,7 +225,8 @@ function resolveWarmHookConfig(value) {
|
|
|
221
225
|
}
|
|
222
226
|
return {
|
|
223
227
|
commands,
|
|
224
|
-
timeoutMs: typeof parsed === "string" ? void 0 : parsed.timeout
|
|
228
|
+
timeoutMs: typeof parsed === "string" ? void 0 : parsed.timeout,
|
|
229
|
+
separate: typeof parsed === "string" ? void 0 : parsed.separate
|
|
225
230
|
};
|
|
226
231
|
}
|
|
227
232
|
|
|
@@ -251,14 +256,17 @@ function parseReplicasConfig(value, filename = "replicas.json") {
|
|
|
251
256
|
if (!isRecord2(value.startHook)) {
|
|
252
257
|
throw new Error(`Invalid ${filename}: "startHook" must be an object with "commands" array`);
|
|
253
258
|
}
|
|
254
|
-
const { commands, timeout } = value.startHook;
|
|
259
|
+
const { commands, timeout, separate } = value.startHook;
|
|
255
260
|
if (!Array.isArray(commands) || !commands.every((entry) => typeof entry === "string")) {
|
|
256
261
|
throw new Error(`Invalid ${filename}: "startHook.commands" must be an array of shell commands`);
|
|
257
262
|
}
|
|
258
263
|
if (timeout !== void 0 && (typeof timeout !== "number" || timeout <= 0)) {
|
|
259
264
|
throw new Error(`Invalid ${filename}: "startHook.timeout" must be a positive number`);
|
|
260
265
|
}
|
|
261
|
-
|
|
266
|
+
if (separate !== void 0 && typeof separate !== "boolean") {
|
|
267
|
+
throw new Error(`Invalid ${filename}: "startHook.separate" must be a boolean`);
|
|
268
|
+
}
|
|
269
|
+
config.startHook = { commands, timeout, separate };
|
|
262
270
|
}
|
|
263
271
|
if ("warmHook" in value) {
|
|
264
272
|
config.warmHook = parseWarmHookConfig(value.warmHook, filename);
|
|
@@ -277,7 +285,7 @@ function parseReplicasConfigString(content, filename) {
|
|
|
277
285
|
}
|
|
278
286
|
|
|
279
287
|
// ../shared/src/engine/environment.ts
|
|
280
|
-
var DAYTONA_SNAPSHOT_ID = "11-05-2026-royal-york-
|
|
288
|
+
var DAYTONA_SNAPSHOT_ID = "11-05-2026-royal-york-v3";
|
|
281
289
|
|
|
282
290
|
// ../shared/src/engine/types.ts
|
|
283
291
|
var DEFAULT_CHAT_TITLES = {
|
|
@@ -1592,6 +1600,43 @@ var ReplicasConfigService = class {
|
|
|
1592
1600
|
console.error("Failed to write to start hooks log:", error);
|
|
1593
1601
|
}
|
|
1594
1602
|
}
|
|
1603
|
+
async execStartHookCommand(params) {
|
|
1604
|
+
const output = [];
|
|
1605
|
+
try {
|
|
1606
|
+
await this.logToFile(`[${params.repoName}] --- Running: ${params.label} ---`);
|
|
1607
|
+
const { stdout, stderr } = await execAsync(params.command, {
|
|
1608
|
+
cwd: params.cwd,
|
|
1609
|
+
timeout: params.timeout,
|
|
1610
|
+
maxBuffer: HOOK_EXEC_MAX_BUFFER_BYTES,
|
|
1611
|
+
env: process.env
|
|
1612
|
+
});
|
|
1613
|
+
if (stdout) {
|
|
1614
|
+
output.push(stdout);
|
|
1615
|
+
await this.logToFile(`[${params.repoName}] [stdout] ${stdout}`);
|
|
1616
|
+
}
|
|
1617
|
+
if (stderr) {
|
|
1618
|
+
output.push(stderr);
|
|
1619
|
+
await this.logToFile(`[${params.repoName}] [stderr] ${stderr}`);
|
|
1620
|
+
}
|
|
1621
|
+
await this.logToFile(`[${params.repoName}] --- Completed: ${params.label} ---`);
|
|
1622
|
+
return { exitCode: 0, timedOut: false, output };
|
|
1623
|
+
} catch (error) {
|
|
1624
|
+
const execError = error;
|
|
1625
|
+
if (execError.stdout) {
|
|
1626
|
+
output.push(execError.stdout);
|
|
1627
|
+
await this.logToFile(`[${params.repoName}] [stdout] ${execError.stdout}`);
|
|
1628
|
+
}
|
|
1629
|
+
if (execError.stderr) {
|
|
1630
|
+
output.push(execError.stderr);
|
|
1631
|
+
await this.logToFile(`[${params.repoName}] [stderr] ${execError.stderr}`);
|
|
1632
|
+
}
|
|
1633
|
+
const rawMessage = execError.message ?? "Unknown error";
|
|
1634
|
+
const errorMessage = rawMessage.split("\n", 1)[0];
|
|
1635
|
+
output.push(`[ERROR] ${params.label} failed: ${errorMessage}`);
|
|
1636
|
+
await this.logToFile(`[${params.repoName}] [ERROR] ${params.label} failed: ${errorMessage}`);
|
|
1637
|
+
return { exitCode: execError.code ?? 1, timedOut: execError.killed === true, output };
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1595
1640
|
/**
|
|
1596
1641
|
* Execute start hooks from all repositories sequentially.
|
|
1597
1642
|
*/
|
|
@@ -1644,46 +1689,43 @@ Repositories: ${hookEntries.length}
|
|
|
1644
1689
|
let repoTimedOut = false;
|
|
1645
1690
|
const repoOutput = [];
|
|
1646
1691
|
await this.logToFile(`[${entry.repoName}] Executing ${startHookConfig.commands.length} hook(s) with timeout ${timeout}ms`);
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
repoOutput.push(stderr);
|
|
1662
|
-
await this.logToFile(`[${entry.repoName}] [stderr] ${stderr}`);
|
|
1663
|
-
}
|
|
1664
|
-
await this.logToFile(`[${entry.repoName}] --- Completed: ${hook} ---`);
|
|
1665
|
-
} catch (error) {
|
|
1666
|
-
const execError = error;
|
|
1667
|
-
lastExitCode = execError.code ?? 1;
|
|
1668
|
-
if (execError.killed) {
|
|
1669
|
-
repoTimedOut = true;
|
|
1670
|
-
}
|
|
1692
|
+
if (startHookConfig.separate === false) {
|
|
1693
|
+
const combinedScript = `set -e
|
|
1694
|
+
${startHookConfig.commands.join("\n")}`;
|
|
1695
|
+
const result = await this.execStartHookCommand({
|
|
1696
|
+
command: combinedScript,
|
|
1697
|
+
label: "combined script",
|
|
1698
|
+
repoName: entry.repoName,
|
|
1699
|
+
cwd: entry.workingDirectory,
|
|
1700
|
+
timeout
|
|
1701
|
+
});
|
|
1702
|
+
repoOutput.push(...result.output);
|
|
1703
|
+
if (result.exitCode !== 0) {
|
|
1704
|
+
lastExitCode = result.exitCode;
|
|
1705
|
+
repoTimedOut = result.timedOut;
|
|
1671
1706
|
this.hooksFailed = true;
|
|
1672
1707
|
repoFailed = true;
|
|
1673
|
-
if (execError.stdout) {
|
|
1674
|
-
repoOutput.push(execError.stdout);
|
|
1675
|
-
await this.logToFile(`[${entry.repoName}] [stdout] ${execError.stdout}`);
|
|
1676
|
-
}
|
|
1677
|
-
if (execError.stderr) {
|
|
1678
|
-
repoOutput.push(execError.stderr);
|
|
1679
|
-
await this.logToFile(`[${entry.repoName}] [stderr] ${execError.stderr}`);
|
|
1680
|
-
}
|
|
1681
|
-
const rawMessage = execError.message ?? "Unknown error";
|
|
1682
|
-
const errorMessage = rawMessage.split("\n", 1)[0];
|
|
1683
|
-
repoOutput.push(`[ERROR] ${hook} failed: ${errorMessage}`);
|
|
1684
|
-
await this.logToFile(`[${entry.repoName}] [ERROR] ${hook} failed: ${errorMessage}`);
|
|
1685
1708
|
await environmentDetailsService.setRepositoryStartHook(entry.repoName, "no");
|
|
1686
1709
|
}
|
|
1710
|
+
} else {
|
|
1711
|
+
for (const hook of startHookConfig.commands) {
|
|
1712
|
+
const result = await this.execStartHookCommand({
|
|
1713
|
+
command: hook,
|
|
1714
|
+
label: hook,
|
|
1715
|
+
repoName: entry.repoName,
|
|
1716
|
+
cwd: entry.workingDirectory,
|
|
1717
|
+
timeout
|
|
1718
|
+
});
|
|
1719
|
+
repoOutput.push(...result.output);
|
|
1720
|
+
if (result.exitCode !== 0) {
|
|
1721
|
+
lastExitCode = result.exitCode;
|
|
1722
|
+
repoTimedOut = result.timedOut;
|
|
1723
|
+
this.hooksFailed = true;
|
|
1724
|
+
repoFailed = true;
|
|
1725
|
+
await environmentDetailsService.setRepositoryStartHook(entry.repoName, "no");
|
|
1726
|
+
break;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1687
1729
|
}
|
|
1688
1730
|
await startHookLogsService.saveRepoLog(entry.repoName, {
|
|
1689
1731
|
hookCommands: startHookConfig.commands,
|
|
@@ -1993,12 +2035,12 @@ function convertClaudeEvent(event, linearSessionId) {
|
|
|
1993
2035
|
}
|
|
1994
2036
|
};
|
|
1995
2037
|
}
|
|
1996
|
-
if (block.type === "thinking" && block.
|
|
2038
|
+
if (block.type === "thinking" && block.thinking) {
|
|
1997
2039
|
return {
|
|
1998
2040
|
linearSessionId,
|
|
1999
2041
|
content: {
|
|
2000
2042
|
type: "thought",
|
|
2001
|
-
body: block.
|
|
2043
|
+
body: block.thinking
|
|
2002
2044
|
}
|
|
2003
2045
|
};
|
|
2004
2046
|
}
|
|
@@ -4451,8 +4493,7 @@ var PlanService = class {
|
|
|
4451
4493
|
var planService = new PlanService();
|
|
4452
4494
|
|
|
4453
4495
|
// src/services/warm-hooks-service.ts
|
|
4454
|
-
import {
|
|
4455
|
-
import { promisify as promisify3 } from "util";
|
|
4496
|
+
import { spawn } from "child_process";
|
|
4456
4497
|
import { readFile as readFile11 } from "fs/promises";
|
|
4457
4498
|
import { existsSync as existsSync8 } from "fs";
|
|
4458
4499
|
import { join as join15 } from "path";
|
|
@@ -4565,34 +4606,6 @@ var WarmHookLogsService = class {
|
|
|
4565
4606
|
var warmHookLogsService = new WarmHookLogsService();
|
|
4566
4607
|
|
|
4567
4608
|
// src/services/warm-hooks-service.ts
|
|
4568
|
-
var execFileAsync2 = promisify3(execFile2);
|
|
4569
|
-
async function executeHookScript(params) {
|
|
4570
|
-
const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
|
|
4571
|
-
try {
|
|
4572
|
-
const { stdout, stderr } = await execFileAsync2("bash", ["-lc", params.content], {
|
|
4573
|
-
cwd: params.cwd,
|
|
4574
|
-
timeout,
|
|
4575
|
-
maxBuffer: HOOK_EXEC_MAX_BUFFER_BYTES,
|
|
4576
|
-
env: process.env
|
|
4577
|
-
});
|
|
4578
|
-
const combined = [`$ ${params.label}`, stdout ?? "", stderr ?? ""].filter(Boolean).join("\n");
|
|
4579
|
-
return {
|
|
4580
|
-
exitCode: 0,
|
|
4581
|
-
output: combined,
|
|
4582
|
-
timedOut: false
|
|
4583
|
-
};
|
|
4584
|
-
} catch (error) {
|
|
4585
|
-
const execError = error;
|
|
4586
|
-
const timedOut = execError.signal === "SIGTERM" || execError.killed === true;
|
|
4587
|
-
const exitCode = typeof execError.code === "number" ? execError.code : 1;
|
|
4588
|
-
const combined = [`$ ${params.label}`, execError.stdout ?? "", execError.stderr ?? "", execError.message].filter(Boolean).join("\n");
|
|
4589
|
-
return {
|
|
4590
|
-
exitCode,
|
|
4591
|
-
output: combined,
|
|
4592
|
-
timedOut
|
|
4593
|
-
};
|
|
4594
|
-
}
|
|
4595
|
-
}
|
|
4596
4609
|
async function readRepoWarmHook(repoPath) {
|
|
4597
4610
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
4598
4611
|
const configPath = join15(repoPath, filename);
|
|
@@ -4628,22 +4641,87 @@ async function collectRepoWarmHooks() {
|
|
|
4628
4641
|
repoName: repo.name,
|
|
4629
4642
|
repoPath: repo.path,
|
|
4630
4643
|
commands: warmHook.commands,
|
|
4631
|
-
timeoutMs: warmHook.timeoutMs
|
|
4644
|
+
timeoutMs: warmHook.timeoutMs,
|
|
4645
|
+
separate: warmHook.separate
|
|
4632
4646
|
});
|
|
4633
4647
|
}
|
|
4634
4648
|
collected.sort((a, b) => a.repoName.localeCompare(b.repoName));
|
|
4635
4649
|
return collected;
|
|
4636
4650
|
}
|
|
4637
4651
|
async function runWarmHooks(params) {
|
|
4652
|
+
return runWarmHooksStreaming({ ...params, onEvent: () => {
|
|
4653
|
+
} });
|
|
4654
|
+
}
|
|
4655
|
+
async function executeHookScriptStreaming(params) {
|
|
4656
|
+
const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
|
|
4657
|
+
const outputParts = [];
|
|
4658
|
+
let outputBytes = 0;
|
|
4659
|
+
let bufferExceeded = false;
|
|
4660
|
+
params.onChunk(`$ ${params.label}
|
|
4661
|
+
`);
|
|
4662
|
+
return new Promise((resolve2) => {
|
|
4663
|
+
const proc = spawn("bash", ["-lc", params.content], {
|
|
4664
|
+
cwd: params.cwd,
|
|
4665
|
+
env: process.env,
|
|
4666
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4667
|
+
});
|
|
4668
|
+
let killed = false;
|
|
4669
|
+
const timer = setTimeout(() => {
|
|
4670
|
+
killed = true;
|
|
4671
|
+
proc.kill("SIGTERM");
|
|
4672
|
+
}, timeout);
|
|
4673
|
+
const handleData = (data) => {
|
|
4674
|
+
outputBytes += data.length;
|
|
4675
|
+
if (bufferExceeded) return;
|
|
4676
|
+
if (outputBytes > HOOK_EXEC_MAX_BUFFER_BYTES) {
|
|
4677
|
+
bufferExceeded = true;
|
|
4678
|
+
const msg = `
|
|
4679
|
+
[output truncated \u2014 exceeded ${HOOK_EXEC_MAX_BUFFER_BYTES} bytes]
|
|
4680
|
+
`;
|
|
4681
|
+
outputParts.push(msg);
|
|
4682
|
+
params.onChunk(msg);
|
|
4683
|
+
proc.kill("SIGTERM");
|
|
4684
|
+
return;
|
|
4685
|
+
}
|
|
4686
|
+
const text = data.toString();
|
|
4687
|
+
outputParts.push(text);
|
|
4688
|
+
params.onChunk(text);
|
|
4689
|
+
};
|
|
4690
|
+
proc.stdout.on("data", handleData);
|
|
4691
|
+
proc.stderr.on("data", handleData);
|
|
4692
|
+
proc.on("close", (code) => {
|
|
4693
|
+
clearTimeout(timer);
|
|
4694
|
+
const output = [`$ ${params.label}`, ...outputParts].join("");
|
|
4695
|
+
resolve2({
|
|
4696
|
+
exitCode: bufferExceeded ? 1 : code ?? 1,
|
|
4697
|
+
output,
|
|
4698
|
+
timedOut: killed && !bufferExceeded
|
|
4699
|
+
});
|
|
4700
|
+
});
|
|
4701
|
+
proc.on("error", (err) => {
|
|
4702
|
+
clearTimeout(timer);
|
|
4703
|
+
const output = [`$ ${params.label}
|
|
4704
|
+
`, ...outputParts, err.message].join("");
|
|
4705
|
+
resolve2({
|
|
4706
|
+
exitCode: 1,
|
|
4707
|
+
output,
|
|
4708
|
+
timedOut: false
|
|
4709
|
+
});
|
|
4710
|
+
});
|
|
4711
|
+
proc.stdin.end();
|
|
4712
|
+
});
|
|
4713
|
+
}
|
|
4714
|
+
async function runWarmHooksStreaming(params) {
|
|
4638
4715
|
const outputBlocks = [];
|
|
4639
4716
|
const globalHook = (params.globalWarmHook ?? params.organizationWarmHook)?.trim();
|
|
4640
4717
|
const environmentHook = params.environmentWarmHook?.trim();
|
|
4641
4718
|
if (globalHook) {
|
|
4642
|
-
const globalResult = await
|
|
4719
|
+
const globalResult = await executeHookScriptStreaming({
|
|
4643
4720
|
label: "global-warm-hook",
|
|
4644
4721
|
cwd: gitService.getWorkspaceRoot(),
|
|
4645
4722
|
content: globalHook,
|
|
4646
|
-
timeoutMs: params.timeoutMs
|
|
4723
|
+
timeoutMs: params.timeoutMs,
|
|
4724
|
+
onChunk: (chunk) => params.onEvent({ type: "output", data: chunk, label: "global-warm-hook" })
|
|
4647
4725
|
});
|
|
4648
4726
|
outputBlocks.push(globalResult.output);
|
|
4649
4727
|
await environmentDetailsService.setGlobalWarmHook(globalResult.exitCode === 0 ? "yes" : "no", globalResult.output);
|
|
@@ -4656,19 +4734,22 @@ async function runWarmHooks(params) {
|
|
|
4656
4734
|
});
|
|
4657
4735
|
if (globalResult.exitCode !== 0) {
|
|
4658
4736
|
await environmentDetailsService.setEnvironmentWarmHook("n/a");
|
|
4659
|
-
|
|
4737
|
+
const result2 = {
|
|
4660
4738
|
exitCode: globalResult.exitCode,
|
|
4661
4739
|
output: outputBlocks.join("\n\n"),
|
|
4662
4740
|
timedOut: globalResult.timedOut
|
|
4663
4741
|
};
|
|
4742
|
+
params.onEvent({ type: "complete", exitCode: result2.exitCode, timedOut: result2.timedOut });
|
|
4743
|
+
return result2;
|
|
4664
4744
|
}
|
|
4665
4745
|
}
|
|
4666
4746
|
if (environmentHook) {
|
|
4667
|
-
const envResult = await
|
|
4747
|
+
const envResult = await executeHookScriptStreaming({
|
|
4668
4748
|
label: "environment-warm-hook",
|
|
4669
4749
|
cwd: gitService.getWorkspaceRoot(),
|
|
4670
4750
|
content: environmentHook,
|
|
4671
|
-
timeoutMs: params.timeoutMs
|
|
4751
|
+
timeoutMs: params.timeoutMs,
|
|
4752
|
+
onChunk: (chunk) => params.onEvent({ type: "output", data: chunk, label: "environment-warm-hook" })
|
|
4672
4753
|
});
|
|
4673
4754
|
outputBlocks.push(envResult.output);
|
|
4674
4755
|
await environmentDetailsService.setEnvironmentWarmHook(envResult.exitCode === 0 ? "yes" : "no", envResult.output);
|
|
@@ -4680,11 +4761,13 @@ async function runWarmHooks(params) {
|
|
|
4680
4761
|
executedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4681
4762
|
});
|
|
4682
4763
|
if (envResult.exitCode !== 0) {
|
|
4683
|
-
|
|
4764
|
+
const result2 = {
|
|
4684
4765
|
exitCode: envResult.exitCode,
|
|
4685
4766
|
output: outputBlocks.join("\n\n"),
|
|
4686
4767
|
timedOut: envResult.timedOut
|
|
4687
4768
|
};
|
|
4769
|
+
params.onEvent({ type: "complete", exitCode: result2.exitCode, timedOut: result2.timedOut });
|
|
4770
|
+
return result2;
|
|
4688
4771
|
}
|
|
4689
4772
|
} else {
|
|
4690
4773
|
await environmentDetailsService.setEnvironmentWarmHook("n/a");
|
|
@@ -4706,53 +4789,73 @@ async function runWarmHooks(params) {
|
|
|
4706
4789
|
let repoFailed = false;
|
|
4707
4790
|
let repoTimedOut = false;
|
|
4708
4791
|
let repoExitCode = 0;
|
|
4709
|
-
|
|
4710
|
-
const
|
|
4792
|
+
if (repoHook.separate === false) {
|
|
4793
|
+
const scriptWithErrExit = `set -e
|
|
4794
|
+
${combinedScript}`;
|
|
4795
|
+
const repoResult = await executeHookScriptStreaming({
|
|
4711
4796
|
label: `repo-warm-hook:${repoHook.repoName}`,
|
|
4712
4797
|
cwd: repoHook.repoPath,
|
|
4713
|
-
content:
|
|
4714
|
-
timeoutMs: repoHook.timeoutMs
|
|
4798
|
+
content: scriptWithErrExit,
|
|
4799
|
+
timeoutMs: repoHook.timeoutMs,
|
|
4800
|
+
onChunk: (chunk) => params.onEvent({ type: "output", data: chunk, label: `repo:${repoHook.repoName}` })
|
|
4715
4801
|
});
|
|
4716
4802
|
outputBlocks.push(repoResult.output);
|
|
4717
4803
|
repoOutputBlocks.push(repoResult.output);
|
|
4718
|
-
await environmentDetailsService.setRepositoryWarmHook(
|
|
4719
|
-
repoHook.repoName,
|
|
4720
|
-
repoResult.exitCode === 0 ? "yes" : "no"
|
|
4721
|
-
);
|
|
4722
4804
|
if (repoResult.exitCode !== 0) {
|
|
4723
4805
|
repoFailed = true;
|
|
4724
4806
|
repoTimedOut = repoResult.timedOut;
|
|
4725
4807
|
repoExitCode = repoResult.exitCode;
|
|
4726
|
-
|
|
4808
|
+
}
|
|
4809
|
+
} else {
|
|
4810
|
+
for (const command of repoHook.commands) {
|
|
4811
|
+
const repoResult = await executeHookScriptStreaming({
|
|
4812
|
+
label: `repo-warm-hook:${repoHook.repoName}`,
|
|
4813
|
+
cwd: repoHook.repoPath,
|
|
4814
|
+
content: command,
|
|
4815
|
+
timeoutMs: repoHook.timeoutMs,
|
|
4816
|
+
onChunk: (chunk) => params.onEvent({ type: "output", data: chunk, label: `repo:${repoHook.repoName}` })
|
|
4817
|
+
});
|
|
4818
|
+
outputBlocks.push(repoResult.output);
|
|
4819
|
+
repoOutputBlocks.push(repoResult.output);
|
|
4820
|
+
if (repoResult.exitCode !== 0) {
|
|
4821
|
+
repoFailed = true;
|
|
4822
|
+
repoTimedOut = repoResult.timedOut;
|
|
4823
|
+
repoExitCode = repoResult.exitCode;
|
|
4824
|
+
break;
|
|
4825
|
+
}
|
|
4727
4826
|
}
|
|
4728
4827
|
}
|
|
4828
|
+
await environmentDetailsService.setRepositoryWarmHook(
|
|
4829
|
+
repoHook.repoName,
|
|
4830
|
+
repoFailed ? "no" : "yes"
|
|
4831
|
+
);
|
|
4832
|
+
const loggedScript = repoHook.separate === false ? `set -e
|
|
4833
|
+
${combinedScript}` : combinedScript;
|
|
4729
4834
|
await warmHookLogsService.saveRepoHookLog(repoHook.repoName, {
|
|
4730
|
-
hookScript:
|
|
4835
|
+
hookScript: loggedScript,
|
|
4731
4836
|
output: repoOutputBlocks.join("\n\n"),
|
|
4732
4837
|
exitCode: repoFailed ? repoExitCode : 0,
|
|
4733
4838
|
timedOut: repoTimedOut,
|
|
4734
4839
|
executedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4735
4840
|
});
|
|
4736
4841
|
if (repoFailed) {
|
|
4737
|
-
|
|
4842
|
+
const result2 = {
|
|
4738
4843
|
exitCode: repoExitCode,
|
|
4739
4844
|
output: outputBlocks.join("\n\n"),
|
|
4740
4845
|
timedOut: repoTimedOut
|
|
4741
4846
|
};
|
|
4847
|
+
params.onEvent({ type: "complete", exitCode: result2.exitCode, timedOut: result2.timedOut });
|
|
4848
|
+
return result2;
|
|
4742
4849
|
}
|
|
4743
4850
|
}
|
|
4744
4851
|
if (outputBlocks.length === 0) {
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
timedOut: false
|
|
4749
|
-
};
|
|
4852
|
+
const result2 = { exitCode: 0, output: "No warm hooks configured.", timedOut: false };
|
|
4853
|
+
params.onEvent({ type: "complete", exitCode: 0, timedOut: false });
|
|
4854
|
+
return result2;
|
|
4750
4855
|
}
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
timedOut: false
|
|
4755
|
-
};
|
|
4856
|
+
const result = { exitCode: 0, output: outputBlocks.join("\n\n"), timedOut: false };
|
|
4857
|
+
params.onEvent({ type: "complete", exitCode: 0, timedOut: false });
|
|
4858
|
+
return result;
|
|
4756
4859
|
}
|
|
4757
4860
|
|
|
4758
4861
|
// src/v1-routes.ts
|
|
@@ -5046,6 +5149,66 @@ function createV1Routes(deps) {
|
|
|
5046
5149
|
);
|
|
5047
5150
|
}
|
|
5048
5151
|
});
|
|
5152
|
+
app2.post("/warm-hooks/run/stream", async (c) => {
|
|
5153
|
+
try {
|
|
5154
|
+
const body = await c.req.json();
|
|
5155
|
+
const encoder = new TextEncoder();
|
|
5156
|
+
const stream = new ReadableStream({
|
|
5157
|
+
start: (controller) => {
|
|
5158
|
+
let closed = false;
|
|
5159
|
+
const safeEnqueue = (chunk) => {
|
|
5160
|
+
if (closed) return false;
|
|
5161
|
+
try {
|
|
5162
|
+
controller.enqueue(encoder.encode(chunk));
|
|
5163
|
+
return true;
|
|
5164
|
+
} catch {
|
|
5165
|
+
closed = true;
|
|
5166
|
+
return false;
|
|
5167
|
+
}
|
|
5168
|
+
};
|
|
5169
|
+
const heartbeat = setInterval(() => {
|
|
5170
|
+
if (!safeEnqueue(": ping\n\n")) clearInterval(heartbeat);
|
|
5171
|
+
}, 15e3);
|
|
5172
|
+
safeEnqueue(": connected\n\n");
|
|
5173
|
+
runWarmHooksStreaming({
|
|
5174
|
+
globalWarmHook: body.globalWarmHook,
|
|
5175
|
+
environmentWarmHook: body.environmentWarmHook,
|
|
5176
|
+
organizationWarmHook: body.organizationWarmHook,
|
|
5177
|
+
timeoutMs: body.timeoutMs,
|
|
5178
|
+
onEvent: (event) => {
|
|
5179
|
+
safeEnqueue(`data: ${JSON.stringify(event)}
|
|
5180
|
+
|
|
5181
|
+
`);
|
|
5182
|
+
}
|
|
5183
|
+
}).then(() => {
|
|
5184
|
+
clearInterval(heartbeat);
|
|
5185
|
+
if (!closed) {
|
|
5186
|
+
closed = true;
|
|
5187
|
+
controller.close();
|
|
5188
|
+
}
|
|
5189
|
+
}).catch((err) => {
|
|
5190
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
5191
|
+
safeEnqueue(`data: ${JSON.stringify({ type: "error", data: message })}
|
|
5192
|
+
|
|
5193
|
+
`);
|
|
5194
|
+
clearInterval(heartbeat);
|
|
5195
|
+
if (!closed) {
|
|
5196
|
+
closed = true;
|
|
5197
|
+
controller.close();
|
|
5198
|
+
}
|
|
5199
|
+
});
|
|
5200
|
+
}
|
|
5201
|
+
});
|
|
5202
|
+
return new Response(stream, {
|
|
5203
|
+
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" }
|
|
5204
|
+
});
|
|
5205
|
+
} catch (error) {
|
|
5206
|
+
return c.json(
|
|
5207
|
+
jsonError("Failed to run warm hooks", error instanceof Error ? error.message : "Unknown error"),
|
|
5208
|
+
500
|
|
5209
|
+
);
|
|
5210
|
+
}
|
|
5211
|
+
});
|
|
5049
5212
|
app2.get("/warm-hooks/logs", async (c) => {
|
|
5050
5213
|
try {
|
|
5051
5214
|
const logs = await warmHookLogsService.getAllLogs();
|