aidevops 3.29.6 → 3.29.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,7 +58,7 @@ The result: an AI operations platform that manages projects across every busines
58
58
  [![Copyright](https://img.shields.io/badge/Copyright-Marcus%20Quinn%202025--2026-blue.svg)](https://github.com/marcusquinn)
59
59
 
60
60
  <!-- Release & Version Info -->
61
- [![Version](https://img.shields.io/badge/Version-3.29.6-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
61
+ [![Version](https://img.shields.io/badge/Version-3.29.7-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
62
62
  [![npm version](https://img.shields.io/npm/v/aidevops)](https://www.npmjs.com/package/aidevops)
63
63
  [![Homebrew](https://img.shields.io/badge/homebrew-marcusquinn%2Ftap-orange)](https://github.com/marcusquinn/homebrew-tap)
64
64
  [![GitHub repository](https://img.shields.io/badge/github-repository-181717.svg?logo=github)](https://github.com/marcusquinn/aidevops)
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.29.6
1
+ 3.29.7
package/aidevops.sh CHANGED
@@ -5,7 +5,7 @@
5
5
  # AI DevOps Framework CLI
6
6
  # Usage: aidevops <command> [options]
7
7
  #
8
- # Version: 3.29.6
8
+ # Version: 3.29.7
9
9
 
10
10
  set -euo pipefail
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidevops",
3
- "version": "3.29.6",
3
+ "version": "3.29.7",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -1,6 +1,6 @@
1
1
  import type { GuiAppActionId, GuiAppActionJobSummary, GuiManagedAppSummary, GuiResponseEnvelope, GuiStatusData } from "@aidevops/gui-shared";
2
2
  import { type Dispatch, type ReactElement, type SetStateAction, useEffect, useState } from "react";
3
- import { FiDownload, FiExternalLink, FiRefreshCw, FiRepeat, FiTrash2 } from "react-icons/fi";
3
+ import { FiChevronDown, FiDownload, FiExternalLink, FiRefreshCw, FiRepeat, FiTrash2 } from "react-icons/fi";
4
4
  import type { InventoryColumn } from "./app-model";
5
5
  import { installationRows, text } from "./app-model";
6
6
 
@@ -13,9 +13,17 @@ let draftRowCounter = 0;
13
13
 
14
14
  export function AppsSurface({ status }: { status: GuiStatusData }): ReactElement {
15
15
  const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
16
+ const [expandedAppIds, setExpandedAppIds] = useState<Set<string>>(() => new Set(status.managed_apps.length > 0 ? [status.managed_apps[0].id] : []));
16
17
  const [jobs, setJobs] = useState<Record<string, GuiAppActionJobSummary>>({});
17
18
  const selectedJob = selectedJobId === null ? null : jobs[selectedJobId] ?? null;
18
19
 
20
+ useEffect(() => {
21
+ if (status.managed_apps.length === 0) {
22
+ return;
23
+ }
24
+ setExpandedAppIds((current) => current.size === 0 ? new Set([status.managed_apps[0].id]) : current);
25
+ }, [status.managed_apps]);
26
+
19
27
  useEffect(() => {
20
28
  if (selectedJob === null || selectedJob.status !== "running") {
21
29
  return undefined;
@@ -37,28 +45,51 @@ export function AppsSurface({ status }: { status: GuiStatusData }): ReactElement
37
45
  </div>
38
46
  <div className="managed-app-list">
39
47
  {status.managed_apps.map((app) => (
40
- <ManagedAppRow app={app} key={app.id} onJob={(job) => { setJobs((current) => ({ ...current, [job.id]: job })); setSelectedJobId(job.id); }} />
48
+ <ManagedAppPanel
49
+ app={app}
50
+ expanded={expandedAppIds.has(app.id)}
51
+ job={jobForApp(app.id, jobs, selectedJob)}
52
+ key={app.id}
53
+ onJob={(job) => {
54
+ setJobs((current) => ({ ...current, [job.id]: job }));
55
+ setSelectedJobId(job.id);
56
+ setExpandedAppIds((current) => toggledAppIds(current, app.id, true));
57
+ }}
58
+ onToggle={() => setExpandedAppIds((current) => toggledAppIds(current, app.id))}
59
+ />
41
60
  ))}
42
61
  </div>
43
- {selectedJob ? <AppActionTerminal job={selectedJob} /> : <p className="empty-state compact-notice">Install/update actions run as allowlisted background jobs and stream here. xterm.js with a node-pty bridge is the right next step for full TUI apps such as OpenCode; this view starts with non-interactive command logs.</p>}
62
+ <p className="empty-state compact-notice">Install/update actions run as allowlisted background jobs and stream inside each app panel. xterm.js with a node-pty bridge is the right next step for full TUI apps such as OpenCode; this view starts with non-interactive command logs.</p>
44
63
  </section>
45
64
  );
46
65
  }
47
66
 
48
- function ManagedAppRow({ app, onJob }: { app: GuiManagedAppSummary; onJob: (job: GuiAppActionJobSummary) => void }): ReactElement {
67
+ function ManagedAppPanel({ app, expanded, job, onJob, onToggle }: { app: GuiManagedAppSummary; expanded: boolean; job: GuiAppActionJobSummary | null; onJob: (job: GuiAppActionJobSummary) => void; onToggle: () => void }): ReactElement {
49
68
  return (
50
- <article className="managed-app-row">
51
- <div className="managed-app-main">
52
- <div>
53
- <p className="eyebrow">{app.category}</p>
54
- <h3>{app.name}</h3>
55
- <p>{app.description}</p>
56
- </div>
57
- <div className="managed-app-links">
58
- <OriginLink href={app.origin_website_url} label={text.website} />
59
- <OriginLink href={app.origin_repo_url} label="Repo" />
69
+ <article className={expanded ? "managed-app-card expanded" : "managed-app-card"}>
70
+ <button aria-expanded={expanded} className="managed-app-summary" data-tooltip={`${expanded ? "Collapse" : "Expand"} ${app.name} controls`} onClick={onToggle} type="button">
71
+ <span className="managed-app-title-block">
72
+ <span className="eyebrow">{app.category}</span>
73
+ <strong>{app.name}</strong>
74
+ <span>{app.description}</span>
75
+ </span>
76
+ <span className="managed-app-summary-meta">
77
+ <SummaryChip label="Status" value={app.status} />
78
+ <SummaryChip label="Installed" value={app.installed_version} />
79
+ <SummaryChip label="Latest" value={app.latest_version} />
80
+ <FiChevronDown aria-hidden="true" className="managed-app-chevron" />
81
+ </span>
82
+ </button>
83
+ {expanded ? <div className="managed-app-body">
84
+ <div className="managed-app-toolbar">
85
+ <div className="managed-app-links">
86
+ <OriginLink href={app.origin_website_url} label={text.website} />
87
+ <OriginLink href={app.origin_repo_url} label="Repo" />
88
+ </div>
89
+ <div className="managed-app-actions">
90
+ {app.actions.map((action) => <AppActionButton action={action.id} app={app} disabled={!action.enabled} key={action.id} onJob={onJob} commandPreview={action.command_preview} />)}
91
+ </div>
60
92
  </div>
61
- </div>
62
93
  <div className="managed-app-details">
63
94
  <ToggleSwitch checked={app.aidevops_install} label="setup installs" />
64
95
  <ToggleSwitch checked={app.aidevops_update} label="update maintains" />
@@ -67,19 +98,22 @@ function ManagedAppRow({ app, onJob }: { app: GuiManagedAppSummary; onJob: (job:
67
98
  <AppMeta label={text.path} value={app.install_path_ref} />
68
99
  <AppMeta label="Status" value={app.status} />
69
100
  </div>
70
- <div className="managed-app-actions">
71
- {app.actions.map((action) => <AppActionButton action={action.id} app={app} disabled={!action.enabled} key={action.id} onJob={onJob} title={action.command_preview} />)}
72
- </div>
101
+ {job ? <AppActionTerminal job={job} /> : <p className="empty-state compact-notice">No recent command output for this app. Run an action to open this app's terminal log.</p>}
102
+ </div> : null}
73
103
  </article>
74
104
  );
75
105
  }
76
106
 
107
+ function SummaryChip({ label, value }: { label: string; value: string }): ReactElement {
108
+ return <span className="managed-summary-chip"><small>{label}</small><span>{value}</span></span>;
109
+ }
110
+
77
111
  function OriginLink({ href, label }: { href: string; label: string }): ReactElement {
78
112
  if (href.length === 0) {
79
113
  return <span className="origin-missing">{label}: source pending</span>;
80
114
  }
81
115
 
82
- return <a href={href} rel="noreferrer" target="_blank">{label} <FiExternalLink aria-hidden="true" /></a>;
116
+ return <a data-tooltip={`Open ${label.toLowerCase()} destination`} href={href} rel="noreferrer" target="_blank">{label} <FiExternalLink aria-hidden="true" /></a>;
83
117
  }
84
118
 
85
119
  function ToggleSwitch({ checked, label }: { checked: boolean; label: string }): ReactElement {
@@ -90,7 +124,7 @@ function AppMeta({ label, value }: { label: string; value: string }): ReactEleme
90
124
  return <span className="app-meta"><small>{label}</small><strong>{value}</strong></span>;
91
125
  }
92
126
 
93
- function AppActionButton({ action, app, disabled, onJob, title }: { action: GuiAppActionId; app: GuiManagedAppSummary; disabled: boolean; onJob: (job: GuiAppActionJobSummary) => void; title: string }): ReactElement {
127
+ function AppActionButton({ action, app, commandPreview, disabled, onJob }: { action: GuiAppActionId; app: GuiManagedAppSummary; commandPreview: string; disabled: boolean; onJob: (job: GuiAppActionJobSummary) => void }): ReactElement {
94
128
  const icon = action === "install" ? <FiDownload /> : action === "update" ? <FiRefreshCw /> : action === "reinstall" ? <FiRepeat /> : <FiTrash2 />;
95
129
 
96
130
  async function runAction(): Promise<void> {
@@ -106,7 +140,7 @@ function AppActionButton({ action, app, disabled, onJob, title }: { action: GuiA
106
140
  onJob(envelope.data);
107
141
  }
108
142
 
109
- return <button aria-label={`${action} ${app.name}`} className="app-action-button" disabled={disabled} onClick={() => void runAction()} title={title} type="button">{icon}</button>;
143
+ return <button aria-label={`${action} ${app.name}`} className={action === "remove" ? "app-action-button remove" : "app-action-button"} data-tooltip={commandPreview} disabled={disabled} onClick={() => void runAction()} type="button">{icon}<span>{action}</span></button>;
110
144
  }
111
145
 
112
146
  function AppActionTerminal({ job }: { job: GuiAppActionJobSummary }): ReactElement {
@@ -127,6 +161,25 @@ async function refreshJob(jobId: string, setJobs: Dispatch<SetStateAction<Record
127
161
  setJobs((current) => ({ ...current, [envelope.data.id]: envelope.data }));
128
162
  }
129
163
 
164
+ function jobForApp(appId: string, jobs: Record<string, GuiAppActionJobSummary>, selectedJob: GuiAppActionJobSummary | null): GuiAppActionJobSummary | null {
165
+ if (selectedJob?.app_id === appId) {
166
+ return selectedJob;
167
+ }
168
+
169
+ return Object.values(jobs).reverse().find((job) => job.app_id === appId) ?? null;
170
+ }
171
+
172
+ function toggledAppIds(current: Set<string>, appId: string, forceOpen = false): Set<string> {
173
+ const next = new Set(current);
174
+ if (forceOpen || !next.has(appId)) {
175
+ next.add(appId);
176
+ } else {
177
+ next.delete(appId);
178
+ }
179
+
180
+ return next;
181
+ }
182
+
130
183
  export function InstallationSurface(): ReactElement {
131
184
  return (
132
185
  <section className="panel" aria-label={text.installation}>
@@ -1869,38 +1869,122 @@ code {
1869
1869
  }
1870
1870
 
1871
1871
  .managed-app-list {
1872
- background: var(--surface-muted);
1873
- border: 1px solid var(--border);
1874
- border-radius: 16px;
1872
+ gap: 12px;
1875
1873
  margin-bottom: 12px;
1874
+ overflow: visible;
1875
+ }
1876
+
1877
+ .managed-app-card {
1878
+ background:
1879
+ linear-gradient(135deg, color-mix(in srgb, var(--accent) 7%, transparent), transparent 34%),
1880
+ var(--surface-muted);
1881
+ border: 1px solid var(--border);
1882
+ border-radius: 18px;
1883
+ box-shadow: inset 0 1px 0 rgb(255 255 255 / 7%);
1884
+ overflow: visible;
1876
1885
  }
1877
1886
 
1878
- .managed-app-row {
1887
+ .managed-app-card.expanded {
1888
+ border-color: color-mix(in srgb, var(--accent) 32%, var(--border));
1889
+ }
1890
+
1891
+ .managed-app-summary {
1892
+ align-items: center;
1893
+ background: transparent;
1894
+ border: 0;
1895
+ color: var(--text-primary);
1879
1896
  display: grid;
1880
- gap: 14px;
1881
- grid-template-columns: minmax(220px, 1.3fr) minmax(260px, 1.7fr) auto;
1882
- padding: 14px;
1897
+ gap: 16px;
1898
+ grid-template-columns: minmax(0, 1fr) auto;
1899
+ padding: 16px;
1900
+ text-align: left;
1901
+ width: 100%;
1883
1902
  }
1884
1903
 
1885
- .managed-app-row + .managed-app-row {
1886
- border-top: 1px solid var(--border);
1904
+ .managed-app-summary:hover,
1905
+ .managed-app-summary:focus-visible {
1906
+ background: color-mix(in srgb, var(--accent) 7%, transparent);
1907
+ outline: none;
1887
1908
  }
1888
1909
 
1889
- .managed-app-main,
1890
- .managed-app-main > div,
1910
+ .managed-app-title-block,
1891
1911
  .app-meta {
1892
1912
  display: grid;
1893
- gap: 6px;
1913
+ gap: 7px;
1894
1914
  min-width: 0;
1895
1915
  }
1896
1916
 
1897
- .managed-app-main h3,
1898
- .managed-app-main p {
1899
- margin: 0;
1917
+ .managed-app-title-block strong {
1918
+ font-size: clamp(1.12rem, 2vw, 1.4rem);
1919
+ letter-spacing: -0.04em;
1920
+ line-height: 1.15;
1900
1921
  }
1901
1922
 
1902
- .managed-app-main p:not(.eyebrow) {
1923
+ .managed-app-title-block > span:last-child {
1903
1924
  color: var(--text-secondary);
1925
+ max-width: 72ch;
1926
+ }
1927
+
1928
+ .managed-app-summary-meta {
1929
+ align-items: center;
1930
+ display: flex;
1931
+ flex-wrap: wrap;
1932
+ gap: 8px;
1933
+ justify-content: end;
1934
+ min-width: min(420px, 38vw);
1935
+ }
1936
+
1937
+ .managed-summary-chip {
1938
+ background: color-mix(in srgb, var(--surface-elevated) 92%, transparent);
1939
+ border: 1px solid var(--border);
1940
+ border-radius: 999px;
1941
+ display: inline-grid;
1942
+ gap: 1px;
1943
+ min-width: 118px;
1944
+ padding: 7px 11px;
1945
+ }
1946
+
1947
+ .managed-summary-chip small {
1948
+ color: var(--text-muted);
1949
+ font-size: 0.66rem;
1950
+ font-weight: 900;
1951
+ line-height: 1;
1952
+ text-transform: uppercase;
1953
+ }
1954
+
1955
+ .managed-summary-chip span {
1956
+ color: var(--text-primary);
1957
+ font-size: 0.84rem;
1958
+ font-weight: 800;
1959
+ max-width: 18ch;
1960
+ overflow: hidden;
1961
+ text-overflow: ellipsis;
1962
+ white-space: nowrap;
1963
+ }
1964
+
1965
+ .managed-app-chevron {
1966
+ color: var(--accent);
1967
+ flex: 0 0 auto;
1968
+ transition: transform 180ms ease;
1969
+ }
1970
+
1971
+ .managed-app-card.expanded .managed-app-chevron {
1972
+ transform: rotate(180deg);
1973
+ }
1974
+
1975
+ .managed-app-body {
1976
+ border-top: 1px solid var(--border);
1977
+ display: grid;
1978
+ gap: 14px;
1979
+ padding: 0 16px 16px;
1980
+ }
1981
+
1982
+ .managed-app-toolbar {
1983
+ align-items: start;
1984
+ display: grid;
1985
+ gap: 12px;
1986
+ grid-template-columns: minmax(0, 1fr) auto;
1987
+ padding-top: 14px;
1904
1988
  }
1905
1989
 
1906
1990
  .managed-app-links,
@@ -1911,6 +1995,10 @@ code {
1911
1995
  gap: 8px;
1912
1996
  }
1913
1997
 
1998
+ .managed-app-actions {
1999
+ justify-content: end;
2000
+ }
2001
+
1914
2002
  .managed-app-links a,
1915
2003
  .origin-missing {
1916
2004
  align-items: center;
@@ -1921,18 +2009,28 @@ code {
1921
2009
  font-size: 0.78rem;
1922
2010
  font-weight: 800;
1923
2011
  gap: 4px;
2012
+ max-width: 100%;
1924
2013
  padding: 6px 9px;
2014
+ position: relative;
1925
2015
  text-decoration: none;
1926
2016
  }
1927
2017
 
2018
+ .managed-app-links a:hover,
2019
+ .managed-app-links a:focus-visible {
2020
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
2021
+ border-color: color-mix(in srgb, var(--accent) 48%, var(--border));
2022
+ color: var(--accent);
2023
+ outline: none;
2024
+ }
2025
+
1928
2026
  .origin-missing {
1929
2027
  color: var(--text-muted);
1930
2028
  }
1931
2029
 
1932
2030
  .managed-app-details {
1933
2031
  display: grid;
1934
- gap: 8px;
1935
- grid-template-columns: repeat(3, minmax(0, 1fr));
2032
+ gap: 10px;
2033
+ grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
1936
2034
  }
1937
2035
 
1938
2036
  .managed-toggle,
@@ -1941,7 +2039,8 @@ code {
1941
2039
  border: 1px solid var(--border);
1942
2040
  border-radius: 12px;
1943
2041
  min-width: 0;
1944
- padding: 8px 10px;
2042
+ min-height: 64px;
2043
+ padding: 10px 12px;
1945
2044
  }
1946
2045
 
1947
2046
  .managed-toggle {
@@ -1987,30 +2086,68 @@ code {
1987
2086
  .app-meta strong {
1988
2087
  color: var(--text-primary);
1989
2088
  font-size: 0.84rem;
2089
+ line-height: 1.35;
1990
2090
  overflow-wrap: anywhere;
2091
+ word-break: break-word;
1991
2092
  }
1992
2093
 
1993
2094
  .app-action-button {
1994
2095
  align-items: center;
1995
- border-radius: 12px;
2096
+ background: color-mix(in srgb, var(--accent) 16%, var(--surface-elevated));
2097
+ border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--border));
2098
+ border-radius: 999px;
2099
+ color: var(--accent);
1996
2100
  display: inline-flex;
1997
- height: 38px;
2101
+ gap: 7px;
2102
+ min-height: 42px;
1998
2103
  justify-content: center;
1999
- padding: 0;
2000
- width: 38px;
2104
+ padding: 0 12px;
2105
+ position: relative;
2106
+ transition: background 140ms ease, border-color 140ms ease, color 140ms ease, transform 140ms ease;
2107
+ }
2108
+
2109
+ .app-action-button span {
2110
+ font-size: 0.7rem;
2111
+ font-weight: 900;
2112
+ text-transform: uppercase;
2113
+ }
2114
+
2115
+ .app-action-button:hover,
2116
+ .app-action-button:focus-visible {
2117
+ background: var(--accent);
2118
+ border-color: var(--accent);
2119
+ color: var(--bg-primary);
2120
+ outline: none;
2121
+ transform: translateY(-1px);
2122
+ }
2123
+
2124
+ .app-action-button.remove {
2125
+ background: color-mix(in srgb, #ef4444 12%, var(--surface-elevated));
2126
+ border-color: color-mix(in srgb, #ef4444 45%, var(--border));
2127
+ color: #ff8a8a;
2128
+ }
2129
+
2130
+ .app-action-button.remove:hover,
2131
+ .app-action-button.remove:focus-visible {
2132
+ background: #ef4444;
2133
+ border-color: #ef4444;
2134
+ color: white;
2001
2135
  }
2002
2136
 
2003
2137
  .app-action-button:disabled {
2004
2138
  color: var(--text-muted);
2005
2139
  cursor: not-allowed;
2006
2140
  opacity: 0.45;
2141
+ transform: none;
2007
2142
  }
2008
2143
 
2009
2144
  .app-terminal {
2010
- background: var(--code-bg);
2145
+ background:
2146
+ linear-gradient(180deg, rgb(255 255 255 / 4%), transparent),
2147
+ #050806;
2011
2148
  border: 1px solid var(--border);
2012
2149
  border-radius: 16px;
2013
- color: var(--text-secondary);
2150
+ color: color-mix(in srgb, var(--accent) 78%, white);
2014
2151
  overflow: hidden;
2015
2152
  }
2016
2153
 
@@ -2018,16 +2155,57 @@ code {
2018
2155
  align-items: center;
2019
2156
  border-bottom: 1px solid var(--border);
2020
2157
  display: flex;
2158
+ gap: 12px;
2021
2159
  justify-content: space-between;
2022
2160
  padding: 10px 12px;
2023
2161
  }
2024
2162
 
2163
+ .app-terminal header span {
2164
+ color: var(--text-muted);
2165
+ text-align: right;
2166
+ }
2167
+
2025
2168
  .app-terminal pre {
2026
2169
  margin: 0;
2027
- max-height: 360px;
2170
+ max-height: 240px;
2028
2171
  overflow: auto;
2029
2172
  padding: 12px;
2030
2173
  white-space: pre-wrap;
2174
+ word-break: break-word;
2175
+ }
2176
+
2177
+ [data-tooltip] {
2178
+ position: relative;
2179
+ }
2180
+
2181
+ [data-tooltip]::after {
2182
+ background: color-mix(in srgb, var(--surface-elevated) 94%, black);
2183
+ border: 1px solid color-mix(in srgb, var(--accent) 36%, var(--border));
2184
+ border-radius: 10px;
2185
+ bottom: calc(100% + 8px);
2186
+ box-shadow: 0 18px 46px rgb(0 0 0 / 38%);
2187
+ color: var(--text-primary);
2188
+ content: attr(data-tooltip);
2189
+ font-size: 0.72rem;
2190
+ font-weight: 700;
2191
+ left: 50%;
2192
+ line-height: 1.35;
2193
+ max-width: min(320px, 72vw);
2194
+ opacity: 0;
2195
+ padding: 8px 10px;
2196
+ pointer-events: none;
2197
+ position: absolute;
2198
+ text-align: left;
2199
+ transform: translate(-50%, 4px);
2200
+ transition: opacity 140ms ease, transform 140ms ease;
2201
+ width: max-content;
2202
+ z-index: 40;
2203
+ }
2204
+
2205
+ [data-tooltip]:hover::after,
2206
+ [data-tooltip]:focus-visible::after {
2207
+ opacity: 1;
2208
+ transform: translate(-50%, 0);
2031
2209
  }
2032
2210
 
2033
2211
  .data-row,
@@ -2715,9 +2893,16 @@ code {
2715
2893
  grid-template-columns: 1fr;
2716
2894
  }
2717
2895
 
2718
- .managed-app-row {
2896
+ .managed-app-summary,
2897
+ .managed-app-toolbar {
2719
2898
  grid-template-columns: 1fr;
2720
2899
  }
2900
+
2901
+ .managed-app-summary-meta,
2902
+ .managed-app-actions {
2903
+ justify-content: start;
2904
+ min-width: 0;
2905
+ }
2721
2906
  }
2722
2907
 
2723
2908
  @media (max-width: 980px) {
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.6
15
+ # Version: 3.29.7
16
16
  #
17
17
  # Quick Install:
18
18
  # npm install -g aidevops && aidevops update (recommended)