aidevops 3.29.40 → 3.29.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 +1 -1
- package/VERSION +1 -1
- package/aidevops.sh +1 -1
- package/package.json +1 -1
- package/packages/gui-api/src/app.ts +119 -4
- package/packages/gui-api/src/status-pulse-workers-attention.ts +24 -0
- package/packages/gui-api/src/status-pulse-workers-classifiers.ts +99 -0
- package/packages/gui-api/src/status-pulse-workers-values.ts +59 -0
- package/packages/gui-api/src/status-pulse-workers.ts +92 -133
- package/packages/gui-shared/src/contracts.ts +76 -3
- package/packages/gui-shared/src/fixtures.ts +11 -0
- package/packages/gui-shared/src/tambo.ts +42 -12
- package/packages/gui-web/src/AppActionTerminal.tsx +12 -5
- package/packages/gui-web/src/GenUiCards.tsx +5 -4
- package/packages/gui-web/src/PulseWorkersSurface.tsx +109 -19
- package/packages/gui-web/src/app-model.ts +19 -1
- package/packages/gui-web/src/status-client.ts +2 -0
- package/packages/gui-web/src/styles.css +36 -11
- package/setup.sh +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type CSSProperties, type ReactElement, useState } from "react";
|
|
2
|
-
import type { GuiPulseWorkerActivityEvent, GuiPulseWorkerChartSeries, GuiStatusData } from "../../gui-shared/src";
|
|
1
|
+
import { type CSSProperties, type Dispatch, type ReactElement, type SetStateAction, useEffect, useState } from "react";
|
|
2
|
+
import type { GuiPulseWorkerActionJobSummary, GuiPulseWorkerActionSummary, GuiPulseWorkerActivityEvent, GuiPulseWorkerChartSeries, GuiResponseEnvelope, GuiStatusData } from "../../gui-shared/src";
|
|
3
|
+
import { AppActionTerminal } from "./AppActionTerminal";
|
|
3
4
|
import { text } from "./app-model";
|
|
4
5
|
|
|
5
6
|
type PulseEvent = GuiPulseWorkerActivityEvent;
|
|
@@ -10,10 +11,32 @@ const quickFilters = ["Needs attention", "Stalled workers", "Failed terminal che
|
|
|
10
11
|
export function PulseWorkersSurface({ status }: { status: GuiStatusData }): ReactElement {
|
|
11
12
|
const pulse = status.pulse_workers;
|
|
12
13
|
const [selectedEventId, setSelectedEventId] = useState(pulse.events[0]?.id ?? "");
|
|
14
|
+
const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
|
|
15
|
+
const [jobs, setJobs] = useState<Record<string, GuiPulseWorkerActionJobSummary>>({});
|
|
16
|
+
const [dismissedJobIds, setDismissedJobIds] = useState<Set<string>>(() => new Set());
|
|
13
17
|
const selectedEvent = pulse.events.find((event) => event.id === selectedEventId) ?? pulse.events[0];
|
|
18
|
+
const selectedJob = selectedJobId === null ? null : jobs[selectedJobId] ?? null;
|
|
14
19
|
const filterGroups = buildFilterGroups(pulse);
|
|
15
20
|
const chartSeries = pulse.charts.slice(0, 4);
|
|
16
21
|
const sampleSize = pulse.kpis.reduce((total, kpi) => total + (kpi.sample_size ?? 0), 0);
|
|
22
|
+
const runningJobIdsKey = Object.values(jobs).filter((job) => job.status === "running").map((job) => job.id).sort().join("|");
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const runningJobIds = runningJobIdsKey.split("|").filter(Boolean);
|
|
26
|
+
if (runningJobIds.length === 0) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const refreshRunningJobs = () => {
|
|
31
|
+
for (const jobId of runningJobIds) {
|
|
32
|
+
void refreshPulseWorkerJob(jobId, setJobs);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
refreshRunningJobs();
|
|
36
|
+
const timer = window.setInterval(refreshRunningJobs, 1_500);
|
|
37
|
+
|
|
38
|
+
return () => window.clearInterval(timer);
|
|
39
|
+
}, [runningJobIdsKey]);
|
|
17
40
|
|
|
18
41
|
return (
|
|
19
42
|
<section className="pulse-workers-surface" aria-label={text.workers}>
|
|
@@ -49,12 +72,27 @@ export function PulseWorkersSurface({ status }: { status: GuiStatusData }): Reac
|
|
|
49
72
|
<p className="eyebrow">Exceptions first</p>
|
|
50
73
|
<h3>Needs attention</h3>
|
|
51
74
|
</div>
|
|
52
|
-
<span className="count-pill">{pulse.attention.length} findings ·
|
|
75
|
+
<span className="count-pill">{pulse.attention.length} findings · allowlisted actions only</span>
|
|
53
76
|
</div>
|
|
54
77
|
<ul>
|
|
55
78
|
{pulse.attention.map((item) => <li className={`pulse-attention-${item.severity}`} key={item.id}><strong>{item.title}</strong>: {item.detail}</li>)}
|
|
56
79
|
</ul>
|
|
57
|
-
<
|
|
80
|
+
<div className="pulse-insight-list" aria-label="Grouped systemic findings">
|
|
81
|
+
{pulse.insights.map((finding) => (
|
|
82
|
+
<article className={`pulse-insight-card pulse-attention-${finding.severity}`} key={finding.id}>
|
|
83
|
+
<p className="eyebrow">{humanize(finding.kind)} · {finding.confidence} confidence · n={finding.sample_size}</p>
|
|
84
|
+
<h4>{finding.title}</h4>
|
|
85
|
+
<p>{finding.detail}</p>
|
|
86
|
+
<dl className="pulse-detail-grid compact-detail-grid">
|
|
87
|
+
<div><dt>Likely cause</dt><dd>{finding.likely_cause}</dd></div>
|
|
88
|
+
<div><dt>Evidence</dt><dd>{finding.evidence_refs.join(" · ") || "metadata evidence pending"}</dd></div>
|
|
89
|
+
<div><dt>Comparison</dt><dd>{finding.period_label} · {finding.scope_label} · {finding.comparison_label}</dd></div>
|
|
90
|
+
<div><dt>Systemic fix</dt><dd>{finding.recommendation}</dd></div>
|
|
91
|
+
</dl>
|
|
92
|
+
</article>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
<button disabled={!actionById(pulse.actions, "create_systemic_fix")?.enabled} onClick={() => void runPulseWorkerAction(actionById(pulse.actions, "create_systemic_fix"), setJobs, setSelectedJobId, setDismissedJobIds)} title="Create a worker-ready systemic-fix task from selected evidence" type="button">Create systemic fix task</button>
|
|
58
96
|
</article>
|
|
59
97
|
|
|
60
98
|
<section className="pulse-chart-grid" aria-label="Pulse trend charts">
|
|
@@ -120,23 +158,66 @@ export function PulseWorkersSurface({ status }: { status: GuiStatusData }): Reac
|
|
|
120
158
|
</button>
|
|
121
159
|
))}
|
|
122
160
|
</div>
|
|
123
|
-
<p className="notice compact-notice">Mobile activity cards replace the dense table on small screens. Detail drawer becomes a full-screen sheet on small screens, and terminal panel becomes full-screen
|
|
161
|
+
<p className="notice compact-notice">Mobile activity cards replace the dense table on small screens. Detail drawer becomes a full-screen sheet on small screens, and terminal panel becomes a full-screen panel/sheet for command output.</p>
|
|
124
162
|
</section>
|
|
125
163
|
|
|
126
164
|
<section className="pulse-layout" aria-label="Drilldown and planned actions">
|
|
127
165
|
<PulseDrilldownPanel event={selectedEvent} />
|
|
128
|
-
<article className="planned-card pulse-actions-panel">
|
|
129
|
-
<p className="eyebrow">
|
|
130
|
-
<h3>
|
|
131
|
-
<
|
|
132
|
-
<
|
|
133
|
-
|
|
166
|
+
<article className="planned-card pulse-actions-panel" aria-label="Pulse and Workers action buttons">
|
|
167
|
+
<p className="eyebrow">Allowlisted controls</p>
|
|
168
|
+
<h3>Safe actions with terminal output</h3>
|
|
169
|
+
<p className="notice compact-notice">Read-only diagnostics are separated from write actions. Destructive controls such as stopping workers, closing PRs, cleanup, and rebase/update branch remain deferred.</p>
|
|
170
|
+
<div className="pulse-action-list">
|
|
171
|
+
{pulse.actions.map((action) => (
|
|
172
|
+
<button disabled={!action.enabled} key={action.id} onClick={() => void runPulseWorkerAction(action, setJobs, setSelectedJobId, setDismissedJobIds)} title={action.scope_copy} type="button">
|
|
173
|
+
<strong>{action.label}</strong>
|
|
174
|
+
<span>{action.classification} · confirmation {action.confirmation} · {action.target_ref}</span>
|
|
175
|
+
</button>
|
|
176
|
+
))}
|
|
177
|
+
</div>
|
|
178
|
+
<dl className="pulse-detail-grid pulse-action-confirmations">
|
|
179
|
+
{pulse.actions.map((action) => <div key={`${action.id}-copy`}><dt>{action.label}</dt><dd>{action.scope_copy} {action.expected_effect} Command preview: {action.command_preview}. Audit/source ref: {action.audit_ref}.</dd></div>)}
|
|
180
|
+
</dl>
|
|
181
|
+
<p className="notice compact-notice">Create systemic fix task generates worker-ready context from the selected event: files/patterns when known, evidence refs, verification commands, and protected-data notes before wrapper dispatch.</p>
|
|
182
|
+
{selectedJob === null || dismissedJobIds.has(selectedJob.id) ? null : <AppActionTerminal job={selectedJob} label={`Pulse & Workers ${selectedJob.action}`} onDismiss={(jobId) => setDismissedJobIds((current) => new Set([...current, jobId]))} />}
|
|
134
183
|
</article>
|
|
135
184
|
</section>
|
|
136
185
|
</section>
|
|
137
186
|
);
|
|
138
187
|
}
|
|
139
188
|
|
|
189
|
+
async function refreshPulseWorkerJob(jobId: string, setJobs: Dispatch<SetStateAction<Record<string, GuiPulseWorkerActionJobSummary>>>): Promise<void> {
|
|
190
|
+
const response = await fetch(`/api/pulse-workers/jobs/${encodeURIComponent(jobId)}`);
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const envelope = await response.json() as GuiResponseEnvelope<GuiPulseWorkerActionJobSummary>;
|
|
195
|
+
setJobs((current) => ({ ...current, [envelope.data.id]: envelope.data }));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function runPulseWorkerAction(action: GuiPulseWorkerActionSummary | undefined, setJobs: Dispatch<SetStateAction<Record<string, GuiPulseWorkerActionJobSummary>>>, setSelectedJobId: Dispatch<SetStateAction<string | null>>, setDismissedJobIds: Dispatch<SetStateAction<Set<string>>>): Promise<void> {
|
|
199
|
+
if (action === undefined || !action.enabled) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (action.confirmation === "required" && typeof window !== "undefined" && !window.confirm(`${action.label}\n\nScope: ${action.scope_copy}\nExpected effect: ${action.expected_effect}\nCommand: ${action.command_preview}`)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const response = await fetch(`/api/pulse-workers/actions/${encodeURIComponent(action.id)}`, { method: "POST" });
|
|
207
|
+
const envelope = await response.json() as GuiResponseEnvelope<GuiPulseWorkerActionJobSummary>;
|
|
208
|
+
setDismissedJobIds((current) => {
|
|
209
|
+
const next = new Set(current);
|
|
210
|
+
next.delete(envelope.data.id);
|
|
211
|
+
return next;
|
|
212
|
+
});
|
|
213
|
+
setJobs((current) => ({ ...current, [envelope.data.id]: envelope.data }));
|
|
214
|
+
setSelectedJobId(envelope.data.id);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function actionById(actions: GuiPulseWorkerActionSummary[], id: GuiPulseWorkerActionSummary["id"]): GuiPulseWorkerActionSummary | undefined {
|
|
218
|
+
return actions.find((action) => action.id === id);
|
|
219
|
+
}
|
|
220
|
+
|
|
140
221
|
function PulseChartPanel({ chart }: { chart: GuiPulseWorkerChartSeries }): ReactElement {
|
|
141
222
|
const maxValue = Math.max(...chart.points.map((point) => point.value), 1);
|
|
142
223
|
|
|
@@ -170,19 +251,24 @@ function PulseDrilldownPanel({ event }: { event: PulseEvent | undefined }): Reac
|
|
|
170
251
|
<dl className="pulse-detail-grid">
|
|
171
252
|
<div><dt>What</dt><dd>{event.summary}</dd></div>
|
|
172
253
|
<div><dt>When</dt><dd>{event.occurred_at} · {durationLabel(event.duration_ms)}</dd></div>
|
|
173
|
-
<div><dt>Why</dt><dd>{event.drilldown_sections
|
|
254
|
+
<div><dt>Why</dt><dd>{event.drilldown_sections?.find((section) => section.label.toLowerCase().includes("failure"))?.body ?? event.drilldown_sections?.[0]?.body ?? "No failure analysis recorded."}</dd></div>
|
|
174
255
|
<div><dt>How</dt><dd>{[event.type, event.status, event.outcome].map((value) => humanize(value)).join(" · ")}</dd></div>
|
|
175
256
|
<div><dt>Who</dt><dd>{event.actor_ref ?? "actor pending"} · {humanize(event.issue_origin, "-")} · {event.author_association}</dd></div>
|
|
176
257
|
<div><dt>Issue / PR / session refs</dt><dd>{[event.repo_ref, event.issue_ref, event.pull_request_ref, event.worker_session_ref, event.command_job_ref, event.pulse_run_ref].filter(Boolean).join(" · ")}</dd></div>
|
|
177
258
|
<div><dt>Usage and cost</dt><dd>{usageSummary(event)}</dd></div>
|
|
178
|
-
<div><dt>Resources</dt><dd>{event.resources
|
|
179
|
-
<div><dt>Suggested systemic fix</dt><dd>{event.drilldown_sections
|
|
259
|
+
<div><dt>Resources</dt><dd>{event.resources?.map((resource) => `${resource.label}: ${resource.available_label} (${resource.pressure})`).join(" · ") || "Resource metadata pending"}</dd></div>
|
|
260
|
+
<div><dt>Suggested systemic fix</dt><dd>{event.drilldown_sections?.find((section) => section.label.toLowerCase().includes("fix"))?.body ?? "Create a worker-ready follow-up when repeated blindspots appear."}</dd></div>
|
|
261
|
+
<div><dt>Related systemic findings</dt><dd>{relatedFindings(event).join(" · ") || "No grouped finding attached to this event."}</dd></div>
|
|
180
262
|
<div><dt>Planned actions</dt><dd>Open terminal output, redispatch worker, save systemic fix, and attach timeline evidence remain disabled until Phase 5 action routes land.</dd></div>
|
|
181
263
|
</dl>
|
|
182
264
|
</article>
|
|
183
265
|
);
|
|
184
266
|
}
|
|
185
267
|
|
|
268
|
+
function relatedFindings(event: PulseEvent): string[] {
|
|
269
|
+
return event.drilldown_sections?.filter((section) => section.label.toLowerCase().includes("systemic") || section.label.toLowerCase().includes("verification")).map((section) => `${section.label}: ${section.body}`) ?? [];
|
|
270
|
+
}
|
|
271
|
+
|
|
186
272
|
function buildFilterGroups(pulse: GuiStatusData["pulse_workers"]): PulseFilterGroup[] {
|
|
187
273
|
return [
|
|
188
274
|
{ label: "Repo", values: optionLabels(pulse.filters.repos) },
|
|
@@ -201,15 +287,15 @@ function buildFilterGroups(pulse: GuiStatusData["pulse_workers"]): PulseFilterGr
|
|
|
201
287
|
];
|
|
202
288
|
}
|
|
203
289
|
|
|
204
|
-
function optionLabels(options: Array<{ label: string; count: number }>): string[] {
|
|
205
|
-
return options
|
|
290
|
+
function optionLabels(options: Array<{ label: string; count: number }> | null | undefined): string[] {
|
|
291
|
+
return options?.map((option) => `${option.label} (${option.count})`) ?? [];
|
|
206
292
|
}
|
|
207
293
|
|
|
208
294
|
function unique(values: string[]): string[] {
|
|
209
295
|
return Array.from(new Set(values.filter(Boolean)));
|
|
210
296
|
}
|
|
211
297
|
|
|
212
|
-
function filterSummary(options: Array<{ label: string; count: number }>): string {
|
|
298
|
+
function filterSummary(options: Array<{ label: string; count: number }> | null | undefined): string {
|
|
213
299
|
return optionLabels(options).slice(0, 3).join(" · ") || "all";
|
|
214
300
|
}
|
|
215
301
|
|
|
@@ -220,7 +306,7 @@ function periodChoices(selected: string): string {
|
|
|
220
306
|
function resourceSummary(row: PulseEvent): string {
|
|
221
307
|
const model = row.usage?.model_ref?.replace("model:", "");
|
|
222
308
|
const providerLabel = row.usage?.provider === "openai" ? "OpenAI" : row.usage?.provider === "anthropic" ? "Anthropic" : row.usage?.provider;
|
|
223
|
-
const provider = row.usage === null ? row.resources[0]?.available_label ?? "metadata only" : `${providerLabel ?? "Provider"} · ${model ?? "model metadata pending"}`;
|
|
309
|
+
const provider = row.usage === null ? row.resources?.[0]?.available_label ?? "metadata only" : `${providerLabel ?? "Provider"} · ${model ?? "model metadata pending"}`;
|
|
224
310
|
const tokens = row.usage === null ? "" : ` · ${row.usage.total_tokens.toLocaleString()} tokens`;
|
|
225
311
|
const cost = row.usage?.estimated_cost_ref === null || row.usage?.estimated_cost_ref === undefined ? "" : ` · ${row.usage.estimated_cost_ref}`;
|
|
226
312
|
|
|
@@ -257,6 +343,10 @@ function durationLabel(durationMs: number | null): string {
|
|
|
257
343
|
return `${Math.round(durationMs / 60000)}m`;
|
|
258
344
|
}
|
|
259
345
|
|
|
260
|
-
function humanize(value: string, replacement = " "): string {
|
|
346
|
+
function humanize(value: string | null | undefined, replacement = " "): string {
|
|
347
|
+
if (!value) {
|
|
348
|
+
return "";
|
|
349
|
+
}
|
|
350
|
+
|
|
261
351
|
return value.replaceAll("_", replacement);
|
|
262
352
|
}
|
|
@@ -148,6 +148,13 @@ export interface InventorySurfaceConfig {
|
|
|
148
148
|
title: string;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
export interface ChatPrimitiveStackDecision {
|
|
152
|
+
adopt: string[];
|
|
153
|
+
defer: string[];
|
|
154
|
+
foundation: string;
|
|
155
|
+
owner: "local_vite_shell";
|
|
156
|
+
}
|
|
157
|
+
|
|
151
158
|
export type SurfaceRecordCounts = Partial<Record<SurfaceId, number>>;
|
|
152
159
|
|
|
153
160
|
export const text = {
|
|
@@ -278,6 +285,8 @@ export const text = {
|
|
|
278
285
|
workersIntro: "Pulse & Workers brings worker sessions, event streams, outcomes, resources, and systemic fixes into one observability console so attention goes to exceptions, bottlenecks, and reusable improvements instead of raw noise.",
|
|
279
286
|
theme: "Theme",
|
|
280
287
|
appearance: "Appearance",
|
|
288
|
+
approvalExecutionDeferred: "Approval execution is deferred until audited approval tooling exists.",
|
|
289
|
+
devOpsCard: "DevOps card",
|
|
281
290
|
hue: "Hue",
|
|
282
291
|
reset: "Reset",
|
|
283
292
|
showBorders: "Show borders",
|
|
@@ -286,6 +295,8 @@ export const text = {
|
|
|
286
295
|
fontSize: "Font size",
|
|
287
296
|
truncated: "Preview truncated for safety.",
|
|
288
297
|
update: "Update",
|
|
298
|
+
tamboGenUiReadOnly: "Tambo GenUI · read-only",
|
|
299
|
+
unsupportedDevOpsCard: "Unsupported DevOps card",
|
|
289
300
|
vault: "Vault",
|
|
290
301
|
vaultAudit: "Audit Logs",
|
|
291
302
|
vaultBackups: "Backups & Recovery",
|
|
@@ -315,6 +326,13 @@ export const DEFAULT_CONTRAST: ContrastPreference = "low";
|
|
|
315
326
|
export const DEFAULT_FONT: FontPreference = "Inter";
|
|
316
327
|
export const DEFAULT_FONT_SIZE: FontSizePreference = "xs";
|
|
317
328
|
|
|
329
|
+
export const chatPrimitiveStackDecision: ChatPrimitiveStackDecision = {
|
|
330
|
+
owner: "local_vite_shell",
|
|
331
|
+
foundation: "Turbostarter AI patterns supply persistence, AI SDK transport, model selection, attachments, tool/reasoning panels, sharing, and session actions behind audited routes.",
|
|
332
|
+
adopt: ["MessageScroller", "Message", "Bubble", "Attachment", "Marker"],
|
|
333
|
+
defer: ["Tambo ordinary chat persistence", "Tambo channel transport", "React Joyride as chat infrastructure", "Next.js route ownership"],
|
|
334
|
+
};
|
|
335
|
+
|
|
318
336
|
export const contrastOptions: ContrastOption[] = [
|
|
319
337
|
{ value: "low", label: "Low" },
|
|
320
338
|
{ value: "medium", label: "Medium" },
|
|
@@ -368,7 +386,7 @@ export const navGroups: SurfaceNavGroup[] = [
|
|
|
368
386
|
mode: "devops",
|
|
369
387
|
items: [
|
|
370
388
|
plannedNavItem("aiSessions", text.aiSessions, "AI chat and OpenCode sessions", "terminal"),
|
|
371
|
-
|
|
389
|
+
{ id: "workers", label: text.workers, description: "Pulse, worker sessions, outcomes, and resources", icon: "activity" },
|
|
372
390
|
{ id: "git", label: text.localRepos, description: "~/Git explorer", icon: "folder" },
|
|
373
391
|
plannedNavItem("repos", text.repos, "Unified local and remote repo context", "git"),
|
|
374
392
|
{ id: "projects", label: text.projects, description: "repos.json summary", icon: "git" },
|
|
@@ -104,9 +104,11 @@ function normalizeStatusEnvelope(envelope: GuiResponseEnvelope<Partial<GuiStatus
|
|
|
104
104
|
...data.pulse_workers,
|
|
105
105
|
kpis: data.pulse_workers?.kpis ?? statusFixture.pulse_workers.kpis,
|
|
106
106
|
attention: data.pulse_workers?.attention ?? statusFixture.pulse_workers.attention,
|
|
107
|
+
insights: data.pulse_workers?.insights ?? statusFixture.pulse_workers.insights,
|
|
107
108
|
filters: { ...statusFixture.pulse_workers.filters, ...data.pulse_workers?.filters },
|
|
108
109
|
charts: data.pulse_workers?.charts ?? statusFixture.pulse_workers.charts,
|
|
109
110
|
events: data.pulse_workers?.events ?? statusFixture.pulse_workers.events,
|
|
111
|
+
actions: data.pulse_workers?.actions ?? statusFixture.pulse_workers.actions,
|
|
110
112
|
},
|
|
111
113
|
capabilities: data.capabilities ?? statusFixture.capabilities,
|
|
112
114
|
secrets: data.secrets ?? statusFixture.secrets,
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
--bg-primary: #0a0a0a;
|
|
51
51
|
--bg-secondary: #111111;
|
|
52
52
|
--bg-tertiary: #1a1a1a;
|
|
53
|
-
--border: color-mix(in srgb, rgb(255 255 255 /
|
|
54
|
-
--border-hover: color-mix(in srgb, rgb(255 255 255 /
|
|
53
|
+
--border: color-mix(in srgb, rgb(255 255 255 / 15%) 78%, rgb(255 255 255 / 32%));
|
|
54
|
+
--border-hover: color-mix(in srgb, rgb(255 255 255 / 28%) 76%, rgb(255 255 255 / 44%));
|
|
55
55
|
--code-bg: hsl(var(--accent-hue) 18% 6%);
|
|
56
|
-
--glass-panel-shadow: inset 0 1px 0 rgb(255 255 255 /
|
|
56
|
+
--glass-panel-shadow: inset 0 1px 0 rgb(255 255 255 / 13%), inset 0 -1px 0 rgb(0 0 0 / 28%), var(--shadow-soft);
|
|
57
57
|
--notification-error-bg: rgb(255 123 114 / 13%);
|
|
58
58
|
--notification-error-fg: #ff7b72;
|
|
59
59
|
--notification-info-bg: rgb(88 166 255 / 13%);
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
--notification-success-fg: #56d364;
|
|
63
63
|
--notification-warning-bg: rgb(210 153 34 / 15%);
|
|
64
64
|
--notification-warning-fg: #d29922;
|
|
65
|
-
--shadow-soft: 0
|
|
65
|
+
--shadow-soft: 0 26px 84px rgb(0 0 0 / 46%);
|
|
66
66
|
--surface-elevated: #151515;
|
|
67
67
|
--surface-muted: #202020;
|
|
68
68
|
--success-bg: hsl(var(--accent-hue) 74% 66% / 12%);
|
|
@@ -88,8 +88,10 @@
|
|
|
88
88
|
--bg-primary: #050505;
|
|
89
89
|
--bg-secondary: #0b0b0b;
|
|
90
90
|
--bg-tertiary: #141414;
|
|
91
|
-
--border: color-mix(in srgb, rgb(255 255 255 /
|
|
92
|
-
--border-hover: color-mix(in srgb, rgb(255 255 255 /
|
|
91
|
+
--border: color-mix(in srgb, rgb(255 255 255 / 24%) 80%, rgb(255 255 255 / 44%));
|
|
92
|
+
--border-hover: color-mix(in srgb, rgb(255 255 255 / 38%) 82%, rgb(255 255 255 / 52%));
|
|
93
|
+
--glass-panel-shadow: inset 0 1px 0 rgb(255 255 255 / 14%), inset 0 -1px 0 rgb(0 0 0 / 30%), var(--shadow-soft);
|
|
94
|
+
--shadow-soft: 0 28px 88px rgb(0 0 0 / 52%);
|
|
93
95
|
--surface-elevated: #101010;
|
|
94
96
|
--surface-muted: #181818;
|
|
95
97
|
--text-muted: rgb(255 255 255 / 58%);
|
|
@@ -205,13 +207,36 @@ body {
|
|
|
205
207
|
background:
|
|
206
208
|
radial-gradient(ellipse at top left, color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 36%),
|
|
207
209
|
var(--bg-secondary);
|
|
210
|
+
background-attachment: fixed;
|
|
211
|
+
background-repeat: no-repeat;
|
|
208
212
|
color: var(--text-primary);
|
|
209
213
|
font-family: var(--font-family-app);
|
|
210
214
|
line-height: 1.6;
|
|
211
215
|
margin: 0;
|
|
216
|
+
min-height: 100vh;
|
|
212
217
|
font-size: var(--font-size-app);
|
|
213
218
|
}
|
|
214
219
|
|
|
220
|
+
:root[data-theme="dark"] body {
|
|
221
|
+
background:
|
|
222
|
+
radial-gradient(ellipse 78% 64% at top left,
|
|
223
|
+
hsl(var(--accent-hue) 96% 22% / 62%) 0%,
|
|
224
|
+
hsl(var(--accent-hue) 92% 18% / 34%) 34%,
|
|
225
|
+
transparent 58%),
|
|
226
|
+
radial-gradient(ellipse 72% 62% at bottom right,
|
|
227
|
+
hsl(var(--accent-hue) 90% 16% / 38%) 0%,
|
|
228
|
+
hsl(var(--accent-hue) 84% 12% / 20%) 38%,
|
|
229
|
+
transparent 66%),
|
|
230
|
+
radial-gradient(ellipse 66% 54% at bottom left,
|
|
231
|
+
hsl(var(--accent-hue) 90% 15% / 30%) 0%,
|
|
232
|
+
hsl(var(--accent-hue) 80% 11% / 16%) 38%,
|
|
233
|
+
transparent 68%),
|
|
234
|
+
linear-gradient(135deg,
|
|
235
|
+
color-mix(in srgb, hsl(var(--accent-hue) 92% 16%) 44%, var(--bg-secondary)) 0%,
|
|
236
|
+
color-mix(in srgb, hsl(var(--accent-hue) 80% 12%) 14%, var(--bg-secondary)) 48%,
|
|
237
|
+
color-mix(in srgb, var(--accent) 6%, var(--bg-primary)) 100%);
|
|
238
|
+
}
|
|
239
|
+
|
|
215
240
|
button,
|
|
216
241
|
input,
|
|
217
242
|
select {
|
|
@@ -1721,7 +1746,7 @@ code {
|
|
|
1721
1746
|
|
|
1722
1747
|
.hero-panel {
|
|
1723
1748
|
background:
|
|
1724
|
-
linear-gradient(135deg, color-mix(in srgb, var(--accent)
|
|
1749
|
+
linear-gradient(135deg, color-mix(in srgb, var(--accent) 11%, transparent), transparent 58%),
|
|
1725
1750
|
var(--surface-elevated);
|
|
1726
1751
|
padding: clamp(24px, 5vw, 42px);
|
|
1727
1752
|
}
|
|
@@ -1849,7 +1874,7 @@ code {
|
|
|
1849
1874
|
|
|
1850
1875
|
.notifications-hero {
|
|
1851
1876
|
background:
|
|
1852
|
-
linear-gradient(135deg, var(--notification-bg, color-mix(in srgb, var(--accent)
|
|
1877
|
+
linear-gradient(135deg, var(--notification-bg, color-mix(in srgb, var(--accent) 10%, transparent)), transparent 62%),
|
|
1853
1878
|
var(--surface-elevated);
|
|
1854
1879
|
}
|
|
1855
1880
|
|
|
@@ -2228,7 +2253,7 @@ code {
|
|
|
2228
2253
|
|
|
2229
2254
|
.pulse-hero {
|
|
2230
2255
|
background:
|
|
2231
|
-
linear-gradient(135deg, color-mix(in srgb, var(--accent)
|
|
2256
|
+
linear-gradient(135deg, color-mix(in srgb, var(--accent) 12%, transparent), transparent 64%),
|
|
2232
2257
|
var(--surface-elevated);
|
|
2233
2258
|
}
|
|
2234
2259
|
|
|
@@ -3149,8 +3174,8 @@ button.managed-toggle:focus-visible {
|
|
|
3149
3174
|
.recommended-app-card:hover,
|
|
3150
3175
|
.recommended-app-card:focus-within {
|
|
3151
3176
|
background:
|
|
3152
|
-
linear-gradient(135deg, color-mix(in srgb, var(--accent)
|
|
3153
|
-
color-mix(in srgb, var(--accent)
|
|
3177
|
+
linear-gradient(135deg, color-mix(in srgb, var(--accent) 11%, transparent), transparent 42%),
|
|
3178
|
+
color-mix(in srgb, var(--accent) 5%, var(--surface-muted));
|
|
3154
3179
|
border-color: var(--border-accent);
|
|
3155
3180
|
box-shadow: 0 18px 46px color-mix(in srgb, var(--accent) 10%, transparent);
|
|
3156
3181
|
transform: translateY(-1px);
|
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.29.
|
|
15
|
+
# Version: 3.29.42
|
|
16
16
|
#
|
|
17
17
|
# Quick Install:
|
|
18
18
|
# npm install -g aidevops && aidevops update (recommended)
|