replicas-cli 0.2.171 → 0.2.173

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/index.mjs +123 -227
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -7842,9 +7842,9 @@ import chalk5 from "chalk";
7842
7842
  // src/lib/ssh.ts
7843
7843
  import { spawn } from "child_process";
7844
7844
  var SSH_OPTIONS = ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"];
7845
- async function connectSSH(token, host) {
7845
+ async function connectSSH(token, host, proxyCommand) {
7846
7846
  return new Promise((resolve, reject) => {
7847
- const sshArgs = [...SSH_OPTIONS, `${token}@${host}`];
7847
+ const sshArgs = proxyCommand ? [...SSH_OPTIONS, "-o", `ProxyCommand=${proxyCommand}`, `${token}@${host}`] : [...SSH_OPTIONS, `${token}@${host}`];
7848
7848
  const ssh = spawn("ssh", sshArgs, {
7849
7849
  stdio: "inherit"
7850
7850
  });
@@ -7949,6 +7949,7 @@ Found ${response.workspaces.length} workspaces matching "${workspaceName}":`));
7949
7949
  workspace: selectedWorkspace,
7950
7950
  sshToken: tokenResponse.token,
7951
7951
  sshHost: tokenResponse.host,
7952
+ sshProxyCommand: tokenResponse.proxyCommand,
7952
7953
  repoName
7953
7954
  };
7954
7955
  }
@@ -7960,13 +7961,13 @@ async function connectCommand(workspaceName) {
7960
7961
  process.exit(1);
7961
7962
  }
7962
7963
  try {
7963
- const { workspace, sshToken, sshHost } = await prepareWorkspaceConnection(workspaceName);
7964
+ const { workspace, sshToken, sshHost, sshProxyCommand } = await prepareWorkspaceConnection(workspaceName);
7964
7965
  console.log(chalk5.blue(`
7965
7966
  Connecting to ${workspace.name}...`));
7966
7967
  const sshCommand = `ssh ${sshToken}@${sshHost}`;
7967
7968
  console.log(chalk5.gray(`SSH command: ${sshCommand}`));
7968
7969
  console.log(chalk5.gray("\nPress Ctrl+D to disconnect.\n"));
7969
- await connectSSH(sshToken, sshHost);
7970
+ await connectSSH(sshToken, sshHost, sshProxyCommand);
7970
7971
  console.log(chalk5.green("\n\u2713 Disconnected from workspace.\n"));
7971
7972
  } catch (error) {
7972
7973
  console.error(chalk5.red(`
@@ -8065,8 +8066,7 @@ var SANDBOX_PATHS = {
8065
8066
  REPLICAS_DIR: "/home/ubuntu/.replicas",
8066
8067
  REPLICAS_FILES_DIR: "/home/ubuntu/.replicas/files",
8067
8068
  REPLICAS_FILES_DISPLAY_DIR: "~/.replicas/files",
8068
- REPLICAS_RUNTIME_ENV_FILE: "/home/ubuntu/.replicas/runtime-env.sh",
8069
- REPLICAS_PREVIEW_PORTS_FILE: "/home/ubuntu/.replicas/preview-ports.json"
8069
+ REPLICAS_RUNTIME_ENV_FILE: "/home/ubuntu/.replicas/runtime-env.sh"
8070
8070
  };
8071
8071
 
8072
8072
  // ../shared/src/urls.ts
@@ -8174,6 +8174,7 @@ function workspaceConfigWithPrFollowups(config2, prFollowups = config2?.capabili
8174
8174
  }
8175
8175
  var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
8176
8176
  var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
8177
+ var WORKSPACE_SIDEBAR_VIEWS = ["owned", "shared", "team", "automation", "integrations", "api"];
8177
8178
 
8178
8179
  // ../shared/src/audit-log.ts
8179
8180
  var AUDIT_LOG_ACTION = {
@@ -9211,74 +9212,8 @@ var SOURCE_CONFIG = {
9211
9212
  merged: { label: "Merged", color: "#a78bfa" }
9212
9213
  };
9213
9214
 
9214
- // ../shared/src/placeholder-names.ts
9215
- var FRUITS = [
9216
- "apple",
9217
- "banana",
9218
- "cherry",
9219
- "fig",
9220
- "grape",
9221
- "kiwi",
9222
- "lemon",
9223
- "lime",
9224
- "mango",
9225
- "melon",
9226
- "olive",
9227
- "orange",
9228
- "peach",
9229
- "pear",
9230
- "plum",
9231
- "guava",
9232
- "papaya",
9233
- "lychee",
9234
- "coconut",
9235
- "apricot",
9236
- "avocado",
9237
- "berry",
9238
- "kumquat",
9239
- "nectarine",
9240
- "tangerine",
9241
- "dragonfruit"
9242
- ];
9243
- var TTC_STATIONS = [
9244
- "kipling",
9245
- "islington",
9246
- "royal-york",
9247
- "old-mill",
9248
- "jane",
9249
- "runnymede",
9250
- "high-park",
9251
- "keele",
9252
- "dundas-west",
9253
- "lansdowne",
9254
- "dufferin",
9255
- "ossington",
9256
- "christie",
9257
- "bathurst",
9258
- "spadina",
9259
- "st-george",
9260
- "bay",
9261
- "bloor-yonge",
9262
- "sherbourne",
9263
- "castle-frank",
9264
- "broadview",
9265
- "chester",
9266
- "pape",
9267
- "donlands",
9268
- "greenwood",
9269
- "woodbine",
9270
- "warden",
9271
- "kennedy"
9272
- ];
9273
- function randomElement(arr) {
9274
- return arr[Math.floor(Math.random() * arr.length)];
9275
- }
9276
- function generatePlaceholderName() {
9277
- return `${randomElement(FRUITS)}-${randomElement(TTC_STATIONS)}`;
9278
- }
9279
-
9280
9215
  // ../shared/src/workspace-groups.ts
9281
- function buildGroups(workspaces, _workspacesData, environments, _repositorySets, mockGroupAssignments) {
9216
+ function buildGroups(workspaces, _workspacesData, environments, _repositorySets) {
9282
9217
  const groupMap = /* @__PURE__ */ new Map();
9283
9218
  for (const env of environments) {
9284
9219
  groupMap.set(env.id, {
@@ -9311,41 +9246,10 @@ function buildGroups(workspaces, _workspacesData, environments, _repositorySets,
9311
9246
  groupMap.get(workspace.environment_id).workspaces.push(workspace);
9312
9247
  continue;
9313
9248
  }
9314
- const assignedGroup = mockGroupAssignments.get(workspace.id);
9315
- if (assignedGroup && groupMap.has(assignedGroup)) {
9316
- groupMap.get(assignedGroup).workspaces.push(workspace);
9317
- continue;
9318
- }
9319
9249
  placeUngrouped(workspace);
9320
9250
  }
9321
9251
  return Array.from(groupMap.values()).sort((a, b) => a.name.localeCompare(b.name));
9322
9252
  }
9323
- function createMockWorkspaceRecord(organizationId, name, environmentId) {
9324
- const id = "mock-" + Array.from(
9325
- { length: 4 },
9326
- () => Math.random().toString(36).slice(2, 6)
9327
- ).join("-");
9328
- return {
9329
- id,
9330
- user_id: null,
9331
- organization_id: organizationId,
9332
- name: name || generatePlaceholderName(),
9333
- sandbox_id: "",
9334
- engine_url: null,
9335
- engine_secret: "",
9336
- github_installation_id: null,
9337
- last_activity_at: null,
9338
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
9339
- status: "preparing",
9340
- creation_source: "dashboard",
9341
- creator_email: null,
9342
- soft_delete: false,
9343
- lifecycle_policy: "default",
9344
- auto_stop_minutes: null,
9345
- config: {},
9346
- environment_id: environmentId ?? null
9347
- };
9348
- }
9349
9253
 
9350
9254
  // ../shared/src/object-store/types.ts
9351
9255
  var MEDIA_KIND = {
@@ -9395,15 +9299,25 @@ function writeSSHConfig(content) {
9395
9299
  ensureSSHDir();
9396
9300
  fs2.writeFileSync(SSH_CONFIG_PATH, content, { mode: 384 });
9397
9301
  }
9302
+ function sanitizeSSHConfigValue(value) {
9303
+ return value.replace(/[\r\n]+/g, " ").trim();
9304
+ }
9398
9305
  function generateConfigBlock(entry) {
9306
+ const host = sanitizeSSHConfigValue(entry.host);
9307
+ const hostname = sanitizeSSHConfigValue(entry.hostname);
9308
+ const user = sanitizeSSHConfigValue(entry.user);
9309
+ const proxyCommand = entry.proxyCommand ? sanitizeSSHConfigValue(entry.proxyCommand) : void 0;
9399
9310
  const lines = [
9400
9311
  REPLICAS_MARKER_START,
9401
- `Host ${entry.host}`,
9402
- ` HostName ${entry.hostname}`,
9403
- ` User ${entry.user}`,
9312
+ `Host ${host}`,
9313
+ ` HostName ${hostname}`,
9314
+ ` User ${user}`,
9404
9315
  ` StrictHostKeyChecking no`,
9405
9316
  ` UserKnownHostsFile /dev/null`
9406
9317
  ];
9318
+ if (proxyCommand) {
9319
+ lines.push(` ProxyCommand ${proxyCommand}`);
9320
+ }
9407
9321
  lines.push(REPLICAS_MARKER_END);
9408
9322
  return lines.join("\n");
9409
9323
  }
@@ -9456,13 +9370,14 @@ async function codeCommand(workspaceName) {
9456
9370
  process.exit(1);
9457
9371
  }
9458
9372
  try {
9459
- const { workspace, sshToken, sshHost, repoName } = await prepareWorkspaceConnection(workspaceName);
9373
+ const { workspace, sshToken, sshHost, sshProxyCommand, repoName } = await prepareWorkspaceConnection(workspaceName);
9460
9374
  const hostAlias = getWorkspaceHostAlias(workspace.name);
9461
9375
  console.log(chalk6.blue("\nConfiguring SSH connection..."));
9462
9376
  addOrUpdateSSHConfigEntry({
9463
9377
  host: hostAlias,
9464
9378
  hostname: sshHost,
9465
- user: sshToken
9379
+ user: sshToken,
9380
+ proxyCommand: sshProxyCommand
9466
9381
  });
9467
9382
  console.log(chalk6.green(`\u2713 SSH config entry created: ${hostAlias}`));
9468
9383
  const remotePath = repoName ? `${SANDBOX_PATHS.WORKSPACES_DIR}/${repoName}` : SANDBOX_PATHS.HOME_DIR;
@@ -11427,7 +11342,7 @@ import { createCliRenderer } from "@opentui/core";
11427
11342
  import { createRoot } from "@opentui/react";
11428
11343
 
11429
11344
  // src/interactive/App.tsx
11430
- import { useState as useState8, useEffect as useEffect3, useMemo as useMemo6, useCallback as useCallback7, useRef as useRef5 } from "react";
11345
+ import { useState as useState8, useEffect as useEffect3, useMemo as useMemo6, useCallback as useCallback8, useRef as useRef5 } from "react";
11431
11346
  import { useKeyboard as useKeyboard5, useRenderer, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
11432
11347
  import { QueryClient } from "@tanstack/react-query";
11433
11348
 
@@ -11515,14 +11430,16 @@ function useRawOrgFetch() {
11515
11430
  }
11516
11431
 
11517
11432
  // ../shared/src/hooks/useWorkspaces.ts
11433
+ import { useCallback as useCallback2 } from "react";
11518
11434
  import { useQuery, useMutation } from "@tanstack/react-query";
11519
- function useWorkspaces(page = 1, limit = 100, scope = "organization") {
11435
+ function useWorkspaces(viewModes, page = 1, limit = 100) {
11520
11436
  const auth = useReplicasAuth();
11521
11437
  const orgFetch = useOrgFetch();
11522
11438
  const orgId = auth.getOrganizationId();
11439
+ const sortedKey = [...viewModes].sort().join(",");
11523
11440
  return useQuery({
11524
- queryKey: ["workspaces", orgId, scope, page, limit],
11525
- queryFn: () => orgFetch(`/v1/workspaces?scope=${scope}&page=${page}&limit=${limit}`),
11441
+ queryKey: ["workspaces", orgId, sortedKey, page, limit],
11442
+ queryFn: () => orgFetch(`/v1/workspaces?viewModes=${sortedKey}&page=${page}&limit=${limit}`),
11526
11443
  enabled: !!orgId,
11527
11444
  staleTime: 1e4
11528
11445
  }, auth.queryClient);
@@ -11531,16 +11448,55 @@ function useCreateWorkspace() {
11531
11448
  const auth = useReplicasAuth();
11532
11449
  const orgFetch = useOrgFetch();
11533
11450
  const orgId = auth.getOrganizationId();
11451
+ const waitForWorkspaceActive = useWaitForWorkspaceActive();
11534
11452
  return useMutation({
11535
11453
  mutationFn: (request) => orgFetch("/v1/workspaces", {
11536
11454
  method: "POST",
11537
11455
  body: request
11538
11456
  }),
11457
+ onSuccess: (data) => {
11458
+ const workspace = data?.workspace;
11459
+ if (!workspace) return;
11460
+ auth.queryClient.setQueriesData(
11461
+ { queryKey: ["workspaces", orgId] },
11462
+ (current) => {
11463
+ if (!current) return current;
11464
+ if (current.workspaces.some((w) => w.id === workspace.id)) return current;
11465
+ return {
11466
+ ...current,
11467
+ workspaces: [workspace, ...current.workspaces],
11468
+ total: (current.total ?? current.workspaces.length) + 1
11469
+ };
11470
+ }
11471
+ );
11472
+ void waitForWorkspaceActive(workspace.id);
11473
+ },
11539
11474
  onSettled: () => {
11540
11475
  auth.queryClient.invalidateQueries({ queryKey: ["workspaces", orgId] });
11541
11476
  }
11542
11477
  }, auth.queryClient);
11543
11478
  }
11479
+ function useWaitForWorkspaceActive() {
11480
+ const auth = useReplicasAuth();
11481
+ const orgFetch = useOrgFetch();
11482
+ const orgId = auth.getOrganizationId();
11483
+ return useCallback2(async (workspaceId) => {
11484
+ try {
11485
+ const result = await auth.queryClient.fetchQuery({
11486
+ queryKey: ["workspace-wait-active", orgId, workspaceId],
11487
+ queryFn: () => orgFetch(
11488
+ `/v1/workspaces/${workspaceId}/wait-active`,
11489
+ { method: "POST" }
11490
+ ),
11491
+ staleTime: Infinity
11492
+ });
11493
+ auth.queryClient.invalidateQueries({ queryKey: ["workspaces", orgId] });
11494
+ return result.workspace;
11495
+ } catch {
11496
+ return null;
11497
+ }
11498
+ }, [auth, orgFetch, orgId]);
11499
+ }
11544
11500
  function useDeleteWorkspace() {
11545
11501
  const auth = useReplicasAuth();
11546
11502
  const orgFetch = useOrgFetch();
@@ -11588,7 +11544,7 @@ function useGenerateWorkspaceName() {
11588
11544
  }
11589
11545
 
11590
11546
  // ../shared/src/hooks/useWorkspaceEngine.ts
11591
- import { useCallback as useCallback2, useEffect, useMemo, useState } from "react";
11547
+ import { useCallback as useCallback3, useEffect, useMemo, useState } from "react";
11592
11548
  import { useQuery as useQuery2, useMutation as useMutation2 } from "@tanstack/react-query";
11593
11549
  function upsertChat(chats, chat) {
11594
11550
  const existingIndex = chats.findIndex((item) => item.id === chat.id);
@@ -11713,7 +11669,7 @@ function useWorkspaceEvents(workspaceId, enabled = true) {
11713
11669
  const rawFetch = useRawOrgFetch();
11714
11670
  const qc = auth.queryClient;
11715
11671
  const [connected, setConnected] = useState(false);
11716
- const handleEvent = useCallback2((event) => {
11672
+ const handleEvent = useCallback3((event) => {
11717
11673
  if (!workspaceId) return;
11718
11674
  const chatsKey = ["workspace-chats", workspaceId];
11719
11675
  if (event.type === "chat.created" || event.type === "chat.updated") {
@@ -11916,7 +11872,7 @@ function StatusBar({ focusPanel, viewingDiff, hasDiffAvailable }) {
11916
11872
  }
11917
11873
 
11918
11874
  // src/interactive/components/WorkspaceSidebar.tsx
11919
- import { useState as useState2, useMemo as useMemo2, useCallback as useCallback3 } from "react";
11875
+ import { useState as useState2, useMemo as useMemo2, useCallback as useCallback4 } from "react";
11920
11876
  import { useKeyboard, useTerminalDimensions } from "@opentui/react";
11921
11877
  import { jsx as jsx2, jsxs as jsxs2 } from "@opentui/react/jsx-runtime";
11922
11878
  function flattenGroups(groups, collapsedGroups) {
@@ -11937,8 +11893,7 @@ function flattenGroups(groups, collapsedGroups) {
11937
11893
  type: "workspace",
11938
11894
  workspaceId: ws.id,
11939
11895
  name: ws.name,
11940
- status: ws.status,
11941
- isMock: ws.status === "preparing"
11896
+ status: ws.status
11942
11897
  });
11943
11898
  }
11944
11899
  if (group.id !== "__ungrouped__") {
@@ -11954,7 +11909,6 @@ function truncate3(text, maxLen) {
11954
11909
  function WorkspaceSidebar({
11955
11910
  groups,
11956
11911
  selectedWorkspaceId,
11957
- mockIds,
11958
11912
  onSelectWorkspace,
11959
11913
  onCreateWorkspace,
11960
11914
  onDeleteWorkspace,
@@ -11974,7 +11928,7 @@ function WorkspaceSidebar({
11974
11928
  setCursorIndex(clampedCursor);
11975
11929
  }
11976
11930
  const visibleHeight = Math.max(1, termHeight - 4);
11977
- const adjustScroll = useCallback3(
11931
+ const adjustScroll = useCallback4(
11978
11932
  (newCursor) => {
11979
11933
  let newOffset = scrollOffset;
11980
11934
  if (newCursor < newOffset) {
@@ -11986,7 +11940,7 @@ function WorkspaceSidebar({
11986
11940
  },
11987
11941
  [scrollOffset, visibleHeight]
11988
11942
  );
11989
- const moveCursor = useCallback3(
11943
+ const moveCursor = useCallback4(
11990
11944
  (next) => {
11991
11945
  const len = items.length;
11992
11946
  if (len === 0) return;
@@ -11996,7 +11950,7 @@ function WorkspaceSidebar({
11996
11950
  },
11997
11951
  [items.length, adjustScroll]
11998
11952
  );
11999
- const handleAction = useCallback3(
11953
+ const handleAction = useCallback4(
12000
11954
  (item) => {
12001
11955
  if (item.type === "group") {
12002
11956
  setCollapsedGroups((prev) => {
@@ -12016,10 +11970,9 @@ function WorkspaceSidebar({
12016
11970
  },
12017
11971
  [onSelectWorkspace, onCreateWorkspace]
12018
11972
  );
12019
- const handleDelete = useCallback3(() => {
11973
+ const handleDelete = useCallback4(() => {
12020
11974
  const item = items[cursorIndex];
12021
11975
  if (!item || item.type !== "workspace") return;
12022
- if (item.isMock) return;
12023
11976
  if (confirmDelete === item.workspaceId) {
12024
11977
  onDeleteWorkspace(item.workspaceId);
12025
11978
  setConfirmDelete(null);
@@ -12027,7 +11980,7 @@ function WorkspaceSidebar({
12027
11980
  setConfirmDelete(item.workspaceId);
12028
11981
  }
12029
11982
  }, [items, cursorIndex, confirmDelete, onDeleteWorkspace]);
12030
- const moveCursorAndClearConfirm = useCallback3(
11983
+ const moveCursorAndClearConfirm = useCallback4(
12031
11984
  (next) => {
12032
11985
  setConfirmDelete(null);
12033
11986
  moveCursor(next);
@@ -12095,7 +12048,7 @@ function WorkspaceSidebar({
12095
12048
  return;
12096
12049
  }
12097
12050
  });
12098
- const handleItemClick = useCallback3(
12051
+ const handleItemClick = useCallback4(
12099
12052
  (globalIndex) => {
12100
12053
  setConfirmDelete(null);
12101
12054
  setCursorIndex(globalIndex);
@@ -12104,7 +12057,7 @@ function WorkspaceSidebar({
12104
12057
  },
12105
12058
  [items, handleAction]
12106
12059
  );
12107
- const handleDeleteClick = useCallback3(
12060
+ const handleDeleteClick = useCallback4(
12108
12061
  (workspaceId, e) => {
12109
12062
  e?.preventDefault?.();
12110
12063
  if (confirmDelete === workspaceId) {
@@ -12209,7 +12162,7 @@ function WorkspaceSidebar({
12209
12162
  ] }),
12210
12163
  /* @__PURE__ */ jsx2("span", { fg: nameColor, children: truncate3(item.name, 17) })
12211
12164
  ] }),
12212
- !item.isMock && /* @__PURE__ */ jsx2("box", { onMouseDown: (e) => handleDeleteClick(item.workspaceId, e), children: /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("span", { fg: isCursor ? "#ff4444" : "#222222", children: "\u2717" }) }) })
12165
+ /* @__PURE__ */ jsx2("box", { onMouseDown: (e) => handleDeleteClick(item.workspaceId, e), children: /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("span", { fg: isCursor ? "#ff4444" : "#222222", children: "\u2717" }) }) })
12213
12166
  ]
12214
12167
  },
12215
12168
  `w-${item.workspaceId}`
@@ -12264,7 +12217,7 @@ function SpinnerLabel({ color, label }) {
12264
12217
  import { useState as useState4 } from "react";
12265
12218
 
12266
12219
  // src/interactive/components/diff-utils.tsx
12267
- import React2, { useState as useState3, useMemo as useMemo3, useCallback as useCallback4, useRef } from "react";
12220
+ import React2, { useState as useState3, useMemo as useMemo3, useCallback as useCallback5, useRef } from "react";
12268
12221
  import { SyntaxStyle } from "@opentui/core";
12269
12222
  import { useKeyboard as useKeyboard2 } from "@opentui/react";
12270
12223
  import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
@@ -12532,12 +12485,12 @@ function DiffViewer({ diff, repoName, focused }) {
12532
12485
  return result;
12533
12486
  }, [tree, collapsedFolders]);
12534
12487
  const safeCursorIndex = Math.min(cursorIndex, Math.max(0, visibleTree.length - 1));
12535
- const scrollCursorIntoView = useCallback4((treeIdx) => {
12488
+ const scrollCursorIntoView = useCallback5((treeIdx) => {
12536
12489
  const node = visibleTree[treeIdx];
12537
12490
  if (!node) return;
12538
12491
  fileTreeScrollRef.current?.scrollChildIntoView(`tree-${node.path}`);
12539
12492
  }, [visibleTree]);
12540
- const moveCursor = useCallback4((next) => {
12493
+ const moveCursor = useCallback5((next) => {
12541
12494
  const len = visibleTree.length;
12542
12495
  if (len === 0) return;
12543
12496
  const wrapped = (next % len + len) % len;
@@ -12548,7 +12501,7 @@ function DiffViewer({ diff, repoName, focused }) {
12548
12501
  setSelectedFileIndex(node.fileIndex);
12549
12502
  }
12550
12503
  }, [visibleTree, scrollCursorIntoView]);
12551
- const toggleFolder = useCallback4((folderPath) => {
12504
+ const toggleFolder = useCallback5((folderPath) => {
12552
12505
  setCollapsedFolders((prev) => {
12553
12506
  const next = new Set(prev);
12554
12507
  if (next.has(folderPath)) next.delete(folderPath);
@@ -12556,7 +12509,7 @@ function DiffViewer({ diff, repoName, focused }) {
12556
12509
  return next;
12557
12510
  });
12558
12511
  }, []);
12559
- const selectFile = useCallback4((fileIndex, treeIndex) => {
12512
+ const selectFile = useCallback5((fileIndex, treeIndex) => {
12560
12513
  setSelectedFileIndex(fileIndex);
12561
12514
  setCursorIndex(treeIndex);
12562
12515
  scrollCursorIntoView(treeIndex);
@@ -13406,7 +13359,7 @@ function ChatArea({
13406
13359
  }
13407
13360
 
13408
13361
  // src/interactive/components/WorkspaceInfo.tsx
13409
- import React6, { useState as useState6, useMemo as useMemo5, useCallback as useCallback5, useRef as useRef3 } from "react";
13362
+ import React6, { useState as useState6, useMemo as useMemo5, useCallback as useCallback6, useRef as useRef3 } from "react";
13410
13363
  import open4 from "open";
13411
13364
  import { useKeyboard as useKeyboard4 } from "@opentui/react";
13412
13365
  import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
@@ -13603,7 +13556,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, a
13603
13556
  const hasAnyDiff = diffItems.length > 0;
13604
13557
  const safeCursor = interactiveItems.length > 0 ? Math.min(cursorIndex, interactiveItems.length - 1) : 0;
13605
13558
  const scrollboxRef = useRef3(null);
13606
- const moveCursor = useCallback5(
13559
+ const moveCursor = useCallback6(
13607
13560
  (next) => {
13608
13561
  const len = interactiveItems.length;
13609
13562
  if (len === 0) return;
@@ -13614,7 +13567,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, a
13614
13567
  },
13615
13568
  [interactiveItems]
13616
13569
  );
13617
- const handleAction = useCallback5(
13570
+ const handleAction = useCallback6(
13618
13571
  (item) => {
13619
13572
  if (!item) return;
13620
13573
  switch (item.type) {
@@ -13683,7 +13636,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, a
13683
13636
  return;
13684
13637
  }
13685
13638
  });
13686
- const isHighlighted = useCallback5(
13639
+ const isHighlighted = useCallback6(
13687
13640
  (item) => {
13688
13641
  if (!focused) return false;
13689
13642
  const idx = interactiveItems.indexOf(item);
@@ -13691,7 +13644,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, a
13691
13644
  },
13692
13645
  [focused, interactiveItems, safeCursor]
13693
13646
  );
13694
- const findItem = useCallback5(
13647
+ const findItem = useCallback6(
13695
13648
  (type, key) => {
13696
13649
  return interactiveItems.find((item) => {
13697
13650
  if (item.type !== type) return false;
@@ -13980,7 +13933,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, a
13980
13933
  import { useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
13981
13934
 
13982
13935
  // src/interactive/toast-context.tsx
13983
- import { createContext as createContext2, useContext as useContext2, useState as useState7, useCallback as useCallback6, useRef as useRef4 } from "react";
13936
+ import { createContext as createContext2, useContext as useContext2, useState as useState7, useCallback as useCallback7, useRef as useRef4 } from "react";
13984
13937
  import { jsx as jsx9 } from "@opentui/react/jsx-runtime";
13985
13938
  var ToastContext = createContext2(null);
13986
13939
  function useToast() {
@@ -13991,7 +13944,7 @@ function useToast() {
13991
13944
  function ToastProvider({ children }) {
13992
13945
  const [current, setCurrent] = useState7(null);
13993
13946
  const timeoutRef = useRef4(null);
13994
- const show = useCallback6((options) => {
13947
+ const show = useCallback7((options) => {
13995
13948
  const { message, variant = "info", duration = 3e3 } = options;
13996
13949
  setCurrent({ message, variant });
13997
13950
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -13999,7 +13952,7 @@ function ToastProvider({ children }) {
13999
13952
  setCurrent(null);
14000
13953
  }, duration);
14001
13954
  }, []);
14002
- const error = useCallback6((err) => {
13955
+ const error = useCallback7((err) => {
14003
13956
  const message = err instanceof Error ? err.message : "An error occurred";
14004
13957
  show({ message, variant: "error", duration: 5e3 });
14005
13958
  }, [show]);
@@ -14098,29 +14051,17 @@ function AppInner() {
14098
14051
  const [selectedChatId, setSelectedChatId] = useState8(null);
14099
14052
  const [focusPanel, setFocusPanel] = useState8("sidebar");
14100
14053
  const [taskMode, setTaskMode] = useState8("build");
14101
- const [mockWorkspaces, setMockWorkspaces] = useState8([]);
14102
- const mockToRealRef = useRef5(/* @__PURE__ */ new Map());
14103
- const mockGroupRef = useRef5(/* @__PURE__ */ new Map());
14104
- const mockPollingRef = useRef5(null);
14105
- if (mockWorkspaces.length > 0 && !mockPollingRef.current) {
14106
- mockPollingRef.current = setInterval(() => {
14107
- queryClient.invalidateQueries({ queryKey: ["workspaces"] });
14108
- rendererRef.current.requestRender();
14109
- }, 5e3);
14110
- } else if (mockWorkspaces.length === 0 && mockPollingRef.current) {
14111
- clearInterval(mockPollingRef.current);
14112
- mockPollingRef.current = null;
14113
- }
14114
14054
  const [mockMessages, setMockMessages] = useState8([]);
14115
14055
  const [mockThinking, setMockThinking] = useState8(false);
14116
14056
  const pendingMessageRef = useRef5(/* @__PURE__ */ new Map());
14117
14057
  const [viewingDiff, setViewingDiff] = useState8(null);
14118
14058
  const [lastViewedDiff, setLastViewedDiff] = useState8(null);
14119
- const mockIds = useMemo6(() => new Set(mockWorkspaces.map((m) => m.id)), [mockWorkspaces]);
14120
- const isMockSelected = selectedWorkspaceId ? mockIds.has(selectedWorkspaceId) : false;
14121
- const { data: workspacesData, isLoading: loadingWorkspaces } = useWorkspaces(1, 100, "organization");
14059
+ const { data: workspacesData, isLoading: loadingWorkspaces } = useWorkspaces(WORKSPACE_SIDEBAR_VIEWS);
14122
14060
  const { data: setsData } = useRepositorySets();
14123
14061
  const { data: envsData } = useEnvironments();
14062
+ const workspaces = workspacesData?.workspaces ?? [];
14063
+ const selectedWorkspace = workspaces.find((ws) => ws.id === selectedWorkspaceId) ?? null;
14064
+ const isMockSelected = selectedWorkspace?.status === "preparing";
14124
14065
  const { data: statusData, isLoading: loadingStatus } = useWorkspaceStatus(
14125
14066
  isMockSelected ? null : selectedWorkspaceId,
14126
14067
  { includeDiffs: true }
@@ -14149,16 +14090,13 @@ function AppInner() {
14149
14090
  const [wakingWorkspaceId, setWakingWorkspaceId] = useState8(null);
14150
14091
  const sendMessageMutation = useSendChatMessage(selectedWorkspaceId, resolvedChatId);
14151
14092
  const interruptMutation = useInterruptChat(selectedWorkspaceId, resolvedChatId);
14152
- const workspaces = workspacesData?.workspaces ?? [];
14153
14093
  const repositorySets = setsData?.repository_sets ?? [];
14154
14094
  const environments = envsData?.environments ?? [];
14155
14095
  const previews = previewsData?.previews ?? [];
14156
- const allWorkspaces = useMemo6(() => [...mockWorkspaces, ...workspaces], [mockWorkspaces, workspaces]);
14157
14096
  const groups = useMemo6(
14158
- () => buildGroups(allWorkspaces, workspacesData, environments, repositorySets, mockGroupRef.current),
14159
- [allWorkspaces, workspacesData, environments, repositorySets]
14097
+ () => buildGroups(workspaces, workspacesData, environments, repositorySets),
14098
+ [workspaces, workspacesData, environments, repositorySets]
14160
14099
  );
14161
- const selectedWorkspace = allWorkspaces.find((ws) => ws.id === selectedWorkspaceId) ?? null;
14162
14100
  const selectedChat = chats.find((c) => c.id === resolvedChatId) ?? null;
14163
14101
  const isProcessing = mockThinking || (selectedChat?.processing ?? false);
14164
14102
  const selectedAgent = selectedChat?.provider ?? "claude";
@@ -14180,33 +14118,15 @@ function AppInner() {
14180
14118
  setSelectedWorkspaceId(workspaces[0].id);
14181
14119
  }
14182
14120
  }, [workspaces, selectedWorkspaceId]);
14183
- useEffect3(() => {
14184
- if (mockWorkspaces.length === 0) return;
14185
- const realIds = new Set(workspaces.map((w) => w.id));
14186
- let changed = false;
14187
- const remaining = mockWorkspaces.filter((mock) => {
14188
- const realId = mockToRealRef.current.get(mock.id);
14189
- if (realId && realIds.has(realId)) {
14190
- if (selectedWorkspaceId === mock.id) {
14191
- setSelectedWorkspaceId(realId);
14192
- }
14193
- const pending = pendingMessageRef.current.get(mock.id);
14194
- if (pending) {
14195
- pendingMessageRef.current.delete(mock.id);
14196
- pendingMessageRef.current.set(realId, pending);
14197
- }
14198
- mockToRealRef.current.delete(mock.id);
14199
- mockGroupRef.current.delete(mock.id);
14200
- setSelectedChatId(null);
14201
- setMockMessages([]);
14202
- setMockThinking(false);
14203
- changed = true;
14204
- return false;
14205
- }
14206
- return true;
14207
- });
14208
- if (changed) setMockWorkspaces(remaining);
14209
- }, [workspaces, mockWorkspaces, selectedWorkspaceId]);
14121
+ const [prevIsMockSelected, setPrevIsMockSelected] = useState8(isMockSelected);
14122
+ if (prevIsMockSelected !== isMockSelected) {
14123
+ setPrevIsMockSelected(isMockSelected);
14124
+ if (prevIsMockSelected && !isMockSelected) {
14125
+ setSelectedChatId(null);
14126
+ setMockMessages([]);
14127
+ setMockThinking(false);
14128
+ }
14129
+ }
14210
14130
  useEffect3(() => {
14211
14131
  if (!selectedWorkspaceId || isMockSelected) return;
14212
14132
  if (!resolvedChatId || resolvedChatId.startsWith("mock-")) return;
@@ -14221,7 +14141,7 @@ function AppInner() {
14221
14141
  }
14222
14142
  sendMessageMutation.mutate({ message: pending });
14223
14143
  }, [selectedWorkspaceId, resolvedChatId, isMockSelected, statusData?.status]);
14224
- const handleSelectWorkspace = useCallback7((workspaceId) => {
14144
+ const handleSelectWorkspace = useCallback8((workspaceId) => {
14225
14145
  setSelectedWorkspaceId(workspaceId);
14226
14146
  setSelectedChatId(null);
14227
14147
  setMockMessages([]);
@@ -14230,55 +14150,32 @@ function AppInner() {
14230
14150
  setLastViewedDiff(null);
14231
14151
  setFocusPanel("chat-input");
14232
14152
  }, []);
14233
- const handleFocus = useCallback7((panel) => {
14153
+ const handleFocus = useCallback8((panel) => {
14234
14154
  if (panel === "sidebar") {
14235
14155
  queryClient.invalidateQueries({ queryKey: ["workspaces"] });
14236
14156
  }
14237
14157
  setFocusPanel(panel);
14238
14158
  }, []);
14239
- const handleCreateWorkspace = useCallback7(
14159
+ const handleCreateWorkspace = useCallback8(
14240
14160
  async (group) => {
14241
14161
  if (group.id === "__ungrouped__") {
14242
14162
  toast.error("Cannot create a workspace in the Other / Ungrouped folder");
14243
14163
  return;
14244
14164
  }
14245
- const orgId = getOrganizationId() ?? "";
14246
- const mock = createMockWorkspaceRecord(orgId);
14247
- mockGroupRef.current.set(mock.id, group.id);
14248
- setMockWorkspaces((prev) => [mock, ...prev]);
14249
- setSelectedWorkspaceId(mock.id);
14250
14165
  setViewingDiff(null);
14251
14166
  setLastViewedDiff(null);
14252
14167
  setFocusPanel("chat-input");
14253
14168
  try {
14254
- const request = {
14255
- environment_id: group.environmentId,
14256
- name: mock.name,
14257
- placeholder: true
14258
- };
14259
- const result = await createWorkspaceMutation.mutateAsync(request);
14260
- mockToRealRef.current.set(mock.id, result.workspace.id);
14169
+ const result = await createWorkspaceMutation.mutateAsync({ environment_id: group.environmentId });
14170
+ setSelectedWorkspaceId(result.workspace.id);
14261
14171
  } catch (err) {
14262
- mockGroupRef.current.delete(mock.id);
14263
- setMockWorkspaces((prev) => prev.filter((m) => m.id !== mock.id));
14264
14172
  toast.error(err);
14265
14173
  }
14266
14174
  },
14267
14175
  [createWorkspaceMutation, toast]
14268
14176
  );
14269
- const handleDeleteWorkspace = useCallback7(
14177
+ const handleDeleteWorkspace = useCallback8(
14270
14178
  async (workspaceId) => {
14271
- if (mockIds.has(workspaceId)) {
14272
- mockGroupRef.current.delete(workspaceId);
14273
- mockToRealRef.current.delete(workspaceId);
14274
- setMockWorkspaces((prev) => prev.filter((m) => m.id !== workspaceId));
14275
- if (selectedWorkspaceId === workspaceId) {
14276
- setSelectedWorkspaceId(null);
14277
- setViewingDiff(null);
14278
- setLastViewedDiff(null);
14279
- }
14280
- return;
14281
- }
14282
14179
  try {
14283
14180
  await deleteWorkspaceMutation.mutateAsync(workspaceId);
14284
14181
  if (selectedWorkspaceId === workspaceId) {
@@ -14291,9 +14188,9 @@ function AppInner() {
14291
14188
  toast.error(err);
14292
14189
  }
14293
14190
  },
14294
- [mockIds, selectedWorkspaceId, deleteWorkspaceMutation]
14191
+ [selectedWorkspaceId, deleteWorkspaceMutation]
14295
14192
  );
14296
- const handleWakeWorkspace = useCallback7(
14193
+ const handleWakeWorkspace = useCallback8(
14297
14194
  async (workspaceId) => {
14298
14195
  setWakingWorkspaceId(workspaceId);
14299
14196
  try {
@@ -14307,7 +14204,7 @@ function AppInner() {
14307
14204
  },
14308
14205
  [wakeWorkspaceMutation, toast]
14309
14206
  );
14310
- const handleSelectChat = useCallback7((chatId) => {
14207
+ const handleSelectChat = useCallback8((chatId) => {
14311
14208
  setSelectedChatId(chatId);
14312
14209
  }, []);
14313
14210
  useKeyboard5((key) => {
@@ -14365,12 +14262,12 @@ function AppInner() {
14365
14262
  }
14366
14263
  if (focusPanel === "chat-input") return;
14367
14264
  });
14368
- const handleViewDiff = useCallback7((diff, repoName) => {
14265
+ const handleViewDiff = useCallback8((diff, repoName) => {
14369
14266
  setViewingDiff({ diff, repoName });
14370
14267
  setLastViewedDiff({ diff, repoName });
14371
14268
  setFocusPanel("chat-history");
14372
14269
  }, []);
14373
- const handleSelectChatMode = useCallback7(() => {
14270
+ const handleSelectChatMode = useCallback8(() => {
14374
14271
  setViewingDiff(null);
14375
14272
  setFocusPanel("chat-history");
14376
14273
  }, []);
@@ -14384,14 +14281,14 @@ function AppInner() {
14384
14281
  }
14385
14282
  return null;
14386
14283
  }, [lastViewedDiff, statusData?.repoStatuses]);
14387
- const handleSelectDiffMode = useCallback7(() => {
14284
+ const handleSelectDiffMode = useCallback8(() => {
14388
14285
  if (firstAvailableDiff) {
14389
14286
  setViewingDiff(firstAvailableDiff);
14390
14287
  setLastViewedDiff(firstAvailableDiff);
14391
14288
  setFocusPanel("chat-history");
14392
14289
  }
14393
14290
  }, [firstAvailableDiff]);
14394
- const handleSendMessage = useCallback7(
14291
+ const handleSendMessage = useCallback8(
14395
14292
  async (message) => {
14396
14293
  if (!selectedWorkspaceId || !resolvedChatId) return;
14397
14294
  if (isMockSelected) {
@@ -14419,7 +14316,6 @@ function AppInner() {
14419
14316
  {
14420
14317
  groups,
14421
14318
  selectedWorkspaceId,
14422
- mockIds,
14423
14319
  onSelectWorkspace: handleSelectWorkspace,
14424
14320
  onCreateWorkspace: handleCreateWorkspace,
14425
14321
  onDeleteWorkspace: handleDeleteWorkspace,
@@ -14927,7 +14823,7 @@ Deleted file ${pathOrId}.
14927
14823
  }
14928
14824
 
14929
14825
  // src/index.ts
14930
- var CLI_VERSION = "0.2.171";
14826
+ var CLI_VERSION = "0.2.173";
14931
14827
  function parseBooleanOption(value) {
14932
14828
  if (value === "true") return true;
14933
14829
  if (value === "false") return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.171",
3
+ "version": "0.2.173",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.mjs",
6
6
  "bin": {