@simonfestl/husky-cli 1.17.0 → 1.18.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.
@@ -79,6 +79,27 @@ export declare function setSessionConfig(session: {
79
79
  permissions?: string[];
80
80
  }): void;
81
81
  export declare function clearSessionConfig(): void;
82
+ /**
83
+ * Check if there's an active (non-expired) session
84
+ */
85
+ export declare function isSessionActive(): boolean;
86
+ /**
87
+ * Check if the API is properly configured for making requests.
88
+ * Returns true if we have either an active session or an API key.
89
+ */
90
+ export declare function isApiConfigured(): boolean;
91
+ /**
92
+ * Get authentication headers for API requests.
93
+ * Returns Bearer token if session is active, otherwise x-api-key.
94
+ * Use this for all API calls to ensure consistent auth.
95
+ */
96
+ export declare function getAuthHeaders(): Record<string, string>;
97
+ /**
98
+ * Ensure the session is valid, refreshing if needed.
99
+ * Call this before long-running operations (like watch modes).
100
+ * Returns true if session is valid (or was refreshed), false if no session/refresh failed.
101
+ */
102
+ export declare function ensureValidSession(): Promise<boolean>;
82
103
  export declare function getSessionConfig(): {
83
104
  token?: string;
84
105
  expiresAt?: string;
@@ -224,6 +224,91 @@ export function clearSessionConfig() {
224
224
  delete config.sessionRole;
225
225
  saveConfig(config);
226
226
  }
227
+ /**
228
+ * Check if there's an active (non-expired) session
229
+ */
230
+ export function isSessionActive() {
231
+ const config = getConfig();
232
+ if (!config.sessionToken || !config.sessionExpiresAt) {
233
+ return false;
234
+ }
235
+ const expiresAt = new Date(config.sessionExpiresAt);
236
+ return expiresAt > new Date();
237
+ }
238
+ /**
239
+ * Check if the API is properly configured for making requests.
240
+ * Returns true if we have either an active session or an API key.
241
+ */
242
+ export function isApiConfigured() {
243
+ const config = getConfig();
244
+ return Boolean(config.apiUrl && (config.apiKey || isSessionActive()));
245
+ }
246
+ /**
247
+ * Get authentication headers for API requests.
248
+ * Returns Bearer token if session is active, otherwise x-api-key.
249
+ * Use this for all API calls to ensure consistent auth.
250
+ */
251
+ export function getAuthHeaders() {
252
+ const config = getConfig();
253
+ // Check if there's an active (non-expired) session
254
+ if (config.sessionToken && config.sessionExpiresAt) {
255
+ const expiresAt = new Date(config.sessionExpiresAt);
256
+ if (expiresAt > new Date()) {
257
+ // Session is active - use Bearer token
258
+ return { Authorization: `Bearer ${config.sessionToken}` };
259
+ }
260
+ }
261
+ // Fall back to API key
262
+ if (config.apiKey) {
263
+ return { "x-api-key": config.apiKey };
264
+ }
265
+ return {};
266
+ }
267
+ /**
268
+ * Ensure the session is valid, refreshing if needed.
269
+ * Call this before long-running operations (like watch modes).
270
+ * Returns true if session is valid (or was refreshed), false if no session/refresh failed.
271
+ */
272
+ export async function ensureValidSession() {
273
+ const config = getConfig();
274
+ if (!config.sessionToken || !config.sessionExpiresAt) {
275
+ return false; // No session, will use API key
276
+ }
277
+ const expiresAt = new Date(config.sessionExpiresAt);
278
+ const now = new Date();
279
+ const fiveMinutes = 5 * 60 * 1000;
280
+ // Refresh if expiring within 5 minutes
281
+ if (expiresAt.getTime() - now.getTime() < fiveMinutes) {
282
+ if (!config.apiUrl || !config.apiKey || !config.sessionAgent) {
283
+ return false; // Can't refresh without these
284
+ }
285
+ try {
286
+ const url = new URL("/api/auth/session", config.apiUrl);
287
+ const res = await fetch(url.toString(), {
288
+ method: "POST",
289
+ headers: {
290
+ "x-api-key": config.apiKey,
291
+ "Content-Type": "application/json",
292
+ },
293
+ body: JSON.stringify({ agent: config.sessionAgent }),
294
+ });
295
+ if (res.ok) {
296
+ const data = await res.json();
297
+ setSessionConfig({
298
+ token: data.token,
299
+ expiresAt: data.expiresAt,
300
+ agent: data.agent,
301
+ role: data.role,
302
+ });
303
+ return true;
304
+ }
305
+ }
306
+ catch {
307
+ // Refresh failed, continue with current token if not expired
308
+ }
309
+ }
310
+ return expiresAt > now;
311
+ }
227
312
  export function getSessionConfig() {
228
313
  const config = getConfig();
229
314
  if (!config.sessionToken)
@@ -4,7 +4,7 @@
4
4
  * Provides menu-based authentication and API key management.
5
5
  */
6
6
  import { select, input, confirm } from "@inquirer/prompts";
7
- import { getConfig, setConfig, setSessionConfig, clearSessionConfig } from "../config.js";
7
+ import { getConfig, setConfig, setSessionConfig, clearSessionConfig, getAuthHeaders } from "../config.js";
8
8
  import { pressEnterToContinue } from "./utils.js";
9
9
  export async function authMenu() {
10
10
  console.log("\n AUTH");
@@ -65,7 +65,7 @@ async function login() {
65
65
  method: "POST",
66
66
  headers: {
67
67
  "Content-Type": "application/json",
68
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
68
+ ...getAuthHeaders(),
69
69
  },
70
70
  body: JSON.stringify({ agent: agentName }),
71
71
  });
@@ -4,6 +4,7 @@
4
4
  * Provides menu-based agent memory and learning management.
5
5
  */
6
6
  import { select, input, confirm } from "@inquirer/prompts";
7
+ import { getAuthHeaders } from "../config.js";
7
8
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
8
9
  export async function brainMenu() {
9
10
  const config = ensureConfig();
@@ -66,7 +67,7 @@ async function listMemories(config) {
66
67
  default: "20",
67
68
  });
68
69
  const res = await fetch(`${config.apiUrl}/api/brain/memories?limit=${limit}`, {
69
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
70
+ headers: getAuthHeaders(),
70
71
  });
71
72
  if (!res.ok) {
72
73
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -103,7 +104,7 @@ async function recallMemory(config) {
103
104
  validate: (v) => (v.length > 0 ? true : "Query is required"),
104
105
  });
105
106
  const res = await fetch(`${config.apiUrl}/api/brain/recall?query=${encodeURIComponent(query)}`, {
106
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
107
+ headers: getAuthHeaders(),
107
108
  });
108
109
  if (!res.ok) {
109
110
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -147,7 +148,7 @@ async function rememberContent(config) {
147
148
  method: "POST",
148
149
  headers: {
149
150
  "Content-Type": "application/json",
150
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
151
+ ...(getAuthHeaders()),
151
152
  },
152
153
  body: JSON.stringify({ content, tags }),
153
154
  });
@@ -183,7 +184,7 @@ async function publishMemory(config) {
183
184
  method: "POST",
184
185
  headers: {
185
186
  "Content-Type": "application/json",
186
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
187
+ ...(getAuthHeaders()),
187
188
  },
188
189
  body: JSON.stringify({ visibility }),
189
190
  });
@@ -204,7 +205,7 @@ async function publishMemory(config) {
204
205
  async function sharedMemories(config) {
205
206
  try {
206
207
  const res = await fetch(`${config.apiUrl}/api/brain/shared`, {
207
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
208
+ headers: getAuthHeaders(),
208
209
  });
209
210
  if (!res.ok) {
210
211
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -242,7 +243,7 @@ async function boostMemory(config) {
242
243
  });
243
244
  const res = await fetch(`${config.apiUrl}/api/brain/boost/${id}`, {
244
245
  method: "POST",
245
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
246
+ headers: getAuthHeaders(),
246
247
  });
247
248
  if (!res.ok) {
248
249
  const error = await res.text();
@@ -275,7 +276,7 @@ async function archiveMemory(config) {
275
276
  }
276
277
  const res = await fetch(`${config.apiUrl}/api/brain/archive/${id}`, {
277
278
  method: "POST",
278
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
279
+ headers: getAuthHeaders(),
279
280
  });
280
281
  if (!res.ok) {
281
282
  const error = await res.text();
@@ -308,7 +309,7 @@ async function deleteMemory(config) {
308
309
  }
309
310
  const res = await fetch(`${config.apiUrl}/api/brain/memories/${id}`, {
310
311
  method: "DELETE",
311
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
312
+ headers: getAuthHeaders(),
312
313
  });
313
314
  if (!res.ok) {
314
315
  const error = await res.text();
@@ -337,7 +338,7 @@ async function cleanupMemories(config) {
337
338
  }
338
339
  const res = await fetch(`${config.apiUrl}/api/brain/cleanup`, {
339
340
  method: "POST",
340
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
341
+ headers: getAuthHeaders(),
341
342
  });
342
343
  if (!res.ok) {
343
344
  const error = await res.text();
@@ -1,5 +1,6 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
2
  import { execSync } from "child_process";
3
+ import { getAuthHeaders } from "../config.js";
3
4
  import { ensureConfig, pressEnterToContinue, truncate, formatDate } from "./utils.js";
4
5
  const TYPE_LABELS = {
5
6
  feature: "New Features",
@@ -55,7 +56,7 @@ async function fetchChangelogs(config, projectId) {
55
56
  url.searchParams.set("projectId", projectId);
56
57
  }
57
58
  const res = await fetch(url.toString(), {
58
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
59
+ headers: getAuthHeaders(),
59
60
  });
60
61
  if (!res.ok)
61
62
  throw new Error(`API returned ${res.status}`);
@@ -63,7 +64,7 @@ async function fetchChangelogs(config, projectId) {
63
64
  }
64
65
  async function fetchProjects(config) {
65
66
  const res = await fetch(`${config.apiUrl}/api/projects`, {
66
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
67
+ headers: getAuthHeaders(),
67
68
  });
68
69
  if (!res.ok)
69
70
  throw new Error(`API returned ${res.status}`);
@@ -86,7 +87,7 @@ async function selectChangelog(config, message) {
86
87
  return null;
87
88
  // Fetch full changelog
88
89
  const res = await fetch(`${config.apiUrl}/api/changelogs/${changelogId}`, {
89
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
90
+ headers: getAuthHeaders(),
90
91
  });
91
92
  if (!res.ok)
92
93
  return null;
@@ -208,7 +209,7 @@ async function generateChangelog(config) {
208
209
  method: "POST",
209
210
  headers: {
210
211
  "Content-Type": "application/json",
211
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
212
+ ...(getAuthHeaders()),
212
213
  },
213
214
  body: JSON.stringify({
214
215
  projectId,
@@ -279,7 +280,7 @@ async function publishChangelog(config) {
279
280
  method: "PATCH",
280
281
  headers: {
281
282
  "Content-Type": "application/json",
282
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
283
+ ...(getAuthHeaders()),
283
284
  },
284
285
  body: JSON.stringify({
285
286
  status: "published",
@@ -315,7 +316,7 @@ async function deleteChangelog(config) {
315
316
  }
316
317
  const res = await fetch(`${config.apiUrl}/api/changelogs/${changelog.id}`, {
317
318
  method: "DELETE",
318
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
319
+ headers: getAuthHeaders(),
319
320
  });
320
321
  if (!res.ok) {
321
322
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { select, input, confirm } from "@inquirer/prompts";
7
7
  import { ensureConfig, pressEnterToContinue, truncate, formatDate } from "./utils.js";
8
+ import { getAuthHeaders, ensureValidSession } from "../config.js";
8
9
  export async function chatMenu() {
9
10
  const config = ensureConfig();
10
11
  console.log("\n CHAT");
@@ -67,7 +68,7 @@ async function showInbox(config) {
67
68
  });
68
69
  const url = `${config.apiUrl}/api/supervisor/inbox${unreadOnly ? "?unread=true" : ""}`;
69
70
  const res = await fetch(url, {
70
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
71
+ headers: getAuthHeaders(),
71
72
  });
72
73
  if (!res.ok) {
73
74
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -102,7 +103,7 @@ async function showInbox(config) {
102
103
  async function showPending(config) {
103
104
  try {
104
105
  const res = await fetch(`${config.apiUrl}/api/supervisor/pending`, {
105
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
106
+ headers: getAuthHeaders(),
106
107
  });
107
108
  if (!res.ok) {
108
109
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -142,7 +143,7 @@ async function sendMessage(config) {
142
143
  method: "POST",
143
144
  headers: {
144
145
  "Content-Type": "application/json",
145
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
146
+ ...getAuthHeaders(),
146
147
  },
147
148
  body: JSON.stringify({ message }),
148
149
  });
@@ -174,7 +175,7 @@ async function replyToMessage(config) {
174
175
  method: "POST",
175
176
  headers: {
176
177
  "Content-Type": "application/json",
177
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
178
+ ...getAuthHeaders(),
178
179
  },
179
180
  body: JSON.stringify({ response }),
180
181
  });
@@ -196,12 +197,15 @@ async function watchMessages(config) {
196
197
  console.log("\n WATCH MODE");
197
198
  console.log(" " + "-".repeat(50));
198
199
  console.log(" Watching for new messages...");
200
+ console.log(" (Session token will auto-refresh if needed)");
199
201
  console.log("");
200
202
  let lastCheck = new Date().toISOString();
201
203
  const checkMessages = async () => {
202
204
  try {
205
+ // Ensure session is valid before each poll (auto-refreshes if needed)
206
+ await ensureValidSession();
203
207
  const res = await fetch(`${config.apiUrl}/api/supervisor/inbox?since=${encodeURIComponent(lastCheck)}`, {
204
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
208
+ headers: getAuthHeaders(),
205
209
  });
206
210
  if (res.ok) {
207
211
  const messages = await res.json();
@@ -211,6 +215,9 @@ async function watchMessages(config) {
211
215
  console.log("");
212
216
  }
213
217
  }
218
+ else if (res.status === 401) {
219
+ console.log(` [${new Date().toLocaleTimeString()}] Auth error - will retry with refreshed token`);
220
+ }
214
221
  lastCheck = new Date().toISOString();
215
222
  }
216
223
  catch {
@@ -229,7 +236,7 @@ async function watchMessages(config) {
229
236
  async function showConversations(config) {
230
237
  try {
231
238
  const res = await fetch(`${config.apiUrl}/api/agent-conversations`, {
232
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
239
+ headers: getAuthHeaders(),
233
240
  });
234
241
  if (!res.ok) {
235
242
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -260,7 +267,7 @@ async function showConversations(config) {
260
267
  async function showSpaces(config) {
261
268
  try {
262
269
  const res = await fetch(`${config.apiUrl}/api/chat/spaces`, {
263
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
270
+ headers: getAuthHeaders(),
264
271
  });
265
272
  if (!res.ok) {
266
273
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -303,7 +310,7 @@ async function askQuestion(config) {
303
310
  method: "POST",
304
311
  headers: {
305
312
  "Content-Type": "application/json",
306
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
313
+ ...getAuthHeaders(),
307
314
  },
308
315
  body: JSON.stringify({ question, wait: waitForAnswer }),
309
316
  });
@@ -342,7 +349,7 @@ async function requestReview(config) {
342
349
  method: "POST",
343
350
  headers: {
344
351
  "Content-Type": "application/json",
345
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
352
+ ...getAuthHeaders(),
346
353
  },
347
354
  body: JSON.stringify({ question }),
348
355
  });
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  export async function departmentsMenu() {
4
5
  const config = ensureConfig();
@@ -36,7 +37,7 @@ export async function departmentsMenu() {
36
37
  }
37
38
  async function fetchDepartments(config) {
38
39
  const res = await fetch(`${config.apiUrl}/api/departments`, {
39
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
40
+ headers: getAuthHeaders(),
40
41
  });
41
42
  if (!res.ok)
42
43
  throw new Error(`API returned ${res.status}`);
@@ -87,7 +88,7 @@ async function viewDepartment(config) {
87
88
  if (!dept)
88
89
  return;
89
90
  const res = await fetch(`${config.apiUrl}/api/departments/${dept.id}`, {
90
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
91
+ headers: getAuthHeaders(),
91
92
  });
92
93
  if (!res.ok) {
93
94
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -128,7 +129,7 @@ async function createDepartment(config) {
128
129
  method: "POST",
129
130
  headers: {
130
131
  "Content-Type": "application/json",
131
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
132
+ ...(getAuthHeaders()),
132
133
  },
133
134
  body: JSON.stringify({
134
135
  name,
@@ -192,7 +193,7 @@ async function updateDepartment(config) {
192
193
  method: "PATCH",
193
194
  headers: {
194
195
  "Content-Type": "application/json",
195
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
196
+ ...(getAuthHeaders()),
196
197
  },
197
198
  body: JSON.stringify(updateData),
198
199
  });
@@ -225,7 +226,7 @@ async function deleteDepartment(config) {
225
226
  }
226
227
  const res = await fetch(`${config.apiUrl}/api/departments/${dept.id}`, {
227
228
  method: "DELETE",
228
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
229
+ headers: getAuthHeaders(),
229
230
  });
230
231
  if (!res.ok) {
231
232
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate, formatDate } from "./utils.js";
3
4
  export async function ideasMenu() {
4
5
  const config = ensureConfig();
@@ -40,7 +41,7 @@ export async function ideasMenu() {
40
41
  }
41
42
  async function fetchIdeas(config) {
42
43
  const res = await fetch(`${config.apiUrl}/api/ideas`, {
43
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
44
+ headers: getAuthHeaders(),
44
45
  });
45
46
  if (!res.ok)
46
47
  throw new Error(`API returned ${res.status}`);
@@ -93,7 +94,7 @@ async function viewIdea(config) {
93
94
  if (!idea)
94
95
  return;
95
96
  const res = await fetch(`${config.apiUrl}/api/ideas/${idea.id}`, {
96
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
97
+ headers: getAuthHeaders(),
97
98
  });
98
99
  if (!res.ok) {
99
100
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -137,7 +138,7 @@ async function createIdea(config) {
137
138
  method: "POST",
138
139
  headers: {
139
140
  "Content-Type": "application/json",
140
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
141
+ ...(getAuthHeaders()),
141
142
  },
142
143
  body: JSON.stringify({
143
144
  title,
@@ -214,7 +215,7 @@ async function updateIdea(config) {
214
215
  method: "PATCH",
215
216
  headers: {
216
217
  "Content-Type": "application/json",
217
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
218
+ ...(getAuthHeaders()),
218
219
  },
219
220
  body: JSON.stringify(updateData),
220
221
  });
@@ -258,7 +259,7 @@ async function convertIdea(config) {
258
259
  method: "POST",
259
260
  headers: {
260
261
  "Content-Type": "application/json",
261
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
262
+ ...(getAuthHeaders()),
262
263
  },
263
264
  body: JSON.stringify({ priority, assignee }),
264
265
  });
@@ -294,7 +295,7 @@ async function deleteIdea(config) {
294
295
  }
295
296
  const res = await fetch(`${config.apiUrl}/api/ideas/${idea.id}`, {
296
297
  method: "DELETE",
297
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
298
+ headers: getAuthHeaders(),
298
299
  });
299
300
  if (!res.ok) {
300
301
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -4,6 +4,7 @@
4
4
  * Provides menu-based infrastructure monitoring and management.
5
5
  */
6
6
  import { select, input, confirm } from "@inquirer/prompts";
7
+ import { getAuthHeaders } from "../config.js";
7
8
  import { ensureConfig, pressEnterToContinue, truncate, formatDate } from "./utils.js";
8
9
  export async function infraMenu() {
9
10
  const config = ensureConfig();
@@ -51,7 +52,7 @@ async function healthCheck(config) {
51
52
  try {
52
53
  console.log("\n Running health checks...\n");
53
54
  const res = await fetch(`${config.apiUrl}/api/infra/health`, {
54
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
55
+ headers: getAuthHeaders(),
55
56
  });
56
57
  if (!res.ok) {
57
58
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -84,7 +85,7 @@ async function healthCheck(config) {
84
85
  async function serviceStatus(config) {
85
86
  try {
86
87
  const res = await fetch(`${config.apiUrl}/api/infra/services`, {
87
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
88
+ headers: getAuthHeaders(),
88
89
  });
89
90
  if (!res.ok) {
90
91
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -116,7 +117,7 @@ async function serviceStatus(config) {
116
117
  async function vmStatus(config) {
117
118
  try {
118
119
  const res = await fetch(`${config.apiUrl}/api/vm-sessions`, {
119
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
120
+ headers: getAuthHeaders(),
120
121
  });
121
122
  if (!res.ok) {
122
123
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -148,7 +149,7 @@ async function vmStatus(config) {
148
149
  async function showMetrics(config) {
149
150
  try {
150
151
  const res = await fetch(`${config.apiUrl}/api/infra/metrics`, {
151
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
152
+ headers: getAuthHeaders(),
152
153
  });
153
154
  if (!res.ok) {
154
155
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -177,7 +178,7 @@ async function showMetrics(config) {
177
178
  async function showAlerts(config) {
178
179
  try {
179
180
  const res = await fetch(`${config.apiUrl}/api/infra/alerts`, {
180
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
181
+ headers: getAuthHeaders(),
181
182
  });
182
183
  if (!res.ok) {
183
184
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -225,7 +226,7 @@ async function restartService(config) {
225
226
  console.log(`\n Restarting ${serviceName}...`);
226
227
  const res = await fetch(`${config.apiUrl}/api/infra/services/${encodeURIComponent(serviceName)}/restart`, {
227
228
  method: "POST",
228
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
229
+ headers: getAuthHeaders(),
229
230
  });
230
231
  if (!res.ok) {
231
232
  const error = await res.text();
@@ -4,6 +4,7 @@
4
4
  * Provides menu-based PR preview deployment management.
5
5
  */
6
6
  import { select, input, confirm } from "@inquirer/prompts";
7
+ import { getAuthHeaders } from "../config.js";
7
8
  import { ensureConfig, pressEnterToContinue, truncate, formatDate } from "./utils.js";
8
9
  export async function previewMenu() {
9
10
  const config = ensureConfig();
@@ -46,7 +47,7 @@ export async function previewMenu() {
46
47
  async function listPreviews(config) {
47
48
  try {
48
49
  const res = await fetch(`${config.apiUrl}/api/previews`, {
49
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
50
+ headers: getAuthHeaders(),
50
51
  });
51
52
  if (!res.ok) {
52
53
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -88,7 +89,7 @@ async function previewStatus(config) {
88
89
  },
89
90
  });
90
91
  const res = await fetch(`${config.apiUrl}/api/previews/pr/${prNumber}`, {
91
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
92
+ headers: getAuthHeaders(),
92
93
  });
93
94
  if (!res.ok) {
94
95
  if (res.status === 404) {
@@ -142,7 +143,7 @@ async function deployPreview(config) {
142
143
  method: "POST",
143
144
  headers: {
144
145
  "Content-Type": "application/json",
145
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
146
+ ...(getAuthHeaders()),
146
147
  },
147
148
  body: JSON.stringify({ prNumber: parseInt(prNumber) }),
148
149
  });
@@ -189,7 +190,7 @@ async function deletePreview(config) {
189
190
  console.log(`\n Deleting preview for PR #${prNumber}...\n`);
190
191
  const res = await fetch(`${config.apiUrl}/api/previews/pr/${prNumber}`, {
191
192
  method: "DELETE",
192
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
193
+ headers: getAuthHeaders(),
193
194
  });
194
195
  if (!res.ok) {
195
196
  const error = await res.text();
@@ -219,7 +220,7 @@ async function cleanupPreviews(config) {
219
220
  console.log("\n Cleaning up old previews...\n");
220
221
  const res = await fetch(`${config.apiUrl}/api/previews/cleanup`, {
221
222
  method: "POST",
222
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
223
+ headers: getAuthHeaders(),
223
224
  });
224
225
  if (!res.ok) {
225
226
  const error = await res.text();
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  export async function processesMenu() {
4
5
  const config = ensureConfig();
@@ -36,7 +37,7 @@ export async function processesMenu() {
36
37
  }
37
38
  async function fetchProcesses(config) {
38
39
  const res = await fetch(`${config.apiUrl}/api/processes`, {
39
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
40
+ headers: getAuthHeaders(),
40
41
  });
41
42
  if (!res.ok)
42
43
  throw new Error(`API returned ${res.status}`);
@@ -87,7 +88,7 @@ async function viewProcess(config) {
87
88
  if (!proc)
88
89
  return;
89
90
  const res = await fetch(`${config.apiUrl}/api/processes/${proc.id}`, {
90
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
91
+ headers: getAuthHeaders(),
91
92
  });
92
93
  if (!res.ok) {
93
94
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -149,7 +150,7 @@ async function createProcess(config) {
149
150
  method: "POST",
150
151
  headers: {
151
152
  "Content-Type": "application/json",
152
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
153
+ ...(getAuthHeaders()),
153
154
  },
154
155
  body: JSON.stringify({
155
156
  name,
@@ -221,7 +222,7 @@ async function updateProcess(config) {
221
222
  method: "PATCH",
222
223
  headers: {
223
224
  "Content-Type": "application/json",
224
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
225
+ ...(getAuthHeaders()),
225
226
  },
226
227
  body: JSON.stringify(updateData),
227
228
  });
@@ -254,7 +255,7 @@ async function deleteProcess(config) {
254
255
  }
255
256
  const res = await fetch(`${config.apiUrl}/api/processes/${proc.id}`, {
256
257
  method: "DELETE",
257
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
258
+ headers: getAuthHeaders(),
258
259
  });
259
260
  if (!res.ok) {
260
261
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  export async function projectsMenu() {
4
5
  const config = ensureConfig();
@@ -36,7 +37,7 @@ export async function projectsMenu() {
36
37
  }
37
38
  async function fetchProjects(config) {
38
39
  const res = await fetch(`${config.apiUrl}/api/projects`, {
39
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
40
+ headers: getAuthHeaders(),
40
41
  });
41
42
  if (!res.ok)
42
43
  throw new Error(`API returned ${res.status}`);
@@ -88,7 +89,7 @@ async function viewProject(config) {
88
89
  if (!project)
89
90
  return;
90
91
  const res = await fetch(`${config.apiUrl}/api/projects/${project.id}`, {
91
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
92
+ headers: getAuthHeaders(),
92
93
  });
93
94
  if (!res.ok) {
94
95
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -148,7 +149,7 @@ async function createProject(config) {
148
149
  method: "POST",
149
150
  headers: {
150
151
  "Content-Type": "application/json",
151
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
152
+ ...(getAuthHeaders()),
152
153
  },
153
154
  body: JSON.stringify({
154
155
  name,
@@ -247,7 +248,7 @@ async function updateProject(config) {
247
248
  method: "PATCH",
248
249
  headers: {
249
250
  "Content-Type": "application/json",
250
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
251
+ ...(getAuthHeaders()),
251
252
  },
252
253
  body: JSON.stringify(updateData),
253
254
  });
@@ -280,7 +281,7 @@ async function deleteProject(config) {
280
281
  }
281
282
  const res = await fetch(`${config.apiUrl}/api/projects/${project.id}`, {
282
283
  method: "DELETE",
283
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
284
+ headers: getAuthHeaders(),
284
285
  });
285
286
  if (!res.ok) {
286
287
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  export async function roadmapsMenu() {
4
5
  const config = ensureConfig();
@@ -60,7 +61,7 @@ export async function roadmapsMenu() {
60
61
  }
61
62
  async function fetchRoadmaps(config) {
62
63
  const res = await fetch(`${config.apiUrl}/api/roadmaps`, {
63
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
64
+ headers: getAuthHeaders(),
64
65
  });
65
66
  if (!res.ok)
66
67
  throw new Error(`API returned ${res.status}`);
@@ -83,7 +84,7 @@ async function selectRoadmap(config, message) {
83
84
  return null;
84
85
  // Fetch full roadmap with phases/features
85
86
  const res = await fetch(`${config.apiUrl}/api/roadmaps/${roadmapId}`, {
86
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
87
+ headers: getAuthHeaders(),
87
88
  });
88
89
  if (!res.ok)
89
90
  return null;
@@ -175,7 +176,7 @@ async function createRoadmap(config) {
175
176
  method: "POST",
176
177
  headers: {
177
178
  "Content-Type": "application/json",
178
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
179
+ ...(getAuthHeaders()),
179
180
  },
180
181
  body: JSON.stringify({
181
182
  name,
@@ -244,7 +245,7 @@ async function updateRoadmap(config) {
244
245
  method: "PATCH",
245
246
  headers: {
246
247
  "Content-Type": "application/json",
247
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
248
+ ...(getAuthHeaders()),
248
249
  },
249
250
  body: JSON.stringify(updateData),
250
251
  });
@@ -277,7 +278,7 @@ async function addPhase(config) {
277
278
  method: "POST",
278
279
  headers: {
279
280
  "Content-Type": "application/json",
280
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
281
+ ...(getAuthHeaders()),
281
282
  },
282
283
  body: JSON.stringify({
283
284
  name,
@@ -348,7 +349,7 @@ async function addFeature(config) {
348
349
  method: "POST",
349
350
  headers: {
350
351
  "Content-Type": "application/json",
351
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
352
+ ...(getAuthHeaders()),
352
353
  },
353
354
  body: JSON.stringify({
354
355
  title,
@@ -453,7 +454,7 @@ async function updateFeature(config) {
453
454
  method: "PATCH",
454
455
  headers: {
455
456
  "Content-Type": "application/json",
456
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
457
+ ...(getAuthHeaders()),
457
458
  },
458
459
  body: JSON.stringify(updateData),
459
460
  });
@@ -512,7 +513,7 @@ async function convertFeature(config) {
512
513
  method: "POST",
513
514
  headers: {
514
515
  "Content-Type": "application/json",
515
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
516
+ ...(getAuthHeaders()),
516
517
  },
517
518
  body: JSON.stringify({ priority, assignee }),
518
519
  });
@@ -565,7 +566,7 @@ async function deleteFeature(config) {
565
566
  }
566
567
  const res = await fetch(`${config.apiUrl}/api/roadmaps/${roadmap.id}/features/${featureId}`, {
567
568
  method: "DELETE",
568
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
569
+ headers: getAuthHeaders(),
569
570
  });
570
571
  if (!res.ok) {
571
572
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -593,7 +594,7 @@ async function generateRoadmap(config) {
593
594
  method: "POST",
594
595
  headers: {
595
596
  "Content-Type": "application/json",
596
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
597
+ ...(getAuthHeaders()),
597
598
  },
598
599
  body: JSON.stringify({
599
600
  roadmapId: roadmap.id,
@@ -633,7 +634,7 @@ async function deleteRoadmap(config) {
633
634
  }
634
635
  const res = await fetch(`${config.apiUrl}/api/roadmaps/${roadmap.id}`, {
635
636
  method: "DELETE",
636
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
637
+ headers: getAuthHeaders(),
637
638
  });
638
639
  if (!res.ok) {
639
640
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  const STATUS_LABELS = {
4
5
  not_started: "Not Started",
@@ -47,7 +48,7 @@ export async function strategyMenu() {
47
48
  async function fetchStrategy(config) {
48
49
  try {
49
50
  const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
50
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
51
+ headers: getAuthHeaders(),
51
52
  });
52
53
  if (!res.ok)
53
54
  return null;
@@ -130,7 +131,7 @@ async function setVision(config) {
130
131
  method: "PATCH",
131
132
  headers: {
132
133
  "Content-Type": "application/json",
133
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
134
+ ...(getAuthHeaders()),
134
135
  },
135
136
  body: JSON.stringify({ vision }),
136
137
  });
@@ -159,7 +160,7 @@ async function setMission(config) {
159
160
  method: "PATCH",
160
161
  headers: {
161
162
  "Content-Type": "application/json",
162
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
163
+ ...(getAuthHeaders()),
163
164
  },
164
165
  body: JSON.stringify({ mission }),
165
166
  });
@@ -239,7 +240,7 @@ async function addValue(config) {
239
240
  method: "POST",
240
241
  headers: {
241
242
  "Content-Type": "application/json",
242
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
243
+ ...(getAuthHeaders()),
243
244
  },
244
245
  body: JSON.stringify({
245
246
  type: "value",
@@ -292,7 +293,7 @@ async function updateValue(config) {
292
293
  method: "PATCH",
293
294
  headers: {
294
295
  "Content-Type": "application/json",
295
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
296
+ ...(getAuthHeaders()),
296
297
  },
297
298
  body: JSON.stringify({ name, description }),
298
299
  });
@@ -337,7 +338,7 @@ async function deleteValue(config) {
337
338
  }
338
339
  const res = await fetch(`${config.apiUrl}/api/business-strategy/values/${valueId}`, {
339
340
  method: "DELETE",
340
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
341
+ headers: getAuthHeaders(),
341
342
  });
342
343
  if (!res.ok) {
343
344
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -435,7 +436,7 @@ async function addGoal(config) {
435
436
  method: "POST",
436
437
  headers: {
437
438
  "Content-Type": "application/json",
438
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
439
+ ...(getAuthHeaders()),
439
440
  },
440
441
  body: JSON.stringify({
441
442
  type: "goal",
@@ -530,7 +531,7 @@ async function updateGoal(config) {
530
531
  method: "PATCH",
531
532
  headers: {
532
533
  "Content-Type": "application/json",
533
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
534
+ ...(getAuthHeaders()),
534
535
  },
535
536
  body: JSON.stringify(updateData),
536
537
  });
@@ -575,7 +576,7 @@ async function deleteGoal(config) {
575
576
  }
576
577
  const res = await fetch(`${config.apiUrl}/api/business-strategy/goals/${goalId}`, {
577
578
  method: "DELETE",
578
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
579
+ headers: getAuthHeaders(),
579
580
  });
580
581
  if (!res.ok) {
581
582
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -662,7 +663,7 @@ async function addPersona(config) {
662
663
  method: "POST",
663
664
  headers: {
664
665
  "Content-Type": "application/json",
665
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
666
+ ...(getAuthHeaders()),
666
667
  },
667
668
  body: JSON.stringify({
668
669
  type: "persona",
@@ -723,7 +724,7 @@ async function updatePersona(config) {
723
724
  method: "PATCH",
724
725
  headers: {
725
726
  "Content-Type": "application/json",
726
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
727
+ ...(getAuthHeaders()),
727
728
  },
728
729
  body: JSON.stringify({ name, description, characteristics }),
729
730
  });
@@ -768,7 +769,7 @@ async function deletePersona(config) {
768
769
  }
769
770
  const res = await fetch(`${config.apiUrl}/api/business-strategy/personas/${personaId}`, {
770
771
  method: "DELETE",
771
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
772
+ headers: getAuthHeaders(),
772
773
  });
773
774
  if (!res.ok) {
774
775
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  import { resolveProject } from "../../lib/project-resolver.js";
4
5
  export async function tasksMenu() {
@@ -52,7 +53,7 @@ async function fetchTasks(config, status) {
52
53
  if (status)
53
54
  url.searchParams.set("status", status);
54
55
  const res = await fetch(url.toString(), {
55
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
56
+ headers: getAuthHeaders(),
56
57
  });
57
58
  if (!res.ok)
58
59
  throw new Error(`API returned ${res.status}`);
@@ -60,7 +61,7 @@ async function fetchTasks(config, status) {
60
61
  }
61
62
  async function fetchProjects(config) {
62
63
  const res = await fetch(`${config.apiUrl}/api/projects`, {
63
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
64
+ headers: getAuthHeaders(),
64
65
  });
65
66
  if (!res.ok)
66
67
  return [];
@@ -112,7 +113,7 @@ async function viewTask(config) {
112
113
  if (!task)
113
114
  return;
114
115
  const res = await fetch(`${config.apiUrl}/api/tasks/${task.id}`, {
115
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
116
+ headers: getAuthHeaders(),
116
117
  });
117
118
  if (!res.ok) {
118
119
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -215,7 +216,7 @@ async function createTask(config) {
215
216
  method: "POST",
216
217
  headers: {
217
218
  "Content-Type": "application/json",
218
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
219
+ ...(getAuthHeaders()),
219
220
  },
220
221
  body: JSON.stringify({
221
222
  title,
@@ -367,7 +368,7 @@ async function updateTask(config) {
367
368
  method: "PATCH",
368
369
  headers: {
369
370
  "Content-Type": "application/json",
370
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
371
+ ...(getAuthHeaders()),
371
372
  },
372
373
  body: JSON.stringify(updateData),
373
374
  });
@@ -391,7 +392,7 @@ async function startTask(config) {
391
392
  return;
392
393
  const res = await fetch(`${config.apiUrl}/api/tasks/${task.id}/start`, {
393
394
  method: "POST",
394
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
395
+ headers: getAuthHeaders(),
395
396
  });
396
397
  if (!res.ok) {
397
398
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -418,7 +419,7 @@ async function markTaskDone(config) {
418
419
  method: "POST",
419
420
  headers: {
420
421
  "Content-Type": "application/json",
421
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
422
+ ...(getAuthHeaders()),
422
423
  },
423
424
  body: JSON.stringify({ prUrl: prUrl || undefined }),
424
425
  });
@@ -451,7 +452,7 @@ async function deleteTask(config) {
451
452
  }
452
453
  const res = await fetch(`${config.apiUrl}/api/tasks/${task.id}`, {
453
454
  method: "DELETE",
454
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
455
+ headers: getAuthHeaders(),
455
456
  });
456
457
  if (!res.ok) {
457
458
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -4,6 +4,7 @@
4
4
  * Provides menu-based access to various utility tools.
5
5
  */
6
6
  import { select, input } from "@inquirer/prompts";
7
+ import { getAuthHeaders } from "../config.js";
7
8
  import { ensureConfig, pressEnterToContinue } from "./utils.js";
8
9
  import { spawnSync } from "child_process";
9
10
  /**
@@ -265,7 +266,7 @@ async function agentMessage() {
265
266
  method: "POST",
266
267
  headers: {
267
268
  "Content-Type": "application/json",
268
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
269
+ ...getAuthHeaders(),
269
270
  },
270
271
  body: JSON.stringify({
271
272
  type: "query",
@@ -304,7 +305,7 @@ async function agentMessage() {
304
305
  method: "POST",
305
306
  headers: {
306
307
  "Content-Type": "application/json",
307
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
308
+ ...getAuthHeaders(),
308
309
  },
309
310
  body: JSON.stringify({
310
311
  type: "approval_request",
@@ -341,7 +342,7 @@ async function agentMessage() {
341
342
  method: "POST",
342
343
  headers: {
343
344
  "Content-Type": "application/json",
344
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
345
+ ...getAuthHeaders(),
345
346
  },
346
347
  body: JSON.stringify({
347
348
  type: "status_update",
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  export async function vmSessionsMenu() {
4
5
  const config = ensureConfig();
@@ -48,7 +49,7 @@ export async function vmSessionsMenu() {
48
49
  }
49
50
  async function fetchVMSessions(config) {
50
51
  const res = await fetch(`${config.apiUrl}/api/vm-sessions`, {
51
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
52
+ headers: getAuthHeaders(),
52
53
  });
53
54
  if (!res.ok)
54
55
  throw new Error(`API returned ${res.status}`);
@@ -101,7 +102,7 @@ async function viewVMSession(config) {
101
102
  if (!session)
102
103
  return;
103
104
  const res = await fetch(`${config.apiUrl}/api/vm-sessions/${session.id}`, {
104
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
105
+ headers: getAuthHeaders(),
105
106
  });
106
107
  if (!res.ok) {
107
108
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -149,7 +150,7 @@ async function createVMSession(config) {
149
150
  method: "POST",
150
151
  headers: {
151
152
  "Content-Type": "application/json",
152
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
153
+ ...(getAuthHeaders()),
153
154
  },
154
155
  body: JSON.stringify({
155
156
  name,
@@ -185,7 +186,7 @@ async function startVM(config) {
185
186
  console.log("\n Starting VM... This may take a moment.\n");
186
187
  const res = await fetch(`${config.apiUrl}/api/vm-sessions/${session.id}/start`, {
187
188
  method: "POST",
188
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
189
+ headers: getAuthHeaders(),
189
190
  });
190
191
  if (!res.ok) {
191
192
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -207,7 +208,7 @@ async function stopVM(config) {
207
208
  return;
208
209
  const res = await fetch(`${config.apiUrl}/api/vm-sessions/${session.id}/stop`, {
209
210
  method: "POST",
210
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
211
+ headers: getAuthHeaders(),
211
212
  });
212
213
  if (!res.ok) {
213
214
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -228,7 +229,7 @@ async function viewLogs(config) {
228
229
  if (!session)
229
230
  return;
230
231
  const res = await fetch(`${config.apiUrl}/api/vm-sessions/${session.id}/logs?tail=30`, {
231
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
232
+ headers: getAuthHeaders(),
232
233
  });
233
234
  if (!res.ok) {
234
235
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -271,7 +272,7 @@ async function approvePlan(config) {
271
272
  }
272
273
  const res = await fetch(`${config.apiUrl}/api/vm-sessions/${session.id}/approve`, {
273
274
  method: "POST",
274
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
275
+ headers: getAuthHeaders(),
275
276
  });
276
277
  if (!res.ok) {
277
278
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -302,7 +303,7 @@ async function deleteVMSession(config) {
302
303
  }
303
304
  const res = await fetch(`${config.apiUrl}/api/vm-sessions/${session.id}`, {
304
305
  method: "DELETE",
305
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
306
+ headers: getAuthHeaders(),
306
307
  });
307
308
  if (!res.ok) {
308
309
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -4,7 +4,7 @@
4
4
  * Provides menu-based worker registration and management.
5
5
  */
6
6
  import { select, input } from "@inquirer/prompts";
7
- import { getConfig, setConfig } from "../config.js";
7
+ import { getConfig, setConfig, getAuthHeaders } from "../config.js";
8
8
  import { ensureConfig, pressEnterToContinue, truncate, formatDate } from "./utils.js";
9
9
  export async function workerMenu() {
10
10
  const config = ensureConfig();
@@ -70,7 +70,7 @@ async function registerWorker(config) {
70
70
  method: "POST",
71
71
  headers: {
72
72
  "Content-Type": "application/json",
73
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
73
+ ...(getAuthHeaders()),
74
74
  },
75
75
  body: JSON.stringify({ name }),
76
76
  });
@@ -101,7 +101,7 @@ async function registerWorker(config) {
101
101
  async function listWorkers(config) {
102
102
  try {
103
103
  const res = await fetch(`${config.apiUrl}/api/workers`, {
104
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
104
+ headers: getAuthHeaders(),
105
105
  });
106
106
  if (!res.ok) {
107
107
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -133,7 +133,7 @@ async function listWorkers(config) {
133
133
  async function showActivity(config) {
134
134
  try {
135
135
  const res = await fetch(`${config.apiUrl}/api/workers/activity`, {
136
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
136
+ headers: getAuthHeaders(),
137
137
  });
138
138
  if (!res.ok) {
139
139
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -176,7 +176,7 @@ async function sendHeartbeat(config) {
176
176
  method: "POST",
177
177
  headers: {
178
178
  "Content-Type": "application/json",
179
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
179
+ ...(getAuthHeaders()),
180
180
  },
181
181
  body: JSON.stringify({ status: "active" }),
182
182
  });
@@ -1,4 +1,5 @@
1
1
  import { select, input, confirm } from "@inquirer/prompts";
2
+ import { getAuthHeaders } from "../config.js";
2
3
  import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
4
  export async function workflowsMenu() {
4
5
  const config = ensureConfig();
@@ -44,7 +45,7 @@ export async function workflowsMenu() {
44
45
  }
45
46
  async function fetchWorkflows(config) {
46
47
  const res = await fetch(`${config.apiUrl}/api/workflows`, {
47
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
48
+ headers: getAuthHeaders(),
48
49
  });
49
50
  if (!res.ok)
50
51
  throw new Error(`API returned ${res.status}`);
@@ -96,7 +97,7 @@ async function viewWorkflow(config) {
96
97
  if (!wf)
97
98
  return;
98
99
  const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
99
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
100
+ headers: getAuthHeaders(),
100
101
  });
101
102
  if (!res.ok) {
102
103
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -174,7 +175,7 @@ async function createWorkflow(config) {
174
175
  method: "POST",
175
176
  headers: {
176
177
  "Content-Type": "application/json",
177
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
178
+ ...(getAuthHeaders()),
178
179
  },
179
180
  body: JSON.stringify({
180
181
  name,
@@ -261,7 +262,7 @@ async function updateWorkflow(config) {
261
262
  method: "PATCH",
262
263
  headers: {
263
264
  "Content-Type": "application/json",
264
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
265
+ ...(getAuthHeaders()),
265
266
  },
266
267
  body: JSON.stringify(updateData),
267
268
  });
@@ -294,7 +295,7 @@ async function deleteWorkflow(config) {
294
295
  }
295
296
  const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
296
297
  method: "DELETE",
297
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
298
+ headers: getAuthHeaders(),
298
299
  });
299
300
  if (!res.ok) {
300
301
  console.error(`\n Error: API returned ${res.status}\n`);
@@ -325,7 +326,7 @@ async function manageSteps(config) {
325
326
  return;
326
327
  if (action === "list") {
327
328
  const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
328
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
329
+ headers: getAuthHeaders(),
329
330
  });
330
331
  const fullWf = await res.json();
331
332
  console.log(`\n Steps for: ${fullWf.name}`);
@@ -356,7 +357,7 @@ async function manageSteps(config) {
356
357
  method: "POST",
357
358
  headers: {
358
359
  "Content-Type": "application/json",
359
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
360
+ ...(getAuthHeaders()),
360
361
  },
361
362
  body: JSON.stringify({ name, description: description || undefined }),
362
363
  });
@@ -370,7 +371,7 @@ async function manageSteps(config) {
370
371
  }
371
372
  else if (action === "delete") {
372
373
  const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
373
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
374
+ headers: getAuthHeaders(),
374
375
  });
375
376
  const fullWf = await res.json();
376
377
  if (!fullWf.steps || fullWf.steps.length === 0) {
@@ -388,7 +389,7 @@ async function manageSteps(config) {
388
389
  return;
389
390
  const delRes = await fetch(`${config.apiUrl}/api/workflows/${wf.id}/steps/${stepId}`, {
390
391
  method: "DELETE",
391
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
392
+ headers: getAuthHeaders(),
392
393
  });
393
394
  if (!delRes.ok) {
394
395
  console.error(`\n Error: API returned ${delRes.status}\n`);
@@ -420,7 +421,7 @@ async function generateSteps(config) {
420
421
  method: "POST",
421
422
  headers: {
422
423
  "Content-Type": "application/json",
423
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
424
+ ...(getAuthHeaders()),
424
425
  },
425
426
  body: JSON.stringify({ sop }),
426
427
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonfestl/husky-cli",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "CLI for Huskyv0 Task Orchestration with Claude Agent SDK",
5
5
  "type": "module",
6
6
  "bin": {