overlord-cli 0.2608211014.0 → 0.2608211436.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
@@ -3311,6 +3311,48 @@ var init_terminal_launch_chord = __esm({
3311
3311
  }
3312
3312
  });
3313
3313
 
3314
+ // src/launch-settings.ts
3315
+ function launchSettingsPath(workspaceId, suffix = "") {
3316
+ const id = workspaceId?.trim();
3317
+ return id ? `/api/workspaces/${encodeURIComponent(id)}/launch-settings${suffix}` : `/api/launch-settings${suffix}`;
3318
+ }
3319
+ async function resolveLaunchSettingsWorkspaceId({
3320
+ backend,
3321
+ workspaceId
3322
+ }) {
3323
+ const explicit = workspaceId?.trim();
3324
+ if (explicit) return explicit;
3325
+ const cached = cachedWorkspaceIds.get(backend);
3326
+ if (cached !== void 0) return cached;
3327
+ let resolved = null;
3328
+ try {
3329
+ const workspaces = await backend.get("/api/workspaces");
3330
+ const rows = Array.isArray(workspaces) ? workspaces : [];
3331
+ const ids = rows.map((row) => typeof row.id === "string" && row.id.trim() ? row.id.trim() : null).filter((id) => id !== null);
3332
+ const active = rows.find((row) => row.isActive === true);
3333
+ const activeId = active && typeof active.id === "string" && active.id.trim() ? active.id.trim() : null;
3334
+ resolved = activeId ?? (ids.length === 1 ? ids[0] ?? null : null);
3335
+ } catch {
3336
+ resolved = null;
3337
+ }
3338
+ cachedWorkspaceIds.set(backend, resolved);
3339
+ return resolved;
3340
+ }
3341
+ async function fetchLaunchSettings({
3342
+ backend,
3343
+ workspaceId
3344
+ }) {
3345
+ const resolved = await resolveLaunchSettingsWorkspaceId({ backend, workspaceId });
3346
+ return backend.get(launchSettingsPath(resolved));
3347
+ }
3348
+ var cachedWorkspaceIds;
3349
+ var init_launch_settings = __esm({
3350
+ "src/launch-settings.ts"() {
3351
+ "use strict";
3352
+ cachedWorkspaceIds = /* @__PURE__ */ new WeakMap();
3353
+ }
3354
+ });
3355
+
3314
3356
  // src/terminal-profile-types.js
3315
3357
  var DEFAULT_TERMINAL_PROFILE, EMPTY_TERMINAL_PROFILE;
