overlord-cli 0.2607161024.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
@@ -3228,7 +3228,8 @@ function terminalProfileFromResponse(body) {
3228
3228
  return {
3229
3229
  launcher: profile.launcher ?? null,
3230
3230
  placement: profile.placement ?? "window",
3231
- chord: profile.chord ?? null
3231
+ chord: profile.chord ?? null,
3232
+ background: profile.background ?? false
3232
3233
  };
3233
3234
  }
3234
3235
  function readLegacyTerminalProfileFromToml() {
@@ -3273,7 +3274,8 @@ function terminalProfileToLaunchSettings(profile) {
3273
3274
  return {
3274
3275
  terminalLauncher: profile.launcher,
3275
3276
  terminalLaunchPlacement: profile.placement,
3276
- terminalLaunchChord: profile.chord
3277
+ terminalLaunchChord: profile.chord,
3278
+ terminalLaunchBackground: profile.background ?? false
3277
3279
  };
3278
3280
  }
3279
3281
  var init_terminal_profile = __esm({
@@ -13536,6 +13538,87 @@ var init_discover_project_local = __esm({
13536
13538
  }
13537
13539
  });
13538
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
+
13539
13622
  // src/terminal-launcher.ts
13540
13623
  function shellQuote2(value) {
13541
13624
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -13581,24 +13664,31 @@ function agentShellCommand({
13581
13664
  const assignments = Object.entries(extraEnv).filter(([key, value]) => key.trim() && value.trim()).map(([key, value]) => `${key}=${shellQuote2(value)}`).join(" ");
13582
13665
  return assignments ? `env ${assignments} ${invocation}` : invocation;
13583
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
+ }
13584
13671
  function terminalInnerCommand({
13585
13672
  workingDirectory,
13586
13673
  agentCommand,
13587
- extraEnv = {}
13674
+ extraEnv = {},
13675
+ preLaunchCommands
13588
13676
  }) {
13589
13677
  const exports = Object.entries({ ...tmpEnvFor(workingDirectory), ...extraEnv }).map(([key, value]) => `export ${key}=${shellQuote2(value)}`).join("; ");
13590
- return `cd ${shellQuote2(workingDirectory)} && ${exports}; ${agentCommand}`;
13678
+ const invocation = withPreLaunchCommands(agentCommand, preLaunchCommands);
13679
+ return `cd ${shellQuote2(workingDirectory)} && ${exports}; ${invocation}`;
13591
13680
  }
13592
13681
  function terminalLaunchScriptContent({
13593
13682
  command,
13594
13683
  args,
13595
13684
  workingDirectory,
13596
13685
  preCommand,
13597
- extraEnv = {}
13686
+ extraEnv = {},
13687
+ preLaunchCommands
13598
13688
  }) {
13599
13689
  const agentCommand = agentShellCommand({ command, args, preCommand });
13600
13690
  return `#!/usr/bin/env bash
13601
- ${terminalInnerCommand({ workingDirectory, agentCommand, extraEnv })}
13691
+ ${terminalInnerCommand({ workingDirectory, agentCommand, extraEnv, preLaunchCommands })}
13602
13692
  `;
13603
13693
  }
13604
13694
  function terminalScriptCommand(scriptPath) {
@@ -13615,13 +13705,21 @@ function resolveItermSplitKind(chord) {
13615
13705
  }
13616
13706
  return "keystroke";
13617
13707
  }
13708
+ function shouldActivateTerminal(placement, background) {
13709
+ return !background || placement === "chord";
13710
+ }
13711
+ function backgroundLauncher(launcher) {
13712
+ if (!/^open(\s|$)/.test(launcher.trim()) || /\s-g(\s|$)/.test(launcher)) return launcher;
13713
+ return launcher.replace(/^(\s*open)\b/, "$1 -g");
13714
+ }
13618
13715
  function buildItermAppleScript({
13619
13716
  inner,
13620
13717
  placement,
13621
13718
  chordClause,
13622
- chord
13719
+ chord,
13720
+ background
13623
13721
  }) {
13624
- const lines2 = ['tell application "iTerm"', "activate"];
13722
+ const lines2 = shouldActivateTerminal(placement, background) ? ['tell application "iTerm"', "activate"] : ['tell application "iTerm"'];
13625
13723
  if (placement === "window") {
13626
13724
  lines2.push(
13627
13725
  "set newWindow to (create window with default profile)",
@@ -13675,9 +13773,10 @@ function buildItermAppleScript({
13675
13773
  function buildTerminalAppleScript({
13676
13774
  inner,
13677
13775
  placement,
13678
- chordClause
13776
+ chordClause,
13777
+ background
13679
13778
  }) {
13680
- const lines2 = ['tell application "Terminal"', "activate"];
13779
+ const lines2 = shouldActivateTerminal(placement, background) ? ['tell application "Terminal"', "activate"] : ['tell application "Terminal"'];
13681
13780
  if (placement === "window") {
13682
13781
  lines2.push(`do script ${appleScriptString(inner)}`);
13683
13782
  } else if (placement === "tab") {
@@ -13708,10 +13807,11 @@ function buildGenericPlacementShell({
13708
13807
  launcher,
13709
13808
  inner,
13710
13809
  placement,
13711
- chordClause
13810
+ chordClause,
13811
+ background
13712
13812
  }) {
13713
13813
  const appName = extractAppNameFromLauncher(launcher);
13714
- const launch = `${launcher} ${inner}`;
13814
+ const launch = `${background ? backgroundLauncher(launcher) : launcher} ${inner}`;
13715
13815
  if (placement === "window" || !appName) {
13716
13816
  return launch;
13717
13817
  }
@@ -13748,7 +13848,9 @@ function resolveLaunchExecution({
13748
13848
  terminalLaunchPlacement = "window",
13749
13849
  terminalLaunchChord,
13750
13850
  terminalScriptPath,
13751
- extraEnv = {}
13851
+ terminalLaunchBackground = false,
13852
+ extraEnv = {},
13853
+ preLaunchCommands
13752
13854
  }) {
13753
13855
  const agentCommand = agentShellCommand({ command, args, preCommand });
13754
13856
  const genericAgentCommand = agentShellCommand({
@@ -13758,20 +13860,31 @@ function resolveLaunchExecution({
13758
13860
  extraEnv,
13759
13861
  includeEnvPrefix: true
13760
13862
  });
13863
+ const hasPreLaunch = (preLaunchCommands ?? []).some((entry) => entry.trim().length > 0);
13761
13864
  const launcher = terminalLauncher?.trim();
13762
13865
  const placement = terminalLaunchPlacement ?? "window";
13763
13866
  const chordClause = resolveChordClause(terminalLaunchChord);
13764
13867
  if (!launcher) {
13765
- 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 };
13766
13873
  }
13767
- const inner = terminalInnerCommand({ workingDirectory, agentCommand, extraEnv });
13874
+ const inner = terminalInnerCommand({
13875
+ workingDirectory,
13876
+ agentCommand,
13877
+ extraEnv,
13878
+ preLaunchCommands
13879
+ });
13768
13880
  const terminalInner = terminalScriptPath?.trim() ? terminalScriptCommand(terminalScriptPath.trim()) : inner;
13769
13881
  const builtin = resolveBuiltinTerminal(launcher);
13770
13882
  if (builtin === "terminal") {
13771
13883
  const script = buildTerminalAppleScript({
13772
13884
  inner: terminalInner,
13773
13885
  placement,
13774
- chordClause
13886
+ chordClause,
13887
+ background: terminalLaunchBackground
13775
13888
  });
13776
13889
  return {
13777
13890
  command: "osascript",
@@ -13786,7 +13899,8 @@ function resolveLaunchExecution({
13786
13899
  inner: terminalInner,
13787
13900
  placement,
13788
13901
  chordClause,
13789
- chord: terminalLaunchChord
13902
+ chord: terminalLaunchChord,
13903
+ background: terminalLaunchBackground
13790
13904
  });
13791
13905
  return {
13792
13906
  command: "osascript",
@@ -13796,12 +13910,13 @@ function resolveLaunchExecution({
13796
13910
  display: `iTerm2 (${placement}) \u203A ${terminalInner}`
13797
13911
  };
13798
13912
  }
13799
- const genericTerminalCommand = terminalScriptPath?.trim() ? terminalScriptCommand(terminalScriptPath.trim()) : genericAgentCommand;
13800
- const full = placement === "window" ? `${launcher} ${genericTerminalCommand}` : buildGenericPlacementShell({
13913
+ const genericTerminalCommand = terminalScriptPath?.trim() ? terminalScriptCommand(terminalScriptPath.trim()) : withPreLaunchCommands(genericAgentCommand, preLaunchCommands);
13914
+ const full = placement === "window" ? `${terminalLaunchBackground ? backgroundLauncher(launcher) : launcher} ${genericTerminalCommand}` : buildGenericPlacementShell({
13801
13915
  launcher,
13802
13916
  inner: genericTerminalCommand,
13803
13917
  placement,
13804
- chordClause
13918
+ chordClause,
13919
+ background: terminalLaunchBackground
13805
13920
  });
13806
13921
  return { command: full, args: [], useShell: true, terminal: launcher, display: full };
13807
13922
  }
@@ -13984,6 +14099,16 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
13984
14099
  executionRequestId: options.executionRequestId,
13985
14100
  projectResources
13986
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 };
13987
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;
13988
14113
  const command = buildAgentCommand({
13989
14114
  agent: options.agent,
@@ -14007,7 +14132,8 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
14007
14132
  args: command.args,
14008
14133
  workingDirectory: options.workingDirectory,
14009
14134
  preCommand: options.preCommand,
14010
- extraEnv: launchEnv
14135
+ extraEnv: exportedEnv,
14136
+ preLaunchCommands
14011
14137
  })
14012
14138
  );
14013
14139
  chmodSync3(terminalScriptPath, 448);
@@ -14020,8 +14146,10 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
14020
14146
  terminalLauncher: options.terminalLauncher,
14021
14147
  terminalLaunchPlacement: options.terminalLaunchPlacement,
14022
14148
  terminalLaunchChord: options.terminalLaunchChord,
14149
+ terminalLaunchBackground: options.terminalLaunchBackground,
14023
14150
  terminalScriptPath,
14024
- extraEnv: launchEnv
14151
+ extraEnv: exportedEnv,
14152
+ preLaunchCommands
14025
14153
  });
14026
14154
  return {
14027
14155
  ...command,
@@ -14029,7 +14157,7 @@ Execution request: ${options.executionRequestId}` : context.launchContext;
14029
14157
  contextFile,
14030
14158
  workingDirectory: options.workingDirectory,
14031
14159
  execution,
14032
- env: launchEnv
14160
+ env: exportedEnv
14033
14161
  };
14034
14162
  }
14035
14163
  async function launchAgent({
@@ -14063,6 +14191,7 @@ var init_launch = __esm({
14063
14191
  "src/launch.ts"() {
14064
14192
  "use strict";
14065
14193
  init_agent_binaries();
14194
+ init_pre_launch();
14066
14195
  init_project_tmp();
14067
14196
  init_terminal_launcher();
14068
14197
  AGENT_HISTORY_EXCLUDED_EVENT_TYPES = /* @__PURE__ */ new Set([
@@ -16388,7 +16517,8 @@ async function resolveTerminalLaunchSettings({
16388
16517
  return {
16389
16518
  terminalLauncher: override,
16390
16519
  terminalLaunchPlacement: profile.placement,
16391
- terminalLaunchChord: profile.chord
16520
+ terminalLaunchChord: profile.chord,
16521
+ terminalLaunchBackground: profile.background ?? false
16392
16522
  };
16393
16523
  } catch {
16394
16524
  return { terminalLauncher: override };
@@ -16404,6 +16534,17 @@ async function resolveTerminalLaunchSettings({
16404
16534
  function asRecord2(value) {
16405
16535
  return value && typeof value === "object" ? value : {};
16406
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
+ }
16407
16548
  async function readWorktreeBranchAutomationEnabled(runtime) {
16408
16549
  try {
16409
16550
  const settings = await runtime.backend.get("/api/launch-settings");
@@ -16915,6 +17056,16 @@ async function runManagementCommand({
16915
17056
  const executionTargetId = await resolvePreferredExecutionTargetId({
16916
17057
  backend: scopedRuntime.backend
16917
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;
16918
17069
  const prepared = await prepareMissionBranch({
16919
17070
  runtime: scopedRuntime,
16920
17071
  options: {
@@ -16942,6 +17093,8 @@ async function runManagementCommand({
16942
17093
  thinking: flagValue(parsed.flags, "--thinking"),
16943
17094
  flags: repeatedFlagValues(rest, "--flag"),
16944
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,
16945
17098
  executionTargetId: executionTargetId ?? void 0,
16946
17099
  ...terminal,
16947
17100
  dryRun
@@ -17164,6 +17317,10 @@ async function runRunnerCommand({
17164
17317
  thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
17165
17318
  flags: Array.isArray(launchConfig.flags) ? launchConfig.flags.filter((value) => typeof value === "string") : [],
17166
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),
17167
17324
  executionRequestId: requestId,
17168
17325
  executionTargetId: executionTargetId || void 0,
17169
17326
  ...terminal,