@simonfestl/husky-cli 1.15.0 → 1.16.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.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Interactive Mode: Auth Module
3
+ *
4
+ * Provides menu-based authentication and API key management.
5
+ */
6
+ export declare function authMenu(): Promise<void>;
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Interactive Mode: Auth Module
3
+ *
4
+ * Provides menu-based authentication and API key management.
5
+ */
6
+ import { select, input, confirm } from "@inquirer/prompts";
7
+ import { getConfig, setConfig, setSessionConfig, clearSessionConfig } from "../config.js";
8
+ import { pressEnterToContinue } from "./utils.js";
9
+ export async function authMenu() {
10
+ console.log("\n AUTH");
11
+ console.log(" " + "-".repeat(50));
12
+ console.log(" API keys and session management");
13
+ console.log("");
14
+ const menuItems = [
15
+ { name: "🔑 Login (Create Session)", value: "login" },
16
+ { name: "📊 Session Status", value: "session" },
17
+ { name: "🔄 Refresh Token", value: "refresh" },
18
+ { name: "🚪 Logout", value: "logout" },
19
+ { name: "🔐 Show API Key", value: "show-key" },
20
+ { name: "✏️ Set API Key", value: "set-key" },
21
+ { name: "← Back", value: "back" },
22
+ ];
23
+ const choice = await select({
24
+ message: "Auth Action:",
25
+ choices: menuItems,
26
+ });
27
+ switch (choice) {
28
+ case "login":
29
+ await login();
30
+ break;
31
+ case "session":
32
+ await sessionStatus();
33
+ break;
34
+ case "refresh":
35
+ await refreshToken();
36
+ break;
37
+ case "logout":
38
+ await logout();
39
+ break;
40
+ case "show-key":
41
+ await showApiKey();
42
+ break;
43
+ case "set-key":
44
+ await setApiKey();
45
+ break;
46
+ case "back":
47
+ return;
48
+ }
49
+ }
50
+ async function login() {
51
+ try {
52
+ const config = getConfig();
53
+ if (!config.apiUrl) {
54
+ console.error("\n Error: API URL not configured. Run 'husky config set api-url <url>' first.\n");
55
+ await pressEnterToContinue();
56
+ return;
57
+ }
58
+ const agentName = await input({
59
+ message: "Agent name:",
60
+ default: "interactive-cli",
61
+ validate: (v) => (v.length > 0 ? true : "Agent name is required"),
62
+ });
63
+ console.log("\n Creating session...");
64
+ const res = await fetch(`${config.apiUrl}/api/auth/session`, {
65
+ method: "POST",
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
69
+ },
70
+ body: JSON.stringify({ agent: agentName }),
71
+ });
72
+ if (!res.ok) {
73
+ const error = await res.text();
74
+ console.error(`\n Error: ${error}\n`);
75
+ }
76
+ else {
77
+ const data = await res.json();
78
+ // Save session token
79
+ setSessionConfig({
80
+ token: data.token,
81
+ expiresAt: data.expiresAt,
82
+ agent: agentName,
83
+ role: data.role || "",
84
+ });
85
+ console.log("\n Session created!");
86
+ console.log(` Agent: ${agentName}`);
87
+ console.log(` Expires: ${new Date(data.expiresAt).toLocaleString()}`);
88
+ console.log("");
89
+ }
90
+ await pressEnterToContinue();
91
+ }
92
+ catch (error) {
93
+ console.error("\n Error creating session:", error);
94
+ await pressEnterToContinue();
95
+ }
96
+ }
97
+ async function sessionStatus() {
98
+ try {
99
+ const config = getConfig();
100
+ console.log("\n SESSION STATUS");
101
+ console.log(" " + "-".repeat(50));
102
+ if (!config.sessionToken) {
103
+ console.log(" No active session.");
104
+ console.log(" Use 'Login' to create a new session.");
105
+ }
106
+ else {
107
+ const expiresAt = config.sessionExpiresAt ? new Date(config.sessionExpiresAt) : null;
108
+ const isExpired = expiresAt ? expiresAt < new Date() : true;
109
+ console.log(` Agent: ${config.sessionAgent || "unknown"}`);
110
+ console.log(` Token: ${config.sessionToken.substring(0, 20)}...`);
111
+ console.log(` Expires: ${expiresAt ? expiresAt.toLocaleString() : "unknown"}`);
112
+ console.log(` Status: ${isExpired ? "❌ EXPIRED" : "✅ VALID"}`);
113
+ }
114
+ console.log("");
115
+ await pressEnterToContinue();
116
+ }
117
+ catch (error) {
118
+ console.error("\n Error checking session:", error);
119
+ await pressEnterToContinue();
120
+ }
121
+ }
122
+ async function refreshToken() {
123
+ try {
124
+ const config = getConfig();
125
+ if (!config.apiUrl) {
126
+ console.error("\n Error: API URL not configured.\n");
127
+ await pressEnterToContinue();
128
+ return;
129
+ }
130
+ if (!config.sessionToken) {
131
+ console.error("\n Error: No active session to refresh.\n");
132
+ await pressEnterToContinue();
133
+ return;
134
+ }
135
+ console.log("\n Refreshing token...");
136
+ const res = await fetch(`${config.apiUrl}/api/auth/refresh`, {
137
+ method: "POST",
138
+ headers: {
139
+ "Content-Type": "application/json",
140
+ Authorization: `Bearer ${config.sessionToken}`,
141
+ },
142
+ });
143
+ if (!res.ok) {
144
+ const error = await res.text();
145
+ console.error(`\n Error: ${error}\n`);
146
+ }
147
+ else {
148
+ const data = await res.json();
149
+ const currentConfig = getConfig();
150
+ setSessionConfig({
151
+ token: data.token,
152
+ expiresAt: data.expiresAt,
153
+ agent: currentConfig.sessionAgent || "",
154
+ role: data.role || currentConfig.sessionRole || "",
155
+ });
156
+ console.log("\n Token refreshed!");
157
+ console.log(` New expiry: ${new Date(data.expiresAt).toLocaleString()}`);
158
+ console.log("");
159
+ }
160
+ await pressEnterToContinue();
161
+ }
162
+ catch (error) {
163
+ console.error("\n Error refreshing token:", error);
164
+ await pressEnterToContinue();
165
+ }
166
+ }
167
+ async function logout() {
168
+ try {
169
+ const config = getConfig();
170
+ if (!config.sessionToken) {
171
+ console.log("\n No active session.\n");
172
+ await pressEnterToContinue();
173
+ return;
174
+ }
175
+ const confirmed = await confirm({
176
+ message: "Are you sure you want to logout?",
177
+ default: true,
178
+ });
179
+ if (!confirmed) {
180
+ console.log("\n Cancelled.\n");
181
+ await pressEnterToContinue();
182
+ return;
183
+ }
184
+ // Clear session from config
185
+ clearSessionConfig();
186
+ console.log("\n Logged out successfully!\n");
187
+ await pressEnterToContinue();
188
+ }
189
+ catch (error) {
190
+ console.error("\n Error logging out:", error);
191
+ await pressEnterToContinue();
192
+ }
193
+ }
194
+ async function showApiKey() {
195
+ const config = getConfig();
196
+ console.log("\n API KEY");
197
+ console.log(" " + "-".repeat(50));
198
+ if (!config.apiKey) {
199
+ console.log(" No API key configured.");
200
+ }
201
+ else {
202
+ const masked = config.apiKey.substring(0, 8) + "..." + config.apiKey.substring(config.apiKey.length - 4);
203
+ console.log(` API Key: ${masked}`);
204
+ console.log(` Length: ${config.apiKey.length} characters`);
205
+ }
206
+ console.log("");
207
+ await pressEnterToContinue();
208
+ }
209
+ async function setApiKey() {
210
+ try {
211
+ const key = await input({
212
+ message: "New API Key:",
213
+ validate: (v) => {
214
+ if (v.length < 16)
215
+ return "API key must be at least 16 characters";
216
+ return true;
217
+ },
218
+ });
219
+ setConfig("apiKey", key);
220
+ console.log("\n API Key saved!\n");
221
+ await pressEnterToContinue();
222
+ }
223
+ catch (error) {
224
+ console.error("\n Error setting API key:", error);
225
+ await pressEnterToContinue();
226
+ }
227
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Interactive Mode: Brain Module
3
+ *
4
+ * Provides menu-based agent memory and learning management.
5
+ */
6
+ export declare function brainMenu(): Promise<void>;
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Interactive Mode: Brain Module
3
+ *
4
+ * Provides menu-based agent memory and learning management.
5
+ */
6
+ import { select, input, confirm } from "@inquirer/prompts";
7
+ import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
8
+ export async function brainMenu() {
9
+ const config = ensureConfig();
10
+ console.log("\n BRAIN");
11
+ console.log(" " + "-".repeat(50));
12
+ console.log(" Agent memory and learning management");
13
+ console.log("");
14
+ const menuItems = [
15
+ { name: "📋 List Memories", value: "list" },
16
+ { name: "🔍 Recall (Search)", value: "recall" },
17
+ { name: "💡 Remember (Add)", value: "remember" },
18
+ { name: "📤 Publish Memory", value: "publish" },
19
+ { name: "👥 Shared Memories", value: "shared" },
20
+ { name: "👍 Boost Memory", value: "boost" },
21
+ { name: "📦 Archive", value: "archive" },
22
+ { name: "🗑️ Delete", value: "delete" },
23
+ { name: "🧹 Cleanup Old", value: "cleanup" },
24
+ { name: "← Back", value: "back" },
25
+ ];
26
+ const choice = await select({
27
+ message: "Brain Action:",
28
+ choices: menuItems,
29
+ });
30
+ switch (choice) {
31
+ case "list":
32
+ await listMemories(config);
33
+ break;
34
+ case "recall":
35
+ await recallMemory(config);
36
+ break;
37
+ case "remember":
38
+ await rememberContent(config);
39
+ break;
40
+ case "publish":
41
+ await publishMemory(config);
42
+ break;
43
+ case "shared":
44
+ await sharedMemories(config);
45
+ break;
46
+ case "boost":
47
+ await boostMemory(config);
48
+ break;
49
+ case "archive":
50
+ await archiveMemory(config);
51
+ break;
52
+ case "delete":
53
+ await deleteMemory(config);
54
+ break;
55
+ case "cleanup":
56
+ await cleanupMemories(config);
57
+ break;
58
+ case "back":
59
+ return;
60
+ }
61
+ }
62
+ async function listMemories(config) {
63
+ try {
64
+ const limit = await input({
65
+ message: "Number of memories to show (default 20):",
66
+ default: "20",
67
+ });
68
+ const res = await fetch(`${config.apiUrl}/api/brain/memories?limit=${limit}`, {
69
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
70
+ });
71
+ if (!res.ok) {
72
+ console.error(`\n Error: API returned ${res.status}\n`);
73
+ await pressEnterToContinue();
74
+ return;
75
+ }
76
+ const memories = await res.json();
77
+ console.log("\n MEMORIES");
78
+ console.log(" " + "-".repeat(70));
79
+ if (memories.length === 0) {
80
+ console.log(" No memories found.");
81
+ }
82
+ else {
83
+ console.log(` ${"ID".padEnd(12)} ${"SCORE".padEnd(8)} ${"TAGS".padEnd(20)} ${"CONTENT"}`);
84
+ console.log(" " + "-".repeat(70));
85
+ for (const mem of memories) {
86
+ const score = mem.qualityScore !== undefined ? String(mem.qualityScore) : "-";
87
+ const tags = mem.tags.slice(0, 2).join(", ");
88
+ console.log(` ${mem.id.substring(0, 10).padEnd(12)} ${score.padEnd(8)} ${truncate(tags, 18).padEnd(20)} ${truncate(mem.content, 30)}`);
89
+ }
90
+ }
91
+ console.log("");
92
+ await pressEnterToContinue();
93
+ }
94
+ catch (error) {
95
+ console.error("\n Error fetching memories:", error);
96
+ await pressEnterToContinue();
97
+ }
98
+ }
99
+ async function recallMemory(config) {
100
+ try {
101
+ const query = await input({
102
+ message: "Search query:",
103
+ validate: (v) => (v.length > 0 ? true : "Query is required"),
104
+ });
105
+ const res = await fetch(`${config.apiUrl}/api/brain/recall?query=${encodeURIComponent(query)}`, {
106
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
107
+ });
108
+ if (!res.ok) {
109
+ console.error(`\n Error: API returned ${res.status}\n`);
110
+ await pressEnterToContinue();
111
+ return;
112
+ }
113
+ const memories = await res.json();
114
+ console.log("\n SEARCH RESULTS");
115
+ console.log(" " + "-".repeat(70));
116
+ if (memories.length === 0) {
117
+ console.log(" No matching memories found.");
118
+ }
119
+ else {
120
+ for (const mem of memories) {
121
+ console.log(` ID: ${mem.id}`);
122
+ console.log(` Tags: ${mem.tags.join(", ")}`);
123
+ console.log(` Content: ${mem.content.substring(0, 100)}...`);
124
+ console.log(" " + "-".repeat(40));
125
+ }
126
+ }
127
+ console.log("");
128
+ await pressEnterToContinue();
129
+ }
130
+ catch (error) {
131
+ console.error("\n Error searching memories:", error);
132
+ await pressEnterToContinue();
133
+ }
134
+ }
135
+ async function rememberContent(config) {
136
+ try {
137
+ const content = await input({
138
+ message: "Content to remember:",
139
+ validate: (v) => (v.length > 0 ? true : "Content is required"),
140
+ });
141
+ const tagsInput = await input({
142
+ message: "Tags (comma-separated):",
143
+ default: "",
144
+ });
145
+ const tags = tagsInput.split(",").map(t => t.trim()).filter(t => t.length > 0);
146
+ const res = await fetch(`${config.apiUrl}/api/brain/remember`, {
147
+ method: "POST",
148
+ headers: {
149
+ "Content-Type": "application/json",
150
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
151
+ },
152
+ body: JSON.stringify({ content, tags }),
153
+ });
154
+ if (!res.ok) {
155
+ const error = await res.text();
156
+ console.error(`\n Error: ${error}\n`);
157
+ }
158
+ else {
159
+ const data = await res.json();
160
+ console.log(`\n Memory saved! ID: ${data.id || "created"}\n`);
161
+ }
162
+ await pressEnterToContinue();
163
+ }
164
+ catch (error) {
165
+ console.error("\n Error saving memory:", error);
166
+ await pressEnterToContinue();
167
+ }
168
+ }
169
+ async function publishMemory(config) {
170
+ try {
171
+ const id = await input({
172
+ message: "Memory ID to publish:",
173
+ validate: (v) => (v.length > 0 ? true : "ID is required"),
174
+ });
175
+ const visibility = await select({
176
+ message: "Visibility:",
177
+ choices: [
178
+ { name: "Team", value: "team" },
179
+ { name: "Public", value: "public" },
180
+ ],
181
+ });
182
+ const res = await fetch(`${config.apiUrl}/api/brain/publish/${id}`, {
183
+ method: "POST",
184
+ headers: {
185
+ "Content-Type": "application/json",
186
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
187
+ },
188
+ body: JSON.stringify({ visibility }),
189
+ });
190
+ if (!res.ok) {
191
+ const error = await res.text();
192
+ console.error(`\n Error: ${error}\n`);
193
+ }
194
+ else {
195
+ console.log(`\n Memory published with visibility: ${visibility}\n`);
196
+ }
197
+ await pressEnterToContinue();
198
+ }
199
+ catch (error) {
200
+ console.error("\n Error publishing memory:", error);
201
+ await pressEnterToContinue();
202
+ }
203
+ }
204
+ async function sharedMemories(config) {
205
+ try {
206
+ const res = await fetch(`${config.apiUrl}/api/brain/shared`, {
207
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
208
+ });
209
+ if (!res.ok) {
210
+ console.error(`\n Error: API returned ${res.status}\n`);
211
+ await pressEnterToContinue();
212
+ return;
213
+ }
214
+ const memories = await res.json();
215
+ console.log("\n SHARED MEMORIES");
216
+ console.log(" " + "-".repeat(70));
217
+ if (memories.length === 0) {
218
+ console.log(" No shared memories found.");
219
+ }
220
+ else {
221
+ for (const mem of memories) {
222
+ console.log(` ID: ${mem.id}`);
223
+ console.log(` From: ${mem.agentId}`);
224
+ console.log(` Score: ${mem.qualityScore || "-"}`);
225
+ console.log(` Content: ${truncate(mem.content, 60)}`);
226
+ console.log(" " + "-".repeat(40));
227
+ }
228
+ }
229
+ console.log("");
230
+ await pressEnterToContinue();
231
+ }
232
+ catch (error) {
233
+ console.error("\n Error fetching shared memories:", error);
234
+ await pressEnterToContinue();
235
+ }
236
+ }
237
+ async function boostMemory(config) {
238
+ try {
239
+ const id = await input({
240
+ message: "Memory ID to boost:",
241
+ validate: (v) => (v.length > 0 ? true : "ID is required"),
242
+ });
243
+ const res = await fetch(`${config.apiUrl}/api/brain/boost/${id}`, {
244
+ method: "POST",
245
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
246
+ });
247
+ if (!res.ok) {
248
+ const error = await res.text();
249
+ console.error(`\n Error: ${error}\n`);
250
+ }
251
+ else {
252
+ console.log(`\n Memory boosted!\n`);
253
+ }
254
+ await pressEnterToContinue();
255
+ }
256
+ catch (error) {
257
+ console.error("\n Error boosting memory:", error);
258
+ await pressEnterToContinue();
259
+ }
260
+ }
261
+ async function archiveMemory(config) {
262
+ try {
263
+ const id = await input({
264
+ message: "Memory ID to archive:",
265
+ validate: (v) => (v.length > 0 ? true : "ID is required"),
266
+ });
267
+ const confirmed = await confirm({
268
+ message: "Are you sure you want to archive this memory?",
269
+ default: false,
270
+ });
271
+ if (!confirmed) {
272
+ console.log("\n Cancelled.\n");
273
+ await pressEnterToContinue();
274
+ return;
275
+ }
276
+ const res = await fetch(`${config.apiUrl}/api/brain/archive/${id}`, {
277
+ method: "POST",
278
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
279
+ });
280
+ if (!res.ok) {
281
+ const error = await res.text();
282
+ console.error(`\n Error: ${error}\n`);
283
+ }
284
+ else {
285
+ console.log(`\n Memory archived!\n`);
286
+ }
287
+ await pressEnterToContinue();
288
+ }
289
+ catch (error) {
290
+ console.error("\n Error archiving memory:", error);
291
+ await pressEnterToContinue();
292
+ }
293
+ }
294
+ async function deleteMemory(config) {
295
+ try {
296
+ const id = await input({
297
+ message: "Memory ID to delete:",
298
+ validate: (v) => (v.length > 0 ? true : "ID is required"),
299
+ });
300
+ const confirmed = await confirm({
301
+ message: "Are you sure you want to DELETE this memory? This cannot be undone.",
302
+ default: false,
303
+ });
304
+ if (!confirmed) {
305
+ console.log("\n Cancelled.\n");
306
+ await pressEnterToContinue();
307
+ return;
308
+ }
309
+ const res = await fetch(`${config.apiUrl}/api/brain/memories/${id}`, {
310
+ method: "DELETE",
311
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
312
+ });
313
+ if (!res.ok) {
314
+ const error = await res.text();
315
+ console.error(`\n Error: ${error}\n`);
316
+ }
317
+ else {
318
+ console.log(`\n Memory deleted!\n`);
319
+ }
320
+ await pressEnterToContinue();
321
+ }
322
+ catch (error) {
323
+ console.error("\n Error deleting memory:", error);
324
+ await pressEnterToContinue();
325
+ }
326
+ }
327
+ async function cleanupMemories(config) {
328
+ try {
329
+ const confirmed = await confirm({
330
+ message: "Clean up old/low-quality memories?",
331
+ default: false,
332
+ });
333
+ if (!confirmed) {
334
+ console.log("\n Cancelled.\n");
335
+ await pressEnterToContinue();
336
+ return;
337
+ }
338
+ const res = await fetch(`${config.apiUrl}/api/brain/cleanup`, {
339
+ method: "POST",
340
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
341
+ });
342
+ if (!res.ok) {
343
+ const error = await res.text();
344
+ console.error(`\n Error: ${error}\n`);
345
+ }
346
+ else {
347
+ const data = await res.json();
348
+ console.log(`\n Cleanup complete! Removed: ${data.removed || 0} memories\n`);
349
+ }
350
+ await pressEnterToContinue();
351
+ }
352
+ catch (error) {
353
+ console.error("\n Error cleaning up:", error);
354
+ await pressEnterToContinue();
355
+ }
356
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Interactive Mode: Chat Module
3
+ *
4
+ * Provides menu-based Google Chat integration and messaging.
5
+ */
6
+ export declare function chatMenu(): Promise<void>;