replicas-cli 0.2.40 → 0.2.42
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/README.md
CHANGED
|
@@ -63,11 +63,12 @@ Create a `replicas.json` or `replicas.yaml` file in your repository root:
|
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Or use YAML:
|
|
66
|
+
Or use YAML for better readability (especially for multiline system prompts):
|
|
67
67
|
|
|
68
68
|
```yaml
|
|
69
69
|
systemPrompt: |
|
|
70
70
|
Custom instructions for coding agents.
|
|
71
|
+
Supports multiline text.
|
|
71
72
|
|
|
72
73
|
startHook:
|
|
73
74
|
commands:
|
|
@@ -77,20 +78,19 @@ startHook:
|
|
|
77
78
|
|
|
78
79
|
## Features
|
|
79
80
|
|
|
80
|
-
- **Interactive Dashboard**: Full-featured TUI with workspace management, chat, and environment monitoring
|
|
81
81
|
- **Secure Authentication**: OAuth-based login with token refresh
|
|
82
82
|
- **Quick SSH Access**: Connect to workspaces with a single command
|
|
83
|
+
- **SSH Key Caching**: Reuse SSH keys across sessions
|
|
83
84
|
- **Multi-Organization**: Switch between different organizations
|
|
84
85
|
|
|
85
|
-
##
|
|
86
|
+
## How it Works
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
# Run from source
|
|
89
|
-
bun run dev
|
|
88
|
+
1. **SSH Key Management**: The CLI securely caches SSH keys in `~/.replicas/keys/`
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
## Requirements
|
|
91
|
+
|
|
92
|
+
- Node.js 18+
|
|
93
|
+
- SSH client (openssh)
|
|
94
94
|
|
|
95
95
|
## Support
|
|
96
96
|
|
|
@@ -7524,6 +7524,22 @@ var MAX_WARM_HOOK_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
|
7524
7524
|
// ../shared/src/replicas-config.ts
|
|
7525
7525
|
var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
|
|
7526
7526
|
|
|
7527
|
+
// ../shared/src/engine/types.ts
|
|
7528
|
+
var DEFAULT_CHAT_TITLES = {
|
|
7529
|
+
claude: "Claude Code",
|
|
7530
|
+
codex: "Codex"
|
|
7531
|
+
};
|
|
7532
|
+
function getInitialChatId(chats, preferredProvider = "claude") {
|
|
7533
|
+
if (chats.length === 0) return null;
|
|
7534
|
+
const isDefault = (c) => c.title === DEFAULT_CHAT_TITLES[c.provider];
|
|
7535
|
+
const hasActivity = (c) => c.updatedAt > c.createdAt;
|
|
7536
|
+
const mostActive = (list) => list.filter(hasActivity).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))[0] ?? null;
|
|
7537
|
+
const fallbackProvider = preferredProvider === "claude" ? "codex" : "claude";
|
|
7538
|
+
const preferred = chats.filter((c) => c.provider === preferredProvider);
|
|
7539
|
+
const fallback = chats.filter((c) => c.provider === fallbackProvider);
|
|
7540
|
+
return mostActive(preferred)?.id ?? mostActive(fallback)?.id ?? preferred.find(isDefault)?.id ?? fallback.find(isDefault)?.id ?? chats[0]?.id ?? null;
|
|
7541
|
+
}
|
|
7542
|
+
|
|
7527
7543
|
// ../shared/src/prompts.ts
|
|
7528
7544
|
var REPLICAS_INSTRUCTIONS_TAG = "replicas_instructions";
|
|
7529
7545
|
function removeTag(text, tag) {
|
|
@@ -8237,6 +8253,7 @@ export {
|
|
|
8237
8253
|
getCurrentUser,
|
|
8238
8254
|
SANDBOX_PATHS,
|
|
8239
8255
|
REPLICAS_CONFIG_FILENAMES,
|
|
8256
|
+
getInitialChatId,
|
|
8240
8257
|
parseAgentEvents,
|
|
8241
8258
|
createUserMessage,
|
|
8242
8259
|
isAgentBackendEvent,
|
package/dist/index.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
setIdeCommand,
|
|
16
16
|
setOrganizationId,
|
|
17
17
|
writeConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-IO4QHXPW.mjs";
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
import "dotenv/config";
|
|
@@ -448,7 +448,7 @@ Found ${response.workspaces.length} workspaces matching "${workspaceName}":`));
|
|
|
448
448
|
console.log(chalk4.gray(` Status: ${selectedWorkspace.status || "unknown"}`));
|
|
449
449
|
if (selectedWorkspace.status === "sleeping") {
|
|
450
450
|
throw new Error(
|
|
451
|
-
"Workspace is currently sleeping.
|
|
451
|
+
"Workspace is currently sleeping. Wake it using `replicas app` (press w on a sleeping workspace) or visit https://replicas.dev"
|
|
452
452
|
);
|
|
453
453
|
}
|
|
454
454
|
console.log(chalk4.blue("\nRequesting SSH access token..."));
|
|
@@ -1855,12 +1855,12 @@ async function interactiveCommand() {
|
|
|
1855
1855
|
);
|
|
1856
1856
|
}
|
|
1857
1857
|
console.log(chalk17.gray("Starting interactive mode..."));
|
|
1858
|
-
const { launchInteractive } = await import("./interactive-
|
|
1858
|
+
const { launchInteractive } = await import("./interactive-M3UR4H6X.mjs");
|
|
1859
1859
|
await launchInteractive();
|
|
1860
1860
|
}
|
|
1861
1861
|
|
|
1862
1862
|
// src/index.ts
|
|
1863
|
-
var CLI_VERSION = "0.2.
|
|
1863
|
+
var CLI_VERSION = "0.2.42";
|
|
1864
1864
|
var program = new Command();
|
|
1865
1865
|
program.name("replicas").description("CLI for managing Replicas workspaces").version(CLI_VERSION);
|
|
1866
1866
|
program.command("login").description("Authenticate with your Replicas account").action(async () => {
|
|
@@ -4,19 +4,20 @@ import {
|
|
|
4
4
|
createMockWorkspaceRecord,
|
|
5
5
|
createUserMessage,
|
|
6
6
|
filterDisplayMessages,
|
|
7
|
+
getInitialChatId,
|
|
7
8
|
getOrganizationId,
|
|
8
9
|
getValidToken,
|
|
9
10
|
isAgentBackendEvent,
|
|
10
11
|
parseAgentEvents
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-IO4QHXPW.mjs";
|
|
12
13
|
|
|
13
14
|
// src/interactive/index.tsx
|
|
14
15
|
import { createCliRenderer } from "@opentui/core";
|
|
15
16
|
import { createRoot } from "@opentui/react";
|
|
16
17
|
|
|
17
18
|
// src/interactive/App.tsx
|
|
18
|
-
import { useState as
|
|
19
|
-
import { useKeyboard as
|
|
19
|
+
import { useState as useState6, useEffect as useEffect3, useMemo as useMemo4, useCallback as useCallback6, useRef as useRef3 } from "react";
|
|
20
|
+
import { useKeyboard as useKeyboard4, useRenderer, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
|
|
20
21
|
import { QueryClient } from "@tanstack/react-query";
|
|
21
22
|
|
|
22
23
|
// ../shared/src/hooks/auth-context.ts
|
|
@@ -145,6 +146,21 @@ function useDeleteWorkspace() {
|
|
|
145
146
|
}
|
|
146
147
|
}, auth.queryClient);
|
|
147
148
|
}
|
|
149
|
+
function useWakeWorkspace() {
|
|
150
|
+
const auth = useReplicasAuth();
|
|
151
|
+
const orgFetch = useOrgFetch();
|
|
152
|
+
const orgId = auth.getOrganizationId();
|
|
153
|
+
return useMutation({
|
|
154
|
+
mutationFn: (workspaceId) => orgFetch(`/v1/workspaces/${workspaceId}/wake`, {
|
|
155
|
+
method: "POST"
|
|
156
|
+
}),
|
|
157
|
+
onSuccess: (_data, workspaceId) => {
|
|
158
|
+
auth.queryClient.invalidateQueries({ queryKey: ["workspaces", orgId] });
|
|
159
|
+
auth.queryClient.invalidateQueries({ queryKey: ["workspace-status", workspaceId] });
|
|
160
|
+
auth.queryClient.invalidateQueries({ queryKey: ["workspace-chats", workspaceId] });
|
|
161
|
+
}
|
|
162
|
+
}, auth.queryClient);
|
|
163
|
+
}
|
|
148
164
|
function useGenerateWorkspaceName() {
|
|
149
165
|
const auth = useReplicasAuth();
|
|
150
166
|
const orgFetch = useOrgFetch();
|
|
@@ -468,11 +484,11 @@ function useEnvironmentFiles() {
|
|
|
468
484
|
// src/interactive/components/StatusBar.tsx
|
|
469
485
|
import { jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
470
486
|
var KEYBINDS = {
|
|
471
|
-
sidebar: "j/k nav \u21B5 select d del a add",
|
|
487
|
+
sidebar: "j/k nav \u21B5 select d del a add w wake",
|
|
472
488
|
"chat-tabs": "\u2190/\u2192 switch tabs",
|
|
473
489
|
"chat-history": "j/k scroll",
|
|
474
490
|
"chat-input": "\u21B5 send \u21E5 plan/build",
|
|
475
|
-
info: "j/k
|
|
491
|
+
info: "j/k nav \u21B5 open o dashboard w wake"
|
|
476
492
|
};
|
|
477
493
|
function StatusBar({ focusPanel }) {
|
|
478
494
|
return /* @__PURE__ */ jsxs(
|
|
@@ -541,6 +557,8 @@ function WorkspaceSidebar({
|
|
|
541
557
|
onSelectWorkspace,
|
|
542
558
|
onCreateWorkspace,
|
|
543
559
|
onDeleteWorkspace,
|
|
560
|
+
onWakeWorkspace,
|
|
561
|
+
wakingWorkspaceId,
|
|
544
562
|
focused,
|
|
545
563
|
loading
|
|
546
564
|
}) {
|
|
@@ -675,6 +693,13 @@ function WorkspaceSidebar({
|
|
|
675
693
|
}
|
|
676
694
|
return;
|
|
677
695
|
}
|
|
696
|
+
if (key.name === "w") {
|
|
697
|
+
const item = items[cursorIndex];
|
|
698
|
+
if (item?.type === "workspace" && item.status === "sleeping" && wakingWorkspaceId !== item.workspaceId) {
|
|
699
|
+
onWakeWorkspace(item.workspaceId);
|
|
700
|
+
}
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
678
703
|
});
|
|
679
704
|
const handleItemClick = useCallback3(
|
|
680
705
|
(globalIndex) => {
|
|
@@ -814,7 +839,8 @@ function WorkspaceSidebar({
|
|
|
814
839
|
focused && /* @__PURE__ */ jsx2("box", { height: 1, paddingX: 1, backgroundColor: "#111111", children: /* @__PURE__ */ jsxs2("text", { children: [
|
|
815
840
|
/* @__PURE__ */ jsx2("span", { fg: "#555555", children: "j/k:nav " }),
|
|
816
841
|
/* @__PURE__ */ jsx2("span", { fg: "#555555", children: "d:del " }),
|
|
817
|
-
/* @__PURE__ */ jsx2("span", { fg: "#555555", children: "a:add" })
|
|
842
|
+
/* @__PURE__ */ jsx2("span", { fg: "#555555", children: "a:add " }),
|
|
843
|
+
/* @__PURE__ */ jsx2("span", { fg: "#555555", children: "w:wake" })
|
|
818
844
|
] }) })
|
|
819
845
|
]
|
|
820
846
|
}
|
|
@@ -1272,10 +1298,45 @@ function ChatArea({
|
|
|
1272
1298
|
}
|
|
1273
1299
|
|
|
1274
1300
|
// src/interactive/components/WorkspaceInfo.tsx
|
|
1275
|
-
import React4 from "react";
|
|
1301
|
+
import React4, { useState as useState4, useMemo as useMemo3, useCallback as useCallback4 } from "react";
|
|
1276
1302
|
import open from "open";
|
|
1303
|
+
import { useKeyboard as useKeyboard3 } from "@opentui/react";
|
|
1277
1304
|
import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
|
|
1278
1305
|
var WEB_APP_URL = process.env.REPLICAS_WEB_URL || "https://replicas.dev";
|
|
1306
|
+
function buildInteractiveItems(workspaceId, status, previews, wakingWorkspaceId) {
|
|
1307
|
+
const items = [];
|
|
1308
|
+
if (workspaceId) {
|
|
1309
|
+
items.push({ type: "dashboard", workspaceId });
|
|
1310
|
+
}
|
|
1311
|
+
if (workspaceId && status.status === "sleeping" && wakingWorkspaceId !== workspaceId) {
|
|
1312
|
+
items.push({ type: "wake", workspaceId });
|
|
1313
|
+
}
|
|
1314
|
+
for (const preview of previews) {
|
|
1315
|
+
if (preview.publicUrl) {
|
|
1316
|
+
items.push({ type: "preview", url: preview.publicUrl, port: preview.port });
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
if (status.repoStatuses) {
|
|
1320
|
+
for (const repo of status.repoStatuses) {
|
|
1321
|
+
if (repo.prUrl) {
|
|
1322
|
+
items.push({ type: "pr", url: repo.prUrl, repoName: repo.name });
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return items;
|
|
1327
|
+
}
|
|
1328
|
+
function getItemLabel(item) {
|
|
1329
|
+
switch (item.type) {
|
|
1330
|
+
case "dashboard":
|
|
1331
|
+
return "\u2197 Open in Dashboard";
|
|
1332
|
+
case "wake":
|
|
1333
|
+
return "\u25B6 Wake";
|
|
1334
|
+
case "preview":
|
|
1335
|
+
return `\u2197 Preview :${item.port}`;
|
|
1336
|
+
case "pr":
|
|
1337
|
+
return `\u2197 View PR (${item.repoName})`;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1279
1340
|
var AUTH_METHOD_LABELS = {
|
|
1280
1341
|
oauth: "OAuth",
|
|
1281
1342
|
api_key: "API Key",
|
|
@@ -1326,8 +1387,108 @@ function DetailList({
|
|
|
1326
1387
|
if (rows.length === 0) return null;
|
|
1327
1388
|
return /* @__PURE__ */ jsx6(Fragment, { children: rows.map((item, i) => /* @__PURE__ */ jsx6(CardItem, { label: item, status: actualSet.has(item) }, i)) });
|
|
1328
1389
|
}
|
|
1329
|
-
function
|
|
1390
|
+
function InteractiveRow({ label, highlighted, onClick }) {
|
|
1391
|
+
return /* @__PURE__ */ jsx6(
|
|
1392
|
+
"box",
|
|
1393
|
+
{
|
|
1394
|
+
paddingX: 1,
|
|
1395
|
+
backgroundColor: highlighted ? "#1a2a1a" : "#111111",
|
|
1396
|
+
onMouseDown: onClick,
|
|
1397
|
+
children: /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: highlighted ? "#66bb6a" : "#7dcfff", children: label }) })
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, envConfig, previews, onWakeWorkspace, wakingWorkspaceId }) {
|
|
1330
1402
|
const borderColor = focused ? "#66bb6a" : "#333333";
|
|
1403
|
+
const [cursorIndex, setCursorIndex] = useState4(0);
|
|
1404
|
+
const interactiveItems = useMemo3(() => {
|
|
1405
|
+
if (!status || !workspaceName) return [];
|
|
1406
|
+
return buildInteractiveItems(workspaceId, status, previews, wakingWorkspaceId);
|
|
1407
|
+
}, [workspaceId, workspaceName, status, previews, wakingWorkspaceId]);
|
|
1408
|
+
const safeCursor = interactiveItems.length > 0 ? Math.min(cursorIndex, interactiveItems.length - 1) : 0;
|
|
1409
|
+
const moveCursor = useCallback4(
|
|
1410
|
+
(next) => {
|
|
1411
|
+
const clamped = Math.max(0, Math.min(interactiveItems.length - 1, next));
|
|
1412
|
+
setCursorIndex(clamped);
|
|
1413
|
+
},
|
|
1414
|
+
[interactiveItems.length]
|
|
1415
|
+
);
|
|
1416
|
+
const handleAction = useCallback4(
|
|
1417
|
+
(item) => {
|
|
1418
|
+
if (!item) return;
|
|
1419
|
+
switch (item.type) {
|
|
1420
|
+
case "dashboard":
|
|
1421
|
+
open(`${WEB_APP_URL}/dashboard?workspaceId=${item.workspaceId}`).catch(() => {
|
|
1422
|
+
});
|
|
1423
|
+
break;
|
|
1424
|
+
case "wake":
|
|
1425
|
+
onWakeWorkspace(item.workspaceId);
|
|
1426
|
+
break;
|
|
1427
|
+
case "preview":
|
|
1428
|
+
open(item.url).catch(() => {
|
|
1429
|
+
});
|
|
1430
|
+
break;
|
|
1431
|
+
case "pr":
|
|
1432
|
+
open(item.url).catch(() => {
|
|
1433
|
+
});
|
|
1434
|
+
break;
|
|
1435
|
+
}
|
|
1436
|
+
},
|
|
1437
|
+
[onWakeWorkspace]
|
|
1438
|
+
);
|
|
1439
|
+
useKeyboard3((key) => {
|
|
1440
|
+
if (!focused) return;
|
|
1441
|
+
if (interactiveItems.length === 0) return;
|
|
1442
|
+
if (key.name === "j" || key.name === "down") {
|
|
1443
|
+
moveCursor(safeCursor + 1);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
if (key.name === "k" || key.name === "up") {
|
|
1447
|
+
moveCursor(safeCursor - 1);
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
if (key.name === "g" && !key.shift) {
|
|
1451
|
+
moveCursor(0);
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
if (key.name === "g" && key.shift) {
|
|
1455
|
+
moveCursor(interactiveItems.length - 1);
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
if (key.name === "enter" || key.name === "return") {
|
|
1459
|
+
handleAction(interactiveItems[safeCursor]);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
if (key.name === "o") {
|
|
1463
|
+
const dashboardItem2 = interactiveItems.find((item) => item.type === "dashboard");
|
|
1464
|
+
if (dashboardItem2) handleAction(dashboardItem2);
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
if (key.name === "w") {
|
|
1468
|
+
const wakeItem2 = interactiveItems.find((item) => item.type === "wake");
|
|
1469
|
+
if (wakeItem2) handleAction(wakeItem2);
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
const isHighlighted = useCallback4(
|
|
1474
|
+
(item) => {
|
|
1475
|
+
if (!focused) return false;
|
|
1476
|
+
const idx = interactiveItems.indexOf(item);
|
|
1477
|
+
return idx === safeCursor;
|
|
1478
|
+
},
|
|
1479
|
+
[focused, interactiveItems, safeCursor]
|
|
1480
|
+
);
|
|
1481
|
+
const findItem = useCallback4(
|
|
1482
|
+
(type, key) => {
|
|
1483
|
+
return interactiveItems.find((item) => {
|
|
1484
|
+
if (item.type !== type) return false;
|
|
1485
|
+
if (type === "preview" && key) return item.port === Number(key);
|
|
1486
|
+
if (type === "pr" && key) return item.repoName === key;
|
|
1487
|
+
return true;
|
|
1488
|
+
});
|
|
1489
|
+
},
|
|
1490
|
+
[interactiveItems]
|
|
1491
|
+
);
|
|
1331
1492
|
if (!workspaceName) {
|
|
1332
1493
|
return /* @__PURE__ */ jsx6(
|
|
1333
1494
|
"box",
|
|
@@ -1367,6 +1528,8 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
|
|
|
1367
1528
|
const expectedSkills = (envConfig.skills?.environment_skills ?? []).map((s) => s.source);
|
|
1368
1529
|
const expectedGlobalVars = (envConfig.variables?.environment_variables ?? []).filter((v) => v.scope_type === "global").map((v) => v.key);
|
|
1369
1530
|
const expectedGlobalFiles = (envConfig.files?.environment_files ?? []).filter((f) => f.scope_type === "global").map((f) => f.path);
|
|
1531
|
+
const dashboardItem = findItem("dashboard");
|
|
1532
|
+
const wakeItem = findItem("wake");
|
|
1370
1533
|
return /* @__PURE__ */ jsx6(
|
|
1371
1534
|
"box",
|
|
1372
1535
|
{
|
|
@@ -1393,24 +1556,24 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
|
|
|
1393
1556
|
" ",
|
|
1394
1557
|
env.engineVersion
|
|
1395
1558
|
] })
|
|
1396
|
-
] })
|
|
1397
|
-
workspaceId && /* @__PURE__ */ jsx6(
|
|
1398
|
-
"box",
|
|
1399
|
-
{
|
|
1400
|
-
onMouseDown: () => {
|
|
1401
|
-
open(`${WEB_APP_URL}/dashboard?workspaceId=${workspaceId}`).catch(() => {
|
|
1402
|
-
});
|
|
1403
|
-
},
|
|
1404
|
-
children: /* @__PURE__ */ jsxs6("text", { children: [
|
|
1405
|
-
/* @__PURE__ */ jsxs6("span", { fg: "#7dcfff", children: [
|
|
1406
|
-
"\u2197",
|
|
1407
|
-
" Open in Dashboard"
|
|
1408
|
-
] }),
|
|
1409
|
-
/* @__PURE__ */ jsx6("span", { fg: "#555555", children: " (o)" })
|
|
1410
|
-
] })
|
|
1411
|
-
}
|
|
1412
|
-
)
|
|
1559
|
+
] })
|
|
1413
1560
|
] }) }),
|
|
1561
|
+
dashboardItem && /* @__PURE__ */ jsx6(
|
|
1562
|
+
InteractiveRow,
|
|
1563
|
+
{
|
|
1564
|
+
label: getItemLabel(dashboardItem),
|
|
1565
|
+
highlighted: isHighlighted(dashboardItem),
|
|
1566
|
+
onClick: () => handleAction(dashboardItem)
|
|
1567
|
+
}
|
|
1568
|
+
),
|
|
1569
|
+
wakeItem && /* @__PURE__ */ jsx6(
|
|
1570
|
+
InteractiveRow,
|
|
1571
|
+
{
|
|
1572
|
+
label: getItemLabel(wakeItem),
|
|
1573
|
+
highlighted: isHighlighted(wakeItem),
|
|
1574
|
+
onClick: () => handleAction(wakeItem)
|
|
1575
|
+
}
|
|
1576
|
+
),
|
|
1414
1577
|
(status.isClaudeProcessing || status.isCodexProcessing) && /* @__PURE__ */ jsxs6("box", { backgroundColor: "#1a1500", paddingX: 1, marginX: 1, marginBottom: 1, children: [
|
|
1415
1578
|
status.isClaudeProcessing && /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsxs6("span", { fg: "#ffaa00", children: [
|
|
1416
1579
|
"\u25C6",
|
|
@@ -1436,42 +1599,60 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
|
|
|
1436
1599
|
/* @__PURE__ */ jsx6(CardItem, { label: "Slack", status: env.slackAccessConfigured }),
|
|
1437
1600
|
/* @__PURE__ */ jsx6(CardItem, { label: "Linear", status: env.linearAccessConfigured })
|
|
1438
1601
|
] }),
|
|
1439
|
-
previews.length > 0 && /* @__PURE__ */ jsx6(Section, { title: "Previews", children: previews.map((preview, i) =>
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1602
|
+
previews.length > 0 && /* @__PURE__ */ jsx6(Section, { title: "Previews", children: previews.map((preview, i) => {
|
|
1603
|
+
const previewItem = findItem("preview", String(preview.port));
|
|
1604
|
+
return /* @__PURE__ */ jsxs6(
|
|
1605
|
+
"box",
|
|
1606
|
+
{
|
|
1607
|
+
flexDirection: "row",
|
|
1608
|
+
justifyContent: "space-between",
|
|
1609
|
+
paddingX: 1,
|
|
1610
|
+
backgroundColor: previewItem && isHighlighted(previewItem) ? "#1a2a1a" : "#111111",
|
|
1611
|
+
onMouseDown: () => previewItem && handleAction(previewItem),
|
|
1612
|
+
children: [
|
|
1613
|
+
/* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsxs6("span", { fg: "#cccccc", children: [
|
|
1614
|
+
":",
|
|
1615
|
+
preview.port
|
|
1616
|
+
] }) }),
|
|
1617
|
+
/* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: previewItem && isHighlighted(previewItem) ? "#66bb6a" : "#7dcfff", children: "\u2197" }) })
|
|
1618
|
+
]
|
|
1619
|
+
},
|
|
1620
|
+
i
|
|
1621
|
+
);
|
|
1622
|
+
}) }),
|
|
1623
|
+
status.repoStatuses && status.repoStatuses.length > 0 && /* @__PURE__ */ jsx6(Section, { title: "Repositories", children: status.repoStatuses.map((repo, i) => {
|
|
1624
|
+
const prItem = findItem("pr", repo.name);
|
|
1625
|
+
return /* @__PURE__ */ jsxs6(React4.Fragment, { children: [
|
|
1626
|
+
/* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx6("strong", { children: repo.name }) }) }) }),
|
|
1627
|
+
/* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: /* @__PURE__ */ jsxs6("text", { children: [
|
|
1628
|
+
/* @__PURE__ */ jsxs6("span", { fg: "#555555", children: [
|
|
1629
|
+
"\u2514",
|
|
1630
|
+
" "
|
|
1631
|
+
] }),
|
|
1632
|
+
/* @__PURE__ */ jsx6("span", { fg: repo.currentBranch !== repo.defaultBranch ? "#ffaa00" : "#66bb6a", children: repo.currentBranch })
|
|
1633
|
+
] }) }),
|
|
1634
|
+
repo.gitDiff && (repo.gitDiff.added > 0 || repo.gitDiff.removed > 0) && /* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, flexDirection: "row", children: /* @__PURE__ */ jsxs6("text", { children: [
|
|
1635
|
+
/* @__PURE__ */ jsx6("span", { fg: "#555555", children: " " }),
|
|
1636
|
+
/* @__PURE__ */ jsxs6("span", { fg: "#66bb6a", children: [
|
|
1637
|
+
"+",
|
|
1638
|
+
repo.gitDiff.added
|
|
1639
|
+
] }),
|
|
1640
|
+
/* @__PURE__ */ jsx6("span", { fg: "#444444", children: " / " }),
|
|
1641
|
+
/* @__PURE__ */ jsxs6("span", { fg: "#ff4444", children: [
|
|
1642
|
+
"-",
|
|
1643
|
+
repo.gitDiff.removed
|
|
1644
|
+
] })
|
|
1645
|
+
] }) }),
|
|
1646
|
+
prItem && /* @__PURE__ */ jsx6(
|
|
1647
|
+
InteractiveRow,
|
|
1648
|
+
{
|
|
1649
|
+
label: ` ${getItemLabel(prItem)}`,
|
|
1650
|
+
highlighted: isHighlighted(prItem),
|
|
1651
|
+
onClick: () => handleAction(prItem)
|
|
1652
|
+
}
|
|
1653
|
+
)
|
|
1654
|
+
] }, i);
|
|
1655
|
+
}) }),
|
|
1475
1656
|
env && /* @__PURE__ */ jsxs6(Section, { title: "Hooks", children: [
|
|
1476
1657
|
/* @__PURE__ */ jsx6(CardItem, { label: "Global warm", status: env.globalWarmHookCompleted.status }),
|
|
1477
1658
|
env.repositories.map((repo, i) => /* @__PURE__ */ jsxs6(React4.Fragment, { children: [
|
|
@@ -1510,7 +1691,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
|
|
|
1510
1691
|
import { useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
|
|
1511
1692
|
|
|
1512
1693
|
// src/interactive/toast-context.tsx
|
|
1513
|
-
import { createContext as createContext2, useContext as useContext2, useState as
|
|
1694
|
+
import { createContext as createContext2, useContext as useContext2, useState as useState5, useCallback as useCallback5, useRef as useRef2 } from "react";
|
|
1514
1695
|
import { jsx as jsx7 } from "@opentui/react/jsx-runtime";
|
|
1515
1696
|
var ToastContext = createContext2(null);
|
|
1516
1697
|
function useToast() {
|
|
@@ -1519,9 +1700,9 @@ function useToast() {
|
|
|
1519
1700
|
return ctx;
|
|
1520
1701
|
}
|
|
1521
1702
|
function ToastProvider({ children }) {
|
|
1522
|
-
const [current, setCurrent] =
|
|
1703
|
+
const [current, setCurrent] = useState5(null);
|
|
1523
1704
|
const timeoutRef = useRef2(null);
|
|
1524
|
-
const show =
|
|
1705
|
+
const show = useCallback5((options) => {
|
|
1525
1706
|
const { message, variant = "info", duration = 3e3 } = options;
|
|
1526
1707
|
setCurrent({ message, variant });
|
|
1527
1708
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
@@ -1529,7 +1710,7 @@ function ToastProvider({ children }) {
|
|
|
1529
1710
|
setCurrent(null);
|
|
1530
1711
|
}, duration);
|
|
1531
1712
|
}, []);
|
|
1532
|
-
const error =
|
|
1713
|
+
const error = useCallback5((err) => {
|
|
1533
1714
|
const message = err instanceof Error ? err.message : "An error occurred";
|
|
1534
1715
|
show({ message, variant: "error", duration: 5e3 });
|
|
1535
1716
|
}, [show]);
|
|
@@ -1614,17 +1795,17 @@ function AppInner() {
|
|
|
1614
1795
|
renderer.off("selection", handler);
|
|
1615
1796
|
};
|
|
1616
1797
|
}, [renderer, toast]);
|
|
1617
|
-
const [selectedWorkspaceId, setSelectedWorkspaceId] =
|
|
1618
|
-
const [selectedChatId, setSelectedChatId] =
|
|
1619
|
-
const [focusPanel, setFocusPanel] =
|
|
1620
|
-
const [taskMode, setTaskMode] =
|
|
1621
|
-
const [mockWorkspaces, setMockWorkspaces] =
|
|
1798
|
+
const [selectedWorkspaceId, setSelectedWorkspaceId] = useState6(null);
|
|
1799
|
+
const [selectedChatId, setSelectedChatId] = useState6(null);
|
|
1800
|
+
const [focusPanel, setFocusPanel] = useState6("sidebar");
|
|
1801
|
+
const [taskMode, setTaskMode] = useState6("build");
|
|
1802
|
+
const [mockWorkspaces, setMockWorkspaces] = useState6([]);
|
|
1622
1803
|
const mockToRealRef = useRef3(/* @__PURE__ */ new Map());
|
|
1623
1804
|
const mockGroupRef = useRef3(/* @__PURE__ */ new Map());
|
|
1624
|
-
const [mockMessages, setMockMessages] =
|
|
1625
|
-
const [mockThinking, setMockThinking] =
|
|
1805
|
+
const [mockMessages, setMockMessages] = useState6([]);
|
|
1806
|
+
const [mockThinking, setMockThinking] = useState6(false);
|
|
1626
1807
|
const pendingMessageRef = useRef3(/* @__PURE__ */ new Map());
|
|
1627
|
-
const mockIds =
|
|
1808
|
+
const mockIds = useMemo4(() => new Set(mockWorkspaces.map((m) => m.id)), [mockWorkspaces]);
|
|
1628
1809
|
const isMockSelected = selectedWorkspaceId ? mockIds.has(selectedWorkspaceId) : false;
|
|
1629
1810
|
const { data: workspacesData, isLoading: loadingWorkspaces } = useWorkspaces(1, 100, "organization");
|
|
1630
1811
|
const { data: reposData } = useRepositories();
|
|
@@ -1637,9 +1818,16 @@ function AppInner() {
|
|
|
1637
1818
|
{ includeDiffs: true }
|
|
1638
1819
|
);
|
|
1639
1820
|
const { data: chatsData } = useWorkspaceChats(isMockSelected ? null : selectedWorkspaceId);
|
|
1821
|
+
const chats = isMockSelected ? MOCK_CHATS : chatsData?.chats ?? [];
|
|
1822
|
+
const resolvedChatId = useMemo4(() => {
|
|
1823
|
+
if (selectedChatId && chats.find((c) => c.id === selectedChatId)) {
|
|
1824
|
+
return selectedChatId;
|
|
1825
|
+
}
|
|
1826
|
+
return getInitialChatId(chats);
|
|
1827
|
+
}, [chats, selectedChatId]);
|
|
1640
1828
|
const { data: historyData, isLoading: loadingMessages } = useChatHistory(
|
|
1641
1829
|
isMockSelected ? null : selectedWorkspaceId,
|
|
1642
|
-
|
|
1830
|
+
resolvedChatId?.startsWith("mock-") ? null : resolvedChatId
|
|
1643
1831
|
);
|
|
1644
1832
|
const { data: previewsData } = useWorkspacePreviews(isMockSelected ? null : selectedWorkspaceId);
|
|
1645
1833
|
const { connected: sseConnected } = useWorkspaceEvents(
|
|
@@ -1647,23 +1835,24 @@ function AppInner() {
|
|
|
1647
1835
|
);
|
|
1648
1836
|
const createWorkspaceMutation = useCreateWorkspace();
|
|
1649
1837
|
const deleteWorkspaceMutation = useDeleteWorkspace();
|
|
1838
|
+
const wakeWorkspaceMutation = useWakeWorkspace();
|
|
1650
1839
|
const generateNameMutation = useGenerateWorkspaceName();
|
|
1651
|
-
const
|
|
1652
|
-
const
|
|
1840
|
+
const [wakingWorkspaceId, setWakingWorkspaceId] = useState6(null);
|
|
1841
|
+
const sendMessageMutation = useSendChatMessage(selectedWorkspaceId, resolvedChatId);
|
|
1842
|
+
const interruptMutation = useInterruptChat(selectedWorkspaceId, resolvedChatId);
|
|
1653
1843
|
const workspaces = workspacesData?.workspaces ?? [];
|
|
1654
1844
|
const repositories = reposData?.repositories ?? [];
|
|
1655
1845
|
const repositorySets = setsData?.repository_sets ?? [];
|
|
1656
|
-
const chats = isMockSelected ? MOCK_CHATS : chatsData?.chats ?? [];
|
|
1657
1846
|
const previews = previewsData?.previews ?? [];
|
|
1658
|
-
const allWorkspaces =
|
|
1659
|
-
const groups =
|
|
1847
|
+
const allWorkspaces = useMemo4(() => [...mockWorkspaces, ...workspaces], [mockWorkspaces, workspaces]);
|
|
1848
|
+
const groups = useMemo4(
|
|
1660
1849
|
() => buildGroups(allWorkspaces, workspacesData, repositories, repositorySets, mockGroupRef.current),
|
|
1661
1850
|
[allWorkspaces, workspacesData, repositories, repositorySets]
|
|
1662
1851
|
);
|
|
1663
1852
|
const selectedWorkspace = allWorkspaces.find((ws) => ws.id === selectedWorkspaceId) ?? null;
|
|
1664
|
-
const selectedChat = chats.find((c) => c.id ===
|
|
1853
|
+
const selectedChat = chats.find((c) => c.id === resolvedChatId) ?? null;
|
|
1665
1854
|
const isProcessing = mockThinking || (selectedChat?.processing ?? false);
|
|
1666
|
-
const displayMessages =
|
|
1855
|
+
const displayMessages = useMemo4(() => {
|
|
1667
1856
|
if (isMockSelected) return mockMessages;
|
|
1668
1857
|
const rawEvents = historyData?.events ?? [];
|
|
1669
1858
|
const events = rawEvents.filter(isAgentBackendEvent);
|
|
@@ -1679,18 +1868,6 @@ function AppInner() {
|
|
|
1679
1868
|
setSelectedWorkspaceId(workspaces[0].id);
|
|
1680
1869
|
}
|
|
1681
1870
|
}, [workspaces, selectedWorkspaceId]);
|
|
1682
|
-
useEffect3(() => {
|
|
1683
|
-
if (chats.length > 0 && !selectedChatId) {
|
|
1684
|
-
setSelectedChatId(chats[0].id);
|
|
1685
|
-
} else if (chats.length > 0 && selectedChatId && !chats.find((c) => c.id === selectedChatId)) {
|
|
1686
|
-
setSelectedChatId(chats[0].id);
|
|
1687
|
-
}
|
|
1688
|
-
}, [chats, selectedChatId]);
|
|
1689
|
-
useEffect3(() => {
|
|
1690
|
-
setMockMessages([]);
|
|
1691
|
-
setMockThinking(false);
|
|
1692
|
-
setSelectedChatId(null);
|
|
1693
|
-
}, [selectedWorkspaceId]);
|
|
1694
1871
|
useEffect3(() => {
|
|
1695
1872
|
if (mockWorkspaces.length === 0) return;
|
|
1696
1873
|
const realIds = new Set(workspaces.map((w) => w.id));
|
|
@@ -1708,6 +1885,7 @@ function AppInner() {
|
|
|
1708
1885
|
}
|
|
1709
1886
|
mockToRealRef.current.delete(mock.id);
|
|
1710
1887
|
mockGroupRef.current.delete(mock.id);
|
|
1888
|
+
setSelectedChatId(null);
|
|
1711
1889
|
setMockMessages([]);
|
|
1712
1890
|
setMockThinking(false);
|
|
1713
1891
|
changed = true;
|
|
@@ -1719,7 +1897,7 @@ function AppInner() {
|
|
|
1719
1897
|
}, [workspaces, mockWorkspaces, selectedWorkspaceId]);
|
|
1720
1898
|
useEffect3(() => {
|
|
1721
1899
|
if (!selectedWorkspaceId || isMockSelected) return;
|
|
1722
|
-
if (!
|
|
1900
|
+
if (!resolvedChatId || resolvedChatId.startsWith("mock-")) return;
|
|
1723
1901
|
if (statusData?.status !== "active") return;
|
|
1724
1902
|
const pending = pendingMessageRef.current.get(selectedWorkspaceId);
|
|
1725
1903
|
if (!pending) return;
|
|
@@ -1730,7 +1908,7 @@ function AppInner() {
|
|
|
1730
1908
|
});
|
|
1731
1909
|
}
|
|
1732
1910
|
sendMessageMutation.mutate({ message: pending });
|
|
1733
|
-
}, [selectedWorkspaceId,
|
|
1911
|
+
}, [selectedWorkspaceId, resolvedChatId, isMockSelected, statusData?.status]);
|
|
1734
1912
|
useEffect3(() => {
|
|
1735
1913
|
if (mockWorkspaces.length === 0) return;
|
|
1736
1914
|
const interval = setInterval(() => {
|
|
@@ -1738,55 +1916,17 @@ function AppInner() {
|
|
|
1738
1916
|
}, 5e3);
|
|
1739
1917
|
return () => clearInterval(interval);
|
|
1740
1918
|
}, [mockWorkspaces.length]);
|
|
1741
|
-
|
|
1742
|
-
if (key.name === "f12") {
|
|
1743
|
-
renderer.console.toggle();
|
|
1744
|
-
return;
|
|
1745
|
-
}
|
|
1746
|
-
if (key.ctrl && key.name === "c") {
|
|
1747
|
-
renderer.destroy();
|
|
1748
|
-
return;
|
|
1749
|
-
}
|
|
1750
|
-
if (key.name === "escape") {
|
|
1751
|
-
if (isProcessing && selectedWorkspaceId && selectedChatId) {
|
|
1752
|
-
interruptMutation.mutate();
|
|
1753
|
-
}
|
|
1754
|
-
return;
|
|
1755
|
-
}
|
|
1756
|
-
if (key.shift && key.name === "tab") {
|
|
1757
|
-
setFocusPanel((current) => {
|
|
1758
|
-
const availablePanels = FOCUS_ORDER.filter((p) => {
|
|
1759
|
-
if (p === "sidebar" && !showWorkspacePanel) return false;
|
|
1760
|
-
if ((p === "chat-tabs" || p === "chat-history" || p === "chat-input") && !selectedWorkspaceId) return false;
|
|
1761
|
-
if (p === "chat-tabs" && chats.length <= 1) return false;
|
|
1762
|
-
if (p === "info" && !showInfoPanel) return false;
|
|
1763
|
-
return true;
|
|
1764
|
-
});
|
|
1765
|
-
const idx = availablePanels.indexOf(current);
|
|
1766
|
-
return availablePanels[(idx + 1) % availablePanels.length];
|
|
1767
|
-
});
|
|
1768
|
-
return;
|
|
1769
|
-
}
|
|
1770
|
-
if (key.name === "tab" && focusPanel === "chat-input") {
|
|
1771
|
-
setTaskMode((m) => m === "build" ? "plan" : "build");
|
|
1772
|
-
return;
|
|
1773
|
-
}
|
|
1774
|
-
if (key.name === "o" && focusPanel === "info" && selectedWorkspaceId) {
|
|
1775
|
-
const webUrl = process.env.REPLICAS_WEB_URL || "https://replicas.dev";
|
|
1776
|
-
import("open").then((mod) => mod.default(`${webUrl}/dashboard?workspaceId=${selectedWorkspaceId}`)).catch(() => {
|
|
1777
|
-
});
|
|
1778
|
-
return;
|
|
1779
|
-
}
|
|
1780
|
-
if (focusPanel === "chat-input") return;
|
|
1781
|
-
});
|
|
1782
|
-
const handleSelectWorkspace = useCallback5((workspaceId) => {
|
|
1919
|
+
const handleSelectWorkspace = useCallback6((workspaceId) => {
|
|
1783
1920
|
setSelectedWorkspaceId(workspaceId);
|
|
1921
|
+
setSelectedChatId(null);
|
|
1922
|
+
setMockMessages([]);
|
|
1923
|
+
setMockThinking(false);
|
|
1784
1924
|
setFocusPanel("chat-input");
|
|
1785
1925
|
}, []);
|
|
1786
|
-
const handleFocus =
|
|
1926
|
+
const handleFocus = useCallback6((panel) => {
|
|
1787
1927
|
setFocusPanel(panel);
|
|
1788
1928
|
}, []);
|
|
1789
|
-
const handleCreateWorkspace =
|
|
1929
|
+
const handleCreateWorkspace = useCallback6(
|
|
1790
1930
|
async (groupId, groupType) => {
|
|
1791
1931
|
const orgId = getOrganizationId() ?? "";
|
|
1792
1932
|
const mock = createMockWorkspaceRecord(orgId);
|
|
@@ -1805,7 +1945,7 @@ function AppInner() {
|
|
|
1805
1945
|
},
|
|
1806
1946
|
[createWorkspaceMutation, toast]
|
|
1807
1947
|
);
|
|
1808
|
-
const handleDeleteWorkspace =
|
|
1948
|
+
const handleDeleteWorkspace = useCallback6(
|
|
1809
1949
|
async (workspaceId) => {
|
|
1810
1950
|
if (mockIds.has(workspaceId)) {
|
|
1811
1951
|
mockGroupRef.current.delete(workspaceId);
|
|
@@ -1824,12 +1964,67 @@ function AppInner() {
|
|
|
1824
1964
|
},
|
|
1825
1965
|
[mockIds, selectedWorkspaceId, deleteWorkspaceMutation]
|
|
1826
1966
|
);
|
|
1827
|
-
const
|
|
1967
|
+
const handleWakeWorkspace = useCallback6(
|
|
1968
|
+
async (workspaceId) => {
|
|
1969
|
+
setWakingWorkspaceId(workspaceId);
|
|
1970
|
+
try {
|
|
1971
|
+
await wakeWorkspaceMutation.mutateAsync(workspaceId);
|
|
1972
|
+
toast.show({ message: "Workspace waking up...", variant: "info" });
|
|
1973
|
+
} catch (err) {
|
|
1974
|
+
toast.error(err);
|
|
1975
|
+
} finally {
|
|
1976
|
+
setWakingWorkspaceId(null);
|
|
1977
|
+
}
|
|
1978
|
+
},
|
|
1979
|
+
[wakeWorkspaceMutation, toast]
|
|
1980
|
+
);
|
|
1981
|
+
const handleSelectChat = useCallback6((chatId) => {
|
|
1828
1982
|
setSelectedChatId(chatId);
|
|
1829
1983
|
}, []);
|
|
1830
|
-
|
|
1984
|
+
useKeyboard4((key) => {
|
|
1985
|
+
if (key.name === "f12") {
|
|
1986
|
+
renderer.console.toggle();
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
if (key.ctrl && key.name === "c") {
|
|
1990
|
+
renderer.destroy();
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1993
|
+
if (key.name === "escape") {
|
|
1994
|
+
if (isProcessing && selectedWorkspaceId && resolvedChatId) {
|
|
1995
|
+
interruptMutation.mutate();
|
|
1996
|
+
}
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
if (key.shift && key.name === "tab") {
|
|
2000
|
+
setFocusPanel((current) => {
|
|
2001
|
+
const availablePanels = FOCUS_ORDER.filter((p) => {
|
|
2002
|
+
if (p === "sidebar" && !showWorkspacePanel) return false;
|
|
2003
|
+
if ((p === "chat-tabs" || p === "chat-history" || p === "chat-input") && (!selectedWorkspaceId || statusData?.status === "sleeping")) return false;
|
|
2004
|
+
if (p === "chat-tabs" && chats.length <= 1) return false;
|
|
2005
|
+
if (p === "info" && !showInfoPanel) return false;
|
|
2006
|
+
return true;
|
|
2007
|
+
});
|
|
2008
|
+
const idx = availablePanels.indexOf(current);
|
|
2009
|
+
return availablePanels[(idx + 1) % availablePanels.length];
|
|
2010
|
+
});
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
if (key.name === "tab" && focusPanel === "chat-input") {
|
|
2014
|
+
setTaskMode((m) => m === "build" ? "plan" : "build");
|
|
2015
|
+
return;
|
|
2016
|
+
}
|
|
2017
|
+
if (key.name === "w" && focusPanel !== "sidebar" && focusPanel !== "info" && selectedWorkspaceId) {
|
|
2018
|
+
if (statusData?.status === "sleeping" && !wakingWorkspaceId) {
|
|
2019
|
+
handleWakeWorkspace(selectedWorkspaceId);
|
|
2020
|
+
}
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
if (focusPanel === "chat-input") return;
|
|
2024
|
+
});
|
|
2025
|
+
const handleSendMessage = useCallback6(
|
|
1831
2026
|
async (message) => {
|
|
1832
|
-
if (!selectedWorkspaceId || !
|
|
2027
|
+
if (!selectedWorkspaceId || !resolvedChatId) return;
|
|
1833
2028
|
if (isMockSelected) {
|
|
1834
2029
|
const { message: userMsg } = createUserMessage(message);
|
|
1835
2030
|
setMockMessages((prev) => [...prev, userMsg]);
|
|
@@ -1842,7 +2037,7 @@ function AppInner() {
|
|
|
1842
2037
|
permissionMode: taskMode === "plan" ? "read" : void 0
|
|
1843
2038
|
});
|
|
1844
2039
|
},
|
|
1845
|
-
[selectedWorkspaceId,
|
|
2040
|
+
[selectedWorkspaceId, resolvedChatId, isMockSelected, sendMessageMutation, taskMode]
|
|
1846
2041
|
);
|
|
1847
2042
|
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", height: "100%", backgroundColor: "#000000", children: [
|
|
1848
2043
|
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", flexGrow: 1, backgroundColor: "#000000", children: [
|
|
@@ -1855,15 +2050,17 @@ function AppInner() {
|
|
|
1855
2050
|
onSelectWorkspace: handleSelectWorkspace,
|
|
1856
2051
|
onCreateWorkspace: handleCreateWorkspace,
|
|
1857
2052
|
onDeleteWorkspace: handleDeleteWorkspace,
|
|
2053
|
+
onWakeWorkspace: handleWakeWorkspace,
|
|
2054
|
+
wakingWorkspaceId,
|
|
1858
2055
|
focused: focusPanel === "sidebar",
|
|
1859
2056
|
loading: loadingWorkspaces
|
|
1860
2057
|
}
|
|
1861
2058
|
),
|
|
1862
|
-
selectedWorkspaceId ? /* @__PURE__ */ jsx9(
|
|
2059
|
+
selectedWorkspaceId && statusData?.status !== "sleeping" ? /* @__PURE__ */ jsx9(
|
|
1863
2060
|
ChatArea,
|
|
1864
2061
|
{
|
|
1865
2062
|
chats,
|
|
1866
|
-
selectedChatId,
|
|
2063
|
+
selectedChatId: resolvedChatId,
|
|
1867
2064
|
displayMessages,
|
|
1868
2065
|
onSelectChat: handleSelectChat,
|
|
1869
2066
|
onSendMessage: handleSendMessage,
|
|
@@ -1873,6 +2070,30 @@ function AppInner() {
|
|
|
1873
2070
|
isProcessing,
|
|
1874
2071
|
loading: loadingMessages
|
|
1875
2072
|
}
|
|
2073
|
+
) : selectedWorkspaceId && statusData?.status === "sleeping" ? /* @__PURE__ */ jsxs7(
|
|
2074
|
+
"box",
|
|
2075
|
+
{
|
|
2076
|
+
flexGrow: 1,
|
|
2077
|
+
border: true,
|
|
2078
|
+
borderStyle: "rounded",
|
|
2079
|
+
borderColor: "#333333",
|
|
2080
|
+
backgroundColor: "#000000",
|
|
2081
|
+
justifyContent: "center",
|
|
2082
|
+
alignItems: "center",
|
|
2083
|
+
flexDirection: "column",
|
|
2084
|
+
gap: 1,
|
|
2085
|
+
children: [
|
|
2086
|
+
/* @__PURE__ */ jsxs7("text", { fg: "#888888", children: [
|
|
2087
|
+
"\u263E",
|
|
2088
|
+
" This workspace is sleeping"
|
|
2089
|
+
] }),
|
|
2090
|
+
/* @__PURE__ */ jsxs7("text", { fg: "#555555", children: [
|
|
2091
|
+
"Press ",
|
|
2092
|
+
/* @__PURE__ */ jsx9("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx9("strong", { children: "w" }) }),
|
|
2093
|
+
" to wake it up"
|
|
2094
|
+
] })
|
|
2095
|
+
]
|
|
2096
|
+
}
|
|
1876
2097
|
) : /* @__PURE__ */ jsx9(
|
|
1877
2098
|
"box",
|
|
1878
2099
|
{
|
|
@@ -1899,7 +2120,9 @@ function AppInner() {
|
|
|
1899
2120
|
variables: envVariables ?? null,
|
|
1900
2121
|
files: envFiles ?? null
|
|
1901
2122
|
},
|
|
1902
|
-
previews
|
|
2123
|
+
previews,
|
|
2124
|
+
onWakeWorkspace: handleWakeWorkspace,
|
|
2125
|
+
wakingWorkspaceId
|
|
1903
2126
|
}
|
|
1904
2127
|
)
|
|
1905
2128
|
] }),
|