overlord-cli 0.2607141512.0 → 0.2607151113.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
@@ -2986,7 +2986,7 @@ ${structured}`);
2986
2986
  }
2987
2987
  return parts.join(" \u2014 ");
2988
2988
  }
2989
- function createBackendClient() {
2989
+ function createBackendClient({ workspaceId } = {}) {
2990
2990
  const baseUrl = normalizeBaseUrl4(resolveBackendUrl(loadConfig()));
2991
2991
  const clientDevice = clientDeviceIdentity();
2992
2992
  async function request({
@@ -3009,6 +3009,7 @@ function createBackendClient() {
3009
3009
  "x-overlord-device-label": clientDevice.deviceLabel,
3010
3010
  "x-overlord-device-platform": clientDevice.devicePlatform,
3011
3011
  ...auth.headers,
3012
+ ...workspaceId ? { "X-Overlord-Active-Workspace": workspaceId } : {},
3012
3013
  ...extraHeaders
3013
3014
  },
3014
3015
  body: rawBody !== void 0 ? rawBody : body === void 0 ? void 0 : JSON.stringify(body)
@@ -3037,6 +3038,7 @@ Start the Desktop/local backend or run \`ovld config set cloud <url>\`.
3037
3038
  }
3038
3039
  return {
3039
3040
  baseUrl,
3041
+ forWorkspace: (nextWorkspaceId) => createBackendClient({ workspaceId: nextWorkspaceId }),
3040
3042
  health: () => request({ method: "GET", path: "/api/health" }),
3041
3043
  get: (path27) => request({ method: "GET", path: path27 }),
3042
3044
  post: ({ path: path27, body }) => request({ method: "POST", path: path27, body }),
@@ -4000,7 +4002,7 @@ function buildExecutionTargetMigrationDoctorCheck({
4000
4002
  name: "execution-target-migration",
4001
4003
  ok: false,
4002
4004
  required: false,
4003
- detail: `${targetSummary} use the hosted backend/container hostname fingerprint and cannot run checkout-local work.` + queueSummary + " In the web app, open Project Settings \u2192 Resources, re-select your client device as the execution target, re-link the primary resource to that target, and clear or re-queue stale execution requests. See docs/upgrading-client-checkout-bridge.md."
4005
+ detail: `${targetSummary} use the hosted backend/container hostname fingerprint and cannot run checkout-local work.` + queueSummary + " In the web app, open Project Settings \u2192 Resources, re-select your client device as the execution target, re-link the primary resource to that target, and clear or re-queue stale execution requests. See developer-instructions/upgrading-client-checkout-bridge.md."
4004
4006
  };
4005
4007
  }
4006
4008
  var init_execution_target_migration_doctor = __esm({
@@ -13195,8 +13197,9 @@ async function resolveMissionProjectSlug({
13195
13197
  const projectId = typeof mission.projectId === "string" ? mission.projectId.trim() : "";
13196
13198
  if (projectId) {
13197
13199
  try {
13198
- const projects = await runtime.backend.get("/api/projects");
13199
- const project = Array.isArray(projects) ? projects.find((candidate) => candidate.id === projectId) : null;
13200
+ const project = await runtime.backend.get(
13201
+ `/api/projects/${encodeURIComponent(projectId)}`
13202
+ );
13200
13203
  if (typeof project?.slug === "string" && project.slug.trim()) return project.slug.trim();
13201
13204
  } catch {
13202
13205
  }
@@ -13419,13 +13422,31 @@ async function resolveProjectByIdOrName({
13419
13422
  projectRef
13420
13423
  }) {
13421
13424
  const trimmed = projectRef.trim();
13422
- const projects = await backend.get("/api/projects");
13423
- const match = projects.find((project) => project.id === trimmed) ?? projects.find((project) => project.name === trimmed) ?? projects.find((project) => project.slug === trimmed);
13425
+ try {
13426
+ return await backend.get(`/api/projects/${encodeURIComponent(trimmed)}`);
13427
+ } catch (error) {
13428
+ if (!(error instanceof CliError) || !/project not found/i.test(error.message)) throw error;
13429
+ }
13430
+ const projects = await listAccessibleProjects({ backend });
13431
+ const match = projects.find((project) => project.name === trimmed) ?? projects.find((project) => project.slug === trimmed);
13424
13432
  if (!match) {
13425
13433
  throw new CliError({ message: `Project not found: ${trimmed}` });
13426
13434
  }
13427
13435
  return match;
13428
13436
  }
13437
+ async function listAccessibleProjects({
13438
+ backend
13439
+ }) {
13440
+ const workspaces = await backend.get("/api/workspaces");
13441
+ const projectLists = await Promise.all(
13442
+ workspaces.map(
13443
+ (workspace) => backend.get(`/api/workspaces/${encodeURIComponent(workspace.id)}/projects`)
13444
+ )
13445
+ );
13446
+ const projects = /* @__PURE__ */ new Map();
13447
+ for (const project of projectLists.flat()) projects.set(project.id, project);
13448
+ return [...projects.values()];
13449
+ }
13429
13450
  async function discoverProjectOnClient({
13430
13451
  backend,
13431
13452
  workingDirectory,
@@ -16427,7 +16448,7 @@ async function resolveProtocolFileInputs({
16427
16448
  }
16428
16449
  async function discoverProjectId(runtime, explicit) {
16429
16450
  if (explicit) return explicit;
16430
- const projects = await runtime.backend.get("/api/projects");
16451
+ const projects = await listAccessibleProjects({ backend: runtime.backend });
16431
16452
  const first = projects[0];
16432
16453
  const id = asRecord2(first).id;
16433
16454
  if (typeof id !== "string") {
@@ -16533,22 +16554,42 @@ async function runProtocolCommand({
16533
16554
  flags["--execution-target-id"] = preferredTargetId;
16534
16555
  }
16535
16556
  }
16536
- const result = await runtime.backend.post({
16537
- path: `/api/protocol/${encodeURIComponent(subcommand)}`,
16538
- body: {
16539
- args,
16540
- positional: parsed.positional,
16541
- flags,
16542
- stdin: protocolStdin,
16543
- fileInputs,
16544
- externalSessionId: flagValue(parsed.flags, "--external-session-id") ?? resolveNativeSessionId({
16545
- explicit: void 0,
16546
- agent: flagValue(parsed.flags, "--agent") ?? "unknown",
16547
- missionId: flagValue(parsed.flags, "--mission-id") ?? "unknown",
16548
- workingDirectory
16549
- })
16550
- }
16551
- });
16557
+ const protocolBody = {
16558
+ args,
16559
+ positional: parsed.positional,
16560
+ flags,
16561
+ stdin: protocolStdin,
16562
+ fileInputs,
16563
+ externalSessionId: flagValue(parsed.flags, "--external-session-id") ?? resolveNativeSessionId({
16564
+ explicit: void 0,
16565
+ agent: flagValue(parsed.flags, "--agent") ?? "unknown",
16566
+ missionId: flagValue(parsed.flags, "--mission-id") ?? "unknown",
16567
+ workingDirectory
16568
+ })
16569
+ };
16570
+ let result;
16571
+ if (subcommand === "search-missions" && !flagValue(parsed.flags, "--project-id")) {
16572
+ const workspaces = await runtime.backend.get("/api/workspaces");
16573
+ const perWorkspace = await Promise.all(
16574
+ workspaces.map(
16575
+ (workspace) => runtime.backend.forWorkspace(workspace.id).post({
16576
+ path: "/api/protocol/search-missions",
16577
+ body: protocolBody
16578
+ })
16579
+ )
16580
+ );
16581
+ const limit = Number.parseInt(flagValue(parsed.flags, "--limit") ?? "25", 10);
16582
+ result = perWorkspace.flat().sort((left, right) => {
16583
+ const leftUpdated = String(asRecord2(left).updatedAt ?? "");
16584
+ const rightUpdated = String(asRecord2(right).updatedAt ?? "");
16585
+ return rightUpdated.localeCompare(leftUpdated);
16586
+ }).slice(0, Number.isFinite(limit) ? limit : 25);
16587
+ } else {
16588
+ result = await runtime.backend.post({
16589
+ path: `/api/protocol/${encodeURIComponent(subcommand)}`,
16590
+ body: protocolBody
16591
+ });
16592
+ }
16552
16593
  if ((subcommand === "attach" || subcommand === "resume-follow-up") && missionId) {
16553
16594
  writeBaseline({
16554
16595
  workingDirectory,
@@ -16671,7 +16712,7 @@ async function runManagementCommand({
16671
16712
  const resourceKey = flagValue(parsed.flags, "--key") ?? existingProjectJson?.resourceKey;
16672
16713
  let projectId = flagValue(parsed.flags, "--project-id");
16673
16714
  if (!projectId) {
16674
- const allProjects = await runtime.backend.get("/api/projects");
16715
+ const allProjects = await listAccessibleProjects({ backend: runtime.backend });
16675
16716
  const projects = allProjects.filter((project) => project.status !== "archived");
16676
16717
  if (projects.length === 0) {
16677
16718
  throw new CliError({
@@ -16803,34 +16844,39 @@ async function runManagementCommand({
16803
16844
  throw new CliError({ message: `Usage: ovld ${command} <agent> --mission-id <missionId>` });
16804
16845
  }
16805
16846
  const workingDirectory = flagValue(parsed.flags, "--working-directory") ?? process.cwd();
16806
- const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
16807
16847
  const dryRun = flagBoolean(parsed.flags, "--dry-run");
16808
16848
  const mission = await runtime.backend.get(
16809
16849
  `/api/missions/${encodeURIComponent(missionId)}`
16810
16850
  );
16851
+ const missionWorkspaceId = asRecord2(mission).workspaceId;
16852
+ const scopedRuntime = typeof missionWorkspaceId === "string" ? { ...runtime, backend: runtime.backend.forWorkspace(missionWorkspaceId) } : runtime;
16853
+ const terminal = await resolveTerminalLaunchSettings({
16854
+ runtime: scopedRuntime,
16855
+ flags: parsed.flags
16856
+ });
16811
16857
  const objectiveId = flagValue(parsed.flags, "--objective-id") ?? launchableObjectiveId(mission);
16812
16858
  const executionTargetId = await resolvePreferredExecutionTargetId({
16813
- backend: runtime.backend
16859
+ backend: scopedRuntime.backend
16814
16860
  });
16815
16861
  const prepared = await prepareMissionBranch({
16816
- runtime,
16862
+ runtime: scopedRuntime,
16817
16863
  options: {
16818
16864
  missionId,
16819
16865
  workingDirectory,
16820
16866
  objectiveId: objectiveId ?? void 0,
16821
- workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
16867
+ workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(scopedRuntime),
16822
16868
  dryRun,
16823
16869
  overrideBranch: flagValue(parsed.flags, "--branch"),
16824
16870
  noWorktree: flagBoolean(parsed.flags, "--no-worktree")
16825
16871
  }
16826
16872
  });
16827
16873
  await recordBranchPrepared({
16828
- runtime,
16874
+ runtime: scopedRuntime,
16829
16875
  missionId,
16830
16876
  branchAutomation: prepared.branchAutomation
16831
16877
  });
16832
16878
  const result = await launchAgent({
16833
- runtime,
16879
+ runtime: scopedRuntime,
16834
16880
  options: {
16835
16881
  agent,
16836
16882
  missionId,
@@ -16870,10 +16916,27 @@ async function runManagementCommand({
16870
16916
  if (query) params.set("q", query);
16871
16917
  if (projectId) params.set("projectId", projectId);
16872
16918
  if (limit) params.set("limit", limit);
16873
- const result = await runtime.backend.get(
16874
- `/api/missions/search?${params}`
16875
- );
16876
- const missions = result.missions;
16919
+ let missions;
16920
+ if (projectId) {
16921
+ const project = await runtime.backend.get(
16922
+ `/api/projects/${encodeURIComponent(projectId)}`
16923
+ );
16924
+ const result = await runtime.backend.forWorkspace(project.workspaceId).get(`/api/missions/search?${params}`);
16925
+ missions = result.missions;
16926
+ } else {
16927
+ const workspaces = await runtime.backend.get("/api/workspaces");
16928
+ const results = await Promise.all(
16929
+ workspaces.map(
16930
+ (workspace) => runtime.backend.forWorkspace(workspace.id).get(`/api/missions/search?${params}`)
16931
+ )
16932
+ );
16933
+ const parsedLimit = Number.parseInt(limit ?? "25", 10);
16934
+ missions = results.flatMap((result) => result.missions).sort(
16935
+ (left, right) => String(asRecord2(right).updatedAt ?? "").localeCompare(
16936
+ String(asRecord2(left).updatedAt ?? "")
16937
+ )
16938
+ ).slice(0, Number.isFinite(parsedLimit) ? parsedLimit : 25);
16939
+ }
16877
16940
  if (json) printJson({ missions });
16878
16941
  else {
16879
16942
  for (const mission of missions) {