overlord-cli 0.2607171335.0 → 0.2607191132.0

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/index.js CHANGED
@@ -13538,6 +13538,87 @@ var init_discover_project_local = __esm({
13538
13538
  }
13539
13539
  });
13540
13540
 
13541
+ // src/pre-launch.ts
13542
+ function substitutePreLaunchVariables(commands, variables) {
13543
+ return commands.map(
13544
+ (command) => command.replace(PLACEHOLDER_PATTERN, (match, name) => {
13545
+ const value = variables[name];
13546
+ return value === void 0 ? match : value;
13547
+ })
13548
+ ).map((command) => command.trim()).filter((command) => command.length > 0);
13549
+ }
13550
+ function substituteLaunchEnvVars(envVars, variables) {
13551
+ const resolved = {};
13552
+ for (const [rawKey, rawValue] of Object.entries(envVars)) {
13553
+ const key = rawKey.trim();
13554
+ if (!key) continue;
13555
+ resolved[key] = rawValue.replace(PLACEHOLDER_PATTERN, (match, name) => {
13556
+ const value = variables[name];
13557
+ return value === void 0 ? match : value;
13558
+ });
13559
+ }
13560
+ return resolved;
13561
+ }
13562
+ function resourcePaths(projectResources) {
13563
+ if (!Array.isArray(projectResources)) return [];
13564
+ const paths = [];
13565
+ for (const entry of projectResources) {
13566
+ if (entry && typeof entry === "object") {
13567
+ const path27 = entry.path;
13568
+ if (typeof path27 === "string" && path27.trim()) paths.push(path27.trim());
13569
+ }
13570
+ }
13571
+ return paths;
13572
+ }
13573
+ function primaryResourcePath(projectResources) {
13574
+ if (!Array.isArray(projectResources)) return "";
13575
+ let currentPath = "";
13576
+ for (const entry of projectResources) {
13577
+ if (!entry || typeof entry !== "object") continue;
13578
+ const record = entry;
13579
+ const path27 = typeof record.path === "string" ? record.path.trim() : "";
13580
+ if (!path27) continue;
13581
+ if (record.isPrimary === true) return path27;
13582
+ if (record.isCurrent === true && !currentPath) currentPath = path27;
13583
+ }
13584
+ return currentPath;
13585
+ }
13586
+ function buildPreLaunchVariables({
13587
+ launchEnv = {},
13588
+ projectResources,
13589
+ workingDirectory,
13590
+ contextFile,
13591
+ tmpDir
13592
+ }) {
13593
+ const paths = resourcePaths(projectResources);
13594
+ const scratch = typeof tmpDir === "string" ? tmpDir.trim() : "";
13595
+ const cwd = typeof workingDirectory === "string" ? workingDirectory.trim() : "";
13596
+ const contextPath = typeof contextFile === "string" ? contextFile.trim() : "";
13597
+ return {
13598
+ // Optional Overlord env keys default to empty so documented placeholders
13599
+ // resolve rather than remaining as literal `{VAR}` tokens.
13600
+ OVERLORD_EXECUTION_REQUEST_ID: "",
13601
+ OVERLORD_PROJECT_RESOURCES: "",
13602
+ ...launchEnv,
13603
+ OVERLORD_PROJECT_RESOURCES_PATHS: paths.join(" "),
13604
+ OVERLORD_PROJECT_RESOURCES_PATHS_CSV: paths.join(","),
13605
+ OVERLORD_PRIMARY_RESOURCE_PATH: primaryResourcePath(projectResources),
13606
+ OVERLORD_WORKING_DIRECTORY: cwd,
13607
+ OVERLORD_CONTEXT_FILE: contextPath,
13608
+ OVERLORD_TMPDIR: scratch,
13609
+ TMPDIR: scratch,
13610
+ TMP: scratch,
13611
+ TEMP: scratch
13612
+ };
13613
+ }
13614
+ var PLACEHOLDER_PATTERN;
13615
+ var init_pre_launch = __esm({
13616
+ "src/pre-launch.ts"() {
13617
+ "use strict";
13618
+ PLACEHOLDER_PATTERN = /\{([A-Z0-9_]+)\}/g;
13619
+ }
13620
+ });
13621
+
13541
13622
  // src/terminal-launcher.ts
