aidevops 3.25.1 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/VERSION +1 -1
- package/aidevops.sh +1 -1
- package/package.json +1 -1
- package/packages/gui-web/src/AppNavigation.tsx +6 -6
- package/packages/gui-web/src/AppWorkspace.tsx +96 -12
- package/packages/gui-web/src/styles.css +39 -21
- package/setup.sh +1 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ The result: an AI operations platform that manages projects across every busines
|
|
|
58
58
|
[](https://github.com/marcusquinn)
|
|
59
59
|
|
|
60
60
|
<!-- Release & Version Info -->
|
|
61
|
-
[](https://github.com/marcusquinn/aidevops/releases)
|
|
62
62
|
[](https://www.npmjs.com/package/aidevops)
|
|
63
63
|
[](https://github.com/marcusquinn/homebrew-tap)
|
|
64
64
|
[](https://github.com/marcusquinn/aidevops)
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.26.0
|
package/aidevops.sh
CHANGED
package/package.json
CHANGED
|
@@ -221,14 +221,14 @@ function ConversationSidebar({ conversationMode, selectedLocalRepoIndex, setConv
|
|
|
221
221
|
{conversationMode === "people" ? <PeopleChannelList /> : <>
|
|
222
222
|
<button className="new-session-button" disabled title="Creating sessions needs an audited OpenCode write route." type="button">{text.newSession}</button>
|
|
223
223
|
<section className="repo-session-selector" aria-label={text.localRepoSelector}>
|
|
224
|
-
<
|
|
225
|
-
<
|
|
226
|
-
<
|
|
227
|
-
<select onChange={(event) => setSelectedLocalRepoIndex(Number.parseInt(event.currentTarget.value, 10))} value={Math.min(selectedLocalRepoIndex, Math.max(0, repos.length - 1))}>
|
|
224
|
+
<span className="repo-selector-heading" id="local-repo-selector-label">{text.localRepos}</span>
|
|
225
|
+
<div className="selector-with-stepper repo-selector-row">
|
|
226
|
+
<button aria-label="Previous local repo" className="selector-step-button" disabled={!canSelectPreviousRepo} onClick={() => setSelectedLocalRepoIndex(Math.max(0, selectedLocalRepoIndex - 1))} type="button"><FiChevronLeft aria-hidden="true" /></button>
|
|
227
|
+
<select aria-labelledby="local-repo-selector-label" onChange={(event) => setSelectedLocalRepoIndex(Number.parseInt(event.currentTarget.value, 10))} value={Math.min(selectedLocalRepoIndex, Math.max(0, repos.length - 1))}>
|
|
228
228
|
{repos.length === 0 ? <option value={0}>No local repos</option> : repos.map((repo, index) => <option key={repo.path_ref} value={index}>{repo.name}</option>)}
|
|
229
229
|
</select>
|
|
230
|
-
|
|
231
|
-
|
|
230
|
+
<button aria-label="Next local repo" className="selector-step-button" disabled={!canSelectNextRepo} onClick={() => setSelectedLocalRepoIndex(Math.min(repos.length - 1, selectedLocalRepoIndex + 1))} type="button"><FiChevronRight aria-hidden="true" /></button>
|
|
231
|
+
</div>
|
|
232
232
|
</section>
|
|
233
233
|
<section className="sidebar-group session-history-list">
|
|
234
234
|
<h2>{text.sessionHistory}</h2>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* jshint esversion: 11 */
|
|
2
|
-
import { type ReactElement, type ReactNode, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { type ReactElement, type ReactNode, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { FiBell, FiChevronLeft, FiChevronRight, FiCommand, FiCpu, FiGlobe, FiHash, FiLogOut, FiMessageSquare, FiSearch, FiSettings, FiShield, FiUser } from "react-icons/fi";
|
|
4
4
|
import type { GuiFileRootId, GuiStatusData } from "../../gui-shared/src";
|
|
5
5
|
import { SurfaceGlyph } from "./AppNavigation";
|
|
@@ -54,6 +54,7 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
|
|
|
54
54
|
status: GuiStatusData;
|
|
55
55
|
}): ReactElement {
|
|
56
56
|
const [assistantOpen, setAssistantOpen] = useState(false);
|
|
57
|
+
const [commandInitialQuery, setCommandInitialQuery] = useState("");
|
|
57
58
|
const [commandOpen, setCommandOpen] = useState(false);
|
|
58
59
|
const [notificationsOpen, setNotificationsOpen] = useState(false);
|
|
59
60
|
const [profileOpen, setProfileOpen] = useState(false);
|
|
@@ -64,6 +65,14 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
|
|
|
64
65
|
const openCommandPalette = (event: KeyboardEvent) => {
|
|
65
66
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
66
67
|
event.preventDefault();
|
|
68
|
+
setCommandInitialQuery("");
|
|
69
|
+
setCommandOpen(true);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (event.key === "/" && !isEditableShortcutTarget(event.target)) {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
setCommandInitialQuery("/");
|
|
67
76
|
setCommandOpen(true);
|
|
68
77
|
}
|
|
69
78
|
};
|
|
@@ -92,16 +101,18 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
|
|
|
92
101
|
<h1>{activeItem.label}</h1>
|
|
93
102
|
</div>
|
|
94
103
|
</div>
|
|
95
|
-
<div className="header-
|
|
104
|
+
<div className="header-center-controls">
|
|
96
105
|
<div className="sidebar-history-controls workspace-history-controls">
|
|
97
106
|
<button aria-label="Previous surface" disabled={!canGoBack} onClick={goBack} type="button"><FiChevronLeft /></button>
|
|
98
107
|
<button aria-label="Next surface" disabled={!canGoForward} onClick={goForward} type="button"><FiChevronRight /></button>
|
|
99
108
|
</div>
|
|
100
|
-
<button className="workspace-search command-trigger" onClick={() => setCommandOpen(true)} type="button">
|
|
109
|
+
<button className="workspace-search command-trigger" onClick={() => { setCommandInitialQuery(""); setCommandOpen(true); }} type="button">
|
|
101
110
|
<FiSearch aria-hidden="true" />
|
|
102
111
|
<span>⌘K</span>
|
|
103
112
|
<strong>{text.searchPlaceholder}</strong>
|
|
104
113
|
</button>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="header-actions">
|
|
105
116
|
<div className="header-action-menu">
|
|
106
117
|
<button aria-expanded={notificationsOpen} aria-label="Open notifications" className="header-icon-button" onClick={() => { setNotificationsOpen((current) => !current); setProfileOpen(false); }} type="button">
|
|
107
118
|
<FiBell aria-hidden="true" />
|
|
@@ -120,11 +131,19 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
|
|
|
120
131
|
</div>
|
|
121
132
|
</div>
|
|
122
133
|
{assistantOpen ? <AssistantPanel activeSurface={activeSurface} userName={userName} /> : null}
|
|
123
|
-
{commandOpen ? <CommandPalette close={() => setCommandOpen(false)} openSurface={openSurface} /> : null}
|
|
134
|
+
{commandOpen ? <CommandPalette close={() => setCommandOpen(false)} initialQuery={commandInitialQuery} openSurface={openSurface} status={status} /> : null}
|
|
124
135
|
</header>
|
|
125
136
|
);
|
|
126
137
|
}
|
|
127
138
|
|
|
139
|
+
function isEditableShortcutTarget(target: EventTarget | null): boolean {
|
|
140
|
+
if (!(target instanceof HTMLElement)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return target.isContentEditable || ["INPUT", "SELECT", "TEXTAREA"].includes(target.tagName);
|
|
145
|
+
}
|
|
146
|
+
|
|
128
147
|
function NotificationsMenu({ openSurface }: { openSurface: (surface: SurfaceId) => void }): ReactElement {
|
|
129
148
|
return (
|
|
130
149
|
<div className="popover-menu notifications-menu" role="menu">
|
|
@@ -216,14 +235,79 @@ function AssistantPanel({ activeSurface, userName }: { activeSurface: SurfaceId;
|
|
|
216
235
|
);
|
|
217
236
|
}
|
|
218
237
|
|
|
219
|
-
|
|
220
|
-
|
|
238
|
+
interface CommandPaletteItem {
|
|
239
|
+
description: string;
|
|
240
|
+
icon: SurfaceNavItem["icon"];
|
|
241
|
+
id: string;
|
|
242
|
+
label: string;
|
|
243
|
+
searchText: string;
|
|
244
|
+
surface: SurfaceId;
|
|
245
|
+
tag: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const slashCommandItems: CommandPaletteItem[] = [
|
|
249
|
+
{ id: "slash-add-device", label: "/add-device", description: "Start device pairing setup", icon: "device", searchText: "/add-device add device pair machine", surface: "devices", tag: "Command" },
|
|
250
|
+
{ id: "slash-add-local-repo", label: "/add-local-repo", description: "Add a local repository path", icon: "folder", searchText: "/add-local-repo add local repo git", surface: "git", tag: "Command" },
|
|
251
|
+
{ id: "slash-add-remote-repo", label: "/add-remote-repo", description: "Register a remote repository", icon: "git", searchText: "/add-remote-repo add remote repo", surface: "projects", tag: "Command" },
|
|
252
|
+
{ id: "slash-add-secret", label: "/add-secret", description: "Add a protected secret reference", icon: "shield", searchText: "/add-secret add secret vault", surface: "security", tag: "Command" },
|
|
253
|
+
{ id: "slash-add-ai-provider", label: "/add-ai-provider", description: "Connect an AI provider account", icon: "users", searchText: "/add-ai-provider add ai provider oauth", surface: "aiProviders", tag: "Command" },
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
const plannedChannelItems: CommandPaletteItem[] = [
|
|
257
|
+
{ id: "channel-devops", label: "#devops", description: "Chat channel for aidevops operations", icon: "message", searchText: "#devops devops channel chat", surface: "messagingAccounts", tag: "Channel" },
|
|
258
|
+
{ id: "channel-comms", label: "#comms", description: "Chat channel for communications", icon: "message", searchText: "#comms comms channel chat", surface: "messagingAccounts", tag: "Channel" },
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
function CommandPalette({ close, initialQuery, openSurface, status }: { close: () => void; initialQuery: string; openSurface: (surface: SurfaceId) => void; status: GuiStatusData }): ReactElement {
|
|
262
|
+
const [query, setQuery] = useState(initialQuery);
|
|
263
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
221
264
|
const matches = useMemo(() => {
|
|
222
265
|
const normalizedQuery = query.trim().toLowerCase();
|
|
223
|
-
|
|
224
|
-
|
|
266
|
+
const surfaceItems: CommandPaletteItem[] = orderedNavItems.map((item) => ({
|
|
267
|
+
description: item.description,
|
|
268
|
+
icon: item.icon,
|
|
269
|
+
id: `surface-${item.id}`,
|
|
270
|
+
label: item.label,
|
|
271
|
+
searchText: `${item.label} ${item.description}`,
|
|
272
|
+
surface: item.id,
|
|
273
|
+
tag: "Surface",
|
|
274
|
+
}));
|
|
275
|
+
const sessionItems: CommandPaletteItem[] = status.opencode_sessions.sessions.map((session) => ({
|
|
276
|
+
description: `AI session in ${session.repo_path_ref}`,
|
|
277
|
+
icon: "terminal",
|
|
278
|
+
id: `session-${session.id_ref}`,
|
|
279
|
+
label: `#${session.title}`,
|
|
280
|
+
searchText: `#${session.title} ${session.title} ${session.repo_path_ref} ${session.agent} ${session.model}`,
|
|
281
|
+
surface: "git",
|
|
282
|
+
tag: "AI session",
|
|
283
|
+
}));
|
|
284
|
+
const directMessageItems: CommandPaletteItem[] = [{
|
|
285
|
+
description: "Direct message thread placeholder",
|
|
286
|
+
icon: "users",
|
|
287
|
+
id: "dm-local-user",
|
|
288
|
+
label: `@${status.machine.username || "local-user"}`,
|
|
289
|
+
searchText: `@${status.machine.username || "local-user"} direct message dm person local user`,
|
|
290
|
+
surface: "messagingAccounts",
|
|
291
|
+
tag: "Direct message",
|
|
292
|
+
}];
|
|
293
|
+
const items = [...surfaceItems, ...sessionItems, ...plannedChannelItems, ...directMessageItems, ...slashCommandItems];
|
|
294
|
+
const prefix = normalizedQuery.charAt(0);
|
|
295
|
+
const scopedItems = prefix === "/"
|
|
296
|
+
? slashCommandItems
|
|
297
|
+
: prefix === "@"
|
|
298
|
+
? directMessageItems
|
|
299
|
+
: prefix === "#"
|
|
300
|
+
? [...sessionItems, ...plannedChannelItems]
|
|
301
|
+
: items;
|
|
302
|
+
|
|
303
|
+
return scopedItems
|
|
304
|
+
.filter((item) => normalizedQuery.length === 0 || item.searchText.toLowerCase().includes(normalizedQuery))
|
|
225
305
|
.slice(0, 8);
|
|
226
|
-
}, [query]);
|
|
306
|
+
}, [query, status.machine.username, status.opencode_sessions.sessions]);
|
|
307
|
+
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
inputRef.current?.focus();
|
|
310
|
+
}, []);
|
|
227
311
|
|
|
228
312
|
useEffect(() => {
|
|
229
313
|
const closeOnEscape = (event: KeyboardEvent) => {
|
|
@@ -242,14 +326,14 @@ function CommandPalette({ close, openSurface }: { close: () => void; openSurface
|
|
|
242
326
|
<section aria-label="Command palette" className="command-palette">
|
|
243
327
|
<label className="command-input-row">
|
|
244
328
|
<FiSearch aria-hidden="true" />
|
|
245
|
-
<input onChange={(event) => setQuery(event.currentTarget.value)} placeholder="Search commands
|
|
329
|
+
<input ref={inputRef} onChange={(event) => setQuery(event.currentTarget.value)} placeholder="Search commands, #sessions, #channels, @people, or /actions" value={query} />
|
|
246
330
|
</label>
|
|
247
331
|
<ul>
|
|
248
332
|
{matches.map((item) => (
|
|
249
333
|
<li key={item.id}>
|
|
250
|
-
<button onClick={() => openSurface(item.
|
|
334
|
+
<button onClick={() => openSurface(item.surface)} type="button">
|
|
251
335
|
<span className="surface-icon" aria-hidden="true"><SurfaceGlyph icon={item.icon} /></span>
|
|
252
|
-
<span><strong>{item.label}</strong><small>{item.description}</small></span>
|
|
336
|
+
<span><strong>{item.label}</strong><small>{item.tag} · {item.description}</small></span>
|
|
253
337
|
</button>
|
|
254
338
|
</li>
|
|
255
339
|
))}
|
|
@@ -211,6 +211,7 @@ code {
|
|
|
211
211
|
.theme-control-heading,
|
|
212
212
|
.appearance-panel-tab,
|
|
213
213
|
.header-title,
|
|
214
|
+
.header-center-controls,
|
|
214
215
|
.header-actions,
|
|
215
216
|
.workspace-search,
|
|
216
217
|
.version-pill,
|
|
@@ -377,10 +378,8 @@ code {
|
|
|
377
378
|
}
|
|
378
379
|
|
|
379
380
|
.repo-session-selector {
|
|
380
|
-
align-items: end;
|
|
381
381
|
display: grid;
|
|
382
|
-
gap:
|
|
383
|
-
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
382
|
+
gap: 7px;
|
|
384
383
|
}
|
|
385
384
|
|
|
386
385
|
.selector-with-stepper {
|
|
@@ -420,19 +419,18 @@ code {
|
|
|
420
419
|
width: 18px;
|
|
421
420
|
}
|
|
422
421
|
|
|
423
|
-
.repo-
|
|
424
|
-
display: grid;
|
|
425
|
-
gap: 5px;
|
|
426
|
-
min-width: 0;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
.repo-session-selector label span {
|
|
422
|
+
.repo-selector-heading {
|
|
430
423
|
color: var(--text-muted);
|
|
431
424
|
font-size: 0.72rem;
|
|
432
425
|
font-weight: 900;
|
|
426
|
+
text-align: center;
|
|
433
427
|
text-transform: uppercase;
|
|
434
428
|
}
|
|
435
429
|
|
|
430
|
+
.repo-selector-row {
|
|
431
|
+
align-items: center;
|
|
432
|
+
}
|
|
433
|
+
|
|
436
434
|
.repo-session-selector select {
|
|
437
435
|
background: var(--surface-muted);
|
|
438
436
|
border: 1px solid var(--border);
|
|
@@ -801,9 +799,9 @@ code {
|
|
|
801
799
|
background: color-mix(in srgb, var(--surface-elevated) 94%, transparent);
|
|
802
800
|
border-bottom: 1px solid var(--border);
|
|
803
801
|
display: flex;
|
|
804
|
-
gap:
|
|
805
|
-
min-height:
|
|
806
|
-
padding:
|
|
802
|
+
gap: 12px;
|
|
803
|
+
min-height: 54px;
|
|
804
|
+
padding: 8px 14px;
|
|
807
805
|
position: sticky;
|
|
808
806
|
top: 0;
|
|
809
807
|
z-index: 2;
|
|
@@ -811,7 +809,7 @@ code {
|
|
|
811
809
|
|
|
812
810
|
.header-title {
|
|
813
811
|
flex: 0 0 auto;
|
|
814
|
-
gap:
|
|
812
|
+
gap: 8px;
|
|
815
813
|
min-width: 0;
|
|
816
814
|
}
|
|
817
815
|
|
|
@@ -819,12 +817,14 @@ code {
|
|
|
819
817
|
color: var(--text-muted);
|
|
820
818
|
font-size: 0.76rem;
|
|
821
819
|
font-weight: 900;
|
|
820
|
+
line-height: 1.15;
|
|
822
821
|
margin: 0;
|
|
823
822
|
text-transform: uppercase;
|
|
824
823
|
}
|
|
825
824
|
|
|
826
825
|
.header-title h1 {
|
|
827
826
|
font-size: 1rem;
|
|
827
|
+
line-height: 1.15;
|
|
828
828
|
margin: 0;
|
|
829
829
|
}
|
|
830
830
|
|
|
@@ -840,15 +840,25 @@ code {
|
|
|
840
840
|
align-items: center;
|
|
841
841
|
color: var(--accent);
|
|
842
842
|
display: inline-flex;
|
|
843
|
-
height:
|
|
843
|
+
height: 32px;
|
|
844
844
|
justify-content: center;
|
|
845
|
-
width:
|
|
845
|
+
width: 32px;
|
|
846
846
|
}
|
|
847
847
|
|
|
848
848
|
.workspace-surface-icon svg {
|
|
849
|
-
height:
|
|
849
|
+
height: 23px;
|
|
850
850
|
stroke-width: 2.2;
|
|
851
|
-
width:
|
|
851
|
+
width: 23px;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.header-center-controls {
|
|
855
|
+
gap: 10px;
|
|
856
|
+
left: 50%;
|
|
857
|
+
max-width: min(560px, 48vw);
|
|
858
|
+
min-width: 0;
|
|
859
|
+
position: absolute;
|
|
860
|
+
transform: translateX(-50%);
|
|
861
|
+
width: min(560px, 48vw);
|
|
852
862
|
}
|
|
853
863
|
|
|
854
864
|
.header-actions {
|
|
@@ -871,7 +881,7 @@ code {
|
|
|
871
881
|
|
|
872
882
|
.command-trigger {
|
|
873
883
|
justify-content: flex-start;
|
|
874
|
-
max-width:
|
|
884
|
+
max-width: none;
|
|
875
885
|
}
|
|
876
886
|
|
|
877
887
|
.command-trigger strong {
|
|
@@ -1055,12 +1065,11 @@ code {
|
|
|
1055
1065
|
}
|
|
1056
1066
|
|
|
1057
1067
|
.command-palette-backdrop {
|
|
1058
|
-
align-items:
|
|
1068
|
+
align-items: center;
|
|
1059
1069
|
background: rgb(0 0 0 / 28%);
|
|
1060
1070
|
display: flex;
|
|
1061
1071
|
inset: 0;
|
|
1062
1072
|
justify-content: center;
|
|
1063
|
-
padding-top: 12vh;
|
|
1064
1073
|
position: fixed;
|
|
1065
1074
|
z-index: 20;
|
|
1066
1075
|
}
|
|
@@ -1083,6 +1092,7 @@ code {
|
|
|
1083
1092
|
max-width: min(680px, calc(100vw - 32px));
|
|
1084
1093
|
padding: 14px;
|
|
1085
1094
|
position: relative;
|
|
1095
|
+
transform: translateY(-2vh);
|
|
1086
1096
|
width: 100%;
|
|
1087
1097
|
}
|
|
1088
1098
|
|
|
@@ -2102,12 +2112,20 @@ code {
|
|
|
2102
2112
|
}
|
|
2103
2113
|
|
|
2104
2114
|
.workspace-header,
|
|
2115
|
+
.header-center-controls,
|
|
2105
2116
|
.header-actions,
|
|
2106
2117
|
.split-heading {
|
|
2107
2118
|
align-items: stretch;
|
|
2108
2119
|
flex-direction: column;
|
|
2109
2120
|
}
|
|
2110
2121
|
|
|
2122
|
+
.header-center-controls {
|
|
2123
|
+
max-width: none;
|
|
2124
|
+
position: static;
|
|
2125
|
+
transform: none;
|
|
2126
|
+
width: 100%;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2111
2129
|
.workspace-search {
|
|
2112
2130
|
min-width: 0;
|
|
2113
2131
|
width: 100%;
|
package/setup.sh
CHANGED
|
@@ -12,7 +12,7 @@ shopt -s inherit_errexit 2>/dev/null || true
|
|
|
12
12
|
# AI Assistant Server Access Framework Setup Script
|
|
13
13
|
# Helps developers set up the framework for their infrastructure
|
|
14
14
|
#
|
|
15
|
-
# Version: 3.
|
|
15
|
+
# Version: 3.26.0
|
|
16
16
|
#
|
|
17
17
|
# Quick Install:
|
|
18
18
|
# npm install -g aidevops && aidevops update (recommended)
|