aidevops 3.21.12 → 3.22.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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/VERSION +1 -1
  3. package/aidevops.sh +3 -2
  4. package/bun.lock +573 -0
  5. package/package.json +12 -1
  6. package/packages/gui-api/package.json +9 -0
  7. package/packages/gui-api/src/app.ts +49 -0
  8. package/packages/gui-api/src/file-adapter.ts +207 -0
  9. package/packages/gui-api/src/server.ts +12 -0
  10. package/packages/gui-api/src/status-adapter-utils.ts +108 -0
  11. package/packages/gui-api/src/status-adapter.ts +484 -0
  12. package/packages/gui-api/src/status-local-repos.ts +239 -0
  13. package/packages/gui-desktop/package.json +6 -0
  14. package/packages/gui-desktop/scripts/install-macos-app.sh +1020 -0
  15. package/packages/gui-shared/package.json +9 -0
  16. package/packages/gui-shared/src/contracts.ts +341 -0
  17. package/packages/gui-shared/src/fixtures.ts +250 -0
  18. package/packages/gui-shared/src/index.ts +2 -0
  19. package/packages/gui-web/index.html +24 -0
  20. package/packages/gui-web/package.json +25 -0
  21. package/packages/gui-web/src/App.tsx +269 -0
  22. package/packages/gui-web/src/AppNavigation.tsx +379 -0
  23. package/packages/gui-web/src/AppWorkspace.tsx +114 -0
  24. package/packages/gui-web/src/FileExplorerSurface.tsx +153 -0
  25. package/packages/gui-web/src/InventorySurfaces.tsx +117 -0
  26. package/packages/gui-web/src/PathActions.tsx +17 -0
  27. package/packages/gui-web/src/StatusSurfaces.tsx +396 -0
  28. package/packages/gui-web/src/app-model.ts +546 -0
  29. package/packages/gui-web/src/dashboard.ts +79 -0
  30. package/packages/gui-web/src/main.tsx +24 -0
  31. package/packages/gui-web/src/status-client.ts +93 -0
  32. package/packages/gui-web/src/styles.css +1540 -0
  33. package/packages/gui-web/vite.config.ts +14 -0
  34. package/setup.sh +78 -3