13542
13623
  function shellQuote2(value) {
13543
13624
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -13583,24 +13664,31 @@ function agentShellCommand({
13583
13664
  const assignments = Object.entries(extraEnv).filter(([key, value]) => key.trim() && value.trim()).map(([key, value]) => `${key}=${shellQuote2(value)}`).join(" ");
13584
13665
  return assignments ? `env ${assignments} ${invocation}` : invocation;
13585
13666
  }
13667
+ function withPreLaunchCommands(agentCommand, preLaunchCommands) {
13668
+ const pre = (preLaunchCommands ?? []).map((command) => command.trim()).filter(Boolean);
13669
+ return pre.length > 0 ? `${pre.join("; ")}; ${agentCommand}` : agentCommand;
13670
+ }
13586
13671
  function terminalInnerCommand({
13587
13672
  workingDirectory,
13588
13673
  agentCommand,
13589
- extraEnv = {}
13674
+ extraEnv = {},
13675
+ preLaunchCommands
13590
13676
  }) {
13591
13677
  const exports = Object.entries({ ...tmpEnvFor(workingDirectory), ...extraEnv }).map(([key, value]) => `export ${key}=${shellQuote2(value)}`).join("; ");
13592
- return `cd ${shellQuote2(workingDirectory)} && ${exports}; ${agentCommand}`;
13678
+ const invocation = withPreLaunchCommands(agentCommand, preLaunchCommands);
13679
+ return `cd ${shellQuote2(workingDirectory)} && ${exports}; ${invocation}`;
13593
13680
  }
13594
13681
  function terminalLaunchScriptContent({
13595
13682
  command,
13596
13683
  args,
13597
13684
  workingDirectory,
13598
13685
  preCommand,
13599
- extraEnv = {}
13686
+ extraEnv = {},
13687
+ preLaunchCommands
13600
13688
  }) {
13601
13689
  const agentCommand = agentShellCommand({ command, args, preCommand });
13602
13690
  return `#!/usr/bin/env bash
13603
- ${terminalInnerCommand({ workingDirectory, agentCommand, extraEnv })}
13691
+ ${terminalInnerCommand({ workingDirectory, agentCommand, extraEnv, preLaunchCommands })}
13604
13692
  `;
13605
13693
  }
13606
13694
  function terminalScriptCommand(scriptPath) {
@@ -13761,7 +13849,8 @@ function resolveLaunchExecution({
13761
13849
  terminalLaunchChord,
13762
13850
  terminalScriptPath,
13763
13851
  terminalLaunchBackground = false,
13764
- extraEnv = {}
13852
+ extraEnv = {},
13853
+ preLaunchCommands
13765
13854
  }) {
13766
13855
  const agentCommand = agentShellCommand({ command, args, preCommand });
13767
13856
  const genericAgentCommand = agentShellCommand({
@@ -13771,13 +13860,23 @@ function resolveLaunchExecution({
13771
13860
  extraEnv,
13772
13861
  includeEnvPrefix: true
13773
13862
  });
13863
+ const hasPreLaunch = (preLaunchCommands ?? []).some((entry) => entry.trim().length > 0);
13774
13864
  const launcher = terminalLauncher?.trim();
13775
13865
  const placement = terminalLaunchPlacement ?? "window";
13776
13866
  const chordClause = resolveChordClause(terminalLaunchChord);
13777
13867
  if (!launcher) {
13778
- return preCommand?.trim() ? { command: agentCommand, args: [], useShell: true, terminal: null, display: agentCommand } : { command, args, useShell: false, terminal: null, display: agentCommand };
13868
+ if (!preCommand?.trim() && !hasPreLaunch) {
13869
+ return { command, args, useShell: false, terminal: null, display: agentCommand };
13870
+ }
13871
+ const inline = withPreLaunchCommands(agentCommand, preLaunchCommands);
13872
+ return { command: inline, args: [], useShell: true, terminal: null, display: inline };
13779
13873
  }
13780
- const inner = terminalInnerCommand({ workingDirectory, agentCommand, extraEnv });
13874
+ const inner = terminalInnerCommand({
13875
+ workingDirectory,
13876
+ agentCommand,
13877
+ extraEnv,
13878
+ preLaunchCommands
13879
+ });
13781
13880
  const terminalInner = terminalScriptPath?.trim() ? terminalScriptCommand(terminalScriptPath.trim()) : inner;
13782
13881
  const builtin = resolveBuiltinTerminal(launcher);
13783
13882
  if (builtin === "terminal") {
@@ -13811,7 +13910,7 @@ function resolveLaunchExecution({
13811
13910
  display: `iTerm2 (${placement}) \u203A ${terminalInner}`
13812
13911
  };
13813
13912
  }
13814
- const genericTerminalCommand = terminalScriptPath?.trim() ? terminalScriptCommand(terminalScriptPath.trim()) : genericAgentCommand;
13913
+ const genericTerminalCommand = terminalScriptPath?.trim() ? terminalScriptCommand(terminalScriptPath.trim()) : withPreLaunchCommands(genericAgentCommand, preLaunchCommands);
13815
13914
  const full = placement === "window" ? `${terminalLaunchBackground ? backgroundLauncher(launcher) : launcher} ${genericTerminalCommand}` : buildGenericPlacementShell({
13816
13915
  launcher,
13817
13916
  inner: genericTerminalCommand,
@@ -14000,6 +14099,16 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
14000
14099
  executionRequestId: options.executionRequestId,
14001
14100
  projectResources
14002
14101
  });
14102
+ const launchVariables = buildPreLaunchVariables({
14103
+ launchEnv,
14104
+ projectResources,
14105
+ workingDirectory: options.workingDirectory,
14106
+ contextFile,
14107
+ tmpDir
14108
+ });
14109
+ const preLaunchCommands = options.preLaunchCommands && options.preLaunchCommands.length > 0 ? substitutePreLaunchVariables(options.preLaunchCommands, launchVariables) : [];
14110
+ const resolvedEnvVars = options.launchEnvVars && Object.keys(options.launchEnvVars).length > 0 ? substituteLaunchEnvVars(options.launchEnvVars, launchVariables) : {};
14111
+ const exportedEnv = { ...launchEnv, ...resolvedEnvVars };
14003
14112
  const prompt = launchContext.length > 4e3 ? `Read the Overlord context file at ${contextFile}, attach to mission ${context.displayId}, then immediately execute its current objective. Do not wait for more instructions.` : launchContext;
14004
14113
  const command = buildAgentCommand({
14005
14114
  agent: options.agent,
@@ -14023,7 +14132,8 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
14023
14132
  args: command.args,
14024
14133
  workingDirectory: options.workingDirectory,
14025
14134
  preCommand: options.preCommand,
14026
- extraEnv: launchEnv
14135
+ extraEnv: exportedEnv,
14136
+ preLaunchCommands
14027
14137
  })
14028
14138
  );
14029
14139
  chmodSync3(terminalScriptPath, 448);
@@ -14038,7 +14148,8 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
14038
14148
  terminalLaunchChord: options.terminalLaunchChord,
14039
14149
  terminalLaunchBackground: options.terminalLaunchBackground,
14040
14150
  terminalScriptPath,
14041
- extraEnv: launchEnv
14151
+ extraEnv: exportedEnv,
14152
+ preLaunchCommands
14042
14153
  });
14043
14154
  return {
14044
14155
  ...command,
@@ -14046,7 +14157,7 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
14046
14157
  contextFile,
14047
14158
  workingDirectory: options.workingDirectory,
14048
14159
  execution,
14049
- env: launchEnv
14160
+ env: exportedEnv
14050
14161
  };
14051
14162
  }
14052
14163
  async function launchAgent({
@@ -14080,6 +14191,7 @@ var init_launch = __esm({
14080
14191
  "src/launch.ts"() {
14081
14192
  "use strict";
14082
14193
  init_agent_binaries();
14194
+ init_pre_launch();
14083
14195
  init_project_tmp();
14084
14196
  init_terminal_launcher();
14085
14197
  AGENT_HISTORY_EXCLUDED_EVENT_TYPES = /* @__PURE__ */ new Set([
@@ -16422,6 +16534,17 @@ async function resolveTerminalLaunchSettings({
16422
16534
  function asRecord2(value) {
16423
16535
  return value && typeof value === "object" ? value : {};
16424
16536
  }
16537
+ function parsePreLaunchCommandsValue(value) {
16538
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
16539
+ }
16540
+ function parseLaunchEnvVarsValue(value) {
16541
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
16542
+ const vars = {};
16543
+ for (const [key, raw] of Object.entries(value)) {
16544
+ if (key.trim() && typeof raw === "string") vars[key] = raw;
16545
+ }
16546
+ return vars;
16547
+ }
16425
16548
  async function readWorktreeBranchAutomationEnabled(runtime) {
16426
16549
  try {
16427
16550
  const settings = await runtime.backend.get("/api/launch-settings");
@@ -16933,6 +17056,16 @@ async function runManagementCommand({
16933
17056
  const executionTargetId = await resolvePreferredExecutionTargetId({
16934
17057
  backend: scopedRuntime.backend
16935
17058
  });
17059
+ const missionProjectId = asRecord2(mission).projectId;
17060
+ const projectLaunchSettings = typeof missionProjectId === "string" ? await scopedRuntime.backend.get(`/api/projects/${encodeURIComponent(missionProjectId)}`).then((project) => ({
17061
+ preLaunchCommands: parsePreLaunchCommandsValue(asRecord2(project).preLaunchCommands),
17062
+ launchEnvVars: parseLaunchEnvVarsValue(asRecord2(project).launchEnvVars)
17063
+ })).catch(() => ({
17064
+ preLaunchCommands: [],
17065
+ launchEnvVars: {}
17066
+ })) : { preLaunchCommands: [], launchEnvVars: {} };
17067
+ const preLaunchCommands = projectLaunchSettings.preLaunchCommands;
17068
+ const launchEnvVars = projectLaunchSettings.launchEnvVars;
16936
17069
  const prepared = await prepareMissionBranch({
16937
17070
  runtime: scopedRuntime,
16938
17071
  options: {
@@ -16960,6 +17093,8 @@ async function runManagementCommand({
16960
17093
  thinking: flagValue(parsed.flags, "--thinking"),
16961
17094
  flags: repeatedFlagValues(rest, "--flag"),
16962
17095
  preCommand: flagValue(parsed.flags, "--pre-command"),
17096
+ preLaunchCommands: preLaunchCommands.length > 0 ? preLaunchCommands : void 0,
17097
+ launchEnvVars: Object.keys(launchEnvVars).length > 0 ? launchEnvVars : void 0,
16963
17098
  executionTargetId: executionTargetId ?? void 0,
16964
17099
  ...terminal,
16965
17100
  dryRun
@@ -17182,6 +17317,10 @@ async function runRunnerCommand({
17182
17317
  thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
17183
17318
  flags: Array.isArray(launchConfig.flags) ? launchConfig.flags.filter((value) => typeof value === "string") : [],
17184
17319
  preCommand: typeof launchConfig.preCommand === "string" ? launchConfig.preCommand : void 0,
17320
+ preLaunchCommands: Array.isArray(requestRecord.preLaunchCommands) ? requestRecord.preLaunchCommands.filter(
17321
+ (value) => typeof value === "string"
17322
+ ) : void 0,
17323
+ launchEnvVars: parseLaunchEnvVarsValue(requestRecord.launchEnvVars),
17185
17324
  executionRequestId: requestId,
17186
17325
  executionTargetId: executionTargetId || void 0,
17187
17326
  ...terminal,