aidevops 3.29.29 → 3.29.30

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.29-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
61
+ [![Version](https://img.shields.io/badge/Version-3.29.30-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.29
1
+ 3.29.30
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.29
8
+ # Version: 3.29.30
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.29",
3
+ "version": "3.29.30",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -79,47 +79,67 @@ function AppLogLink({ job }: { job: GuiAppActionJobSummary }): ReactElement {
79
79
  return <a className="app-meta app-log-link" href={`#app-job-${job.id}`}><small>Logs</small><strong>{job.action} · {job.status}</strong></a>;
80
80
  }
81
81
 
82
- function AppActionButton({ action, app, commandPreview, disabled, onJob }: { action: GuiAppActionId; app: GuiManagedAppSummary; commandPreview: string; disabled: boolean; onJob: (job: GuiAppActionJobSummary) => void }): ReactElement {
83
- const icon = action === "install" ? <FiDownload /> : action === "update" ? <FiRefreshCw /> : action === "reinstall" ? <FiRepeat /> : <FiTrash2 />;
84
- const [confirmOpen, setConfirmOpen] = useState(false);
82
+ function appActionIcon(action: GuiAppActionId): ReactElement {
83
+ const icons: Record<GuiAppActionId, ReactElement> = {
84
+ install: <FiDownload />,
85
+ reinstall: <FiRepeat />,
86
+ remove: <FiTrash2 />,
87
+ update: <FiRefreshCw />,
88
+ };
89
+
90
+ return icons[action];
91
+ }
85
92
 
86
- async function runAction(): Promise<void> {
87
- if (disabled) {
88
- return;
89
- }
93
+ function appActionButtonClass(action: GuiAppActionId): string {
94
+ return action === "remove" ? "app-action-button remove" : "app-action-button";
95
+ }
90
96
 
91
- try {
92
- const response = await fetch(`/api/apps/${encodeURIComponent(app.id)}/actions/${action}`, { method: "POST" });
93
- if (!response.ok) {
94
- console.error(`Failed to run ${action} for ${app.id}: ${response.status} ${response.statusText}`);
95
- return;
96
- }
97
+ function envelopeErrorSummary(envelope: Partial<GuiResponseEnvelope<GuiAppActionJobSummary>>): string {
98
+ return Array.isArray(envelope.errors) && envelope.errors.length > 0 ? envelope.errors.join("; ") : "unknown error";
99
+ }
97
100
 
101
+ async function requestAppActionJob(app: GuiManagedAppSummary, action: GuiAppActionId): Promise<GuiAppActionJobSummary | null> {
102
+ let job: GuiAppActionJobSummary | null = null;
103
+
104
+ try {
105
+ const response = await fetch(`/api/apps/${encodeURIComponent(app.id)}/actions/${action}`, { method: "POST" });
106
+ if (!response.ok) {
107
+ console.error(`Failed to run ${action} for ${app.id}: ${response.status} ${response.statusText}`);
108
+ } else {
98
109
  const envelope = await response.json() as Partial<GuiResponseEnvelope<GuiAppActionJobSummary>> | null;
99
110
  if (envelope === null || typeof envelope !== "object") {
100
111
  console.error(`Action ${action} for ${app.id} returned an invalid response envelope`);
101
- return;
112
+ } else if (envelope.ok !== true) {
113
+ console.error(`Action ${action} for ${app.id} returned an error envelope: ${envelopeErrorSummary(envelope)}`);
114
+ } else if (!("data" in envelope)) {
115
+ console.error(`Action ${action} for ${app.id} returned a response envelope without job data`);
116
+ } else {
117
+ job = envelope.data as GuiAppActionJobSummary;
102
118
  }
119
+ }
120
+ } catch (error) {
121
+ console.error(`Network error running ${action} for ${app.id}:`, error);
122
+ }
103
123
 
104
- if (envelope.ok !== true) {
105
- const errors = Array.isArray(envelope.errors) && envelope.errors.length > 0 ? envelope.errors.join("; ") : "unknown error";
106
- console.error(`Action ${action} for ${app.id} returned an error envelope: ${errors}`);
107
- return;
108
- }
124
+ return job;
125
+ }
109
126
 
110
- if (!("data" in envelope)) {
111
- console.error(`Action ${action} for ${app.id} returned a response envelope without job data`);
112
- return;
113
- }
127
+ function AppActionButton({ action, app, commandPreview, disabled, onJob }: { action: GuiAppActionId; app: GuiManagedAppSummary; commandPreview: string; disabled: boolean; onJob: (job: GuiAppActionJobSummary) => void }): ReactElement {
128
+ const [confirmOpen, setConfirmOpen] = useState(false);
129
+
130
+ async function runAction(): Promise<void> {
131
+ if (disabled) {
132
+ return;
133
+ }
114
134
 
115
- onJob(envelope.data as GuiAppActionJobSummary);
116
- } catch (error) {
117
- console.error(`Network error running ${action} for ${app.id}:`, error);
135
+ const job = await requestAppActionJob(app, action);
136
+ if (job !== null) {
137
+ onJob(job);
118
138
  }
119
139
  }
120
140
 
121
141
  return <>
122
- <button aria-label={`${action} ${app.name}`} className={action === "remove" ? "app-action-button remove" : "app-action-button"} data-tooltip={commandPreview} disabled={disabled} onClick={() => setConfirmOpen(true)} type="button">{icon}<span>{action}</span></button>
142
+ <button aria-label={`${action} ${app.name}`} className={appActionButtonClass(action)} data-tooltip={commandPreview} disabled={disabled} onClick={() => setConfirmOpen(true)} type="button">{appActionIcon(action)}<span>{action}</span></button>
123
143
  {confirmOpen ? <ConfirmActionModal action={action} app={app} commandPreview={commandPreview} close={() => setConfirmOpen(false)} confirm={() => { setConfirmOpen(false); void runAction(); }} /> : null}
124
144
  </>;
125
145
  }
@@ -134,7 +154,7 @@ function ConfirmActionModal({ action, app, close, commandPreview, confirm }: { a
134
154
  <code>{commandPreview}</code>
135
155
  <div className="confirm-modal-actions">
136
156
  <button className="secondary-action" onClick={close} type="button">Cancel</button>
137
- <button className={action === "remove" ? "app-action-button remove" : "app-action-button"} onClick={confirm} type="button">Confirm</button>
157
+ <button className={appActionButtonClass(action)} onClick={confirm} type="button">Confirm</button>
138
158
  </div>
139
159
  </section>
140
160
  </div>
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.29
15
+ # Version: 3.29.30
16
16
  #
17
17
  # Quick Install:
18
18
  # npm install -g aidevops && aidevops update (recommended)