3316
3358
  var init_terminal_profile_types = __esm({
@@ -3350,9 +3392,10 @@ function readLegacyTerminalProfileFromToml() {
3350
3392
  };
3351
3393
  }
3352
3394
  async function fetchTerminalProfile({
3353
- backend
3395
+ backend,
3396
+ workspaceId
3354
3397
  }) {
3355
- const response = await backend.get("/api/launch-settings");
3398
+ const response = await fetchLaunchSettings({ backend, workspaceId });
3356
3399
  const profile = terminalProfileFromResponse(response);
3357
3400
  return {
3358
3401
  ...profile,
@@ -3362,10 +3405,12 @@ async function fetchTerminalProfile({
3362
3405
  }
3363
3406
  async function saveTerminalProfile({
3364
3407
  backend,
3365
- profile
3408
+ profile,
3409
+ workspaceId
3366
3410
  }) {
3411
+ const resolved = await resolveLaunchSettingsWorkspaceId({ backend, workspaceId });
3367
3412
  const response = await backend.patch({
3368
- path: "/api/launch-settings/terminal-profile",
3413
+ path: launchSettingsPath(resolved, "/terminal-profile"),
3369
3414
  body: profile
3370
3415
  });
3371
3416
  const saved = terminalProfileFromResponse(response);
@@ -3388,6 +3433,7 @@ var init_terminal_profile = __esm({
3388
3433
  "use strict";
3389
3434
  init_dist();
3390
3435
  init_config();
3436
+ init_launch_settings();
3391
3437
  init_terminal_launch_chord();
3392
3438
  init_terminal_profile_types();
3393
3439
  }
@@ -17263,6 +17309,8 @@ function readBinaryMtimeMs(resolvedPath) {
17263
17309
  }
17264
17310
  }
17265
17311
  function isLatchDiscoveryCacheFresh({ entry, resolvePath = resolveLatchExecutablePath }) {
17312
+ if (entry.supportedProtocolVersion !== SUPPORTED_LATCH_PROTOCOL_VERSION)
17313
+ return false;
17266
17314
  const { result } = entry;
17267
17315
  if (result.state === "not_installed") {
17268
17316
  return resolvePath(result.executable) === null;
@@ -17277,15 +17325,15 @@ function isLatchDiscoveryCacheFresh({ entry, resolvePath = resolveLatchExecutabl
17277
17325
  }
17278
17326
  function readLatchDiscoveryCacheFile(filePath) {
17279
17327
  if (!existsSync16(filePath))
17280
- return { version: 1, entries: {} };
17328
+ return { version: 2, entries: {} };
17281
17329
  try {
17282
17330
  const parsed = JSON.parse(readFileSync15(filePath, "utf8"));
17283
- if (parsed.version !== 1 || !parsed.entries || typeof parsed.entries !== "object") {
17284
- return { version: 1, entries: {} };
17331
+ if (parsed.version !== 2 || !parsed.entries || typeof parsed.entries !== "object") {
17332
+ return { version: 2, entries: {} };
17285
17333
  }
17286
- return { version: 1, entries: parsed.entries };
17334
+ return { version: 2, entries: parsed.entries };
17287
17335
  } catch {
17288
- return { version: 1, entries: {} };
17336
+ return { version: 2, entries: {} };
17289
17337
  }
17290
17338
  }
17291
17339
  function writeLatchDiscoveryCacheFile({ filePath, cache: cache2 }) {
@@ -17305,6 +17353,7 @@ function discoverLatchForExecutionTarget({ executionTargetId, executable = DEFAU
17305
17353
  const binaryMtimeMs = result.state === "not_installed" ? null : readBinaryMtimeMs(result.resolvedPath);
17306
17354
  cache2.entries[key] = {
17307
17355
  result,
17356
+ supportedProtocolVersion: SUPPORTED_LATCH_PROTOCOL_VERSION,
17308
17357
  binaryMtimeMs,
17309
17358
  cachedAt: result.checkedAt
17310
17359
  };
@@ -19137,12 +19186,14 @@ var init_branch_preparation = __esm({
19137
19186
  // src/discover-project-local.ts
19138
19187
  import path22 from "node:path";
19139
19188
  async function resolvePreferredExecutionTargetId({
19140
- backend
19189
+ backend,
19190
+ workspaceId
19141
19191
  }) {
19142
19192
  try {
19143
- const launchSettings = await backend.get(
19144
- "/api/launch-settings"
19145
- );
19193
+ const launchSettings = await fetchLaunchSettings({
19194
+ backend,
19195
+ workspaceId
19196
+ });
19146
19197
  return typeof launchSettings.executionTargetId === "string" ? launchSettings.executionTargetId : null;
19147
19198
  } catch {
19148
19199
  return null;
@@ -19330,6 +19381,7 @@ var init_discover_project_local = __esm({
19330
19381
  "use strict";
19331
19382
  init_project_metadata();
19332
19383
  init_errors();
19384
+ init_launch_settings();
19333
19385
  }
19334
19386
  });
19335
19387
 
@@ -22843,12 +22895,15 @@ var init_session_key = __esm({
22843
22895
  var commands_exports = {};
22844
22896
  __export(commands_exports, {
22845
22897
  firstObjectiveId: () => firstObjectiveId,
22898
+ launchFailureMessage: () => launchFailureMessage,
22846
22899
  queueableObjectiveId: () => queueableObjectiveId,
22847
22900
  resolveActiveObjectiveId: () => resolveActiveObjectiveId,
22848
22901
  resolveProtocolFileInputs: () => resolveProtocolFileInputs,
22849
22902
  runManagementCommand: () => runManagementCommand,
22850
22903
  runProtocolCommand: () => runProtocolCommand,
22851
- runRunnerOnce: () => runRunnerOnce
22904
+ runRunnerOnce: () => runRunnerOnce,
22905
+ sanitizeRunnerFailureMessage: () => sanitizeRunnerFailureMessage,
22906
+ shouldRefuseInlineRunnerLaunch: () => shouldRefuseInlineRunnerLaunch
22852
22907
  });
22853
22908
  import { existsSync as existsSync29, readFileSync as readFileSync24 } from "node:fs";
22854
22909
  import os6 from "node:os";
@@ -23149,30 +23204,27 @@ function repeatedLaunchFlags(args, name) {
23149
23204
  }
23150
23205
  async function resolveTerminalLaunchSettings({
23151
23206
  runtime,
23152
- flags
23207
+ flags,
23208
+ workspaceId
23153
23209
  }) {
23154
23210
  if (flagBoolean(flags, "--no-terminal")) {
23155
23211
  return { terminalLauncher: null };
23156
23212
  }
23157
23213
  const override = flagValue(flags, "--terminal");
23158
- if (override) {
23159
- try {
23160
- const profile = await fetchTerminalProfile({ backend: runtime.backend });
23161
- return {
23162
- terminalLauncher: override,
23163
- terminalLaunchPlacement: profile.placement,
23164
- terminalLaunchChord: profile.chord,
23165
- terminalLaunchBackground: profile.background ?? false
23166
- };
23167
- } catch {
23168
- return { terminalLauncher: override };
23169
- }
23170
- }
23171
23214
  try {
23172
- const profile = await fetchTerminalProfile({ backend: runtime.backend });
23173
- return terminalProfileToLaunchSettings(profile);
23174
- } catch {
23175
- return { terminalLauncher: null };
23215
+ const profile = await fetchTerminalProfile({ backend: runtime.backend, workspaceId });
23216
+ if (!override) return terminalProfileToLaunchSettings(profile);
23217
+ return {
23218
+ terminalLauncher: override,
23219
+ terminalLaunchPlacement: profile.placement,
23220
+ terminalLaunchChord: profile.chord,
23221
+ terminalLaunchBackground: profile.background ?? false
23222
+ };
23223
+ } catch (error) {
23224
+ console.error(
23225
+ `[overlord] Could not read your terminal profile (${error instanceof Error ? error.message : String(error)}). ${override ? `Launching with --terminal ${override} and default placement.` : "The agent will run inline in this process instead of a new terminal window/tab."}`
23226
+ );
23227
+ return override ? { terminalLauncher: override } : { terminalLauncher: null };
23176
23228
  }
23177
23229
  }
23178
23230
  function asRecord5(value) {
@@ -23189,11 +23241,17 @@ function parseLaunchEnvVarsValue(value) {
23189
23241
  }
23190
23242
  return vars;
23191
23243
  }
23192
- async function readWorktreeBranchAutomationEnabled(runtime) {
23244
+ async function readWorktreeBranchAutomationEnabled(runtime, workspaceId) {
23193
23245
  try {
23194
- const settings = await runtime.backend.get("/api/launch-settings");
23246
+ const settings = await fetchLaunchSettings({
23247
+ backend: runtime.backend,
23248
+ workspaceId
23249
+ });
23195
23250
  return settings.worktreeBranchAutomationEnabled === true;
23196
- } catch {
23251
+ } catch (error) {
23252
+ console.error(
23253
+ `[overlord] Could not read launch settings (${error instanceof Error ? error.message : String(error)}); continuing with worktree branch automation disabled.`
23254
+ );
23197
23255
  return false;
23198
23256
  }
23199
23257
  }
@@ -23869,13 +23927,16 @@ async function runManagementCommand({
23869
23927
  `/api/missions/${encodeURIComponent(missionId)}`
23870
23928
  );
23871
23929
  const scopedRuntime = runtime;
23930
+ const missionWorkspaceId = typeof asRecord5(mission).workspaceId === "string" ? asRecord5(mission).workspaceId : null;
23872
23931
  const terminal = await resolveTerminalLaunchSettings({
23873
23932
  runtime: scopedRuntime,
23874
- flags: parsed.flags
23933
+ flags: parsed.flags,
23934
+ workspaceId: missionWorkspaceId
23875
23935
  });
23876
23936
  const objectiveId = objectiveRef ? objectiveIdForRef(mission, objectiveRef) : resolveActiveObjectiveId(mission);
23877
23937
  const executionTargetId = await resolvePreferredExecutionTargetId({
23878
- backend: scopedRuntime.backend
23938
+ backend: scopedRuntime.backend,
23939
+ workspaceId: missionWorkspaceId
23879
23940
  });
23880
23941
  const missionProjectId = asRecord5(mission).projectId;
23881
23942
  const projectLaunchSettings = typeof missionProjectId === "string" ? await scopedRuntime.backend.get(`/api/projects/${encodeURIComponent(missionProjectId)}`).then((project) => ({
@@ -23893,7 +23954,10 @@ async function runManagementCommand({
23893
23954
  missionId,
23894
23955
  workingDirectory,
23895
23956
  objectiveId: objectiveId ?? void 0,
23896
- workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(scopedRuntime),
23957
+ workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(
23958
+ scopedRuntime,
23959
+ missionWorkspaceId
23960
+ ),
23897
23961
  dryRun,
23898
23962
  overrideBranch: flagValue(parsed.flags, "--branch"),
23899
23963
  noWorktree: flagBoolean(parsed.flags, "--no-worktree")
@@ -23926,7 +23990,13 @@ async function runManagementCommand({
23926
23990
  printJson({ plan: result.plan, status: result.status, signal: result.signal });
23927
23991
  }
23928
23992
  if (result.status && result.status !== 0) {
23929
- throw new CliError({ message: `Launch command exited with status ${result.status}` });
23993
+ throw new CliError({
23994
+ message: launchFailureMessage({
23995
+ status: result.status,
23996
+ execution: result.plan.execution,
23997
+ terminal
23998
+ })
23999
+ });
23930
24000
  }
23931
24001
  return;
23932
24002
  }
@@ -24173,10 +24243,32 @@ async function runManagementCommand({
24173
24243
  throw new CliError({ message: `Unknown command: ${command}` });
24174
24244
  }
24175
24245
  }
24246
+ function sanitizeRunnerFailureMessage(value) {
24247
+ const redacted = redactSecrets(value);
24248
+ return redacted.length <= MAX_RUNNER_FAILURE_MESSAGE_LENGTH ? redacted : `${redacted.slice(0, MAX_RUNNER_FAILURE_MESSAGE_LENGTH)}\u2026 [truncated]`;
24249
+ }
24250
+ function launchFailureMessage({
24251
+ status,
24252
+ execution,
24253
+ terminal
24254
+ }) {
24255
+ const where = execution.terminal ? `in ${execution.terminal}${terminal.terminalLaunchPlacement ? ` (${terminal.terminalLaunchPlacement})` : ""}` : "inline (no terminal launcher resolved for this device)";
24256
+ return sanitizeRunnerFailureMessage(
24257
+ `Launch command exited with status ${status ?? "unknown"} running ${where}: ${execution.display}`
24258
+ );
24259
+ }
24176
24260
  function isNoExecutionTargetRegisteredError(error) {
24177
24261
  const message = error instanceof Error ? error.message : String(error);
24178
24262
  return message.includes("(no_execution_target_registered)");
24179
24263
  }
24264
+ function shouldRefuseInlineRunnerLaunch({
24265
+ dryRun,
24266
+ terminalLauncher,
24267
+ executionProvider,
24268
+ stdoutIsTTY
24269
+ }) {
24270
+ return !dryRun && !terminalLauncher && executionProvider !== "latch" && !stdoutIsTTY;
24271
+ }
24180
24272
  async function runRunnerOnce({
24181
24273
  runtime,
24182
24274
  parsed,
@@ -24248,7 +24340,12 @@ The runner will not register a target for you \u2014 declare this machine first,
24248
24340
  });
24249
24341
  }
24250
24342
  const launchConfig = asRecord5(requestRecord.launchConfig);
24251
- const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
24343
+ const requestWorkspaceId = typeof requestRecord.workspaceId === "string" ? requestRecord.workspaceId : null;
24344
+ const terminal = await resolveTerminalLaunchSettings({
24345
+ runtime,
24346
+ flags: parsed.flags,
24347
+ workspaceId: requestWorkspaceId
24348
+ });
24252
24349
  const missionId = String(requestRecord.missionId);
24253
24350
  const dryRun = flagBoolean(parsed.flags, "--dry-run");
24254
24351
  const prepared = await prepareMissionBranch({
@@ -24257,7 +24354,10 @@ The runner will not register a target for you \u2014 declare this machine first,
24257
24354
  missionId,
24258
24355
  workingDirectory: String(requestRecord.workingDirectory ?? process.cwd()),
24259
24356
  objectiveId: String(requestRecord.objectiveId ?? ""),
24260
- workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
24357
+ workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(
24358
+ runtime,
24359
+ requestWorkspaceId
24360
+ ),
24261
24361
  dryRun,
24262
24362
  overrideBranch: flagValue(parsed.flags, "--branch"),
24263
24363
  noWorktree: flagBoolean(parsed.flags, "--no-worktree")
@@ -24270,6 +24370,16 @@ The runner will not register a target for you \u2014 declare this machine first,
24270
24370
  branchAutomation: prepared.branchAutomation
24271
24371
  });
24272
24372
  const launchSession = launchSessionSnapshotFromMetadata(asRecord5(requestRecord.metadata));
24373
+ if (shouldRefuseInlineRunnerLaunch({
24374
+ dryRun,
24375
+ terminalLauncher: terminal.terminalLauncher,
24376
+ executionProvider: launchSession?.executionProvider.kind,
24377
+ stdoutIsTTY: process.stdout.isTTY
24378
+ })) {
24379
+ throw new CliError({
24380
+ message: `Refusing to launch ${requestedAgent} inline: this runner has no TTY and no terminal launcher is configured for this device, so the agent would have nowhere to run. Set a terminal in Settings \u2192 Terminal & IDE (or run \`ovld setup\`), or select the Latch execution provider for this target, then re-run the objective.`
24381
+ });
24382
+ }
24273
24383
  const result = await launchAgent({
24274
24384
  runtime,
24275
24385
  options: {
@@ -24299,7 +24409,13 @@ The runner will not register a target for you \u2014 declare this machine first,
24299
24409
  console.error(`[overlord] ${result.viewerOpen.warning}`);
24300
24410
  }
24301
24411
  if (result.status && result.status !== 0) {
24302
- throw new CliError({ message: `Launch command exited with status ${result.status}` });
24412
+ throw new CliError({
24413
+ message: launchFailureMessage({
24414
+ status: result.status,
24415
+ execution: result.plan.execution,
24416
+ terminal
24417
+ })
24418
+ });
24303
24419
  }
24304
24420
  await runtime.backend.post({
24305
24421
  path: `/api/runner/requests/${requestId}/launched`,
@@ -24322,7 +24438,9 @@ The runner will not register a target for you \u2014 declare this machine first,
24322
24438
  try {
24323
24439
  await runtime.backend.post({
24324
24440
  path: `/api/runner/requests/${requestId}/failed`,
24325
- body: { error: error instanceof Error ? error.message : String(error) }
24441
+ body: {
24442
+ error: sanitizeRunnerFailureMessage(error instanceof Error ? error.message : error)
24443
+ }
24326
24444
  });
24327
24445
  } catch {
24328
24446
  }
@@ -24379,7 +24497,15 @@ async function runRunnerCommand({
24379
24497
  const intervalMs = Number.parseInt(flagValue(parsed.flags, "--poll-interval-ms") ?? "3000", 10);
24380
24498
  if (!json) console.log(`Runner started. Polling every ${intervalMs}ms for execution requests.`);
24381
24499
  while (true) {
24382
- const result = await runOnce();
24500
+ let result = { launched: false, longPoll: false };
24501
+ try {
24502
+ result = await runOnce();
24503
+ } catch (error) {
24504
+ if (isNoExecutionTargetRegisteredError(error)) throw error;
24505
+ console.error(
24506
+ `[overlord] Runner poll failed: ${error instanceof Error ? error.message : String(error)}`
24507
+ );
24508
+ }
24383
24509
  if (!result.longPoll) await new Promise((resolve) => setTimeout(resolve, intervalMs));
24384
24510
  }
24385
24511
  }
@@ -24528,7 +24654,7 @@ Note: ${reinstallHint}`);
24528
24654
  message: "Usage: ovld runner service <install|start|stop|restart|status|uninstall> [--no-start] [--json]"
24529
24655
  });
24530
24656
  }
24531
- var PROTOCOL_FILE_FLAGS, SESSION_KEY_SUBCOMMANDS, OBJECTIVE_SCOPED_SUBCOMMANDS;
24657
+ var PROTOCOL_FILE_FLAGS, SESSION_KEY_SUBCOMMANDS, OBJECTIVE_SCOPED_SUBCOMMANDS, MAX_RUNNER_FAILURE_MESSAGE_LENGTH;
24532
24658
  var init_commands = __esm({
24533
24659
  "src/commands.ts"() {
24534
24660
  "use strict";
@@ -24545,12 +24671,14 @@ var init_commands = __esm({
24545
24671
  init_errors();
24546
24672
  init_launch();
24547
24673
  init_launch_bootstrap();
24674
+ init_launch_settings();
24548
24675
  init_native_session();
24549
24676
  init_org_setup();
24550
24677
  init_output();
24551
24678
  init_project_tmp();
24552
24679
  init_protocol_help();
24553
24680
  init_record_touched();
24681
+ init_redact_secrets();
24554
24682
  init_resource_observations();
24555
24683
  init_runner_identity();
24556
24684
  init_runner_service();
@@ -24604,6 +24732,7 @@ var init_commands = __esm({
24604
24732
  "update-artifact",
24605
24733
  "write-context"
24606
24734
  ]);
24735
+ MAX_RUNNER_FAILURE_MESSAGE_LENGTH = 1200;
24607
24736
  }
24608
24737
  });
24609
24738