@@ -0,0 +1,546 @@
1
+ import type { GuiFileRootId, GuiStatusData } from "@aidevops/gui-shared";
2
+
3
+ export type ThemePreference = "system" | "light" | "dark";
4
+ export type SidebarMode = "devops" | "comms";
5
+ export type FontPreference =
6
+ | "IBM Plex Mono"
7
+ | "IBM Plex Sans"
8
+ | "IBM Plex Serif"
9
+ | "Inter"
10
+ | "Menlo"
11
+ | "Playpen Sans"
12
+ | "Poppins"
13
+ | "Source Sans"
14
+ | "Source Serif"
15
+ | "Tilt Neon"
16
+ | "Ubuntu Mono";
17
+ export type FontSizePreference = "xs" | "s" | "m" | "lg" | "xl";
18
+ export const surfaceIconNames = {
19
+ apps: true,
20
+ bookmark: true,
21
+ brand: true,
22
+ calendar: true,
23
+ chain: true,
24
+ clock: true,
25
+ device: true,
26
+ document: true,
27
+ download: true,
28
+ folder: true,
29
+ git: true,
30
+ globe: true,
31
+ grid: true,
32
+ hardDrive: true,
33
+ link: true,
34
+ list: true,
35
+ mail: true,
36
+ message: true,
37
+ note: true,
38
+ package: true,
39
+ server: true,
40
+ settings: true,
41
+ shield: true,
42
+ terminal: true,
43
+ users: true,
44
+ } as const;
45
+ export type SurfaceIconName = keyof typeof surfaceIconNames;
46
+ export const surfaceIds = [
47
+ "overview",
48
+ "agents",
49
+ "config",
50
+ "localSetup",
51
+ "git",
52
+ "routines",
53
+ "devices",
54
+ "vpnsProxies",
55
+ "apps",
56
+ "installation",
57
+ "registrars",
58
+ "hosts",
59
+ "servers",
60
+ "brands",
61
+ "domains",
62
+ "personas",
63
+ "websites",
64
+ "forums",
65
+ "socialMedia",
66
+ "marketplaces",
67
+ "projects",
68
+ "security",
69
+ "aiProviders",
70
+ "emailAccounts",
71
+ "messagingAccounts",
72
+ "tasks",
73
+ "contacts",
74
+ "events",
75
+ "notes",
76
+ "bookmarks",
77
+ "inbox",
78
+ "campaigns",
79
+ "cases",
80
+ "projectConfig",
81
+ "feedback",
82
+ "knowledge",
83
+ "maintenance",
84
+ "performance",
85
+ "reports",
86
+ ] as const;
87
+
88
+ export type SurfaceId = (typeof surfaceIds)[number];
89
+
90
+ export interface SurfaceNavItem {
91
+ badge?: string;
92
+ description: string;
93
+ icon: SurfaceIconName;
94
+ id: SurfaceId;
95
+ label: string;
96
+ }
97
+
98
+ export interface SurfaceNavGroup {
99
+ items: SurfaceNavItem[];
100
+ label: string;
101
+ mode: SidebarMode;
102
+ }
103
+
104
+ export interface FontOption {
105
+ fontFamily: string;
106
+ label: string;
107
+ value: FontPreference;
108
+ }
109
+
110
+ export interface FontSizeOption {
111
+ label: FontSizePreference;
112
+ value: FontSizePreference;
113
+ cssSize: string;
114
+ }
115
+
116
+ export interface InventoryColumn {
117
+ key: string;
118
+ label: string;
119
+ }
120
+
121
+ export interface InventorySurfaceConfig {
122
+ columns: InventoryColumn[];
123
+ initialRows: Record<string, string>[];
124
+ intro: string;
125
+ title: string;
126
+ }
127
+
128
+ export type SurfaceRecordCounts = Partial<Record<SurfaceId, number>>;
129
+
130
+ export const text = {
131
+ aidevops: "aidevops",
132
+ appShell: "App shell",
133
+ apps: "Apps",
134
+ appsIntro: "App and CLI inventory for tools installed or updated by aidevops. Version and homepage adapters are planned.",
135
+ appStatusPending: "adapter pending",
136
+ brands: "Brands",
137
+ brandsIntro: "Draft brand inventory for names and website references.",
138
+ codeView: "Code",
139
+ config: "Config",
140
+ configIntro: "Read-only file explorer for ~/.config/aidevops. Contents stay hidden until redaction rules land.",
141
+ copyPath: "Copy path",
142
+ dashboard: "Dashboard",
143
+ domains: "Domains",
144
+ domainsIntro: "Draft domain ownership inventory across providers.",
145
+ draftOnly: "Local browser draft only. Saving requires a write-action manifest, confirmation, and audit trail.",
146
+ fileBrowser: "File browser",
147
+ folderOpenBlocked: "Native folder opening needs a desktop bridge and an allowlisted action.",
148
+ git: "Git",
149
+ gitIntro: "Read-only file explorer for ~/Git workspaces and worktrees.",
150
+ hosts: "Hosts",
151
+ hostsIntro: "Recommended and user-owned hosting providers with setup notes later.",
152
+ addRow: "Add row",
153
+ channel: "Channel",
154
+ installation: "Installation",
155
+ installationIntro: "Optional aidevops setup/update components. Toggles are shown as planned controls until writes are enabled.",
156
+ install: "Install",
157
+ bookmarks: "Bookmarks",
158
+ calendars: "Calendars",
159
+ campaigns: "Campaigns",
160
+ cases: "Cases",
161
+ contacts: "Addressbooks",
162
+ development: "Development",
163
+ devops: "DevOps",
164
+ devices: "Devices",
165
+ devicesIntro: "Device inventory and pairing controls are planned for local and remote aidevops installations.",
166
+ emailAccounts: "Email Accounts",
167
+ events: "Events",
168
+ feedback: "Feedback",
169
+ forums: "Forums",
170
+ forumsIntro: "Forum profiles, communities, moderation notes, and owned discussion homes are planned.",
171
+ infrastructure: "Infrastructure",
172
+ identities: "Identities",
173
+ inbox: "Inbox",
174
+ knowledge: "Knowledge",
175
+ latest: "Latest",
176
+ localSetup: "Local Setup",
177
+ localSetupIntro: "Read-only file explorer for ~/.aidevops runtime folders, cache, memory, and deployed assets.",
178
+ localRepos: "Local Repos",
179
+ localReposSetupIntro: "Canonical local repo folders only. Linked worktrees are excluded; controls are read-only until audited write routes land.",
180
+ aiApps: "AI Apps",
181
+ aiAppsIntro: "Local AI app, CLI, config, and aidevops prompt targets. Versions are read-only and compare installed aidevops files with the current checkout.",
182
+ aiProviders: "AI Providers",
183
+ aiProvidersIntro: "OAuth pool account metadata by provider. Tokens stay hidden; management controls are shown as planned safe actions.",
184
+ madeForCreators: "Made for creators",
185
+ maintenance: "Maintenance",
186
+ management: "Management",
187
+ managementIntro: "Email accounts, messaging accounts, calendars, addressbooks, tasks, notebooks, and bookmarks will sync through account setup. macOS can use Internet Accounts where available; Linux and Windows need equivalent account providers.",
188
+ markdownFormatted: "Markdown formatted",
189
+ markdownView: "Markdown",
190
+ name: "Name",
191
+ messagingAccounts: "Messaging Accounts",
192
+ marketplaces: "Marketplaces",
193
+ marketplacesIntro: "Marketplace storefronts, seller accounts, product listings, and reputation profiles are planned.",
194
+ noProjectEntries: "No project registry entries are available yet.",
195
+ noPreview: "Select a file to preview safe content.",
196
+ notes: "Notebooks",
197
+ operations: "Operations",
198
+ parentDirectory: "..",
199
+ path: "Path",
200
+ personas: "Personas",
201
+ personasIntro: "Draft identity list for first and last names.",
202
+ planned: "planned",
203
+ plannedHomes: "Planned homes",
204
+ plannedNotice: "This surface is a local UI placeholder. Persistence and actions need explicit trust-boundary work.",
205
+ comms: "Comms",
206
+ performance: "Performance",
207
+ projectWorkIntro: "Document folders for inboxes, campaigns, cases, configuration, feedback, knowledge, maintenance, performance, and reports are planned.",
208
+ projectWork: "Documents",
209
+ projects: "Remote Repos",
210
+ readOnly: "Read-only",
211
+ registrars: "Registrars",
212
+ registrarsIntro: "Recommended and user-owned domain registrars.",
213
+ repoDescription: "AI-assisted development workflows, code quality, and deployment automation.",
214
+ reports: "Reports",
215
+ roadmapIntro: "Made for creators",
216
+ routines: "Routines",
217
+ routineDetail: "Routine schedule, next run, run history, script/LLM-backed type, and diagnostic homes are planned.",
218
+ searchPlaceholder: "Search setup, files, providers",
219
+ secrets: "Secrets",
220
+ security: "Secrets",
221
+ securityBoundary: "No write routes, no shell bridge, no hosted app control, no pairing, no secret values.",
222
+ servers: "Servers",
223
+ serversIntro: "Server inventory by provider, operating system, orchestrator, and installed apps.",
224
+ setup: "Setup",
225
+ setupTargets: "Installed aidevops targets",
226
+ setupTargetsIntro: "Files and settings paths that setup/update deploys into local runtimes. Missing or older targets need setup/update before release.",
227
+ sites: "Sites",
228
+ socialMedia: "Social Media",
229
+ socialMediaIntro: "Social profiles, pages, handles, audience notes, and channel ownership are planned.",
230
+ tasks: "Tasks",
231
+ theme: "Theme",
232
+ appearance: "Appearance",
233
+ hue: "Hue",
234
+ reset: "Reset",
235
+ showBorders: "Show borders",
236
+ showCounts: "Show counts",
237
+ font: "Font",
238
+ fontSize: "Font size",
239
+ truncated: "Preview truncated for safety.",
240
+ update: "Update",
241
+ website: "Website",
242
+ websites: "Websites",
243
+ websitesIntro: "Websites, landing pages, publishing homes, and ownership status are planned.",
244
+ workspaceLabel: "aidevops workspace",
245
+ vpnsProxies: "VPNs & Proxies",
246
+ vpnsProxiesIntro: "VPN and proxy inventory is planned for network-aware device management.",
247
+ navigationLabel: "aidevops navigation",
248
+ } as const;
249
+
250
+ export const DEFAULT_ACCENT_HUE = 123;
251
+ export const DEFAULT_FONT: FontPreference = "Menlo";
252
+ export const DEFAULT_FONT_SIZE: FontSizePreference = "m";
253
+
254
+ export const fontOptions: FontOption[] = [
255
+ { value: "IBM Plex Mono", label: "IBM Plex Mono", fontFamily: '"IBM Plex Mono", Menlo, Monaco, Consolas, monospace' },
256
+ { value: "IBM Plex Sans", label: "IBM Plex Sans", fontFamily: '"IBM Plex Sans", Inter, system-ui, sans-serif' },
257
+ { value: "IBM Plex Serif", label: "IBM Plex Serif", fontFamily: '"IBM Plex Serif", Georgia, serif' },
258
+ { value: "Inter", label: "Inter", fontFamily: 'Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' },
259
+ { value: "Menlo", label: "Menlo (default)", fontFamily: 'Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace' },
260
+ { value: "Playpen Sans", label: "Playpen Sans", fontFamily: '"Playpen Sans", "Comic Sans MS", Inter, system-ui, sans-serif' },
261
+ { value: "Poppins", label: "Poppins", fontFamily: 'Poppins, Inter, system-ui, sans-serif' },
262
+ { value: "Source Sans", label: "Source Sans", fontFamily: '"Source Sans 3", "Source Sans Pro", "Source Sans", Inter, system-ui, sans-serif' },
263
+ { value: "Source Serif", label: "Source Serif", fontFamily: '"Source Serif 4", "Source Serif Pro", "Source Serif", Georgia, serif' },
264
+ { value: "Tilt Neon", label: "Tilt Neon", fontFamily: '"Tilt Neon", Poppins, Inter, system-ui, sans-serif' },
265
+ { value: "Ubuntu Mono", label: "Ubuntu Mono", fontFamily: '"Ubuntu Mono", Menlo, Monaco, Consolas, monospace' },
266
+ ];
267
+
268
+ export const fontSizeOptions: FontSizeOption[] = [
269
+ { value: "xs", label: "xs", cssSize: "14px" },
270
+ { value: "s", label: "s", cssSize: "15px" },
271
+ { value: "m", label: "m", cssSize: "16px" },
272
+ { value: "lg", label: "lg", cssSize: "17px" },
273
+ { value: "xl", label: "xl", cssSize: "18px" },
274
+ ];
275
+
276
+ export const navGroups: SurfaceNavGroup[] = [
277
+ {
278
+ label: text.development,
279
+ mode: "devops",
280
+ items: [
281
+ { id: "git", label: text.localRepos, description: "~/Git explorer", icon: "folder" },
282
+ { id: "projects", label: text.projects, description: "repos.json summary", icon: "git" },
283
+ { id: "security", label: text.secrets, description: "Secret references and trust boundary", icon: "shield" },
284
+ { id: "aiProviders", label: text.aiProviders, description: "OAuth account pools", icon: "users" },
285
+ ],
286
+ },
287
+ {
288
+ label: text.operations,
289
+ mode: "devops",
290
+ items: [
291
+ { id: "agents", label: "Agents", description: "~/.aidevops/agents explorer", icon: "document" },
292
+ { id: "config", label: text.config, description: "~/.config/aidevops explorer", icon: "settings" },
293
+ { id: "localSetup", label: text.localSetup, description: "~/.aidevops explorer", icon: "terminal" },
294
+ { id: "routines", label: text.routines, description: "Scheduled workflows", icon: "clock", badge: text.planned },
295
+ ],
296
+ },
297
+ {
298
+ label: text.infrastructure,
299
+ mode: "devops",
300
+ items: [
301
+ { id: "devices", label: text.devices, description: "Installed aidevops machines", icon: "device", badge: text.planned },
302
+ { id: "vpnsProxies", label: text.vpnsProxies, description: "VPN and proxy inventory", icon: "shield", badge: text.planned },
303
+ { id: "apps", label: text.apps, description: "Installed tools and apps", icon: "package" },
304
+ { id: "installation", label: text.installation, description: "Optional setup toggles", icon: "download" },
305
+ { id: "registrars", label: text.registrars, description: "Domain registrars", icon: "chain" },
306
+ { id: "hosts", label: text.hosts, description: "Hosting providers", icon: "server" },
307
+ { id: "servers", label: text.servers, description: "Servers and orchestrators", icon: "hardDrive" },
308
+ ],
309
+ },
310
+ {
311
+ label: text.identities,
312
+ mode: "comms",
313
+ items: [
314
+ { id: "brands", label: text.brands, description: "Brands and websites", icon: "brand" },
315
+ { id: "domains", label: text.domains, description: "Owned domains", icon: "globe" },
316
+ { id: "personas", label: text.personas, description: "People and identities", icon: "users" },
317
+ ],
318
+ },
319
+ {
320
+ label: text.sites,
321
+ mode: "comms",
322
+ items: [
323
+ { id: "websites", label: text.websites, description: "Owned websites and landing pages", icon: "globe", badge: text.planned },
324
+ { id: "forums", label: text.forums, description: "Forums and communities", icon: "message", badge: text.planned },
325
+ { id: "socialMedia", label: text.socialMedia, description: "Social channels and handles", icon: "users", badge: text.planned },
326
+ { id: "marketplaces", label: text.marketplaces, description: "Storefronts and seller profiles", icon: "package", badge: text.planned },
327
+ ],
328
+ },
329
+ {
330
+ label: text.management,
331
+ mode: "comms",
332
+ items: [
333
+ { id: "emailAccounts", label: text.emailAccounts, description: "Mail account sync", icon: "mail", badge: text.planned },
334
+ { id: "messagingAccounts", label: text.messagingAccounts, description: "Messaging account sync", icon: "message", badge: text.planned },
335
+ { id: "events", label: text.calendars, description: "CalDAV calendar sync", icon: "calendar", badge: text.planned },
336
+ { id: "contacts", label: text.contacts, description: "CardDAV contact sync", icon: "users", badge: text.planned },
337
+ { id: "tasks", label: text.tasks, description: "CalDAV task sync", icon: "list", badge: text.planned },
338
+ { id: "notes", label: text.notes, description: "Synced notes", icon: "note", badge: text.planned },
339
+ { id: "bookmarks", label: text.bookmarks, description: "Saved links and references", icon: "bookmark", badge: text.planned },
340
+ ],
341
+ },
342
+ {
343
+ label: text.projectWork,
344
+ mode: "comms",
345
+ items: [
346
+ { id: "inbox", label: text.inbox, description: "Project intake", icon: "folder", badge: text.planned },
347
+ { id: "campaigns", label: text.campaigns, description: "Campaign folders", icon: "folder", badge: text.planned },
348
+ { id: "cases", label: text.cases, description: "Case folders", icon: "folder", badge: text.planned },
349
+ { id: "projectConfig", label: text.config, description: "Project configuration", icon: "folder", badge: text.planned },
350
+ { id: "feedback", label: text.feedback, description: "Feedback folders", icon: "folder", badge: text.planned },
351
+ { id: "knowledge", label: text.knowledge, description: "Knowledge folders", icon: "folder", badge: text.planned },
352
+ { id: "maintenance", label: text.maintenance, description: "Maintenance folders", icon: "folder", badge: text.planned },
353
+ { id: "performance", label: text.performance, description: "Performance folders", icon: "folder", badge: text.planned },
354
+ { id: "reports", label: text.reports, description: "Report folders", icon: "folder", badge: text.planned },
355
+ ],
356
+ },
357
+ ];
358
+
359
+ export const dashboardNavItem: SurfaceNavItem = {
360
+ id: "overview",
361
+ label: text.dashboard,
362
+ description: "Setup, status, and roadmap homes",
363
+ icon: "grid",
364
+ };
365
+
366
+ export const orderedNavItems: SurfaceNavItem[] = [
367
+ dashboardNavItem,
368
+ ...navGroups.flatMap((group) => group.items),
369
+ ];
370
+
371
+ export const fileRootBySurface: Partial<Record<SurfaceId, GuiFileRootId>> = {
372
+ agents: "agents",
373
+ config: "config",
374
+ localSetup: "localSetup",
375
+ git: "git",
376
+ };
377
+
378
+ export const appRows = [
379
+ { name: "aidevops", latest: text.appStatusPending, channel: "setup/update", website: "https://aidevops.sh" },
380
+ { name: "OpenCode", latest: text.appStatusPending, channel: "runtime", website: text.appStatusPending },
381
+ { name: "Bun", latest: text.appStatusPending, channel: "toolchain", website: text.appStatusPending },
382
+ { name: "GitHub CLI", latest: text.appStatusPending, channel: "git", website: text.appStatusPending },
383
+ { name: "ShellCheck", latest: text.appStatusPending, channel: "quality", website: text.appStatusPending },
384
+ { name: "Biome", latest: text.appStatusPending, channel: "quality", website: text.appStatusPending },
385
+ ];
386
+
387
+ export const installationRows = [
388
+ { name: "OpenCode runtime", install: true, update: true, scope: "core" },
389
+ { name: "GUI desktop launcher", install: true, update: true, scope: "local" },
390
+ { name: "Shell quality tools", install: true, update: true, scope: "quality" },
391
+ { name: "Cloudron helpers", install: false, update: false, scope: "optional" },
392
+ { name: "Calendar sync helpers", install: false, update: false, scope: "optional" },
393
+ ];
394
+
395
+ export const plannedHomes = [
396
+ { area: "Setup/status", home: "Dashboard + Local Setup", phase: "P6" },
397
+ { area: "Local and remote repos", home: "Local Repos + Remote Repos", phase: "P7" },
398
+ { area: "Infrastructure inventory", home: "Domains, Registrars, Hosts, Servers", phase: "P8" },
399
+ { area: "Provider catalog", home: "Registrars, Hosts, Apps", phase: "P9" },
400
+ { area: "Routines", home: "Routines", phase: "P10" },
401
+ { area: "Agents and knowledgebase", home: "Agents", phase: "P12" },
402
+ { area: "Cloudron and machine pairing", home: "Servers + Apps", phase: "P13/P14" },
403
+ { area: "OpenCode sessions", home: "Future Sessions surface", phase: "P15" },
404
+ { area: "Desktop wrapper", home: "Installation", phase: "P16" },
405
+ ];
406
+
407
+ export const inventorySurfaceConfigs: Partial<Record<SurfaceId, InventorySurfaceConfig>> = {
408
+ personas: {
409
+ title: text.personas,
410
+ intro: text.personasIntro,
411
+ columns: [
412
+ { key: "firstName", label: "First Name" },
413
+ { key: "lastName", label: "Last Name" },
414
+ ],
415
+ initialRows: [{ firstName: "", lastName: "" }],
416
+ },
417
+ brands: {
418
+ title: text.brands,
419
+ intro: text.brandsIntro,
420
+ columns: [
421
+ { key: "brandName", label: "Brand Name" },
422
+ { key: "website", label: "Website URL" },
423
+ ],
424
+ initialRows: [{ brandName: "", website: "" }],
425
+ },
426
+ domains: {
427
+ title: text.domains,
428
+ intro: text.domainsIntro,
429
+ columns: [
430
+ { key: "domain", label: "Domain" },
431
+ { key: "provider", label: "Provider" },
432
+ { key: "status", label: "Status" },
433
+ ],
434
+ initialRows: [{ domain: "", provider: "", status: "" }],
435
+ },
436
+ registrars: {
437
+ title: text.registrars,
438
+ intro: text.registrarsIntro,
439
+ columns: [
440
+ { key: "name", label: "Registrar" },
441
+ { key: "recommendation", label: "Recommendation" },
442
+ { key: "notes", label: "Notes" },
443
+ ],
444
+ initialRows: [
445
+ { name: "Porkbun", recommendation: "recommended", notes: "provider catalog" },
446
+ { name: "Cloudflare Registrar", recommendation: "recommended", notes: "provider catalog" },
447
+ ],
448
+ },
449
+ hosts: {
450
+ title: text.hosts,
451
+ intro: text.hostsIntro,
452
+ columns: [
453
+ { key: "name", label: "Host" },
454
+ { key: "category", label: "Category" },
455
+ { key: "notes", label: "Notes" },
456
+ ],
457
+ initialRows: [
458
+ { name: "Cloudron", category: "app platform", notes: "recommended" },
459
+ { name: "Coolify", category: "app platform", notes: "recommended" },
460
+ { name: "Ubicloud", category: "cloud", notes: "recommended" },
461
+ ],
462
+ },
463
+ servers: {
464
+ title: text.servers,
465
+ intro: text.serversIntro,
466
+ columns: [
467
+ { key: "provider", label: "Provider" },
468
+ { key: "server", label: "Server" },
469
+ { key: "os", label: "OS" },
470
+ { key: "orchestrator", label: "Orchestrator" },
471
+ { key: "apps", label: "Apps" },
472
+ ],
473
+ initialRows: [{ provider: "", server: "", os: "", orchestrator: "", apps: "" }],
474
+ },
475
+ };
476
+
477
+ export function getSystemTheme(): "light" | "dark" {
478
+ if (typeof window === "undefined") {
479
+ return "light";
480
+ }
481
+
482
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
483
+ }
484
+
485
+ export function fontFamilyForPreference(font: FontPreference): string {
486
+ return fontOptions.find((option) => option.value === font)?.fontFamily ?? fontOptions.find((option) => option.value === DEFAULT_FONT)?.fontFamily ?? "Menlo, monospace";
487
+ }
488
+
489
+ export function fontSizeForPreference(size: FontSizePreference): string {
490
+ return fontSizeOptions.find((option) => option.value === size)?.cssSize ?? fontSizeOptions.find((option) => option.value === DEFAULT_FONT_SIZE)?.cssSize ?? "16px";
491
+ }
492
+
493
+ export function isFontPreference(value: string | null): value is FontPreference {
494
+ return fontOptions.some((option) => option.value === value);
495
+ }
496
+
497
+ export function isFontSizePreference(value: string | null): value is FontSizePreference {
498
+ return fontSizeOptions.some((option) => option.value === value);
499
+ }
500
+
501
+ export function sidebarModeForSurface(id: SurfaceId): SidebarMode {
502
+ return navGroups.find((group) => group.items.some((item) => item.id === id))?.mode ?? "devops";
503
+ }
504
+
505
+ export function findSurface(id: SurfaceId): SurfaceNavItem {
506
+ return orderedNavItems.find((item) => item.id === id) ?? dashboardNavItem;
507
+ }
508
+
509
+ export function findSurfaceSectionLabel(id: SurfaceId): string {
510
+ if (id === dashboardNavItem.id) {
511
+ return text.aidevops;
512
+ }
513
+
514
+ for (const group of navGroups) {
515
+ for (const item of group.items) {
516
+ if (item.id === id) {
517
+ return group.label;
518
+ }
519
+ }
520
+ }
521
+
522
+ return text.aidevops;
523
+ }
524
+
525
+ export function surfaceRecordCounts(status: GuiStatusData): SurfaceRecordCounts {
526
+ const oauthAccounts = status.oauth_pool.providers.reduce((total, provider) => total + provider.total, 0);
527
+
528
+ return {
529
+ agents: status.capabilities.length,
530
+ aiProviders: oauthAccounts,
531
+ apps: appRows.length,
532
+ config: status.settings.key_count || status.settings.keys.length,
533
+ git: status.local_repos.total || status.local_repos.repos.length,
534
+ hosts: populatedInventoryRowCount("hosts"),
535
+ installation: installationRows.length,
536
+ localSetup: status.paths.length,
537
+ projects: status.repos.total || status.repos.repos.length,
538
+ registrars: populatedInventoryRowCount("registrars"),
539
+ security: status.secrets.length,
540
+ servers: populatedInventoryRowCount("servers"),
541
+ };
542
+ }
543
+
544
+ function populatedInventoryRowCount(surface: SurfaceId): number {
545
+ return inventorySurfaceConfigs[surface]?.initialRows.filter((row) => Object.values(row).some((value) => value.trim().length > 0)).length ?? 0;
546
+ }
@@ -0,0 +1,79 @@
1
+ import { assertNoSecretSentinels, type GuiResponseEnvelope, type GuiStatusData } from "../../gui-shared/src";
2
+
3
+ export function renderDashboardHtml(status: GuiResponseEnvelope<GuiStatusData>): string {
4
+ assertNoSecretSentinels(status);
5
+
6
+ const paths = status.data.paths
7
+ .map((path) => `<li><strong>${escapeHtml(path.label)}</strong>: ${escapeHtml(path.health)} <code>${escapeHtml(path.path_ref)}</code></li>`)
8
+ .join("");
9
+ const helpers = status.data.helper_availability
10
+ .map((helper) => `<li>${escapeHtml(helper.name)}: ${escapeHtml(helper.status)}</li>`)
11
+ .join("");
12
+ const secrets = status.data.secrets
13
+ .map((secret) => `<li>${escapeHtml(secret.name)}: ${escapeHtml(secret.status)}</li>`)
14
+ .join("");
15
+ const repos = status.data.repos.repos
16
+ .map((repo) => `<li>${escapeHtml(repo.name)}: ${escapeHtml(repo.platform)} ${escapeHtml(repo.local_path_status)}</li>`)
17
+ .join("");
18
+ const localRepos = status.data.local_repos.repos
19
+ .map((repo) => `<li>${escapeHtml(repo.name)}: ${escapeHtml(repo.aidevops_version)} ${escapeHtml(repo.default_branch)}</li>`)
20
+ .join("");
21
+ const aiProviders = status.data.oauth_pool.providers
22
+ .map((provider) => `<li>${escapeHtml(provider.provider)}: ${provider.available}/${provider.total} available</li>`)
23
+ .join("");
24
+ const setupTargets = status.data.setup_targets
25
+ .map((target) => `<li>${escapeHtml(target.label)}: ${escapeHtml(target.installed_version)} → ${escapeHtml(target.latest_version)} ${target.needs_update ? "update" : "current"}</li>`)
26
+ .join("");
27
+ const aiApps = status.data.ai_apps
28
+ .map((app) => `<li>${escapeHtml(app.name)}: ${escapeHtml(app.status)} ${escapeHtml(app.aidevops_version)} → ${escapeHtml(app.latest_version)}</li>`)
29
+ .join("");
30
+ const settings = status.data.settings.keys
31
+ .map((key) => `<li>${escapeHtml(key)}</li>`)
32
+ .join("");
33
+ const capabilities = status.data.capabilities
34
+ .map((capability) => `<li>${escapeHtml(capability.label)}: ${escapeHtml(capability.status)}</li>`)
35
+ .join("");
36
+ const navSections = [
37
+ { heading: "Development", items: ["Local Repos", "Remote Repos", "Secrets", "AI Providers"] },
38
+ { heading: "Operations", items: ["Dashboard", "Agents file explorer", "Config", "Local Setup", "Routines"] },
39
+ { heading: "Infrastructure", items: ["Devices", "VPNs & Proxies", "Apps", "Installation", "Registrars", "Hosts", "Servers"] },
40
+ { heading: "Identities", items: ["Brands", "Domains", "Personas"] },
41
+ { heading: "Sites", items: ["Websites", "Forums", "Social Media", "Marketplaces"] },
42
+ { heading: "Management", items: ["Email Accounts", "Messaging Accounts", "Calendars", "Addressbooks", "Tasks", "Notebooks", "Bookmarks"] },
43
+ { heading: "Documents", items: ["Inbox", "Campaigns", "Cases", "Config", "Feedback", "Knowledge", "Maintenance", "Performance", "Reports"] },
44
+ ];
45
+ const renderedNavSections = navSections
46
+ .map(({ heading, items }) => `<h2>${escapeHtml(heading)}</h2><ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`)
47
+ .join("");
48
+
49
+ return [
50
+ `<section aria-label="aidevops status">`,
51
+ `<h1>aidevops app interface</h1>`,
52
+ `<p>Made for creators.</p>`,
53
+ `<p>AI-assisted development workflows, code quality, and deployment automation.</p>`,
54
+ `<p>Theme follows system preferences with light and dark overrides. Sidebar modes: DevOps and Comms. Appearance controls can be hidden or shown, and include editable Hue, icon Reset, Show borders toggle, Show counts toggle, Font size choices xs, s, m, lg, xl, and Font options: IBM Plex Mono, IBM Plex Sans, IBM Plex Serif, Inter, Menlo (default), Playpen Sans, Poppins, Source Sans, Source Serif, Tilt Neon, Ubuntu Mono. A desktop status bar shows local readiness, repo totals, secret reference count, provider accounts, and update state.</p>`,
55
+ `<p>Secret references stay read-only and redacted.</p>`,
56
+ `<p>${escapeHtml(status.data.update.message)}</p>`,
57
+ renderedNavSections,
58
+ `<h2>Path health</h2><ul>${paths}</ul>`,
59
+ `<h2>Installed aidevops targets</h2><ul>${setupTargets}</ul>`,
60
+ `<h2>AI Apps</h2><ul>${aiApps}</ul>`,
61
+ `<h2>Local Repo Setup</h2><ul>${localRepos}</ul>`,
62
+ `<h2>Remote Repos</h2><ul>${repos}</ul>`,
63
+ `<h2>AI Provider Pools</h2><ul>${aiProviders}</ul>`,
64
+ `<h2>Settings</h2><ul>${settings}</ul>`,
65
+ `<h2>Helper availability</h2><ul>${helpers}</ul>`,
66
+ `<h2>Secrets</h2><ul>${secrets}</ul>`,
67
+ `<h2>Capabilities</h2><ul>${capabilities}</ul>`,
68
+ `</section>`,
69
+ ].join("");
70
+ }
71
+
72
+ function escapeHtml(value: string): string {
73
+ return value
74
+ .replaceAll("&", "&amp;")
75
+ .replaceAll("<", "&lt;")
76
+ .replaceAll(">", "&gt;")
77
+ .replaceAll('"', "&quot;")
78
+ .replaceAll("'", "&#39;");
79
+ }
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { App } from "./App";
4
+ import "@fontsource/ibm-plex-mono";
5
+ import "@fontsource/ibm-plex-sans";
6
+ import "@fontsource/ibm-plex-serif";
7
+ import "@fontsource/inter";
8
+ import "@fontsource/playpen-sans";
9
+ import "@fontsource/poppins";
10
+ import "@fontsource/source-sans-3";
11
+ import "@fontsource/source-serif-4";
12
+ import "@fontsource/tilt-neon";
13
+ import "@fontsource/ubuntu-mono";
14
+ import "./styles.css";
15
+
16
+ const rootElement = document.getElementById("root");
17
+
18
+ if (rootElement !== null) {
19
+ createRoot(rootElement).render(
20
+ <React.StrictMode>
21
+ <App />
22
+ </React.StrictMode>,
23
+ );
24
+ }