aidevops 3.32.176 → 3.32.178

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
@@ -60,7 +60,7 @@ The result: an AI operations platform that manages projects across every busines
60
60
  [![Copyright](https://img.shields.io/badge/Copyright-Marcus%20Quinn%202025--2026-blue.svg)](https://github.com/marcusquinn)
61
61
 
62
62
  <!-- Release & Version Info -->
63
- [![Version](https://img.shields.io/badge/Version-3.32.176-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
63
+ [![Version](https://img.shields.io/badge/Version-3.32.178-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
64
64
  [![npm version](https://img.shields.io/npm/v/aidevops)](https://www.npmjs.com/package/aidevops)
65
65
  [![Homebrew](https://img.shields.io/badge/homebrew-marcusquinn%2Ftap-orange)](https://github.com/marcusquinn/homebrew-tap)
66
66
  [![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.32.176
1
+ 3.32.178
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.32.176
8
+ # Version: 3.32.178
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.32.176",
3
+ "version": "3.32.178",
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 { spawn } from "node:child_process";
2
2
  import * as readline from "node:readline";
3
- import { Hono } from "hono";
3
+ import { type Context, Hono } from "hono";
4
4
  import { APP_ACTION_ROUTE_MANIFEST, APP_ACTION_STATUS_ROUTE_MANIFEST, BANNED_ROUTE_PATTERNS, createEnvelope, FILE_EXPLORER_ROUTE_MANIFEST, type GuiAppActionId, type GuiAppActionJobSummary, type GuiPulseWorkerActionId, type GuiPulseWorkerActionJobSummary, PULSE_WORKERS_ACTION_ROUTE_MANIFEST, PULSE_WORKERS_ACTION_STATUS_ROUTE_MANIFEST, STATUS_ROUTE_MANIFEST, TAMBO_PROVIDER_CONFIG, TAMBO_PROXY_ROUTE_MANIFEST, VAULT_STATUS_ROUTE_MANIFEST } from "../../gui-shared/src";
5
5
  import { readFileExplorer } from "./file-adapter";
6
6
  import { readStatus, readVaultStatus } from "./status-adapter";
@@ -46,157 +46,139 @@ const pulseWorkerActionJobs = new Map<string, GuiPulseWorkerActionJobSummary>();
46
46
  export function createGuiApiApp() {
47
47
  const app = new Hono();
48
48
 
49
- app.get("/api/health", (context) => {
50
- return context.json(createEnvelope({
49
+ app.get("/api/health", (context) => context.json(createEnvelope({
51
50
  operation_id: "capabilities.read",
52
51
  source: { surface: "health", authority: "in-process readiness probe", path_refs: [] },
53
52
  data: { status: "ok", service: "aidevops-gui-api" },
54
- }));
55
- });
56
-
57
- app.get(STATUS_ROUTE_MANIFEST.route, (context) => {
58
- return context.json(readStatus());
59
- });
53
+ })));
60
54
 
61
- app.get(VAULT_STATUS_ROUTE_MANIFEST.route, (context) => {
62
- return context.json(readVaultStatus());
63
- });
55
+ app.get(STATUS_ROUTE_MANIFEST.route, (context) => context.json(readStatus()));
64
56
 
65
- app.get(TAMBO_PROXY_ROUTE_MANIFEST.route, (context) => {
66
- const tenantRef = threadScopeRef(context.req.query("tenant_ref") ?? "local");
67
- const workspaceRef = threadScopeRef(context.req.query("workspace_ref") ?? "aidevops");
68
- const sessionRef = context.req.query("session_ref") ?? "conversation:local";
57
+ app.get(VAULT_STATUS_ROUTE_MANIFEST.route, (context) => context.json(readVaultStatus()));
69
58
 
70
- return context.json(createEnvelope({
59
+ app.get(TAMBO_PROXY_ROUTE_MANIFEST.route, (context) => context.json(createEnvelope({
71
60
  operation_id: TAMBO_PROXY_ROUTE_MANIFEST.operation_id,
72
61
  source: { surface: "conversations", authority: "server-side Tambo proxy metadata", path_refs: ["packages/gui-shared/src/tambo.ts"] },
73
62
  data: {
74
63
  ...TAMBO_PROVIDER_CONFIG,
75
- thread_key_ref: `${tenantRef}:${workspaceRef}:${sessionRef}`,
64
+ thread_key_ref: `${threadScopeRef(context.req.query("tenant_ref") ?? "local")}:${threadScopeRef(context.req.query("workspace_ref") ?? "aidevops")}:${context.req.query("session_ref") ?? "conversation:local"}`,
76
65
  },
77
66
  warnings: ["Tambo provider secrets stay server-side; this route exposes component metadata and scoped thread keys only."],
78
- }));
79
- });
80
-
81
- app.post(APP_ACTION_ROUTE_MANIFEST.route, (context) => {
82
- const appId = context.req.param("appId");
83
- const action = context.req.param("action") as GuiAppActionId;
84
- const command = appActionCommands[appId]?.[action];
85
-
86
- if (command === undefined) {
87
- return context.json(createEnvelope({
88
- operation_id: APP_ACTION_ROUTE_MANIFEST.operation_id,
89
- source: { surface: "apps", authority: "allowlisted local command runner", path_refs: [] },
90
- data: rejectedJob(appId, action, "No allowlisted command for this app action."),
91
- errors: ["action_not_allowlisted"],
92
- }), 400);
93
- }
94
-
95
- const job = startAppActionJob(appId, action, command);
96
- return context.json(createEnvelope({
97
- operation_id: APP_ACTION_ROUTE_MANIFEST.operation_id,
98
- source: { surface: "apps", authority: "allowlisted local command runner", path_refs: ["setup.sh", "aidevops.sh"] },
99
- data: job,
100
- warnings: ["Command runs locally in the background. Output is retained in memory for this GUI API process only."],
101
- }), 202);
102
- });
103
-
104
- app.get(APP_ACTION_STATUS_ROUTE_MANIFEST.route, (context) => {
105
- const jobId = context.req.param("jobId");
106
- const job = appActionJobs.get(jobId);
107
- if (job === undefined) {
108
- return context.json(createEnvelope({
109
- operation_id: APP_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
110
- source: { surface: "apps", authority: "background job store", path_refs: [] },
111
- data: rejectedJob("unknown", "install", "Unknown job."),
112
- errors: ["unknown_job"],
113
- }), 404);
114
- }
115
-
116
- return context.json(createEnvelope({
117
- operation_id: APP_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
118
- source: { surface: "apps", authority: "background job store", path_refs: [] },
119
- data: job,
120
- }));
121
- });
122
-
123
- app.post(PULSE_WORKERS_ACTION_ROUTE_MANIFEST.route, (context) => {
124
- const action = context.req.param("action") as GuiPulseWorkerActionId;
125
-
126
- if (!Object.hasOwn(pulseWorkerActionCommands, action)) {
127
- return context.json(createEnvelope({
128
- operation_id: PULSE_WORKERS_ACTION_ROUTE_MANIFEST.operation_id,
129
- source: { surface: "pulse_workers", authority: "allowlisted local command runner", path_refs: [] },
130
- data: rejectedPulseWorkerJob(action, "No allowlisted command for this Pulse & Workers action."),
131
- errors: ["action_not_allowlisted"],
132
- }), 400);
133
- }
134
-
135
- const actionCommand = pulseWorkerActionCommands[action];
136
- const job = startPulseWorkerActionJob(action, actionCommand.command, actionCommand.target_ref, actionCommand.audit_ref);
137
- return context.json(createEnvelope({
138
- operation_id: PULSE_WORKERS_ACTION_ROUTE_MANIFEST.operation_id,
139
- source: { surface: "pulse_workers", authority: "allowlisted local command runner", path_refs: [".agents/scripts", "packages/gui-api/src/app.ts"] },
140
- data: job,
141
- warnings: ["Command runs locally in the background through an explicit allowlist. Output is redacted and retained in memory for this GUI API process only."],
142
- }), 202);
143
- });
144
-
145
- app.get(PULSE_WORKERS_ACTION_STATUS_ROUTE_MANIFEST.route, (context) => {
146
- const jobId = context.req.param("jobId");
147
- const job = pulseWorkerActionJobs.get(jobId);
148
- if (job === undefined) {
149
- return context.json(createEnvelope({
150
- operation_id: PULSE_WORKERS_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
151
- source: { surface: "pulse_workers", authority: "background job store", path_refs: [] },
152
- data: rejectedPulseWorkerJob("diagnose", "Unknown Pulse & Workers job."),
153
- errors: ["unknown_job"],
154
- }), 404);
155
- }
156
-
157
- return context.json(createEnvelope({
158
- operation_id: PULSE_WORKERS_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
159
- source: { surface: "pulse_workers", authority: "background job store", path_refs: [] },
160
- data: job,
161
- }));
162
- });
67
+ })));
163
68
 
164
- app.get(FILE_EXPLORER_ROUTE_MANIFEST.route, (context) => {
165
- const root = context.req.param("root");
166
- const path = context.req.query("path") ?? "";
167
- const response = readFileExplorer(root, path);
168
- const status = response.ok ? 200 : response.errors.includes("unknown_file_root") ? 404 : 400;
69
+ app.post(APP_ACTION_ROUTE_MANIFEST.route, handleAppAction);
70
+ app.get(APP_ACTION_STATUS_ROUTE_MANIFEST.route, handleAppActionStatus);
71
+ app.post(PULSE_WORKERS_ACTION_ROUTE_MANIFEST.route, handlePulseWorkerAction);
72
+ app.get(PULSE_WORKERS_ACTION_STATUS_ROUTE_MANIFEST.route, handlePulseWorkerActionStatus);
169
73
 
170
- return context.json(response, status);
171
- });
74
+ app.get(FILE_EXPLORER_ROUTE_MANIFEST.route, (context) => context.json(
75
+ ...fileExplorerResult(context.req.param("root"), context.req.query("path") ?? ""),
76
+ ));
172
77
 
173
78
  for (const route of BANNED_ROUTE_PATTERNS) {
174
- app.post(route, (context) => {
175
- return context.json(
79
+ app.post(route, (context) => context.json(
176
80
  {
177
81
  ok: false,
178
82
  operation_id: "capabilities.read",
179
83
  errors: ["write_actions_disabled"],
180
84
  },
181
85
  405,
182
- );
183
- });
86
+ ));
184
87
  }
185
88
 
186
- app.notFound((context) => {
187
- return context.json(
89
+ app.notFound((context) => context.json(
188
90
  {
189
91
  ok: false,
190
92
  operation_id: "capabilities.read",
191
93
  errors: ["unknown_route"],
192
94
  },
193
95
  404,
194
- );
195
- });
96
+ ));
196
97
 
197
98
  return app;
198
99
  }
199
100
 
101
+ function handleAppAction(context: Context) {
102
+ const appId = context.req.param("appId");
103
+ const action = context.req.param("action") as GuiAppActionId;
104
+ const command = appActionCommands[appId]?.[action];
105
+ if (command === undefined) {
106
+ return context.json(createEnvelope({
107
+ operation_id: APP_ACTION_ROUTE_MANIFEST.operation_id,
108
+ source: { surface: "apps", authority: "allowlisted local command runner", path_refs: [] },
109
+ data: rejectedJob(appId, action, "No allowlisted command for this app action."),
110
+ errors: ["action_not_allowlisted"],
111
+ }), 400);
112
+ }
113
+ const job = startAppActionJob(appId, action, command);
114
+ return context.json(createEnvelope({
115
+ operation_id: APP_ACTION_ROUTE_MANIFEST.operation_id,
116
+ source: { surface: "apps", authority: "allowlisted local command runner", path_refs: ["setup.sh", "aidevops.sh"] },
117
+ data: job,
118
+ warnings: ["Command runs locally in the background. Output is retained in memory for this GUI API process only."],
119
+ }), 202);
120
+ }
121
+
122
+ function handleAppActionStatus(context: Context) {
123
+ const job = appActionJobs.get(context.req.param("jobId"));
124
+ if (job === undefined) {
125
+ return context.json(createEnvelope({
126
+ operation_id: APP_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
127
+ source: { surface: "apps", authority: "background job store", path_refs: [] },
128
+ data: rejectedJob("unknown", "install", "Unknown job."),
129
+ errors: ["unknown_job"],
130
+ }), 404);
131
+ }
132
+ return context.json(createEnvelope({
133
+ operation_id: APP_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
134
+ source: { surface: "apps", authority: "background job store", path_refs: [] },
135
+ data: job,
136
+ }));
137
+ }
138
+
139
+ function handlePulseWorkerAction(context: Context) {
140
+ const action = context.req.param("action") as GuiPulseWorkerActionId;
141
+ if (!Object.hasOwn(pulseWorkerActionCommands, action)) {
142
+ return context.json(createEnvelope({
143
+ operation_id: PULSE_WORKERS_ACTION_ROUTE_MANIFEST.operation_id,
144
+ source: { surface: "pulse_workers", authority: "allowlisted local command runner", path_refs: [] },
145
+ data: rejectedPulseWorkerJob(action, "No allowlisted command for this Pulse & Workers action."),
146
+ errors: ["action_not_allowlisted"],
147
+ }), 400);
148
+ }
149
+ const actionCommand = pulseWorkerActionCommands[action];
150
+ const job = startPulseWorkerActionJob(action, actionCommand.command, actionCommand.target_ref, actionCommand.audit_ref);
151
+ return context.json(createEnvelope({
152
+ operation_id: PULSE_WORKERS_ACTION_ROUTE_MANIFEST.operation_id,
153
+ source: { surface: "pulse_workers", authority: "allowlisted local command runner", path_refs: [".agents/scripts", "packages/gui-api/src/app.ts"] },
154
+ data: job,
155
+ warnings: ["Command runs locally in the background through an explicit allowlist. Output is redacted and retained in memory for this GUI API process only."],
156
+ }), 202);
157
+ }
158
+
159
+ function handlePulseWorkerActionStatus(context: Context) {
160
+ const job = pulseWorkerActionJobs.get(context.req.param("jobId"));
161
+ if (job === undefined) {
162
+ return context.json(createEnvelope({
163
+ operation_id: PULSE_WORKERS_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
164
+ source: { surface: "pulse_workers", authority: "background job store", path_refs: [] },
165
+ data: rejectedPulseWorkerJob("diagnose", "Unknown Pulse & Workers job."),
166
+ errors: ["unknown_job"],
167
+ }), 404);
168
+ }
169
+ return context.json(createEnvelope({
170
+ operation_id: PULSE_WORKERS_ACTION_STATUS_ROUTE_MANIFEST.operation_id,
171
+ source: { surface: "pulse_workers", authority: "background job store", path_refs: [] },
172
+ data: job,
173
+ }));
174
+ }
175
+
176
+ function fileExplorerResult(root: string, path: string) {
177
+ const response = readFileExplorer(root, path);
178
+ const status = response.ok ? 200 : response.errors.includes("unknown_file_root") ? 404 : 400;
179
+ return [response, status] as const;
180
+ }
181
+
200
182
  function startAppActionJob(appId: string, action: GuiAppActionId, command: string[]): GuiAppActionJobSummary {
201
183
  const now = new Date().toISOString();
202
184
  const redactLine = createOutputLineRedactor();
package/setup.sh CHANGED
@@ -17,7 +17,7 @@ fi
17
17
  # AI Assistant Server Access Framework Setup Script
18
18
  # Helps developers set up the framework for their infrastructure
19
19
  #
20
- # Version: 3.32.176
20
+ # Version: 3.32.178
21
21
  #
22
22
  # Quick Install:
23
23
  # npm install -g aidevops && aidevops update (recommended)