umbrella-context 0.1.37 → 0.1.39
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 +9 -2
- package/dist/adapters/umbrella-onboarding.js +3 -2
- package/dist/commands/connect.js +3 -2
- package/dist/commands/connectors.js +3 -0
- package/dist/commands/curate.d.ts +13 -1
- package/dist/commands/curate.js +146 -10
- package/dist/commands/fix.js +2 -10
- package/dist/commands/hub.d.ts +3 -1
- package/dist/commands/hub.js +218 -68
- package/dist/commands/interactive.js +21 -10
- package/dist/commands/locations.d.ts +6 -1
- package/dist/commands/locations.js +91 -5
- package/dist/commands/mcp.js +7 -2
- package/dist/commands/pull.d.ts +6 -1
- package/dist/commands/pull.js +119 -3
- package/dist/commands/push.d.ts +6 -1
- package/dist/commands/push.js +147 -3
- package/dist/commands/restart.js +6 -1
- package/dist/commands/search.d.ts +5 -1
- package/dist/commands/search.js +857 -36
- package/dist/commands/session.js +0 -3
- package/dist/commands/setup.js +186 -26
- package/dist/commands/space.js +4 -3
- package/dist/commands/status.d.ts +16 -1
- package/dist/commands/status.js +111 -47
- package/dist/commands/tui.js +339 -107
- package/dist/config.d.ts +4 -0
- package/dist/config.js +6 -0
- package/dist/index.js +21 -3
- package/dist/repo-state.d.ts +115 -0
- package/dist/repo-state.js +195 -12
- package/package.json +2 -2
package/dist/commands/tui.js
CHANGED
|
@@ -8,7 +8,7 @@ import { formatUmbrellaAuthStatus } from "../auth-state.js";
|
|
|
8
8
|
import { getUmbrellaContextRuntimeSummary, useUmbrellaContextRuntimeBridgeStore, hydrateUmbrellaContextRuntimeBridgeFromSnapshot, } from "../adapters/byterover-context-runtime-store.js";
|
|
9
9
|
import { getUmbrellaTransportTaskBridgeSummary, useUmbrellaTransportTaskBridgeStore, hydrateUmbrellaTransportTaskBridgeFromSnapshot, } from "../adapters/byterover-transport-task-store.js";
|
|
10
10
|
import { buildVendorRuntimeBridgeSnapshot, } from "../adapters/byterover-runtime-bridge.js";
|
|
11
|
-
import { addPendingMemory, completeTask, createTask, ensureRepoContext, getContextTreeState, ensureSessionState, getConnectorRuns, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, getRepoContext, getSessionState, getTasks, getTransportState, recordSessionEvent, setSessionPanel, summarizeLocalMemoryMatches, } from "../repo-state.js";
|
|
11
|
+
import { addPendingMemory, completeTask, createTask, ensureRepoContext, getContextTreeState, ensureSessionState, getConnectorRuns, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, getRepoContext, removeTask, getSessionState, getTasks, getTransportState, recordSessionEvent, setSessionPanel, summarizeLocalMemoryMatches, } from "../repo-state.js";
|
|
12
12
|
import { configManager } from "../config.js";
|
|
13
13
|
import { getStoredUmbrellaAuthSnapshot, setUmbrellaAuthAuthorized, setUmbrellaAuthChecking, setUmbrellaAuthFailure, } from "../adapters/umbrella-auth-runtime.js";
|
|
14
14
|
import { checkUmbrellaOnboardingServer, connectUmbrellaDevice, createUmbrellaOnboardingCompany, createUmbrellaOnboardingSpace, loadUmbrellaOnboardingSpaces, signInUmbrellaOnboarding, } from "../adapters/umbrella-onboarding.js";
|
|
@@ -20,6 +20,7 @@ import { pullCommandAction } from "./pull.js";
|
|
|
20
20
|
import { pushCommandAction } from "./push.js";
|
|
21
21
|
import { getProviderReadinessSummary, providersCommandAction } from "./providers.js";
|
|
22
22
|
import { spaceCommandAction } from "./space.js";
|
|
23
|
+
import { buildConnectionPresentation } from "./status.js";
|
|
23
24
|
const PANELS = [
|
|
24
25
|
{ id: "home", title: "Home", description: "Repo status, sync, and next steps" },
|
|
25
26
|
{ id: "tasks", title: "Tasks", description: "Live local task activity from .um" },
|
|
@@ -31,12 +32,52 @@ const PANELS = [
|
|
|
31
32
|
{ id: "connectors", title: "Connectors", description: "Repo automations and MCP wiring" },
|
|
32
33
|
{ id: "session", title: "Session", description: "Live terminal memory for this repo" },
|
|
33
34
|
];
|
|
35
|
+
const PANEL_SHORTCUTS = [
|
|
36
|
+
{ id: "home", key: "h", label: "Home" },
|
|
37
|
+
{ id: "tasks", key: "t", label: "Tasks" },
|
|
38
|
+
{ id: "activity", key: "a", label: "Activity" },
|
|
39
|
+
{ id: "providers", key: "v", label: "Providers" },
|
|
40
|
+
{ id: "model", key: "m", label: "Models" },
|
|
41
|
+
{ id: "spaces", key: "w", label: "Spaces" },
|
|
42
|
+
{ id: "hub", key: "b", label: "Hub" },
|
|
43
|
+
{ id: "connectors", key: "c", label: "Connectors" },
|
|
44
|
+
{ id: "session", key: "u", label: "Session" },
|
|
45
|
+
];
|
|
46
|
+
const DEFAULT_UMBRELLA_SERVER_URL = "http://5.161.55.138:3100";
|
|
47
|
+
const LOCAL_UMBRELLA_SERVER_URL = "http://127.0.0.1:3100";
|
|
48
|
+
const SETUP_SERVER_CHOICES = [
|
|
49
|
+
{
|
|
50
|
+
value: "live",
|
|
51
|
+
title: `Live Umbrella server (${DEFAULT_UMBRELLA_SERVER_URL})`,
|
|
52
|
+
description: "Recommended for most people. Uses the shared live Umbrella server.",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
value: "local",
|
|
56
|
+
title: `Local development server (${LOCAL_UMBRELLA_SERVER_URL})`,
|
|
57
|
+
description: "Only use this if Umbrella is running on this same computer right now.",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
value: "custom",
|
|
61
|
+
title: "Custom URL",
|
|
62
|
+
description: "Type a different Umbrella server URL yourself.",
|
|
63
|
+
},
|
|
64
|
+
];
|
|
34
65
|
function trimSlash(value) {
|
|
35
66
|
return value.trim().replace(/\/+$/, "");
|
|
36
67
|
}
|
|
37
68
|
function panelIndexFor(id) {
|
|
38
69
|
return Math.max(0, PANELS.findIndex((panel) => panel.id === id));
|
|
39
70
|
}
|
|
71
|
+
function panelForShortcut(input) {
|
|
72
|
+
const normalized = input.trim().toLowerCase();
|
|
73
|
+
if (!normalized)
|
|
74
|
+
return null;
|
|
75
|
+
const number = Number(normalized);
|
|
76
|
+
if (Number.isInteger(number) && number >= 1 && number <= PANELS.length) {
|
|
77
|
+
return PANELS[number - 1]?.id ?? null;
|
|
78
|
+
}
|
|
79
|
+
return PANEL_SHORTCUTS.find((entry) => entry.key === normalized)?.id ?? null;
|
|
80
|
+
}
|
|
40
81
|
function panelForTaskKind(kind) {
|
|
41
82
|
switch (kind) {
|
|
42
83
|
case "provider":
|
|
@@ -194,20 +235,23 @@ function ActivityPanelView(props) {
|
|
|
194
235
|
return (_jsxs(Text, { color: highlighted ? "yellow" : event.status === "success" ? "green" : event.status === "failure" ? "red" : event.status === "warning" ? "yellow" : "white", children: [highlighted ? ">" : " ", " [", event.kind, "] ", event.title] }, event.id));
|
|
195
236
|
}) }), _jsx(Section, { title: "Selected Event", children: selectedEvent ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: selectedEvent.detail ?? "No extra detail recorded." }), _jsxs(Text, { color: "gray", children: ["When: ", new Date(selectedEvent.at).toLocaleString()] }), _jsxs(Text, { color: "gray", children: ["Panel: ", panelForActivityItem(selectedEvent) ?? "None", " | Focus: ", selectedEvent.focus ?? "None"] }), mode === "inspect" ? (_jsxs(Section, { title: "Deep Inspect", children: [_jsxs(Text, { color: "gray", children: ["Kind: ", selectedEvent.kind] }), _jsxs(Text, { color: "gray", children: ["Status: ", selectedEvent.status] }), _jsxs(Text, { color: "gray", children: ["Suggested panel: ", panelForActivityItem(selectedEvent) ?? "None"] }), _jsx(Text, { color: "gray", children: "Open the related panel with Enter or o." })] })) : null] })) : (_jsx(Text, { color: "gray", children: "Choose an event to view its detail." })) }), _jsx(Text, { color: "gray", children: mode === "inspect" ? "Esc goes back to the event list. Enter or o jumps to the related panel. r refreshes the runtime." : "Up and down move through events. Enter or o jumps to the related panel when one exists. r refreshes the runtime." })] }));
|
|
196
237
|
}
|
|
197
|
-
function PanelShell({ activePanel, children, footer, message, selectedPanelIndex, }) {
|
|
198
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "UMBRELLA CONTEXT TUI" }), _jsx(Text, { color: "gray", children: " pane-based repo Context terminal" })] }), _jsxs(Box, { children: [_jsx(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
238
|
+
function PanelShell({ activePanel, children, footer, message, navigationFocus, selectedPanelIndex, }) {
|
|
239
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "UMBRELLA CONTEXT TUI" }), _jsx(Text, { color: "gray", children: " pane-based repo Context terminal" })] }), _jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsx(Text, { color: "gray", children: "Active panel:" }), _jsx(Text, { color: "white", children: PANELS.find((panel) => panel.id === activePanel)?.title ?? activePanel }), _jsx(Text, { color: "gray", children: "Focus:" }), _jsx(Text, { color: navigationFocus === "sidebar" ? "yellow" : "green", children: navigationFocus === "sidebar" ? "Sidebar" : "Panel" }), _jsx(Text, { color: "gray", children: navigationFocus === "sidebar"
|
|
240
|
+
? "Enter or Right opens the panel."
|
|
241
|
+
: "Left or Esc returns to the sidebar." })] }), _jsxs(Box, { children: [_jsxs(Box, { flexDirection: "column", width: 26, marginRight: 2, borderStyle: "round", borderColor: navigationFocus === "sidebar" ? "yellow" : undefined, paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: navigationFocus === "sidebar" ? "yellow" : "gray", children: navigationFocus === "sidebar" ? "Sidebar focus" : "Panels" }), PANELS.map((panel, index) => {
|
|
242
|
+
const active = panel.id === activePanel;
|
|
243
|
+
const selected = index === selectedPanelIndex;
|
|
244
|
+
const shortcut = PANEL_SHORTCUTS.find((entry) => entry.id === panel.id)?.key ?? String(index + 1);
|
|
245
|
+
return (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: active ? "green" : selected ? "yellow" : "white", children: [active ? "*" : selected ? ">" : " ", " [", shortcut, "] ", panel.title] }) }, panel.id));
|
|
246
|
+
})] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", paddingX: 1, paddingY: 1, children: children })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [message ? _jsx(Text, { color: "green", children: message }) : null, _jsx(Text, { color: "gray", children: footer }), _jsx(Text, { color: "gray", children: "Tip: number keys or h/t/a/v/m/w/b/c/u jump straight to a panel." })] })] }));
|
|
203
247
|
}
|
|
204
248
|
function SetupView(props) {
|
|
205
|
-
const { busyLabel, companies, config, authSnapshot, inputValue, message, onInputChange, selectedCompanyIndex, selectedSpaceIndex, serverUrl, spaces, step, } = props;
|
|
206
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: config ? "Connected Device" : "Connect This Device" }), _jsx(Text, { color: "gray", children: "Sign into Umbrella, choose a company, then choose the team space for this repo." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Server", value: serverUrl || "Not set" }), _jsx(StatusLine, { label: "Step", value: step }), _jsx(StatusLine, { label: "Auth state", value: formatUmbrellaAuthStatus(authSnapshot.status) }), _jsx(StatusLine, { label: "Auth message", value: authSnapshot.message ?? "No auth check yet" }), _jsx(StatusLine, { label: "Auth hint", value: authSnapshot.hint ?? "No auth hint yet" }), config ? _jsx(StatusLine, { label: "Current link", value: `${config.companyName} / ${config.projectName}` }) : null] }), busyLabel ? (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "cyan", children: [_jsx(Spinner, { type: "dots" }), " ", busyLabel] }) })) : null, message ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "green", children: message }) })) : null, (step === "server" || step === "email" || step === "password" || step === "company-create" || step === "space-create") ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: step === "server" ? "Server URL" :
|
|
249
|
+
const { busyLabel, companies, config, authSnapshot, inputValue, message, onInputChange, selectedCompanyIndex, selectedServerChoiceIndex, selectedSpaceIndex, serverUrl, spaces, step, } = props;
|
|
250
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: config ? "Connected Device" : "Connect This Device" }), _jsx(Text, { color: "gray", children: "Sign into Umbrella, choose a company, then choose the team space for this repo." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Server", value: serverUrl || "Not set" }), _jsx(StatusLine, { label: "Step", value: step }), _jsx(StatusLine, { label: "Auth state", value: formatUmbrellaAuthStatus(authSnapshot.status) }), _jsx(StatusLine, { label: "Auth message", value: authSnapshot.message ?? "No auth check yet" }), _jsx(StatusLine, { label: "Auth hint", value: authSnapshot.hint ?? "No auth hint yet" }), config ? _jsx(StatusLine, { label: "Current link", value: `${config.companyName} / ${config.projectName}` }) : null] }), busyLabel ? (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "cyan", children: [_jsx(Spinner, { type: "dots" }), " ", busyLabel] }) })) : null, message ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "green", children: message }) })) : null, step === "server-choice" ? (_jsx(Section, { title: "Choose an Umbrella Server", children: SETUP_SERVER_CHOICES.map((choice, index) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: index === selectedServerChoiceIndex ? "yellow" : "white", children: [index === selectedServerChoiceIndex ? ">" : " ", " ", choice.title] }), _jsx(Text, { color: "gray", children: choice.description })] }, choice.value))) })) : null, (step === "server" || step === "email" || step === "password" || step === "company-create" || step === "space-create") ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: step === "server" ? "Server URL" :
|
|
207
251
|
step === "email" ? "Umbrella email" :
|
|
208
252
|
step === "password" ? "Umbrella password" :
|
|
209
253
|
step === "company-create" ? "New company name" :
|
|
210
|
-
"New space name" }), _jsx(TextInput, { value: inputValue, onChange: onInputChange })] })) : null, step === "company" ? (_jsxs(Section, { title: "Companies", children: [companies.map((company, index) => (_jsxs(Text, { color: index === selectedCompanyIndex ? "yellow" : "white", children: [index === selectedCompanyIndex ? ">" : " ", " ", company.name, " (", company.issuePrefix, ")"] }, company.id))), _jsxs(Text, { color: selectedCompanyIndex === companies.length ? "yellow" : "white", children: [selectedCompanyIndex === companies.length ? ">" : " ", " + Create new company"] })] })) : null, step === "space" ? (_jsxs(Section, { title: "Spaces", children: [spaces.map((space, index) => (_jsxs(Text, { color: index === selectedSpaceIndex ? "yellow" : "white", children: [index === selectedSpaceIndex ? ">" : " ", " ", space.name, space.isPrimary ? " (core)" : ""] }, space.id))), _jsxs(Text, { color: selectedSpaceIndex === spaces.length ? "yellow" : "white", children: [selectedSpaceIndex === spaces.length ? ">" : " ", " + Create new space"] })] })) : null, _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Enter confirms the current step. Up and down move through companies or spaces." }), _jsx(Text, { color: "gray", children: "Esc leaves setup. This terminal saves the final link into your device config and the repo's .um folder." })] })] }));
|
|
254
|
+
"New space name" }), _jsx(TextInput, { value: inputValue, onChange: onInputChange })] })) : null, step === "company" ? (_jsxs(Section, { title: "Companies", children: [companies.map((company, index) => (_jsxs(Text, { color: index === selectedCompanyIndex ? "yellow" : "white", children: [index === selectedCompanyIndex ? ">" : " ", " ", company.name, " (", company.issuePrefix, ")"] }, company.id))), _jsxs(Text, { color: selectedCompanyIndex === companies.length ? "yellow" : "white", children: [selectedCompanyIndex === companies.length ? ">" : " ", " + Create new company"] })] })) : null, step === "space" ? (_jsxs(Section, { title: "Spaces", children: [spaces.map((space, index) => (_jsxs(Text, { color: index === selectedSpaceIndex ? "yellow" : "white", children: [index === selectedSpaceIndex ? ">" : " ", " ", space.name, space.isPrimary ? " (core)" : ""] }, space.id))), _jsxs(Text, { color: selectedSpaceIndex === spaces.length ? "yellow" : "white", children: [selectedSpaceIndex === spaces.length ? ">" : " ", " + Create new space"] })] })) : null, _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Enter confirms the current step. Up and down move through server choices, companies, or spaces." }), _jsx(Text, { color: "gray", children: "Esc leaves setup. This terminal saves the final link into your device config and the repo's .um folder." })] })] }));
|
|
211
255
|
}
|
|
212
256
|
function App() {
|
|
213
257
|
const { exit } = useApp();
|
|
@@ -221,6 +265,7 @@ function App() {
|
|
|
221
265
|
const vendorConnectorsStore = useUmbrellaContextRuntimeBridgeStore((state) => state.connectorsStore);
|
|
222
266
|
const [selectedPanelIndex, setSelectedPanelIndex] = useState(0);
|
|
223
267
|
const [activePanel, setActivePanel] = useState(configManager.config ? "home" : "setup");
|
|
268
|
+
const [navigationFocus, setNavigationFocus] = useState("sidebar");
|
|
224
269
|
const [refreshTick, setRefreshTick] = useState(0);
|
|
225
270
|
const [message, setMessage] = useState(null);
|
|
226
271
|
const [busyLabel, setBusyLabel] = useState(null);
|
|
@@ -228,8 +273,9 @@ function App() {
|
|
|
228
273
|
const [inputValue, setInputValue] = useState("");
|
|
229
274
|
const [queryResult, setQueryResult] = useState(null);
|
|
230
275
|
const [refreshState, setRefreshState] = useState(null);
|
|
231
|
-
const [setupStep, setSetupStep] = useState(configManager.config ? "done" : "server");
|
|
232
|
-
const [serverUrl, setServerUrl] = useState(configManager.config?.umbrellaUrl ?? configManager.config?.serverUrl ??
|
|
276
|
+
const [setupStep, setSetupStep] = useState(configManager.config ? "done" : "server-choice");
|
|
277
|
+
const [serverUrl, setServerUrl] = useState(configManager.config?.umbrellaUrl ?? configManager.config?.serverUrl ?? DEFAULT_UMBRELLA_SERVER_URL);
|
|
278
|
+
const [selectedServerChoiceIndex, setSelectedServerChoiceIndex] = useState(0);
|
|
233
279
|
const [setupEmail, setSetupEmail] = useState("");
|
|
234
280
|
const [authSnapshot, setAuthSnapshot] = useState(getStoredUmbrellaAuthSnapshot());
|
|
235
281
|
const [setupCookie, setSetupCookie] = useState(null);
|
|
@@ -251,12 +297,18 @@ function App() {
|
|
|
251
297
|
const [modelPanelRecentModel, setModelPanelRecentModel] = useState(null);
|
|
252
298
|
const [hubPanelMode, setHubPanelMode] = useState("browse");
|
|
253
299
|
const [hubPanelSelectedIndex, setHubPanelSelectedIndex] = useState(0);
|
|
300
|
+
const [hubRegistryEntries, setHubRegistryEntries] = useState([]);
|
|
254
301
|
const [connectorsPanelMode, setConnectorsPanelMode] = useState("browse");
|
|
255
302
|
const [connectorsPanelSelectedIndex, setConnectorsPanelSelectedIndex] = useState(0);
|
|
256
303
|
const [tasksPanelMode, setTasksPanelMode] = useState("browse");
|
|
257
304
|
const [tasksPanelSelectedIndex, setTasksPanelSelectedIndex] = useState(0);
|
|
258
305
|
const [activityPanelMode, setActivityPanelMode] = useState("browse");
|
|
259
306
|
const [activityPanelSelectedIndex, setActivityPanelSelectedIndex] = useState(0);
|
|
307
|
+
function focusPanel(panelId, focus = "sidebar") {
|
|
308
|
+
setActivePanel(panelId);
|
|
309
|
+
setSelectedPanelIndex(panelIndexFor(panelId));
|
|
310
|
+
setNavigationFocus(focus);
|
|
311
|
+
}
|
|
260
312
|
async function refreshAll() {
|
|
261
313
|
try {
|
|
262
314
|
if (configManager.config) {
|
|
@@ -309,6 +361,12 @@ function App() {
|
|
|
309
361
|
const vendorContextSummary = getUmbrellaContextRuntimeSummary();
|
|
310
362
|
const vendorTasks = useMemo(() => buildVendorTasksList(vendorTaskStore), [vendorTaskStore]);
|
|
311
363
|
const vendorActivity = useMemo(() => buildActivityItems(vendorTransportStore, refreshState?.session?.events ?? []), [vendorTransportStore, refreshState?.session?.events]);
|
|
364
|
+
const connectionPresentation = useMemo(() => configManager.config
|
|
365
|
+
? buildConnectionPresentation({
|
|
366
|
+
umbrellaUrl: configManager.config.umbrellaUrl ?? undefined,
|
|
367
|
+
serverUrl: configManager.config.serverUrl,
|
|
368
|
+
}, authSnapshot)
|
|
369
|
+
: null, [authSnapshot]);
|
|
312
370
|
const vendorProviders = useMemo(() => vendorProviderStore?.providers ?? [], [vendorProviderStore]);
|
|
313
371
|
const vendorModels = useMemo(() => (vendorModelStore?.models ?? []).map((model) => model.label), [vendorModelStore]);
|
|
314
372
|
const vendorSpaces = useMemo(() => (vendorSpaceStore?.spaces ?? []).map((space) => ({
|
|
@@ -347,11 +405,14 @@ function App() {
|
|
|
347
405
|
useEffect(() => {
|
|
348
406
|
if (activePanel !== "hub" || busyLabel)
|
|
349
407
|
return;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
408
|
+
void (async () => {
|
|
409
|
+
const registryEntries = await listHubRegistryEntries();
|
|
410
|
+
setHubRegistryEntries(registryEntries);
|
|
411
|
+
const recentSlug = vendorHubStore?.recentSlug ?? null;
|
|
412
|
+
const recentIndex = registryEntries.findIndex((entry) => entry.slug === recentSlug);
|
|
413
|
+
setHubPanelSelectedIndex(recentIndex >= 0 ? recentIndex : 0);
|
|
414
|
+
setHubPanelMode("browse");
|
|
415
|
+
})();
|
|
355
416
|
}, [activePanel, refreshTick, busyLabel, vendorHubStore?.recentSlug]);
|
|
356
417
|
useEffect(() => {
|
|
357
418
|
if (activePanel !== "connectors" || busyLabel)
|
|
@@ -369,14 +430,24 @@ function App() {
|
|
|
369
430
|
vendorTransportStore?.queue.find((item) => item.status === "running")?.id ??
|
|
370
431
|
null;
|
|
371
432
|
const activeIndex = Math.max(0, vendorTasks.findIndex((task) => task.taskId === activeTaskId));
|
|
372
|
-
setTasksPanelSelectedIndex(
|
|
373
|
-
|
|
433
|
+
setTasksPanelSelectedIndex((current) => {
|
|
434
|
+
if (vendorTasks.length === 0)
|
|
435
|
+
return 0;
|
|
436
|
+
if (current >= 0 && current < vendorTasks.length)
|
|
437
|
+
return current;
|
|
438
|
+
return activeIndex === -1 ? 0 : activeIndex;
|
|
439
|
+
});
|
|
374
440
|
}, [activePanel, busyLabel, refreshState, vendorTasks, vendorTransportStore]);
|
|
375
441
|
useEffect(() => {
|
|
376
442
|
if (activePanel !== "activity" || busyLabel || !refreshState)
|
|
377
443
|
return;
|
|
378
|
-
setActivityPanelSelectedIndex(
|
|
379
|
-
|
|
444
|
+
setActivityPanelSelectedIndex((current) => {
|
|
445
|
+
if (vendorActivity.length === 0)
|
|
446
|
+
return 0;
|
|
447
|
+
if (current >= 0 && current < vendorActivity.length)
|
|
448
|
+
return current;
|
|
449
|
+
return 0;
|
|
450
|
+
});
|
|
380
451
|
}, [activePanel, busyLabel, refreshState, vendorActivity]);
|
|
381
452
|
async function runPanelAction(busyText, action, successMessage) {
|
|
382
453
|
setBusyLabel(busyText);
|
|
@@ -434,6 +505,12 @@ function App() {
|
|
|
434
505
|
createdAt: entry.createdAt ?? new Date().toISOString(),
|
|
435
506
|
})),
|
|
436
507
|
});
|
|
508
|
+
if ((data.results ?? []).length === 0 && localMatches.length === 0) {
|
|
509
|
+
await removeTask(task.id);
|
|
510
|
+
setMessage(`No local or shared context matched "${query}".`);
|
|
511
|
+
setRefreshTick((value) => value + 1);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
437
514
|
await recordSessionEvent({
|
|
438
515
|
kind: "query",
|
|
439
516
|
title: `Queried Context for "${query}"`,
|
|
@@ -444,8 +521,7 @@ function App() {
|
|
|
444
521
|
});
|
|
445
522
|
await completeTask(task.id, "completed", "Query finished.");
|
|
446
523
|
setMessage(`Query finished for "${query}".`);
|
|
447
|
-
|
|
448
|
-
setSelectedPanelIndex(panelIndexFor("tasks"));
|
|
524
|
+
focusPanel("tasks", "panel");
|
|
449
525
|
setRefreshTick((value) => value + 1);
|
|
450
526
|
}
|
|
451
527
|
finally {
|
|
@@ -472,6 +548,9 @@ function App() {
|
|
|
472
548
|
const entry = await addPendingMemory({
|
|
473
549
|
content,
|
|
474
550
|
tags: [],
|
|
551
|
+
keywords: [],
|
|
552
|
+
category: null,
|
|
553
|
+
accessLevel: "space",
|
|
475
554
|
source: "cli",
|
|
476
555
|
systemType: "system1_knowledge",
|
|
477
556
|
});
|
|
@@ -485,46 +564,48 @@ function App() {
|
|
|
485
564
|
});
|
|
486
565
|
await completeTask(task.id, "completed", `Saved local draft ${entry.id}.`);
|
|
487
566
|
setMessage(`Saved local Context note for ${config.companyName} / ${config.projectName}.`);
|
|
488
|
-
|
|
489
|
-
setSelectedPanelIndex(panelIndexFor("tasks"));
|
|
567
|
+
focusPanel("tasks", "panel");
|
|
490
568
|
setRefreshTick((value) => value + 1);
|
|
491
569
|
}
|
|
492
570
|
finally {
|
|
493
571
|
setBusyLabel(null);
|
|
494
572
|
}
|
|
495
573
|
}
|
|
574
|
+
async function startSetupServerCheck(nextServer) {
|
|
575
|
+
const normalizedServer = trimSlash(nextServer);
|
|
576
|
+
setServerUrl(normalizedServer);
|
|
577
|
+
setInputValue("");
|
|
578
|
+
setBusyLabel("Checking Umbrella server...");
|
|
579
|
+
try {
|
|
580
|
+
setAuthSnapshot(setUmbrellaAuthChecking(normalizedServer, setupEmail || null));
|
|
581
|
+
const serverCheck = await checkUmbrellaOnboardingServer(normalizedServer);
|
|
582
|
+
if (serverCheck.deploymentMode === "authenticated") {
|
|
583
|
+
setSetupStep("email");
|
|
584
|
+
setMessage("Umbrella is reachable. Enter the email for the account you want to use on this device.");
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
setAuthSnapshot(setUmbrellaAuthAuthorized(normalizedServer, null, serverCheck.companies.length));
|
|
588
|
+
setCompanies(serverCheck.companies);
|
|
589
|
+
setSelectedCompanyIndex(0);
|
|
590
|
+
setSetupStep("company");
|
|
591
|
+
setMessage("Umbrella is reachable and trusted mode is active. Choose a company next.");
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
const snapshot = setUmbrellaAuthFailure(normalizedServer, error, setupEmail || null);
|
|
596
|
+
setAuthSnapshot(snapshot);
|
|
597
|
+
setMessage(`Could not reach Umbrella: ${snapshot.message}${snapshot.hint ? ` ${snapshot.hint}` : ""}`);
|
|
598
|
+
}
|
|
599
|
+
finally {
|
|
600
|
+
setBusyLabel(null);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
496
603
|
async function submitSetupText() {
|
|
497
604
|
const nextValue = inputValue.trim();
|
|
498
605
|
if (!nextValue && setupStep !== "password")
|
|
499
606
|
return;
|
|
500
607
|
if (setupStep === "server") {
|
|
501
|
-
|
|
502
|
-
setServerUrl(nextServer);
|
|
503
|
-
setInputValue("");
|
|
504
|
-
setBusyLabel("Checking Umbrella server...");
|
|
505
|
-
try {
|
|
506
|
-
setAuthSnapshot(setUmbrellaAuthChecking(nextServer, setupEmail || null));
|
|
507
|
-
const serverCheck = await checkUmbrellaOnboardingServer(nextServer);
|
|
508
|
-
if (serverCheck.deploymentMode === "authenticated") {
|
|
509
|
-
setSetupStep("email");
|
|
510
|
-
setMessage("Umbrella is reachable. Enter the email for the account you want to use on this device.");
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
setAuthSnapshot(setUmbrellaAuthAuthorized(nextServer, null, serverCheck.companies.length));
|
|
514
|
-
setCompanies(serverCheck.companies);
|
|
515
|
-
setSelectedCompanyIndex(0);
|
|
516
|
-
setSetupStep("company");
|
|
517
|
-
setMessage("Umbrella is reachable and trusted mode is active. Choose a company next.");
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
catch (error) {
|
|
521
|
-
const snapshot = setUmbrellaAuthFailure(nextServer, error, setupEmail || null);
|
|
522
|
-
setAuthSnapshot(snapshot);
|
|
523
|
-
setMessage(`Could not reach Umbrella: ${snapshot.message}${snapshot.hint ? ` ${snapshot.hint}` : ""}`);
|
|
524
|
-
}
|
|
525
|
-
finally {
|
|
526
|
-
setBusyLabel(null);
|
|
527
|
-
}
|
|
608
|
+
await startSetupServerCheck(nextValue || serverUrl);
|
|
528
609
|
return;
|
|
529
610
|
}
|
|
530
611
|
if (setupStep === "email") {
|
|
@@ -600,6 +681,17 @@ function App() {
|
|
|
600
681
|
}
|
|
601
682
|
}
|
|
602
683
|
async function confirmSetupSelection() {
|
|
684
|
+
if (setupStep === "server-choice") {
|
|
685
|
+
const choice = SETUP_SERVER_CHOICES[selectedServerChoiceIndex]?.value ?? "live";
|
|
686
|
+
if (choice === "custom") {
|
|
687
|
+
setInputValue(serverUrl);
|
|
688
|
+
setSetupStep("server");
|
|
689
|
+
setMessage("Type the Umbrella server URL you want this device to use.");
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
await startSetupServerCheck(choice === "local" ? LOCAL_UMBRELLA_SERVER_URL : DEFAULT_UMBRELLA_SERVER_URL);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
603
695
|
if (setupStep === "company") {
|
|
604
696
|
if (selectedCompanyIndex === companies.length) {
|
|
605
697
|
setInputValue("");
|
|
@@ -649,8 +741,7 @@ function App() {
|
|
|
649
741
|
setAuthSnapshot(setUmbrellaAuthAuthorized(serverUrl, setupEmail || null, companies.length));
|
|
650
742
|
setSetupStep("done");
|
|
651
743
|
setMessage(`Connected to ${setup.companyName} / ${setup.activeSpaceName}.`);
|
|
652
|
-
|
|
653
|
-
setSelectedPanelIndex(panelIndexFor("home"));
|
|
744
|
+
focusPanel("home", "sidebar");
|
|
654
745
|
setRefreshTick((value) => value + 1);
|
|
655
746
|
}
|
|
656
747
|
catch (error) {
|
|
@@ -690,7 +781,7 @@ function App() {
|
|
|
690
781
|
}
|
|
691
782
|
if (activePanel === "setup" && setupStep !== "done") {
|
|
692
783
|
if (key.return) {
|
|
693
|
-
if (setupStep === "company" || setupStep === "space") {
|
|
784
|
+
if (setupStep === "server-choice" || setupStep === "company" || setupStep === "space") {
|
|
694
785
|
void confirmSetupSelection();
|
|
695
786
|
}
|
|
696
787
|
else {
|
|
@@ -700,11 +791,18 @@ function App() {
|
|
|
700
791
|
}
|
|
701
792
|
if (key.escape) {
|
|
702
793
|
if (configManager.config) {
|
|
703
|
-
|
|
704
|
-
setSelectedPanelIndex(panelIndexFor("home"));
|
|
794
|
+
focusPanel("home", "sidebar");
|
|
705
795
|
}
|
|
706
796
|
return;
|
|
707
797
|
}
|
|
798
|
+
if (key.upArrow && setupStep === "server-choice") {
|
|
799
|
+
setSelectedServerChoiceIndex((value) => Math.max(0, value - 1));
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (key.downArrow && setupStep === "server-choice") {
|
|
803
|
+
setSelectedServerChoiceIndex((value) => Math.min(SETUP_SERVER_CHOICES.length - 1, value + 1));
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
708
806
|
if (key.upArrow && setupStep === "company") {
|
|
709
807
|
setSelectedCompanyIndex((value) => Math.max(0, value - 1));
|
|
710
808
|
return;
|
|
@@ -844,33 +942,89 @@ function App() {
|
|
|
844
942
|
exit();
|
|
845
943
|
return;
|
|
846
944
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
945
|
+
if (key.backspace && activePanel !== "setup") {
|
|
946
|
+
setNavigationFocus("sidebar");
|
|
947
|
+
if (activePanel === "tasks")
|
|
948
|
+
setTasksPanelMode("browse");
|
|
949
|
+
if (activePanel === "activity")
|
|
950
|
+
setActivityPanelMode("browse");
|
|
951
|
+
setMessage("Returned focus to the sidebar.");
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
if (key.leftArrow) {
|
|
955
|
+
setNavigationFocus("sidebar");
|
|
956
|
+
if (activePanel === "tasks")
|
|
957
|
+
setTasksPanelMode("browse");
|
|
958
|
+
if (activePanel === "activity")
|
|
959
|
+
setActivityPanelMode("browse");
|
|
960
|
+
setMessage("Returned focus to the sidebar.");
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
if (key.escape) {
|
|
964
|
+
if (navigationFocus === "panel") {
|
|
965
|
+
setNavigationFocus("sidebar");
|
|
966
|
+
if (activePanel === "tasks")
|
|
967
|
+
setTasksPanelMode("browse");
|
|
968
|
+
if (activePanel === "activity")
|
|
969
|
+
setActivityPanelMode("browse");
|
|
970
|
+
setMessage("Returned focus to the sidebar.");
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
if (key.rightArrow && activePanel !== "setup") {
|
|
975
|
+
setNavigationFocus("panel");
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (key.return && navigationFocus === "sidebar" && activePanel !== "setup") {
|
|
979
|
+
setNavigationFocus("panel");
|
|
980
|
+
setMessage(`Opened ${PANELS[selectedPanelIndex]?.title ?? "panel"}.`);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const shortcutPanel = panelForShortcut(input);
|
|
984
|
+
if (shortcutPanel && activePanel !== "setup") {
|
|
985
|
+
focusPanel(shortcutPanel, "panel");
|
|
986
|
+
setMessage(`Jumped to ${PANELS[panelIndexFor(shortcutPanel)]?.title ?? shortcutPanel}.`);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
854
989
|
if (key.upArrow || input === "k") {
|
|
855
|
-
if (
|
|
990
|
+
if (navigationFocus === "sidebar") {
|
|
856
991
|
const nextIndex = Math.max(0, selectedPanelIndex - 1);
|
|
857
|
-
|
|
858
|
-
setActivePanel(PANELS[nextIndex]?.id ?? "home");
|
|
992
|
+
focusPanel(PANELS[nextIndex]?.id ?? "home", "sidebar");
|
|
859
993
|
return;
|
|
860
994
|
}
|
|
861
995
|
}
|
|
862
996
|
if (key.downArrow || input === "j") {
|
|
863
|
-
if (
|
|
997
|
+
if (navigationFocus === "sidebar") {
|
|
864
998
|
const nextIndex = Math.min(PANELS.length - 1, selectedPanelIndex + 1);
|
|
865
|
-
|
|
866
|
-
setActivePanel(PANELS[nextIndex]?.id ?? "home");
|
|
999
|
+
focusPanel(PANELS[nextIndex]?.id ?? "home", "sidebar");
|
|
867
1000
|
return;
|
|
868
1001
|
}
|
|
869
1002
|
}
|
|
870
|
-
if (
|
|
871
|
-
const nextIndex =
|
|
872
|
-
|
|
873
|
-
|
|
1003
|
+
if (input === "[" && activePanel !== "setup") {
|
|
1004
|
+
const nextIndex = selectedPanelIndex === 0 ? PANELS.length - 1 : selectedPanelIndex - 1;
|
|
1005
|
+
focusPanel(PANELS[nextIndex]?.id ?? "home", navigationFocus);
|
|
1006
|
+
setMessage(`Moved to ${PANELS[nextIndex]?.title ?? "panel"}.`);
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
if (input === "]" && activePanel !== "setup") {
|
|
1010
|
+
const nextIndex = selectedPanelIndex === PANELS.length - 1 ? 0 : selectedPanelIndex + 1;
|
|
1011
|
+
focusPanel(PANELS[nextIndex]?.id ?? "home", navigationFocus);
|
|
1012
|
+
setMessage(`Moved to ${PANELS[nextIndex]?.title ?? "panel"}.`);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
if (key.tab && activePanel !== "setup") {
|
|
1016
|
+
if (navigationFocus === "sidebar") {
|
|
1017
|
+
setNavigationFocus("panel");
|
|
1018
|
+
setMessage(`Opened ${PANELS[selectedPanelIndex]?.title ?? "panel"}.`);
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
setNavigationFocus("sidebar");
|
|
1022
|
+
if (activePanel === "tasks")
|
|
1023
|
+
setTasksPanelMode("browse");
|
|
1024
|
+
if (activePanel === "activity")
|
|
1025
|
+
setActivityPanelMode("browse");
|
|
1026
|
+
setMessage("Returned focus to the sidebar.");
|
|
1027
|
+
}
|
|
874
1028
|
return;
|
|
875
1029
|
}
|
|
876
1030
|
if (input === "?") {
|
|
@@ -886,9 +1040,12 @@ function App() {
|
|
|
886
1040
|
if (input === "s") {
|
|
887
1041
|
setActivePanel("setup");
|
|
888
1042
|
setSelectedPanelIndex(0);
|
|
1043
|
+
setNavigationFocus("panel");
|
|
889
1044
|
return;
|
|
890
1045
|
}
|
|
891
1046
|
if (activePanel === "providers") {
|
|
1047
|
+
if (navigationFocus === "sidebar")
|
|
1048
|
+
return;
|
|
892
1049
|
if (key.upArrow || input === "k") {
|
|
893
1050
|
setProviderPanelSelectedIndex((value) => Math.max(0, value - 1));
|
|
894
1051
|
return;
|
|
@@ -941,12 +1098,13 @@ function App() {
|
|
|
941
1098
|
return;
|
|
942
1099
|
}
|
|
943
1100
|
if (input === "m") {
|
|
944
|
-
|
|
945
|
-
setSelectedPanelIndex(panelIndexFor("model"));
|
|
1101
|
+
focusPanel("model", "panel");
|
|
946
1102
|
return;
|
|
947
1103
|
}
|
|
948
1104
|
}
|
|
949
1105
|
if (activePanel === "model") {
|
|
1106
|
+
if (navigationFocus === "sidebar")
|
|
1107
|
+
return;
|
|
950
1108
|
if (key.upArrow || input === "k") {
|
|
951
1109
|
setModelPanelSelectedIndex((value) => Math.max(0, value - 1));
|
|
952
1110
|
return;
|
|
@@ -979,6 +1137,8 @@ function App() {
|
|
|
979
1137
|
}
|
|
980
1138
|
}
|
|
981
1139
|
if (activePanel === "spaces") {
|
|
1140
|
+
if (navigationFocus === "sidebar")
|
|
1141
|
+
return;
|
|
982
1142
|
if (key.upArrow || input === "k") {
|
|
983
1143
|
setSpacePanelSelectedIndex((value) => Math.max(0, value - 1));
|
|
984
1144
|
return;
|
|
@@ -1018,6 +1178,19 @@ function App() {
|
|
|
1018
1178
|
}
|
|
1019
1179
|
}
|
|
1020
1180
|
if (activePanel === "tasks") {
|
|
1181
|
+
if (key.escape) {
|
|
1182
|
+
if (tasksPanelMode === "inspect") {
|
|
1183
|
+
setTasksPanelMode("browse");
|
|
1184
|
+
setMessage("Returned to the task list.");
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
setNavigationFocus("sidebar");
|
|
1188
|
+
setMessage("Returned focus to the sidebar.");
|
|
1189
|
+
}
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
if (navigationFocus === "sidebar")
|
|
1193
|
+
return;
|
|
1021
1194
|
if (key.upArrow || input === "k") {
|
|
1022
1195
|
if (tasksPanelMode === "inspect")
|
|
1023
1196
|
return;
|
|
@@ -1030,28 +1203,52 @@ function App() {
|
|
|
1030
1203
|
setTasksPanelSelectedIndex((value) => Math.min(Math.max(0, vendorTasks.length - 1), value + 1));
|
|
1031
1204
|
return;
|
|
1032
1205
|
}
|
|
1033
|
-
if (
|
|
1206
|
+
if (input === "i") {
|
|
1034
1207
|
const task = vendorTasks[tasksPanelSelectedIndex];
|
|
1035
1208
|
if (!task)
|
|
1036
1209
|
return;
|
|
1037
1210
|
setTasksPanelMode("inspect");
|
|
1211
|
+
setMessage(`Inspecting task ${task.taskId}.`);
|
|
1038
1212
|
return;
|
|
1039
1213
|
}
|
|
1040
|
-
if (
|
|
1214
|
+
if (key.return) {
|
|
1041
1215
|
const task = vendorTasks[tasksPanelSelectedIndex];
|
|
1042
1216
|
if (!task)
|
|
1043
1217
|
return;
|
|
1218
|
+
if (tasksPanelMode === "browse") {
|
|
1219
|
+
setTasksPanelMode("inspect");
|
|
1220
|
+
setMessage(`Inspecting task ${task.taskId}.`);
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1044
1223
|
const nextPanel = panelForTaskKind(task.type);
|
|
1045
|
-
|
|
1046
|
-
|
|
1224
|
+
focusPanel(nextPanel, "panel");
|
|
1225
|
+
setMessage(`Opened ${PANELS[panelIndexFor(nextPanel)]?.title ?? nextPanel} from task ${task.taskId}.`);
|
|
1047
1226
|
return;
|
|
1048
1227
|
}
|
|
1049
|
-
if (
|
|
1050
|
-
|
|
1228
|
+
if (input === "o") {
|
|
1229
|
+
const task = vendorTasks[tasksPanelSelectedIndex];
|
|
1230
|
+
if (!task)
|
|
1231
|
+
return;
|
|
1232
|
+
const nextPanel = panelForTaskKind(task.type);
|
|
1233
|
+
focusPanel(nextPanel, "panel");
|
|
1234
|
+
setMessage(`Opened ${PANELS[panelIndexFor(nextPanel)]?.title ?? nextPanel} from task ${task.taskId}.`);
|
|
1051
1235
|
return;
|
|
1052
1236
|
}
|
|
1053
1237
|
}
|
|
1054
1238
|
if (activePanel === "activity") {
|
|
1239
|
+
if (key.escape) {
|
|
1240
|
+
if (activityPanelMode === "inspect") {
|
|
1241
|
+
setActivityPanelMode("browse");
|
|
1242
|
+
setMessage("Returned to the activity list.");
|
|
1243
|
+
}
|
|
1244
|
+
else {
|
|
1245
|
+
setNavigationFocus("sidebar");
|
|
1246
|
+
setMessage("Returned focus to the sidebar.");
|
|
1247
|
+
}
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
if (navigationFocus === "sidebar")
|
|
1251
|
+
return;
|
|
1055
1252
|
if (key.upArrow || input === "k") {
|
|
1056
1253
|
if (activityPanelMode === "inspect")
|
|
1057
1254
|
return;
|
|
@@ -1064,32 +1261,48 @@ function App() {
|
|
|
1064
1261
|
setActivityPanelSelectedIndex((value) => Math.min(Math.max(0, vendorActivity.length - 1), value + 1));
|
|
1065
1262
|
return;
|
|
1066
1263
|
}
|
|
1067
|
-
if (key.return
|
|
1264
|
+
if (key.return) {
|
|
1068
1265
|
const event = vendorActivity[activityPanelSelectedIndex];
|
|
1069
1266
|
if (!event)
|
|
1070
1267
|
return;
|
|
1071
|
-
if (
|
|
1268
|
+
if (activityPanelMode === "browse") {
|
|
1072
1269
|
setActivityPanelMode("inspect");
|
|
1270
|
+
setMessage(`Inspecting activity item: ${event.title}.`);
|
|
1073
1271
|
return;
|
|
1074
1272
|
}
|
|
1075
1273
|
const nextPanel = panelForActivityItem(event);
|
|
1076
1274
|
if (!nextPanel)
|
|
1077
1275
|
return;
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
if (key.escape && activityPanelMode === "inspect") {
|
|
1083
|
-
setActivityPanelMode("browse");
|
|
1276
|
+
focusPanel(nextPanel, "panel");
|
|
1277
|
+
setMessage(`Opened ${PANELS[panelIndexFor(nextPanel)]?.title ?? nextPanel} from activity.`);
|
|
1084
1278
|
return;
|
|
1085
1279
|
}
|
|
1086
1280
|
if (input === "i") {
|
|
1281
|
+
const event = vendorActivity[activityPanelSelectedIndex];
|
|
1087
1282
|
setActivityPanelMode("inspect");
|
|
1283
|
+
if (event) {
|
|
1284
|
+
setMessage(`Inspecting activity item: ${event.title}.`);
|
|
1285
|
+
}
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
if (input === "o") {
|
|
1289
|
+
const event = vendorActivity[activityPanelSelectedIndex];
|
|
1290
|
+
if (!event)
|
|
1291
|
+
return;
|
|
1292
|
+
const nextPanel = panelForActivityItem(event);
|
|
1293
|
+
if (!nextPanel) {
|
|
1294
|
+
setMessage("That activity item does not point to a related panel.");
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
focusPanel(nextPanel, "panel");
|
|
1298
|
+
setMessage(`Opened ${PANELS[panelIndexFor(nextPanel)]?.title ?? nextPanel} from activity.`);
|
|
1088
1299
|
return;
|
|
1089
1300
|
}
|
|
1090
1301
|
}
|
|
1091
1302
|
if (activePanel === "hub") {
|
|
1092
|
-
|
|
1303
|
+
if (navigationFocus === "sidebar")
|
|
1304
|
+
return;
|
|
1305
|
+
const registryEntries = hubRegistryEntries;
|
|
1093
1306
|
if (key.upArrow || input === "k") {
|
|
1094
1307
|
setHubPanelSelectedIndex((value) => Math.max(0, value - 1));
|
|
1095
1308
|
return;
|
|
@@ -1129,6 +1342,8 @@ function App() {
|
|
|
1129
1342
|
}
|
|
1130
1343
|
}
|
|
1131
1344
|
if (activePanel === "connectors") {
|
|
1345
|
+
if (navigationFocus === "sidebar")
|
|
1346
|
+
return;
|
|
1132
1347
|
const registryEntries = listConnectorTemplates();
|
|
1133
1348
|
if (key.upArrow || input === "k") {
|
|
1134
1349
|
setConnectorsPanelSelectedIndex((value) => Math.max(0, value - 1));
|
|
@@ -1205,43 +1420,61 @@ function App() {
|
|
|
1205
1420
|
? "Setup mode: Enter confirms, Up/Down move through companies or spaces, Esc leaves setup"
|
|
1206
1421
|
: activePanel === "providers"
|
|
1207
1422
|
? providerPanelMode === "browse"
|
|
1208
|
-
?
|
|
1423
|
+
? navigationFocus === "sidebar"
|
|
1424
|
+
? "Sidebar focus: Up/Down choose panel | Enter, Right, or Tab opens it | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | s setup | q quit"
|
|
1425
|
+
: "Providers: Up/Down choose | Enter or w switch | c connect | d disconnect | t test | i inspect recent | g guided setup | m model | Esc or Left returns to sidebar | ? query | + curate | q quit"
|
|
1209
1426
|
: providerPanelMode === "connect-kind"
|
|
1210
1427
|
? "Providers connect: Up/Down choose provider type | Enter confirm | Esc back"
|
|
1211
1428
|
: "Providers connect: type value | Enter continue | Esc back"
|
|
1212
1429
|
: activePanel === "model"
|
|
1213
1430
|
? modelPanelMode === "browse"
|
|
1214
|
-
?
|
|
1431
|
+
? navigationFocus === "sidebar"
|
|
1432
|
+
? "Sidebar focus: Up/Down choose panel | Enter, Right, or Tab opens it | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | s setup | q quit"
|
|
1433
|
+
: "Model: Up/Down choose | Enter or w switch | i inspect recent | t readiness | Esc or Left returns to sidebar | ? query | + curate | q quit"
|
|
1215
1434
|
: "Model runtime panel"
|
|
1216
1435
|
: activePanel === "spaces"
|
|
1217
|
-
?
|
|
1436
|
+
? navigationFocus === "sidebar"
|
|
1437
|
+
? "Sidebar focus: Up/Down choose panel | Enter, Right, or Tab opens it | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | s setup | q quit"
|
|
1438
|
+
: "Spaces: Enter or w switch | n create | i refresh | Esc or Left returns to sidebar | ? query | + curate | q quit"
|
|
1218
1439
|
: activePanel === "hub"
|
|
1219
1440
|
? hubPanelMode === "browse"
|
|
1220
|
-
?
|
|
1441
|
+
? navigationFocus === "sidebar"
|
|
1442
|
+
? "Sidebar focus: Up/Down choose panel | Enter, Right, or Tab opens it | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | s setup | q quit"
|
|
1443
|
+
: "Hub: Up/Down choose | Enter or n install | i inspect | b guided browse | l refresh | Esc or Left returns to sidebar | ? query | + curate | q quit"
|
|
1221
1444
|
: "Hub live panel"
|
|
1222
1445
|
: activePanel === "connectors"
|
|
1223
1446
|
? connectorsPanelMode === "browse"
|
|
1224
|
-
?
|
|
1447
|
+
? navigationFocus === "sidebar"
|
|
1448
|
+
? "Sidebar focus: Up/Down choose panel | Enter, Right, or Tab opens it | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | s setup | q quit"
|
|
1449
|
+
: "Connectors: Up/Down choose | Enter or i install | r run | o outputs | v inspect | Esc or Left returns to sidebar | ? query | + curate | q quit"
|
|
1225
1450
|
: "Connectors live panel"
|
|
1226
1451
|
: activePanel === "tasks"
|
|
1227
1452
|
? tasksPanelMode === "browse"
|
|
1228
|
-
?
|
|
1229
|
-
|
|
1453
|
+
? navigationFocus === "sidebar"
|
|
1454
|
+
? "Sidebar focus: Up/Down choose panel | Enter, Right, or Tab opens tasks | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | s setup | q quit"
|
|
1455
|
+
: "Tasks: Up/Down choose | Enter or i inspect | o open related panel | Esc or Left returns to sidebar | r refresh | ? query | + curate | q quit"
|
|
1456
|
+
: "Tasks inspect: Esc goes back to the task list | Enter or o opens related panel | Left returns to sidebar | r refresh | ? query | + curate | q quit"
|
|
1230
1457
|
: activePanel === "activity"
|
|
1231
1458
|
? activityPanelMode === "browse"
|
|
1232
|
-
?
|
|
1233
|
-
|
|
1234
|
-
|
|
1459
|
+
? navigationFocus === "sidebar"
|
|
1460
|
+
? "Sidebar focus: Up/Down choose panel | Enter, Right, or Tab opens activity | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | s setup | q quit"
|
|
1461
|
+
: "Activity: Up/Down choose | Enter or i inspect | o open related panel | Esc or Left returns to sidebar | r refresh | ? query | + curate | q quit"
|
|
1462
|
+
: "Activity inspect: Esc goes back to the event list | Left returns to sidebar | Enter or o opens related panel | r refresh | ? query | + curate | q quit"
|
|
1463
|
+
: "Sidebar focus: Up/Down or j/k move panels | Enter, Right, or Tab opens a panel | [ ] cycle panels | 1-9 or h/t/a/v/m/w/b/c/u jumps panels | ? query | + curate | p push | l pull | r refresh | s setup | q quit";
|
|
1235
1464
|
const config = configManager.config;
|
|
1236
1465
|
let mainContent = null;
|
|
1237
1466
|
if (activePanel === "setup") {
|
|
1238
|
-
mainContent = (_jsx(SetupView, { authSnapshot: authSnapshot, busyLabel: busyLabel, companies: companies, config: config, inputValue: inputValue, message: message, onInputChange: setInputValue, selectedCompanyIndex: selectedCompanyIndex, selectedSpaceIndex: selectedSpaceIndex, serverUrl: serverUrl, spaces: spaces, step: setupStep }));
|
|
1467
|
+
mainContent = (_jsx(SetupView, { authSnapshot: authSnapshot, busyLabel: busyLabel, companies: companies, config: config, inputValue: inputValue, message: message, onInputChange: setInputValue, selectedCompanyIndex: selectedCompanyIndex, selectedServerChoiceIndex: selectedServerChoiceIndex, selectedSpaceIndex: selectedSpaceIndex, serverUrl: serverUrl, spaces: spaces, step: setupStep }));
|
|
1239
1468
|
}
|
|
1240
1469
|
else if (!config || !refreshState) {
|
|
1241
|
-
mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: "This
|
|
1470
|
+
mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: "This terminal app can open before live sign-in is finished." }), _jsx(Text, { color: "gray", children: "Right now this repo is not fully linked to a company and space yet." }), _jsx(Text, { color: "gray", children: "Open Setup and finish the device link before you expect shared server context to work." })] }));
|
|
1242
1471
|
}
|
|
1243
1472
|
else if (activePanel === "home") {
|
|
1244
|
-
mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [config.companyName, " / ", config.projectName] }), _jsx(Text, { color: "gray", children: "This is the repo-level Context home for the current company space." }),
|
|
1473
|
+
mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [config.companyName, " / ", config.projectName] }), _jsx(Text, { color: "gray", children: "This is the repo-level Context home for the current company space." }), _jsx(Text, { color: "gray", children: "The TUI is local-first: it reads this repo's .um folder even before the live server is queried." }), connectionPresentation ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: connectionPresentation.connectionMode === "verified_live_connection" ? "green" : "yellow", children: connectionPresentation.connectionMode === "verified_live_connection"
|
|
1474
|
+
? "Live connection verified"
|
|
1475
|
+
: "Saved local setup only" }), _jsx(Text, { color: "gray", children: connectionPresentation.connectionSummary }), connectionPresentation.warnings.map((warning) => (_jsxs(Text, { color: "yellow", children: ["- ", warning] }, warning)))] })) : null, _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Section, { title: "Connection", children: [_jsx(StatusLine, { label: "Umbrella server", value: config.umbrellaUrl ?? config.serverUrl }), _jsx(StatusLine, { label: "Auth state", value: formatUmbrellaAuthStatus(connectionPresentation?.authSnapshot.status ?? authSnapshot.status) }), _jsx(StatusLine, { label: "Shared API key", value: config.apiKey ? "Saved" : "Missing" }), _jsx(StatusLine, { label: "Connection mode", value: connectionPresentation?.connectionMode === "verified_live_connection"
|
|
1476
|
+
? "Verified live connection"
|
|
1477
|
+
: "Saved local config only" })] }), _jsxs(Section, { title: "Sync", children: [_jsx(StatusLine, { label: "Pending drafts", value: String(refreshState.pending.length) }), _jsx(StatusLine, { label: "Pulled context", value: String(refreshState.pulled.length) }), _jsx(StatusLine, { label: "Known fixes", value: String(refreshState.fixes.length) }), _jsx(StatusLine, { label: "Last push", value: formatAgo(refreshState.repoContext.state?.lastPushAt) }), _jsx(StatusLine, { label: "Last pull", value: formatAgo(refreshState.repoContext.state?.lastPullAt) })] }), _jsxs(Section, { title: "Runtime", children: [_jsx(StatusLine, { label: "Provider", value: readiness.activeProvider?.name ?? "Not connected" }), _jsx(StatusLine, { label: "Model", value: readiness.activeModel ?? "Not selected" }), _jsx(StatusLine, { label: "Transport", value: vendorTransportStore?.status ?? refreshState.transport.status }), _jsx(StatusLine, { label: "Repo .um", value: refreshState.repoContext.umDir })] }), _jsxs(Section, { title: "Context Tree", children: [_jsx(StatusLine, { label: "Summary", value: vendorTreeStore?.summaryHandle ?? refreshState.treeState.summaryHandle }), _jsx(StatusLine, { label: "Nodes", value: String(vendorTreeStore?.nodes.length ?? refreshState.treeState.nodes.length) }), _jsx(StatusLine, { label: "Runtime provider", value: vendorTreeStore?.runtime.provider ?? "No provider" }), _jsx(StatusLine, { label: "Runtime model", value: vendorTreeStore?.runtime.model ?? "No model" })] }), queryResult ? (_jsxs(Section, { title: `Latest Query: ${queryResult.query}`, children: [_jsxs(Text, { children: ["Local matches: ", String(queryResult.localMatches.length)] }), _jsxs(Text, { children: ["Shared matches: ", String(queryResult.remoteMatches.length)] })] })) : null, connectionPresentation?.connectionMode === "saved_local_config" ? (_jsx(Section, { title: "Next Step", children: _jsx(Text, { color: "yellow", children: "Run setup again before you trust shared server actions for this repo." }) })) : null] })] }));
|
|
1245
1478
|
}
|
|
1246
1479
|
else if (activePanel === "tasks") {
|
|
1247
1480
|
mainContent = (_jsx(TasksPanelView, { mode: tasksPanelMode, selectedIndex: tasksPanelSelectedIndex, tasks: vendorTasks, transportStatus: vendorTransportStore?.status ?? vendorTransportSummary.transportStatus, queueLength: vendorTransportStore?.queue.length ?? vendorTransportSummary.queueDepth, activeTaskId: vendorTransportStore?.queue[0]?.id ??
|
|
@@ -1266,8 +1499,7 @@ function App() {
|
|
|
1266
1499
|
mainContent = (_jsx(SpacePanelView, { companyName: vendorContextSummary.companyName ?? config.companyName, currentSpaceName: vendorContextSummary.spaceName ?? config.projectName, draftName: spacePanelDraftName, mode: spacePanelMode, onDraftChange: setSpacePanelDraftName, selectedIndex: spacePanelSelectedIndex, spaces: vendorSpaces }));
|
|
1267
1500
|
}
|
|
1268
1501
|
else if (activePanel === "hub") {
|
|
1269
|
-
|
|
1270
|
-
mainContent = (_jsx(HubPanelView, { installedSlugs: vendorHubStore?.installedSlugs ?? [], recentSlug: vendorHubStore?.recentSlug ?? null, registryEntries: registryEntries, selectedIndex: hubPanelSelectedIndex }));
|
|
1502
|
+
mainContent = (_jsx(HubPanelView, { installedSlugs: vendorHubStore?.installedSlugs ?? [], recentSlug: vendorHubStore?.recentSlug ?? null, registryEntries: hubRegistryEntries, selectedIndex: hubPanelSelectedIndex }));
|
|
1271
1503
|
}
|
|
1272
1504
|
else if (activePanel === "connectors") {
|
|
1273
1505
|
const registryEntries = listConnectorTemplates();
|
|
@@ -1276,7 +1508,7 @@ function App() {
|
|
|
1276
1508
|
else {
|
|
1277
1509
|
mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Session Memory" }), _jsx(Text, { color: "gray", children: "This is the live terminal memory saved inside the repo, plus the internal runtime stores hydrated from the same Context engine." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Session ID", value: refreshState.session?.id ?? "None" }), _jsx(StatusLine, { label: "Current panel", value: refreshState.session?.currentPanel ?? "None" }), _jsx(StatusLine, { label: "Current focus", value: refreshState.session?.currentFocus ?? "None" }), _jsx(StatusLine, { label: "Commands", value: String(refreshState.session?.commandHistory.length ?? 0) }), _jsx(StatusLine, { label: "Events", value: String(refreshState.session?.events.length ?? 0) }), _jsx(StatusLine, { label: "Transport", value: vendorTransportStore?.status ?? refreshState.transport.status }), _jsx(StatusLine, { label: "Tree summary", value: vendorTreeStore?.summaryHandle ?? refreshState.treeState.summaryHandle })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Section, { title: "Transport and Tasks", children: [_jsx(StatusLine, { label: "Queue depth", value: String(vendorTransportStore?.queue.length ?? 0) }), _jsx(StatusLine, { label: "Recent tasks", value: String(vendorTasks.length) }), _jsx(StatusLine, { label: "Active task", value: vendorTransportSummary.activeTaskId ?? "None" }), _jsx(StatusLine, { label: "Started tasks", value: String(vendorTransportSummary.startedTasks) }), vendorRecentTransportEvents.length ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: "yellow", children: "Recent transport events" }), vendorRecentTransportEvents.map((event) => (_jsxs(Text, { color: "gray", children: [new Date(event.at).toLocaleString(), " | ", event.kind, " | ", event.title] }, `${event.kind}-${event.at}-${event.taskId ?? "no-task"}`)))] })) : (_jsx(Text, { color: "gray", children: "No transport events yet." }))] }), _jsxs(Section, { title: "Context Runtime Tree", children: [_jsx(StatusLine, { label: "Nodes", value: String(vendorTreeStore?.nodes.length ?? 0) }), _jsx(StatusLine, { label: "Pending drafts", value: String(vendorTreeStore?.stats.pendingDrafts ?? 0) }), _jsx(StatusLine, { label: "Pulled context", value: String(vendorTreeStore?.stats.pulledContext ?? 0) }), _jsx(StatusLine, { label: "Known fixes", value: String(vendorTreeStore?.stats.pulledFixes ?? 0) }), _jsx(StatusLine, { label: "Runtime transport", value: vendorTreeStore?.runtime.transportStatus ?? "idle" }), vendorRootTreeNodes.length ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: "yellow", children: "Top runtime nodes" }), vendorRootTreeNodes.map((node) => (_jsx(Text, { color: "gray", children: `${" ".repeat(node.depth)}- ${node.label}${node.detail ? ` | ${node.detail}` : ""}` }, node.id)))] })) : (_jsx(Text, { color: "gray", children: "The context tree has not been built yet." }))] }), _jsxs(Section, { title: "Session Continuity", children: [_jsx(StatusLine, { label: "Panel history", value: refreshState.session?.panelHistory.join(" -> ") || "None" }), _jsx(StatusLine, { label: "Recent providers", value: refreshState.session?.recentProviderIds.join(", ") || "None" }), _jsx(StatusLine, { label: "Recent models", value: refreshState.session?.recentModels.join(", ") || "None" }), _jsx(StatusLine, { label: "Recent hub items", value: refreshState.session?.recentHubSlugs.join(", ") || "None" }), _jsx(StatusLine, { label: "Recent connectors", value: refreshState.session?.recentConnectorSources.join(", ") || "None" })] }), _jsxs(Section, { title: "Runtime Bridge", children: [_jsx(StatusLine, { label: "Hydrated", value: vendorTransportSummary.isHydrated ? "Yes" : "No" }), _jsx(StatusLine, { label: "Generated", value: formatAgo(vendorTransportSummary.generatedAt) }), _jsx(StatusLine, { label: "Bridge queue", value: String(vendorTransportSummary.queueDepth) }), _jsx(StatusLine, { label: "Bridge tasks", value: String(vendorTransportSummary.recentTaskCount) }), _jsx(StatusLine, { label: "Bridge company", value: vendorContextSummary.companyName ?? "Unlinked company" }), _jsx(StatusLine, { label: "Bridge space", value: vendorContextSummary.spaceName ?? "Unlinked space" }), _jsx(StatusLine, { label: "Bridge provider", value: vendorContextSummary.providerName ?? "No provider" }), _jsx(StatusLine, { label: "Bridge model", value: vendorContextSummary.modelName ?? "No model" }), _jsx(StatusLine, { label: "Tree nodes", value: String(vendorContextSummary.treeNodes) }), _jsx(StatusLine, { label: "Runtime status", value: vendorContextSummary.runtimeStatus })] })] })] }));
|
|
1278
1510
|
}
|
|
1279
|
-
return (_jsxs(PanelShell, { activePanel: activePanel, footer: footer, message: busyLabel ? `${busyLabel}` : message, selectedPanelIndex: selectedPanelIndex, children: [mainContent, inputMode ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: inputMode === "query" ? "Ask Context" : "Save a local Context note" }), _jsx(TextInput, { value: inputValue, onChange: setInputValue })] })) : null] }));
|
|
1511
|
+
return (_jsxs(PanelShell, { activePanel: activePanel, footer: footer, message: busyLabel ? `${busyLabel}` : message, navigationFocus: navigationFocus, selectedPanelIndex: selectedPanelIndex, children: [mainContent, inputMode ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: inputMode === "query" ? "Ask Context" : "Save a local Context note" }), _jsx(TextInput, { value: inputValue, onChange: setInputValue })] })) : null] }));
|
|
1280
1512
|
}
|
|
1281
1513
|
export async function runTui() {
|
|
1282
1514
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|