replicas-engine 0.1.156 → 0.1.159

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.
Files changed (2) hide show
  1. package/dist/src/index.js +114 -50
  2. 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
- config.startHook = { commands, timeout };
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-v2";
288
+ var DAYTONA_SNAPSHOT_ID = "11-05-2026-royal-york-v5";
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
- for (const hook of startHookConfig.commands) {
1648
- try {
1649
- await this.logToFile(`[${entry.repoName}] --- Running: ${hook} ---`);
1650
- const { stdout, stderr } = await execAsync(hook, {
1651
- cwd: entry.workingDirectory,
1652
- timeout,
1653
- maxBuffer: HOOK_EXEC_MAX_BUFFER_BYTES,
1654
- env: process.env
1655
- });
1656
- if (stdout) {
1657
- repoOutput.push(stdout);
1658
- await this.logToFile(`[${entry.repoName}] [stdout] ${stdout}`);
1659
- }
1660
- if (stderr) {
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,
@@ -4599,7 +4641,8 @@ async function collectRepoWarmHooks() {
4599
4641
  repoName: repo.name,
4600
4642
  repoPath: repo.path,
4601
4643
  commands: warmHook.commands,
4602
- timeoutMs: warmHook.timeoutMs
4644
+ timeoutMs: warmHook.timeoutMs,
4645
+ separate: warmHook.separate
4603
4646
  });
4604
4647
  }
4605
4648
  collected.sort((a, b) => a.repoName.localeCompare(b.repoName));
@@ -4746,29 +4789,50 @@ async function runWarmHooksStreaming(params) {
4746
4789
  let repoFailed = false;
4747
4790
  let repoTimedOut = false;
4748
4791
  let repoExitCode = 0;
4749
- for (const command of repoHook.commands) {
4792
+ if (repoHook.separate === false) {
4793
+ const scriptWithErrExit = `set -e
4794
+ ${combinedScript}`;
4750
4795
  const repoResult = await executeHookScriptStreaming({
4751
4796
  label: `repo-warm-hook:${repoHook.repoName}`,
4752
4797
  cwd: repoHook.repoPath,
4753
- content: command,
4798
+ content: scriptWithErrExit,
4754
4799
  timeoutMs: repoHook.timeoutMs,
4755
4800
  onChunk: (chunk) => params.onEvent({ type: "output", data: chunk, label: `repo:${repoHook.repoName}` })
4756
4801
  });
4757
4802
  outputBlocks.push(repoResult.output);
4758
4803
  repoOutputBlocks.push(repoResult.output);
4759
- await environmentDetailsService.setRepositoryWarmHook(
4760
- repoHook.repoName,
4761
- repoResult.exitCode === 0 ? "yes" : "no"
4762
- );
4763
4804
  if (repoResult.exitCode !== 0) {
4764
4805
  repoFailed = true;
4765
4806
  repoTimedOut = repoResult.timedOut;
4766
4807
  repoExitCode = repoResult.exitCode;
4767
- break;
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
+ }
4768
4826
  }
4769
4827
  }
4828
+ await environmentDetailsService.setRepositoryWarmHook(
4829
+ repoHook.repoName,
4830
+ repoFailed ? "no" : "yes"
4831
+ );
4832
+ const loggedScript = repoHook.separate === false ? `set -e
4833
+ ${combinedScript}` : combinedScript;
4770
4834
  await warmHookLogsService.saveRepoHookLog(repoHook.repoName, {
4771
- hookScript: combinedScript,
4835
+ hookScript: loggedScript,
4772
4836
  output: repoOutputBlocks.join("\n\n"),
4773
4837
  exitCode: repoFailed ? repoExitCode : 0,
4774
4838
  timedOut: repoTimedOut,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.156",
3
+ "version": "0.1.159",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",