olakai-cli 0.6.3 → 0.6.5
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/dist/api-63UDJX5M.js +70 -0
- package/dist/chunk-2Q7JYGCK.js +316 -0
- package/dist/chunk-2Q7JYGCK.js.map +1 -0
- package/dist/{chunk-OHPX4STO.js → chunk-AVB4N2UN.js} +2 -310
- package/dist/chunk-AVB4N2UN.js.map +1 -0
- package/dist/chunk-GU4HEL24.js +876 -0
- package/dist/chunk-GU4HEL24.js.map +1 -0
- package/dist/index.js +246 -1033
- package/dist/index.js.map +1 -1
- package/dist/{status-CAHO5FHI.js → status-USHUUHK6.js} +3 -2
- package/dist/status-USHUUHK6.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-OHPX4STO.js.map +0 -1
- /package/dist/{status-CAHO5FHI.js.map → api-63UDJX5M.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,31 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createAgent,
|
|
4
|
+
createCustomDataConfig,
|
|
5
|
+
createKpi,
|
|
6
|
+
createWorkflow,
|
|
7
|
+
deleteAgent,
|
|
8
|
+
deleteCustomDataConfig,
|
|
9
|
+
deleteKpi,
|
|
10
|
+
deleteWorkflow,
|
|
11
|
+
getActivity,
|
|
12
|
+
getActivityKpis,
|
|
13
|
+
getAgent,
|
|
14
|
+
getCurrentUser,
|
|
15
|
+
getCustomDataConfig,
|
|
16
|
+
getKpi,
|
|
17
|
+
getKpiContextVariables,
|
|
18
|
+
getWorkflow,
|
|
19
|
+
listActivity,
|
|
20
|
+
listAgents,
|
|
21
|
+
listCustomDataConfigs,
|
|
22
|
+
listKpis,
|
|
23
|
+
listMyAgents,
|
|
24
|
+
listSessions,
|
|
25
|
+
listWorkflows,
|
|
26
|
+
pollForToken,
|
|
27
|
+
regenerateAgentApiKey,
|
|
28
|
+
requestDeviceCode,
|
|
29
|
+
updateAgent,
|
|
30
|
+
updateCustomDataConfig,
|
|
31
|
+
updateKpi,
|
|
32
|
+
updateWorkflow,
|
|
33
|
+
validateKpiFormula
|
|
34
|
+
} from "./chunk-GU4HEL24.js";
|
|
2
35
|
import {
|
|
3
36
|
CLAUDE_DIR,
|
|
4
|
-
CLIENT_ID,
|
|
5
|
-
HOSTS,
|
|
6
37
|
OLAKAI_DIR,
|
|
7
38
|
OLAKAI_HOOK_MARKER,
|
|
8
39
|
SETTINGS_FILE,
|
|
9
|
-
clearToken,
|
|
10
40
|
deleteClaudeCodeConfig,
|
|
11
41
|
findConfiguredWorkspace,
|
|
12
|
-
getBaseUrl,
|
|
13
42
|
getClaudeCodeConfigPath,
|
|
14
43
|
getClaudeCodeStatus,
|
|
15
44
|
getClaudeDir,
|
|
16
|
-
getEnvironment,
|
|
17
45
|
getLegacyClaudeMonitorConfigPath,
|
|
18
46
|
getMonitorConfigPath,
|
|
19
47
|
getSettingsPath,
|
|
48
|
+
loadClaudeCodeConfig,
|
|
49
|
+
mergeHooksSettings,
|
|
50
|
+
readJsonFile,
|
|
51
|
+
writeClaudeCodeConfig,
|
|
52
|
+
writeJsonFile
|
|
53
|
+
} from "./chunk-2Q7JYGCK.js";
|
|
54
|
+
import {
|
|
55
|
+
HOSTS,
|
|
56
|
+
clearToken,
|
|
57
|
+
getBaseUrl,
|
|
58
|
+
getEnvironment,
|
|
20
59
|
getValidEnvironments,
|
|
21
60
|
getValidToken,
|
|
22
61
|
isTokenValid,
|
|
23
62
|
isValidEnvironment,
|
|
24
|
-
loadClaudeCodeConfig,
|
|
25
63
|
loadToken,
|
|
26
|
-
mergeHooksSettings,
|
|
27
64
|
patchProfile,
|
|
28
|
-
readJsonFile,
|
|
29
65
|
readProfilesFile,
|
|
30
66
|
removeProfile,
|
|
31
67
|
resolveActiveProfile,
|
|
@@ -34,10 +70,8 @@ import {
|
|
|
34
70
|
setDefaultProfile,
|
|
35
71
|
setEnvironment,
|
|
36
72
|
setHostOverride,
|
|
37
|
-
setProfileOverride
|
|
38
|
-
|
|
39
|
-
writeJsonFile
|
|
40
|
-
} from "./chunk-OHPX4STO.js";
|
|
73
|
+
setProfileOverride
|
|
74
|
+
} from "./chunk-AVB4N2UN.js";
|
|
41
75
|
|
|
42
76
|
// src/index.ts
|
|
43
77
|
import { createRequire } from "module";
|
|
@@ -45,753 +79,6 @@ import { Command } from "commander";
|
|
|
45
79
|
|
|
46
80
|
// src/commands/login.ts
|
|
47
81
|
import open from "open";
|
|
48
|
-
|
|
49
|
-
// src/lib/api.ts
|
|
50
|
-
async function requestDeviceCode() {
|
|
51
|
-
const response = await fetch(`${getBaseUrl()}/api/auth/device/code`, {
|
|
52
|
-
method: "POST",
|
|
53
|
-
headers: {
|
|
54
|
-
"Content-Type": "application/json"
|
|
55
|
-
},
|
|
56
|
-
body: JSON.stringify({ client_id: CLIENT_ID })
|
|
57
|
-
});
|
|
58
|
-
if (!response.ok) {
|
|
59
|
-
const error = await response.json();
|
|
60
|
-
throw new Error(error.error_description || error.error || "Failed to request device code");
|
|
61
|
-
}
|
|
62
|
-
return await response.json();
|
|
63
|
-
}
|
|
64
|
-
async function pollForToken(deviceCode) {
|
|
65
|
-
const response = await fetch(`${getBaseUrl()}/api/auth/device/token`, {
|
|
66
|
-
method: "POST",
|
|
67
|
-
headers: {
|
|
68
|
-
"Content-Type": "application/json"
|
|
69
|
-
},
|
|
70
|
-
body: JSON.stringify({
|
|
71
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
72
|
-
device_code: deviceCode,
|
|
73
|
-
client_id: CLIENT_ID
|
|
74
|
-
})
|
|
75
|
-
});
|
|
76
|
-
const data = await response.json();
|
|
77
|
-
if (!response.ok) {
|
|
78
|
-
if ("error" in data && data.error === "authorization_pending") {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
const errorMsg = "error_description" in data ? data.error_description : "error" in data ? data.error : "Token exchange failed";
|
|
82
|
-
throw new Error(errorMsg || "Token exchange failed");
|
|
83
|
-
}
|
|
84
|
-
return data;
|
|
85
|
-
}
|
|
86
|
-
async function getCurrentUser() {
|
|
87
|
-
const token = getValidToken();
|
|
88
|
-
if (!token) {
|
|
89
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
90
|
-
}
|
|
91
|
-
const response = await fetch(`${getBaseUrl()}/api/user/me`, {
|
|
92
|
-
headers: {
|
|
93
|
-
Authorization: `Bearer ${token}`
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
if (!response.ok) {
|
|
97
|
-
if (response.status === 401) {
|
|
98
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
99
|
-
}
|
|
100
|
-
throw new Error("Failed to get user info");
|
|
101
|
-
}
|
|
102
|
-
return await response.json();
|
|
103
|
-
}
|
|
104
|
-
async function listAgents(options) {
|
|
105
|
-
const token = getValidToken();
|
|
106
|
-
if (!token) {
|
|
107
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
108
|
-
}
|
|
109
|
-
const params = new URLSearchParams();
|
|
110
|
-
if (options?.includeKpis) {
|
|
111
|
-
params.set("includeKpis", "true");
|
|
112
|
-
}
|
|
113
|
-
const url = `${getBaseUrl()}/api/config/agents${params.toString() ? `?${params}` : ""}`;
|
|
114
|
-
const response = await fetch(url, {
|
|
115
|
-
headers: {
|
|
116
|
-
Authorization: `Bearer ${token}`
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
if (!response.ok) {
|
|
120
|
-
if (response.status === 401) {
|
|
121
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
122
|
-
}
|
|
123
|
-
const error = await response.json();
|
|
124
|
-
throw new Error(error.error || "Failed to list agents");
|
|
125
|
-
}
|
|
126
|
-
const data = await response.json();
|
|
127
|
-
return data.agents;
|
|
128
|
-
}
|
|
129
|
-
async function getAgent(id) {
|
|
130
|
-
const token = getValidToken();
|
|
131
|
-
if (!token) {
|
|
132
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
133
|
-
}
|
|
134
|
-
const response = await fetch(`${getBaseUrl()}/api/config/agents/${id}`, {
|
|
135
|
-
headers: {
|
|
136
|
-
Authorization: `Bearer ${token}`
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
if (!response.ok) {
|
|
140
|
-
if (response.status === 401) {
|
|
141
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
142
|
-
}
|
|
143
|
-
if (response.status === 404) {
|
|
144
|
-
throw new Error("Agent not found");
|
|
145
|
-
}
|
|
146
|
-
const error = await response.json();
|
|
147
|
-
throw new Error(error.error || "Failed to get agent");
|
|
148
|
-
}
|
|
149
|
-
return await response.json();
|
|
150
|
-
}
|
|
151
|
-
async function createAgent(payload) {
|
|
152
|
-
const token = getValidToken();
|
|
153
|
-
if (!token) {
|
|
154
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
155
|
-
}
|
|
156
|
-
const response = await fetch(`${getBaseUrl()}/api/config/agents`, {
|
|
157
|
-
method: "POST",
|
|
158
|
-
headers: {
|
|
159
|
-
Authorization: `Bearer ${token}`,
|
|
160
|
-
"Content-Type": "application/json"
|
|
161
|
-
},
|
|
162
|
-
body: JSON.stringify(payload)
|
|
163
|
-
});
|
|
164
|
-
if (!response.ok) {
|
|
165
|
-
if (response.status === 401) {
|
|
166
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
167
|
-
}
|
|
168
|
-
if (response.status === 409) {
|
|
169
|
-
throw new Error("An agent with this name already exists");
|
|
170
|
-
}
|
|
171
|
-
const error = await response.json();
|
|
172
|
-
throw new Error(error.error || "Failed to create agent");
|
|
173
|
-
}
|
|
174
|
-
return await response.json();
|
|
175
|
-
}
|
|
176
|
-
async function updateAgent(id, payload) {
|
|
177
|
-
const token = getValidToken();
|
|
178
|
-
if (!token) {
|
|
179
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
180
|
-
}
|
|
181
|
-
const response = await fetch(`${getBaseUrl()}/api/config/agents/${id}`, {
|
|
182
|
-
method: "PUT",
|
|
183
|
-
headers: {
|
|
184
|
-
Authorization: `Bearer ${token}`,
|
|
185
|
-
"Content-Type": "application/json"
|
|
186
|
-
},
|
|
187
|
-
body: JSON.stringify(payload)
|
|
188
|
-
});
|
|
189
|
-
if (!response.ok) {
|
|
190
|
-
if (response.status === 401) {
|
|
191
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
192
|
-
}
|
|
193
|
-
if (response.status === 404) {
|
|
194
|
-
throw new Error("Agent not found");
|
|
195
|
-
}
|
|
196
|
-
if (response.status === 409) {
|
|
197
|
-
throw new Error("An agent with this name already exists");
|
|
198
|
-
}
|
|
199
|
-
const error = await response.json();
|
|
200
|
-
throw new Error(error.error || "Failed to update agent");
|
|
201
|
-
}
|
|
202
|
-
return await response.json();
|
|
203
|
-
}
|
|
204
|
-
async function deleteAgent(id) {
|
|
205
|
-
const token = getValidToken();
|
|
206
|
-
if (!token) {
|
|
207
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
208
|
-
}
|
|
209
|
-
const response = await fetch(`${getBaseUrl()}/api/config/agents/${id}`, {
|
|
210
|
-
method: "DELETE",
|
|
211
|
-
headers: {
|
|
212
|
-
Authorization: `Bearer ${token}`
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
if (!response.ok) {
|
|
216
|
-
if (response.status === 401) {
|
|
217
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
218
|
-
}
|
|
219
|
-
if (response.status === 404) {
|
|
220
|
-
throw new Error("Agent not found");
|
|
221
|
-
}
|
|
222
|
-
const error = await response.json();
|
|
223
|
-
throw new Error(error.error || "Failed to delete agent");
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
async function listWorkflows(options) {
|
|
227
|
-
const token = getValidToken();
|
|
228
|
-
if (!token) {
|
|
229
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
230
|
-
}
|
|
231
|
-
const params = new URLSearchParams();
|
|
232
|
-
if (options?.includeAgents) {
|
|
233
|
-
params.set("includeAgents", "true");
|
|
234
|
-
}
|
|
235
|
-
if (options?.includeInactive) {
|
|
236
|
-
params.set("includeInactive", "true");
|
|
237
|
-
}
|
|
238
|
-
const url = `${getBaseUrl()}/api/config/workflows${params.toString() ? `?${params}` : ""}`;
|
|
239
|
-
const response = await fetch(url, {
|
|
240
|
-
headers: {
|
|
241
|
-
Authorization: `Bearer ${token}`
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
if (!response.ok) {
|
|
245
|
-
if (response.status === 401) {
|
|
246
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
247
|
-
}
|
|
248
|
-
const error = await response.json();
|
|
249
|
-
throw new Error(error.error || "Failed to list workflows");
|
|
250
|
-
}
|
|
251
|
-
const data = await response.json();
|
|
252
|
-
return data.workflows;
|
|
253
|
-
}
|
|
254
|
-
async function getWorkflow(id) {
|
|
255
|
-
const token = getValidToken();
|
|
256
|
-
if (!token) {
|
|
257
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
258
|
-
}
|
|
259
|
-
const response = await fetch(`${getBaseUrl()}/api/config/workflows/${id}`, {
|
|
260
|
-
headers: {
|
|
261
|
-
Authorization: `Bearer ${token}`
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
if (!response.ok) {
|
|
265
|
-
if (response.status === 401) {
|
|
266
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
267
|
-
}
|
|
268
|
-
if (response.status === 404) {
|
|
269
|
-
throw new Error("Workflow not found");
|
|
270
|
-
}
|
|
271
|
-
const error = await response.json();
|
|
272
|
-
throw new Error(error.error || "Failed to get workflow");
|
|
273
|
-
}
|
|
274
|
-
return await response.json();
|
|
275
|
-
}
|
|
276
|
-
async function createWorkflow(payload) {
|
|
277
|
-
const token = getValidToken();
|
|
278
|
-
if (!token) {
|
|
279
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
280
|
-
}
|
|
281
|
-
const response = await fetch(`${getBaseUrl()}/api/config/workflows`, {
|
|
282
|
-
method: "POST",
|
|
283
|
-
headers: {
|
|
284
|
-
Authorization: `Bearer ${token}`,
|
|
285
|
-
"Content-Type": "application/json"
|
|
286
|
-
},
|
|
287
|
-
body: JSON.stringify(payload)
|
|
288
|
-
});
|
|
289
|
-
if (!response.ok) {
|
|
290
|
-
if (response.status === 401) {
|
|
291
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
292
|
-
}
|
|
293
|
-
if (response.status === 409) {
|
|
294
|
-
throw new Error("A workflow with this name already exists");
|
|
295
|
-
}
|
|
296
|
-
const error = await response.json();
|
|
297
|
-
throw new Error(error.error || "Failed to create workflow");
|
|
298
|
-
}
|
|
299
|
-
return await response.json();
|
|
300
|
-
}
|
|
301
|
-
async function updateWorkflow(id, payload) {
|
|
302
|
-
const token = getValidToken();
|
|
303
|
-
if (!token) {
|
|
304
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
305
|
-
}
|
|
306
|
-
const response = await fetch(`${getBaseUrl()}/api/config/workflows/${id}`, {
|
|
307
|
-
method: "PUT",
|
|
308
|
-
headers: {
|
|
309
|
-
Authorization: `Bearer ${token}`,
|
|
310
|
-
"Content-Type": "application/json"
|
|
311
|
-
},
|
|
312
|
-
body: JSON.stringify(payload)
|
|
313
|
-
});
|
|
314
|
-
if (!response.ok) {
|
|
315
|
-
if (response.status === 401) {
|
|
316
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
317
|
-
}
|
|
318
|
-
if (response.status === 404) {
|
|
319
|
-
throw new Error("Workflow not found");
|
|
320
|
-
}
|
|
321
|
-
if (response.status === 409) {
|
|
322
|
-
throw new Error("A workflow with this name already exists");
|
|
323
|
-
}
|
|
324
|
-
const error = await response.json();
|
|
325
|
-
throw new Error(error.error || "Failed to update workflow");
|
|
326
|
-
}
|
|
327
|
-
return await response.json();
|
|
328
|
-
}
|
|
329
|
-
async function deleteWorkflow(id) {
|
|
330
|
-
const token = getValidToken();
|
|
331
|
-
if (!token) {
|
|
332
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
333
|
-
}
|
|
334
|
-
const response = await fetch(`${getBaseUrl()}/api/config/workflows/${id}`, {
|
|
335
|
-
method: "DELETE",
|
|
336
|
-
headers: {
|
|
337
|
-
Authorization: `Bearer ${token}`
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
if (!response.ok) {
|
|
341
|
-
if (response.status === 401) {
|
|
342
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
343
|
-
}
|
|
344
|
-
if (response.status === 404) {
|
|
345
|
-
throw new Error("Workflow not found");
|
|
346
|
-
}
|
|
347
|
-
const error = await response.json();
|
|
348
|
-
throw new Error(error.error || "Failed to delete workflow");
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
async function listKpis(options) {
|
|
352
|
-
const token = getValidToken();
|
|
353
|
-
if (!token) {
|
|
354
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
355
|
-
}
|
|
356
|
-
const params = new URLSearchParams();
|
|
357
|
-
if (options?.agentId) {
|
|
358
|
-
params.set("agentId", options.agentId);
|
|
359
|
-
}
|
|
360
|
-
if (options?.includeInactive) {
|
|
361
|
-
params.set("includeInactive", "true");
|
|
362
|
-
}
|
|
363
|
-
const url = `${getBaseUrl()}/api/config/kpis${params.toString() ? `?${params}` : ""}`;
|
|
364
|
-
const response = await fetch(url, {
|
|
365
|
-
headers: {
|
|
366
|
-
Authorization: `Bearer ${token}`
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
if (!response.ok) {
|
|
370
|
-
if (response.status === 401) {
|
|
371
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
372
|
-
}
|
|
373
|
-
const error = await response.json();
|
|
374
|
-
throw new Error(error.error || "Failed to list KPIs");
|
|
375
|
-
}
|
|
376
|
-
const data = await response.json();
|
|
377
|
-
return data.kpiDefinitions;
|
|
378
|
-
}
|
|
379
|
-
async function getKpi(id) {
|
|
380
|
-
const token = getValidToken();
|
|
381
|
-
if (!token) {
|
|
382
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
383
|
-
}
|
|
384
|
-
const response = await fetch(`${getBaseUrl()}/api/config/kpis/${id}`, {
|
|
385
|
-
headers: {
|
|
386
|
-
Authorization: `Bearer ${token}`
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
if (!response.ok) {
|
|
390
|
-
if (response.status === 401) {
|
|
391
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
392
|
-
}
|
|
393
|
-
if (response.status === 404) {
|
|
394
|
-
throw new Error("KPI definition not found");
|
|
395
|
-
}
|
|
396
|
-
const error = await response.json();
|
|
397
|
-
throw new Error(error.error || "Failed to get KPI");
|
|
398
|
-
}
|
|
399
|
-
return await response.json();
|
|
400
|
-
}
|
|
401
|
-
async function createKpi(payload) {
|
|
402
|
-
const token = getValidToken();
|
|
403
|
-
if (!token) {
|
|
404
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
405
|
-
}
|
|
406
|
-
const response = await fetch(`${getBaseUrl()}/api/config/kpis`, {
|
|
407
|
-
method: "POST",
|
|
408
|
-
headers: {
|
|
409
|
-
Authorization: `Bearer ${token}`,
|
|
410
|
-
"Content-Type": "application/json"
|
|
411
|
-
},
|
|
412
|
-
body: JSON.stringify(payload)
|
|
413
|
-
});
|
|
414
|
-
if (!response.ok) {
|
|
415
|
-
if (response.status === 401) {
|
|
416
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
417
|
-
}
|
|
418
|
-
if (response.status === 409) {
|
|
419
|
-
throw new Error("A KPI definition with this name already exists");
|
|
420
|
-
}
|
|
421
|
-
const error = await response.json();
|
|
422
|
-
throw new Error(error.error || "Failed to create KPI");
|
|
423
|
-
}
|
|
424
|
-
return await response.json();
|
|
425
|
-
}
|
|
426
|
-
async function updateKpi(id, payload) {
|
|
427
|
-
const token = getValidToken();
|
|
428
|
-
if (!token) {
|
|
429
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
430
|
-
}
|
|
431
|
-
const response = await fetch(`${getBaseUrl()}/api/config/kpis/${id}`, {
|
|
432
|
-
method: "PUT",
|
|
433
|
-
headers: {
|
|
434
|
-
Authorization: `Bearer ${token}`,
|
|
435
|
-
"Content-Type": "application/json"
|
|
436
|
-
},
|
|
437
|
-
body: JSON.stringify(payload)
|
|
438
|
-
});
|
|
439
|
-
if (!response.ok) {
|
|
440
|
-
if (response.status === 401) {
|
|
441
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
442
|
-
}
|
|
443
|
-
if (response.status === 404) {
|
|
444
|
-
throw new Error("KPI definition not found");
|
|
445
|
-
}
|
|
446
|
-
if (response.status === 409) {
|
|
447
|
-
throw new Error("A KPI definition with this name already exists");
|
|
448
|
-
}
|
|
449
|
-
const error = await response.json();
|
|
450
|
-
throw new Error(error.error || "Failed to update KPI");
|
|
451
|
-
}
|
|
452
|
-
return await response.json();
|
|
453
|
-
}
|
|
454
|
-
async function deleteKpi(id) {
|
|
455
|
-
const token = getValidToken();
|
|
456
|
-
if (!token) {
|
|
457
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
458
|
-
}
|
|
459
|
-
const response = await fetch(`${getBaseUrl()}/api/config/kpis/${id}`, {
|
|
460
|
-
method: "DELETE",
|
|
461
|
-
headers: {
|
|
462
|
-
Authorization: `Bearer ${token}`
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
if (!response.ok) {
|
|
466
|
-
if (response.status === 401) {
|
|
467
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
468
|
-
}
|
|
469
|
-
if (response.status === 404) {
|
|
470
|
-
throw new Error("KPI definition not found");
|
|
471
|
-
}
|
|
472
|
-
const error = await response.json();
|
|
473
|
-
throw new Error(error.error || "Failed to delete KPI");
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
async function getKpiContextVariables(agentId, scope) {
|
|
477
|
-
const token = getValidToken();
|
|
478
|
-
if (!token) {
|
|
479
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
480
|
-
}
|
|
481
|
-
const params = new URLSearchParams();
|
|
482
|
-
if (agentId) {
|
|
483
|
-
params.set("agentId", agentId);
|
|
484
|
-
}
|
|
485
|
-
if (scope) {
|
|
486
|
-
params.set("scope", scope);
|
|
487
|
-
}
|
|
488
|
-
const url = `${getBaseUrl()}/api/config/kpis/context-variables${params.toString() ? `?${params}` : ""}`;
|
|
489
|
-
const response = await fetch(url, {
|
|
490
|
-
headers: {
|
|
491
|
-
Authorization: `Bearer ${token}`
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
if (!response.ok) {
|
|
495
|
-
if (response.status === 401) {
|
|
496
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
497
|
-
}
|
|
498
|
-
const error = await response.json();
|
|
499
|
-
throw new Error(error.error || "Failed to get context variables");
|
|
500
|
-
}
|
|
501
|
-
const data = await response.json();
|
|
502
|
-
return data.contextVariables;
|
|
503
|
-
}
|
|
504
|
-
async function validateKpiFormula(formula, agentId, scope) {
|
|
505
|
-
const token = getValidToken();
|
|
506
|
-
if (!token) {
|
|
507
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
508
|
-
}
|
|
509
|
-
const body = { formula, agentId };
|
|
510
|
-
if (scope) {
|
|
511
|
-
body.scope = scope;
|
|
512
|
-
}
|
|
513
|
-
const response = await fetch(`${getBaseUrl()}/api/config/kpis/validate`, {
|
|
514
|
-
method: "POST",
|
|
515
|
-
headers: {
|
|
516
|
-
Authorization: `Bearer ${token}`,
|
|
517
|
-
"Content-Type": "application/json"
|
|
518
|
-
},
|
|
519
|
-
body: JSON.stringify(body)
|
|
520
|
-
});
|
|
521
|
-
if (!response.ok) {
|
|
522
|
-
if (response.status === 401) {
|
|
523
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
524
|
-
}
|
|
525
|
-
const error = await response.json();
|
|
526
|
-
throw new Error(error.error || "Failed to validate formula");
|
|
527
|
-
}
|
|
528
|
-
return await response.json();
|
|
529
|
-
}
|
|
530
|
-
async function validateMonitoringApiKey(monitoringEndpoint, apiKey) {
|
|
531
|
-
const normalizedEndpoint = monitoringEndpoint.replace(/\/+$/, "");
|
|
532
|
-
const url = `${normalizedEndpoint}/me`;
|
|
533
|
-
const controller = new AbortController();
|
|
534
|
-
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
535
|
-
try {
|
|
536
|
-
const response = await fetch(url, {
|
|
537
|
-
method: "GET",
|
|
538
|
-
headers: {
|
|
539
|
-
"x-api-key": apiKey
|
|
540
|
-
},
|
|
541
|
-
signal: controller.signal
|
|
542
|
-
});
|
|
543
|
-
if (!response.ok) {
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
const data = await response.json();
|
|
547
|
-
if (typeof data?.accountId !== "string" || typeof data?.apiKeyId !== "string") {
|
|
548
|
-
return null;
|
|
549
|
-
}
|
|
550
|
-
return { accountId: data.accountId, apiKeyId: data.apiKeyId };
|
|
551
|
-
} catch {
|
|
552
|
-
return null;
|
|
553
|
-
} finally {
|
|
554
|
-
clearTimeout(timeout);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
async function listActivity(options = {}) {
|
|
558
|
-
const token = getValidToken();
|
|
559
|
-
if (!token) {
|
|
560
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
561
|
-
}
|
|
562
|
-
const params = new URLSearchParams();
|
|
563
|
-
if (options.agentId) params.set("agentId", options.agentId);
|
|
564
|
-
if (options.workflowId) params.set("workflowId", options.workflowId);
|
|
565
|
-
if (options.since) params.set("since", options.since);
|
|
566
|
-
if (options.until) params.set("until", options.until);
|
|
567
|
-
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
568
|
-
if (options.offset !== void 0) params.set("offset", String(options.offset));
|
|
569
|
-
if (options.includeContent) params.set("includeContent", "true");
|
|
570
|
-
if (options.includeAnalytics) params.set("includeAnalytics", "true");
|
|
571
|
-
const url = `${getBaseUrl()}/api/activity/prompts${params.toString() ? `?${params}` : ""}`;
|
|
572
|
-
const response = await fetch(url, {
|
|
573
|
-
headers: {
|
|
574
|
-
Authorization: `Bearer ${token}`
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
if (!response.ok) {
|
|
578
|
-
if (response.status === 401) {
|
|
579
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
580
|
-
}
|
|
581
|
-
const error = await response.json();
|
|
582
|
-
throw new Error(error.error || "Failed to list activity");
|
|
583
|
-
}
|
|
584
|
-
return await response.json();
|
|
585
|
-
}
|
|
586
|
-
async function getActivity(id, includeContent) {
|
|
587
|
-
const token = getValidToken();
|
|
588
|
-
if (!token) {
|
|
589
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
590
|
-
}
|
|
591
|
-
const params = new URLSearchParams();
|
|
592
|
-
if (includeContent) params.set("includeContent", "true");
|
|
593
|
-
const url = `${getBaseUrl()}/api/activity/prompts/${id}${params.toString() ? `?${params}` : ""}`;
|
|
594
|
-
const response = await fetch(url, {
|
|
595
|
-
headers: {
|
|
596
|
-
Authorization: `Bearer ${token}`
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
if (!response.ok) {
|
|
600
|
-
if (response.status === 401) {
|
|
601
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
602
|
-
}
|
|
603
|
-
if (response.status === 404) {
|
|
604
|
-
throw new Error("Prompt request not found");
|
|
605
|
-
}
|
|
606
|
-
const error = await response.json();
|
|
607
|
-
throw new Error(error.error || "Failed to get activity");
|
|
608
|
-
}
|
|
609
|
-
return await response.json();
|
|
610
|
-
}
|
|
611
|
-
async function getActivityKpis(options) {
|
|
612
|
-
const token = getValidToken();
|
|
613
|
-
if (!token) {
|
|
614
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
615
|
-
}
|
|
616
|
-
if (!options.agentId && !options.workflowId) {
|
|
617
|
-
throw new Error("Either agentId or workflowId is required");
|
|
618
|
-
}
|
|
619
|
-
const params = new URLSearchParams();
|
|
620
|
-
if (options.agentId) params.set("agentId", options.agentId);
|
|
621
|
-
if (options.workflowId) params.set("workflowId", options.workflowId);
|
|
622
|
-
if (options.since) params.set("since", options.since);
|
|
623
|
-
if (options.until) params.set("until", options.until);
|
|
624
|
-
if (options.period) params.set("period", options.period);
|
|
625
|
-
if (options.includeAtoms) params.set("includeAtoms", "true");
|
|
626
|
-
const url = `${getBaseUrl()}/api/activity/kpis${params.toString() ? `?${params}` : ""}`;
|
|
627
|
-
const response = await fetch(url, {
|
|
628
|
-
headers: {
|
|
629
|
-
Authorization: `Bearer ${token}`
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
if (!response.ok) {
|
|
633
|
-
if (response.status === 401) {
|
|
634
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
635
|
-
}
|
|
636
|
-
const error = await response.json();
|
|
637
|
-
throw new Error(error.error || "Failed to get activity KPIs");
|
|
638
|
-
}
|
|
639
|
-
return await response.json();
|
|
640
|
-
}
|
|
641
|
-
async function listSessions(options) {
|
|
642
|
-
const token = getValidToken();
|
|
643
|
-
if (!token) {
|
|
644
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
645
|
-
}
|
|
646
|
-
const params = new URLSearchParams();
|
|
647
|
-
params.set("agentId", options.agentId);
|
|
648
|
-
if (options.since) params.set("since", options.since);
|
|
649
|
-
if (options.until) params.set("until", options.until);
|
|
650
|
-
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
651
|
-
if (options.offset !== void 0) params.set("offset", String(options.offset));
|
|
652
|
-
const url = `${getBaseUrl()}/api/activity/sessions?${params}`;
|
|
653
|
-
const response = await fetch(url, {
|
|
654
|
-
headers: {
|
|
655
|
-
Authorization: `Bearer ${token}`
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
if (!response.ok) {
|
|
659
|
-
if (response.status === 401) {
|
|
660
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
661
|
-
}
|
|
662
|
-
const error = await response.json();
|
|
663
|
-
throw new Error(error.error || "Failed to list sessions");
|
|
664
|
-
}
|
|
665
|
-
return await response.json();
|
|
666
|
-
}
|
|
667
|
-
async function listCustomDataConfigs(agentId) {
|
|
668
|
-
const token = getValidToken();
|
|
669
|
-
if (!token) {
|
|
670
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
671
|
-
}
|
|
672
|
-
const params = new URLSearchParams();
|
|
673
|
-
if (agentId) {
|
|
674
|
-
params.set("agentId", agentId);
|
|
675
|
-
}
|
|
676
|
-
const url = `${getBaseUrl()}/api/config/custom-data${params.toString() ? `?${params}` : ""}`;
|
|
677
|
-
const response = await fetch(url, {
|
|
678
|
-
headers: {
|
|
679
|
-
Authorization: `Bearer ${token}`
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
|
-
if (!response.ok) {
|
|
683
|
-
if (response.status === 401) {
|
|
684
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
685
|
-
}
|
|
686
|
-
const error = await response.json();
|
|
687
|
-
throw new Error(error.error || "Failed to list custom data configurations");
|
|
688
|
-
}
|
|
689
|
-
const data = await response.json();
|
|
690
|
-
return data.customDataConfigs;
|
|
691
|
-
}
|
|
692
|
-
async function getCustomDataConfig(id) {
|
|
693
|
-
const token = getValidToken();
|
|
694
|
-
if (!token) {
|
|
695
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
696
|
-
}
|
|
697
|
-
const url = `${getBaseUrl()}/api/config/custom-data/${encodeURIComponent(id)}`;
|
|
698
|
-
const response = await fetch(url, {
|
|
699
|
-
headers: {
|
|
700
|
-
Authorization: `Bearer ${token}`
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
if (!response.ok) {
|
|
704
|
-
if (response.status === 401) {
|
|
705
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
706
|
-
}
|
|
707
|
-
if (response.status === 404) {
|
|
708
|
-
throw new Error("Custom data configuration not found");
|
|
709
|
-
}
|
|
710
|
-
const error = await response.json();
|
|
711
|
-
throw new Error(error.error || "Failed to get custom data configuration");
|
|
712
|
-
}
|
|
713
|
-
return await response.json();
|
|
714
|
-
}
|
|
715
|
-
async function createCustomDataConfig(payload) {
|
|
716
|
-
const token = getValidToken();
|
|
717
|
-
if (!token) {
|
|
718
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
719
|
-
}
|
|
720
|
-
const url = `${getBaseUrl()}/api/config/custom-data`;
|
|
721
|
-
const response = await fetch(url, {
|
|
722
|
-
method: "POST",
|
|
723
|
-
headers: {
|
|
724
|
-
Authorization: `Bearer ${token}`,
|
|
725
|
-
"Content-Type": "application/json"
|
|
726
|
-
},
|
|
727
|
-
body: JSON.stringify(payload)
|
|
728
|
-
});
|
|
729
|
-
if (!response.ok) {
|
|
730
|
-
if (response.status === 401) {
|
|
731
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
732
|
-
}
|
|
733
|
-
if (response.status === 409) {
|
|
734
|
-
throw new Error("A custom data configuration with this name already exists");
|
|
735
|
-
}
|
|
736
|
-
const error = await response.json();
|
|
737
|
-
throw new Error(error.error || "Failed to create custom data configuration");
|
|
738
|
-
}
|
|
739
|
-
return await response.json();
|
|
740
|
-
}
|
|
741
|
-
async function updateCustomDataConfig(id, payload) {
|
|
742
|
-
const token = getValidToken();
|
|
743
|
-
if (!token) {
|
|
744
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
745
|
-
}
|
|
746
|
-
const url = `${getBaseUrl()}/api/config/custom-data/${encodeURIComponent(id)}`;
|
|
747
|
-
const response = await fetch(url, {
|
|
748
|
-
method: "PUT",
|
|
749
|
-
headers: {
|
|
750
|
-
Authorization: `Bearer ${token}`,
|
|
751
|
-
"Content-Type": "application/json"
|
|
752
|
-
},
|
|
753
|
-
body: JSON.stringify(payload)
|
|
754
|
-
});
|
|
755
|
-
if (!response.ok) {
|
|
756
|
-
if (response.status === 401) {
|
|
757
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
758
|
-
}
|
|
759
|
-
if (response.status === 404) {
|
|
760
|
-
throw new Error("Custom data configuration not found");
|
|
761
|
-
}
|
|
762
|
-
if (response.status === 409) {
|
|
763
|
-
throw new Error("A custom data configuration with this name already exists");
|
|
764
|
-
}
|
|
765
|
-
const error = await response.json();
|
|
766
|
-
throw new Error(error.error || "Failed to update custom data configuration");
|
|
767
|
-
}
|
|
768
|
-
return await response.json();
|
|
769
|
-
}
|
|
770
|
-
async function deleteCustomDataConfig(id) {
|
|
771
|
-
const token = getValidToken();
|
|
772
|
-
if (!token) {
|
|
773
|
-
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
774
|
-
}
|
|
775
|
-
const url = `${getBaseUrl()}/api/config/custom-data/${encodeURIComponent(id)}`;
|
|
776
|
-
const response = await fetch(url, {
|
|
777
|
-
method: "DELETE",
|
|
778
|
-
headers: {
|
|
779
|
-
Authorization: `Bearer ${token}`
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
if (!response.ok) {
|
|
783
|
-
if (response.status === 401) {
|
|
784
|
-
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
785
|
-
}
|
|
786
|
-
if (response.status === 404) {
|
|
787
|
-
throw new Error("Custom data configuration not found");
|
|
788
|
-
}
|
|
789
|
-
const error = await response.json();
|
|
790
|
-
throw new Error(error.error || "Failed to delete custom data configuration");
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// src/commands/login.ts
|
|
795
82
|
var POLL_INTERVAL_MS = 5e3;
|
|
796
83
|
async function loginCommand() {
|
|
797
84
|
if (isTokenValid()) {
|
|
@@ -1133,7 +420,7 @@ function isInteractive() {
|
|
|
1133
420
|
|
|
1134
421
|
// src/monitor/detect-all.ts
|
|
1135
422
|
import * as fs15 from "fs";
|
|
1136
|
-
import * as
|
|
423
|
+
import * as path14 from "path";
|
|
1137
424
|
|
|
1138
425
|
// src/monitor/plugins/codex/paths.ts
|
|
1139
426
|
import * as os from "os";
|
|
@@ -1257,44 +544,98 @@ function shouldReportTurn(existing, currentUserTimestamp) {
|
|
|
1257
544
|
// src/monitor/plugins/claude-code/install.ts
|
|
1258
545
|
import * as fs2 from "fs";
|
|
1259
546
|
|
|
1260
|
-
// src/monitor/
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
);
|
|
1266
|
-
|
|
547
|
+
// src/monitor/self-monitor-provision.ts
|
|
548
|
+
import path3 from "path";
|
|
549
|
+
async function provisionSelfMonitorAgent(opts) {
|
|
550
|
+
const me = await getCurrentUser();
|
|
551
|
+
const localPart = (me.email.split("@")[0] ?? "user").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
552
|
+
const workspaceName = path3.basename(opts.projectRoot).toLowerCase();
|
|
553
|
+
const defaultName = `${workspaceName}-${localPart}`;
|
|
554
|
+
let existing = [];
|
|
555
|
+
try {
|
|
556
|
+
existing = await listMyAgents({
|
|
557
|
+
source: opts.source,
|
|
558
|
+
name: defaultName
|
|
559
|
+
});
|
|
560
|
+
} catch (err) {
|
|
561
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
562
|
+
if (!message.includes("Failed to list your agents")) {
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (existing.length > 0) {
|
|
566
|
+
const found = existing[0];
|
|
1267
567
|
console.log(
|
|
1268
|
-
|
|
568
|
+
`
|
|
569
|
+
Found your existing ${opts.displayName} agent "${found.name}".`
|
|
1269
570
|
);
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
571
|
+
console.log(
|
|
572
|
+
"Re-using it requires rotating its API key. Any other workspace currently using this agent will start failing on the next monitor request until it re-runs 'olakai monitor init'."
|
|
573
|
+
);
|
|
574
|
+
const ack = await promptUser("Rotate the API key and reuse? [y/N]: ");
|
|
575
|
+
if (ack.trim().toLowerCase() !== "y") {
|
|
576
|
+
console.error(
|
|
577
|
+
"Cancelled. To use this workspace without affecting the other workspace, run 'olakai monitor init' again and pick a different agent name when prompted."
|
|
578
|
+
);
|
|
579
|
+
process.exit(1);
|
|
580
|
+
}
|
|
581
|
+
const rotated = await regenerateAgentApiKey(found.id);
|
|
582
|
+
return {
|
|
583
|
+
id: found.id,
|
|
584
|
+
name: found.name,
|
|
585
|
+
description: found.description,
|
|
586
|
+
role: found.role,
|
|
587
|
+
// `source` on the listMyAgents response is the AgentSource enum
|
|
588
|
+
// string; the Agent type narrows it more loosely. Cast through
|
|
589
|
+
// the shared union.
|
|
590
|
+
source: found.source,
|
|
591
|
+
apiKey: {
|
|
592
|
+
id: rotated.id,
|
|
593
|
+
key: rotated.key,
|
|
594
|
+
keyMasked: rotated.keyMasked,
|
|
595
|
+
isActive: rotated.isActive
|
|
596
|
+
},
|
|
597
|
+
workflowId: null,
|
|
598
|
+
category: opts.category
|
|
599
|
+
};
|
|
1277
600
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
601
|
+
const nameInput = await promptUser(`Agent name [${defaultName}]: `);
|
|
602
|
+
const agentName = nameInput.trim() || defaultName;
|
|
603
|
+
try {
|
|
604
|
+
return await createAgent({
|
|
605
|
+
name: agentName,
|
|
606
|
+
description: `${opts.displayName} local agent for ${agentName}`,
|
|
607
|
+
role: "WORKER",
|
|
608
|
+
createApiKey: true,
|
|
609
|
+
category: opts.category,
|
|
610
|
+
source: opts.source
|
|
611
|
+
});
|
|
612
|
+
} catch (err) {
|
|
613
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
614
|
+
if (message.toLowerCase().includes("already exists") || message.toLowerCase().includes("conflict")) {
|
|
615
|
+
console.log(
|
|
616
|
+
`
|
|
617
|
+
An agent named "${agentName}" already exists on this account (created by someone else or an admin).`
|
|
618
|
+
);
|
|
619
|
+
const retry = await promptUser("Try a different name: ");
|
|
620
|
+
if (!retry.trim()) {
|
|
621
|
+
console.error("No name provided. Aborting.");
|
|
622
|
+
process.exit(1);
|
|
623
|
+
}
|
|
624
|
+
return createAgent({
|
|
625
|
+
name: retry.trim(),
|
|
626
|
+
description: `${opts.displayName} local agent for ${retry.trim()}`,
|
|
627
|
+
role: "WORKER",
|
|
628
|
+
createApiKey: true,
|
|
629
|
+
category: opts.category,
|
|
630
|
+
source: opts.source
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
throw err;
|
|
1293
634
|
}
|
|
1294
635
|
}
|
|
1295
636
|
|
|
1296
637
|
// src/monitor/plugins/claude-code/install.ts
|
|
1297
|
-
import
|
|
638
|
+
import path4 from "path";
|
|
1298
639
|
var CLAUDE_CODE_SOURCE = "claude-code";
|
|
1299
640
|
var CLAUDE_CODE_AGENT_SOURCE = "CLAUDE_CODE";
|
|
1300
641
|
var CLAUDE_CODE_AGENT_CATEGORY = "CODING";
|
|
@@ -1306,65 +647,19 @@ async function installClaudeCode(opts) {
|
|
|
1306
647
|
process.exit(1);
|
|
1307
648
|
}
|
|
1308
649
|
console.log("Setting up Claude Code monitoring for this workspace...\n");
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
const agents = await listAgents();
|
|
1316
|
-
if (agents.length === 0) {
|
|
1317
|
-
console.log("No agents found. Creating a new one instead.\n");
|
|
1318
|
-
agent = await createNewAgent(dirName);
|
|
1319
|
-
} else {
|
|
1320
|
-
console.log("\nAvailable agents:");
|
|
1321
|
-
for (let i = 0; i < agents.length; i++) {
|
|
1322
|
-
console.log(
|
|
1323
|
-
` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`
|
|
1324
|
-
);
|
|
1325
|
-
}
|
|
1326
|
-
const selection = await promptUser(`
|
|
1327
|
-
Select agent (1-${agents.length}): `);
|
|
1328
|
-
const idx = parseInt(selection, 10) - 1;
|
|
1329
|
-
if (isNaN(idx) || idx < 0 || idx >= agents.length) {
|
|
1330
|
-
console.error("Invalid selection.");
|
|
1331
|
-
process.exit(1);
|
|
1332
|
-
}
|
|
1333
|
-
agent = await getAgent(agents[idx].id);
|
|
1334
|
-
if (!agent.apiKey?.key && !agent.apiKey?.isActive) {
|
|
1335
|
-
console.log(
|
|
1336
|
-
"\nThis agent has no active API key. Please create a new agent instead,"
|
|
1337
|
-
);
|
|
1338
|
-
console.log("or generate an API key via the Olakai dashboard.\n");
|
|
1339
|
-
const createNew = await promptUser("Create a new agent? (y/n) [y]: ");
|
|
1340
|
-
if (createNew.toLowerCase() !== "n") {
|
|
1341
|
-
agent = await createNewAgent(dirName);
|
|
1342
|
-
} else {
|
|
1343
|
-
process.exit(1);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
} else {
|
|
1348
|
-
agent = await createNewAgent(dirName);
|
|
1349
|
-
}
|
|
650
|
+
const agent = await provisionSelfMonitorAgent({
|
|
651
|
+
projectRoot,
|
|
652
|
+
source: CLAUDE_CODE_AGENT_SOURCE,
|
|
653
|
+
displayName: "Claude Code",
|
|
654
|
+
category: CLAUDE_CODE_AGENT_CATEGORY
|
|
655
|
+
});
|
|
1350
656
|
const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
|
|
1351
|
-
|
|
657
|
+
const apiKey = agent.apiKey?.key;
|
|
1352
658
|
if (!apiKey) {
|
|
1353
|
-
console.
|
|
1354
|
-
"
|
|
659
|
+
console.error(
|
|
660
|
+
"Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
|
|
1355
661
|
);
|
|
1356
|
-
|
|
1357
|
-
if (!apiKey) {
|
|
1358
|
-
console.error("API key is required for monitoring.");
|
|
1359
|
-
process.exit(1);
|
|
1360
|
-
}
|
|
1361
|
-
await validatePastedApiKey({
|
|
1362
|
-
monitoringEndpoint,
|
|
1363
|
-
apiKey,
|
|
1364
|
-
expectedAgentId: agent.id,
|
|
1365
|
-
expectedAgentName: agent.name,
|
|
1366
|
-
expectedApiKeyId: agent.apiKey?.id ?? null
|
|
1367
|
-
});
|
|
662
|
+
process.exit(1);
|
|
1368
663
|
}
|
|
1369
664
|
const claudeDir = getClaudeDir(projectRoot);
|
|
1370
665
|
if (!fs2.existsSync(claudeDir)) {
|
|
@@ -1388,7 +683,7 @@ Select agent (1-${agents.length}): `);
|
|
|
1388
683
|
};
|
|
1389
684
|
writeClaudeCodeConfig(projectRoot, monitorConfig);
|
|
1390
685
|
const configPath = getClaudeCodeConfigPath(projectRoot);
|
|
1391
|
-
const configRel =
|
|
686
|
+
const configRel = path4.relative(projectRoot, configPath);
|
|
1392
687
|
console.log("");
|
|
1393
688
|
console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
|
|
1394
689
|
if (agent.apiKey?.key) {
|
|
@@ -1417,18 +712,6 @@ Select agent (1-${agents.length}): `);
|
|
|
1417
712
|
monitoringEndpoint
|
|
1418
713
|
};
|
|
1419
714
|
}
|
|
1420
|
-
async function createNewAgent(defaultName) {
|
|
1421
|
-
const nameInput = await promptUser(`Agent name [${defaultName}]: `);
|
|
1422
|
-
const agentName = nameInput || defaultName;
|
|
1423
|
-
return createAgent({
|
|
1424
|
-
name: agentName,
|
|
1425
|
-
description: `Claude Code local agent for ${agentName}`,
|
|
1426
|
-
role: "WORKER",
|
|
1427
|
-
createApiKey: true,
|
|
1428
|
-
category: CLAUDE_CODE_AGENT_CATEGORY,
|
|
1429
|
-
source: CLAUDE_CODE_AGENT_SOURCE
|
|
1430
|
-
});
|
|
1431
|
-
}
|
|
1432
715
|
async function uninstallClaudeCode(opts) {
|
|
1433
716
|
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
1434
717
|
const settingsPath = getSettingsPath(projectRoot);
|
|
@@ -1457,13 +740,13 @@ async function uninstallClaudeCode(opts) {
|
|
|
1457
740
|
}
|
|
1458
741
|
if (!opts.keepConfig) {
|
|
1459
742
|
const configPath = getClaudeCodeConfigPath(projectRoot);
|
|
1460
|
-
const configRel =
|
|
743
|
+
const configRel = path4.relative(projectRoot, configPath);
|
|
1461
744
|
if (deleteClaudeCodeConfig(projectRoot)) {
|
|
1462
745
|
console.log(`\u2713 Monitor config removed (${configRel})`);
|
|
1463
746
|
}
|
|
1464
747
|
} else {
|
|
1465
748
|
const configPath = getClaudeCodeConfigPath(projectRoot);
|
|
1466
|
-
const configRel =
|
|
749
|
+
const configRel = path4.relative(projectRoot, configPath);
|
|
1467
750
|
console.log(`Monitor config retained at ${configRel}`);
|
|
1468
751
|
}
|
|
1469
752
|
console.log("");
|
|
@@ -1867,7 +1150,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
1867
1150
|
|
|
1868
1151
|
// src/monitor/plugins/codex/install.ts
|
|
1869
1152
|
import * as fs6 from "fs";
|
|
1870
|
-
import * as
|
|
1153
|
+
import * as path6 from "path";
|
|
1871
1154
|
import * as TOML from "@iarna/toml";
|
|
1872
1155
|
|
|
1873
1156
|
// src/monitor/plugins/codex/hooks.ts
|
|
@@ -1943,7 +1226,7 @@ function hasOlakaiHooksInstalled(parsed) {
|
|
|
1943
1226
|
|
|
1944
1227
|
// src/monitor/plugins/codex/config.ts
|
|
1945
1228
|
import * as fs5 from "fs";
|
|
1946
|
-
import * as
|
|
1229
|
+
import * as path5 from "path";
|
|
1947
1230
|
function getCodexConfigPath2(projectRoot) {
|
|
1948
1231
|
return getMonitorConfigPath(projectRoot, "codex");
|
|
1949
1232
|
}
|
|
@@ -1959,7 +1242,7 @@ function loadCodexConfig(projectRoot) {
|
|
|
1959
1242
|
}
|
|
1960
1243
|
function writeCodexConfig(projectRoot, config) {
|
|
1961
1244
|
const filePath = getCodexConfigPath2(projectRoot);
|
|
1962
|
-
const dir =
|
|
1245
|
+
const dir = path5.dirname(filePath);
|
|
1963
1246
|
if (!fs5.existsSync(dir)) {
|
|
1964
1247
|
fs5.mkdirSync(dir, { recursive: true });
|
|
1965
1248
|
}
|
|
@@ -1992,65 +1275,19 @@ async function installCodex(opts) {
|
|
|
1992
1275
|
process.exit(1);
|
|
1993
1276
|
}
|
|
1994
1277
|
console.log("Setting up Codex CLI monitoring for this workspace...\n");
|
|
1995
|
-
const
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
const agents = await listAgents();
|
|
2002
|
-
if (agents.length === 0) {
|
|
2003
|
-
console.log("No agents found. Creating a new one instead.\n");
|
|
2004
|
-
agent = await createNewAgent2(dirName);
|
|
2005
|
-
} else {
|
|
2006
|
-
console.log("\nAvailable agents:");
|
|
2007
|
-
for (let i = 0; i < agents.length; i++) {
|
|
2008
|
-
console.log(
|
|
2009
|
-
` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`
|
|
2010
|
-
);
|
|
2011
|
-
}
|
|
2012
|
-
const selection = await promptUser(`
|
|
2013
|
-
Select agent (1-${agents.length}): `);
|
|
2014
|
-
const idx = parseInt(selection, 10) - 1;
|
|
2015
|
-
if (isNaN(idx) || idx < 0 || idx >= agents.length) {
|
|
2016
|
-
console.error("Invalid selection.");
|
|
2017
|
-
process.exit(1);
|
|
2018
|
-
}
|
|
2019
|
-
agent = await getAgent(agents[idx].id);
|
|
2020
|
-
if (!agent.apiKey?.key && !agent.apiKey?.isActive) {
|
|
2021
|
-
console.log(
|
|
2022
|
-
"\nThis agent has no active API key. Please create a new agent instead,"
|
|
2023
|
-
);
|
|
2024
|
-
console.log("or generate an API key via the Olakai dashboard.\n");
|
|
2025
|
-
const createNew = await promptUser("Create a new agent? (y/n) [y]: ");
|
|
2026
|
-
if (createNew.toLowerCase() !== "n") {
|
|
2027
|
-
agent = await createNewAgent2(dirName);
|
|
2028
|
-
} else {
|
|
2029
|
-
process.exit(1);
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
} else {
|
|
2034
|
-
agent = await createNewAgent2(dirName);
|
|
2035
|
-
}
|
|
1278
|
+
const agent = await provisionSelfMonitorAgent({
|
|
1279
|
+
projectRoot,
|
|
1280
|
+
source: CODEX_AGENT_SOURCE,
|
|
1281
|
+
displayName: "Codex CLI",
|
|
1282
|
+
category: CODEX_AGENT_CATEGORY
|
|
1283
|
+
});
|
|
2036
1284
|
const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
|
|
2037
|
-
|
|
1285
|
+
const apiKey = agent.apiKey?.key;
|
|
2038
1286
|
if (!apiKey) {
|
|
2039
|
-
console.
|
|
2040
|
-
"
|
|
1287
|
+
console.error(
|
|
1288
|
+
"Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
|
|
2041
1289
|
);
|
|
2042
|
-
|
|
2043
|
-
if (!apiKey) {
|
|
2044
|
-
console.error("API key is required for monitoring.");
|
|
2045
|
-
process.exit(1);
|
|
2046
|
-
}
|
|
2047
|
-
await validatePastedApiKey({
|
|
2048
|
-
monitoringEndpoint,
|
|
2049
|
-
apiKey,
|
|
2050
|
-
expectedAgentId: agent.id,
|
|
2051
|
-
expectedAgentName: agent.name,
|
|
2052
|
-
expectedApiKeyId: agent.apiKey?.id ?? null
|
|
2053
|
-
});
|
|
1290
|
+
process.exit(1);
|
|
2054
1291
|
}
|
|
2055
1292
|
const { configExisted: codexConfigExisted } = installCodexHooksConfig();
|
|
2056
1293
|
const monitorConfig = {
|
|
@@ -2063,7 +1300,7 @@ Select agent (1-${agents.length}): `);
|
|
|
2063
1300
|
};
|
|
2064
1301
|
writeCodexConfig(projectRoot, monitorConfig);
|
|
2065
1302
|
const configPath = getCodexConfigPath2(projectRoot);
|
|
2066
|
-
const configRel =
|
|
1303
|
+
const configRel = path6.relative(projectRoot, configPath);
|
|
2067
1304
|
console.log("");
|
|
2068
1305
|
console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
|
|
2069
1306
|
if (agent.apiKey?.key) {
|
|
@@ -2100,18 +1337,6 @@ Select agent (1-${agents.length}): `);
|
|
|
2100
1337
|
monitoringEndpoint
|
|
2101
1338
|
};
|
|
2102
1339
|
}
|
|
2103
|
-
async function createNewAgent2(defaultName) {
|
|
2104
|
-
const nameInput = await promptUser(`Agent name [${defaultName}]: `);
|
|
2105
|
-
const agentName = nameInput || defaultName;
|
|
2106
|
-
return createAgent({
|
|
2107
|
-
name: agentName,
|
|
2108
|
-
description: `Codex CLI local agent for ${agentName}`,
|
|
2109
|
-
role: "WORKER",
|
|
2110
|
-
createApiKey: true,
|
|
2111
|
-
category: CODEX_AGENT_CATEGORY,
|
|
2112
|
-
source: CODEX_AGENT_SOURCE
|
|
2113
|
-
});
|
|
2114
|
-
}
|
|
2115
1340
|
function installCodexHooksConfig() {
|
|
2116
1341
|
const homeDir = getCodexHomeDir();
|
|
2117
1342
|
const configPath = getCodexConfigPath();
|
|
@@ -2139,13 +1364,13 @@ async function uninstallCodex(opts) {
|
|
|
2139
1364
|
}
|
|
2140
1365
|
if (!opts.keepConfig) {
|
|
2141
1366
|
const configPath = getCodexConfigPath2(projectRoot);
|
|
2142
|
-
const configRel =
|
|
1367
|
+
const configRel = path6.relative(projectRoot, configPath);
|
|
2143
1368
|
if (deleteCodexConfig(projectRoot)) {
|
|
2144
1369
|
console.log(`\u2713 Monitor config removed (${configRel})`);
|
|
2145
1370
|
}
|
|
2146
1371
|
} else {
|
|
2147
1372
|
const configPath = getCodexConfigPath2(projectRoot);
|
|
2148
|
-
const configRel =
|
|
1373
|
+
const configRel = path6.relative(projectRoot, configPath);
|
|
2149
1374
|
console.log(`Monitor config retained at ${configRel}`);
|
|
2150
1375
|
}
|
|
2151
1376
|
console.log("");
|
|
@@ -2183,7 +1408,7 @@ function readCodexConfigToml(configPath) {
|
|
|
2183
1408
|
}
|
|
2184
1409
|
}
|
|
2185
1410
|
function writeCodexConfigToml(configPath, data) {
|
|
2186
|
-
const dir =
|
|
1411
|
+
const dir = path6.dirname(configPath);
|
|
2187
1412
|
if (!fs6.existsSync(dir)) {
|
|
2188
1413
|
fs6.mkdirSync(dir, { recursive: true });
|
|
2189
1414
|
}
|
|
@@ -2192,7 +1417,7 @@ function writeCodexConfigToml(configPath, data) {
|
|
|
2192
1417
|
}
|
|
2193
1418
|
|
|
2194
1419
|
// src/monitor/plugins/codex/status.ts
|
|
2195
|
-
import
|
|
1420
|
+
import path7 from "path";
|
|
2196
1421
|
import * as fs7 from "fs";
|
|
2197
1422
|
async function getCodexStatus(opts) {
|
|
2198
1423
|
const projectRoot = opts?.projectRoot ?? process.cwd();
|
|
@@ -2232,7 +1457,7 @@ function isHooksBlockInstalled() {
|
|
|
2232
1457
|
|
|
2233
1458
|
// src/monitor/plugins/codex/transcript.ts
|
|
2234
1459
|
import * as fs8 from "fs";
|
|
2235
|
-
import * as
|
|
1460
|
+
import * as path8 from "path";
|
|
2236
1461
|
function emptyRollout() {
|
|
2237
1462
|
return {
|
|
2238
1463
|
prompt: "",
|
|
@@ -2272,7 +1497,7 @@ function findRolloutPathForSession(sessionId, sessionsDir = getCodexSessionsDir(
|
|
|
2272
1497
|
}
|
|
2273
1498
|
for (const entry of entries) {
|
|
2274
1499
|
if (filesScanned++ > merged.maxFiles) break;
|
|
2275
|
-
const full =
|
|
1500
|
+
const full = path8.join(dir, entry.name);
|
|
2276
1501
|
if (entry.isDirectory()) {
|
|
2277
1502
|
queue.push(full);
|
|
2278
1503
|
continue;
|
|
@@ -2590,11 +1815,11 @@ import * as os7 from "os";
|
|
|
2590
1815
|
// src/monitor/plugins/cursor/install.ts
|
|
2591
1816
|
import * as fs11 from "fs";
|
|
2592
1817
|
import * as os4 from "os";
|
|
2593
|
-
import * as
|
|
1818
|
+
import * as path11 from "path";
|
|
2594
1819
|
|
|
2595
1820
|
// src/monitor/plugins/cursor/config.ts
|
|
2596
1821
|
import * as fs10 from "fs";
|
|
2597
|
-
import * as
|
|
1822
|
+
import * as path9 from "path";
|
|
2598
1823
|
function getCursorConfigPath(projectRoot) {
|
|
2599
1824
|
return getMonitorConfigPath(projectRoot, "cursor");
|
|
2600
1825
|
}
|
|
@@ -2610,7 +1835,7 @@ function loadCursorConfig(projectRoot) {
|
|
|
2610
1835
|
}
|
|
2611
1836
|
function writeCursorConfig(projectRoot, config) {
|
|
2612
1837
|
const filePath = getCursorConfigPath(projectRoot);
|
|
2613
|
-
const dir =
|
|
1838
|
+
const dir = path9.dirname(filePath);
|
|
2614
1839
|
if (!fs10.existsSync(dir)) {
|
|
2615
1840
|
fs10.mkdirSync(dir, { recursive: true });
|
|
2616
1841
|
}
|
|
@@ -2633,14 +1858,14 @@ function deleteCursorConfig(projectRoot) {
|
|
|
2633
1858
|
|
|
2634
1859
|
// src/monitor/plugins/cursor/paths.ts
|
|
2635
1860
|
import * as os3 from "os";
|
|
2636
|
-
import * as
|
|
1861
|
+
import * as path10 from "path";
|
|
2637
1862
|
var CURSOR_DIR_NAME = ".cursor";
|
|
2638
1863
|
var CURSOR_HOOKS_FILE = "hooks.json";
|
|
2639
1864
|
function getCursorUserDir(homeDir = os3.homedir()) {
|
|
2640
|
-
return
|
|
1865
|
+
return path10.join(homeDir, CURSOR_DIR_NAME);
|
|
2641
1866
|
}
|
|
2642
1867
|
function getCursorHooksPath(homeDir = os3.homedir()) {
|
|
2643
|
-
return
|
|
1868
|
+
return path10.join(getCursorUserDir(homeDir), CURSOR_HOOKS_FILE);
|
|
2644
1869
|
}
|
|
2645
1870
|
|
|
2646
1871
|
// src/monitor/plugins/cursor/hooks-config.ts
|
|
@@ -2738,7 +1963,7 @@ function readJsonFileTolerant(filePath) {
|
|
|
2738
1963
|
}
|
|
2739
1964
|
}
|
|
2740
1965
|
function writeJsonFileWithDir(filePath, data) {
|
|
2741
|
-
const dir =
|
|
1966
|
+
const dir = path11.dirname(filePath);
|
|
2742
1967
|
if (!fs11.existsSync(dir)) {
|
|
2743
1968
|
fs11.mkdirSync(dir, { recursive: true });
|
|
2744
1969
|
}
|
|
@@ -2760,26 +1985,19 @@ async function installCursor(opts) {
|
|
|
2760
1985
|
`Activity from this workspace (${projectRoot}) will be associated with`
|
|
2761
1986
|
);
|
|
2762
1987
|
console.log("the agent you select below.\n");
|
|
2763
|
-
const
|
|
2764
|
-
|
|
1988
|
+
const agent = await provisionSelfMonitorAgent({
|
|
1989
|
+
projectRoot,
|
|
1990
|
+
source: CURSOR_AGENT_SOURCE,
|
|
1991
|
+
displayName: "Cursor",
|
|
1992
|
+
category: CURSOR_AGENT_CATEGORY
|
|
1993
|
+
});
|
|
2765
1994
|
const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
|
|
2766
|
-
|
|
1995
|
+
const apiKey = agent.apiKey?.key;
|
|
2767
1996
|
if (!apiKey) {
|
|
2768
|
-
console.
|
|
2769
|
-
"
|
|
1997
|
+
console.error(
|
|
1998
|
+
"Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
|
|
2770
1999
|
);
|
|
2771
|
-
|
|
2772
|
-
if (!apiKey) {
|
|
2773
|
-
console.error("API key is required for monitoring.");
|
|
2774
|
-
process.exit(1);
|
|
2775
|
-
}
|
|
2776
|
-
await validatePastedApiKey({
|
|
2777
|
-
monitoringEndpoint,
|
|
2778
|
-
apiKey,
|
|
2779
|
-
expectedAgentId: agent.id,
|
|
2780
|
-
expectedAgentName: agent.name,
|
|
2781
|
-
expectedApiKeyId: agent.apiKey?.id ?? null
|
|
2782
|
-
});
|
|
2000
|
+
process.exit(1);
|
|
2783
2001
|
}
|
|
2784
2002
|
const cursorDir = getCursorUserDir(homeDir);
|
|
2785
2003
|
if (!fs11.existsSync(cursorDir)) {
|
|
@@ -2799,8 +2017,8 @@ async function installCursor(opts) {
|
|
|
2799
2017
|
};
|
|
2800
2018
|
writeCursorConfig(projectRoot, monitorConfig);
|
|
2801
2019
|
const configPath = getCursorConfigPath(projectRoot);
|
|
2802
|
-
const configRel =
|
|
2803
|
-
const hooksDisplay = `~/${
|
|
2020
|
+
const configRel = path11.relative(projectRoot, configPath);
|
|
2021
|
+
const hooksDisplay = `~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`;
|
|
2804
2022
|
console.log("");
|
|
2805
2023
|
console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
|
|
2806
2024
|
if (agent.apiKey?.key) {
|
|
@@ -2827,57 +2045,6 @@ async function installCursor(opts) {
|
|
|
2827
2045
|
monitoringEndpoint
|
|
2828
2046
|
};
|
|
2829
2047
|
}
|
|
2830
|
-
async function chooseOrCreateAgent(defaultName) {
|
|
2831
|
-
const choice = await promptUser(
|
|
2832
|
-
"Create a new agent or use an existing one? (new/existing) [new]: "
|
|
2833
|
-
);
|
|
2834
|
-
if (choice.toLowerCase() === "existing" || choice.toLowerCase() === "e") {
|
|
2835
|
-
const agents = await listAgents();
|
|
2836
|
-
if (agents.length === 0) {
|
|
2837
|
-
console.log("No agents found. Creating a new one instead.\n");
|
|
2838
|
-
return createNewAgent3(defaultName);
|
|
2839
|
-
}
|
|
2840
|
-
console.log("\nAvailable agents:");
|
|
2841
|
-
for (let i = 0; i < agents.length; i++) {
|
|
2842
|
-
console.log(
|
|
2843
|
-
` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`
|
|
2844
|
-
);
|
|
2845
|
-
}
|
|
2846
|
-
const selection = await promptUser(`
|
|
2847
|
-
Select agent (1-${agents.length}): `);
|
|
2848
|
-
const idx = parseInt(selection, 10) - 1;
|
|
2849
|
-
if (isNaN(idx) || idx < 0 || idx >= agents.length) {
|
|
2850
|
-
console.error("Invalid selection.");
|
|
2851
|
-
process.exit(1);
|
|
2852
|
-
}
|
|
2853
|
-
const fetched = await getAgent(agents[idx].id);
|
|
2854
|
-
if (!fetched.apiKey?.key && !fetched.apiKey?.isActive) {
|
|
2855
|
-
console.log(
|
|
2856
|
-
"\nThis agent has no active API key. Please create a new agent instead,"
|
|
2857
|
-
);
|
|
2858
|
-
console.log("or generate an API key via the Olakai dashboard.\n");
|
|
2859
|
-
const createNew = await promptUser("Create a new agent? (y/n) [y]: ");
|
|
2860
|
-
if (createNew.toLowerCase() !== "n") {
|
|
2861
|
-
return createNewAgent3(defaultName);
|
|
2862
|
-
}
|
|
2863
|
-
process.exit(1);
|
|
2864
|
-
}
|
|
2865
|
-
return fetched;
|
|
2866
|
-
}
|
|
2867
|
-
return createNewAgent3(defaultName);
|
|
2868
|
-
}
|
|
2869
|
-
async function createNewAgent3(defaultName) {
|
|
2870
|
-
const nameInput = await promptUser(`Agent name [${defaultName}]: `);
|
|
2871
|
-
const agentName = nameInput || defaultName;
|
|
2872
|
-
return createAgent({
|
|
2873
|
-
name: agentName,
|
|
2874
|
-
description: `Cursor local agent for ${agentName}`,
|
|
2875
|
-
role: "WORKER",
|
|
2876
|
-
createApiKey: true,
|
|
2877
|
-
category: CURSOR_AGENT_CATEGORY,
|
|
2878
|
-
source: CURSOR_AGENT_SOURCE
|
|
2879
|
-
});
|
|
2880
|
-
}
|
|
2881
2048
|
async function uninstallCursor(opts) {
|
|
2882
2049
|
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
2883
2050
|
const homeDir = opts.homeDir ?? os4.homedir();
|
|
@@ -2902,20 +2069,20 @@ async function uninstallCursor(opts) {
|
|
|
2902
2069
|
writeJsonFileWithDir(hooksPath, cleaned);
|
|
2903
2070
|
}
|
|
2904
2071
|
console.log(
|
|
2905
|
-
`\u2713 Olakai hooks removed from ~/${
|
|
2072
|
+
`\u2713 Olakai hooks removed from ~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`
|
|
2906
2073
|
);
|
|
2907
2074
|
} else {
|
|
2908
2075
|
console.log("No Cursor hooks file found.");
|
|
2909
2076
|
}
|
|
2910
2077
|
if (!opts.keepConfig) {
|
|
2911
2078
|
const configPath = getCursorConfigPath(projectRoot);
|
|
2912
|
-
const configRel =
|
|
2079
|
+
const configRel = path11.relative(projectRoot, configPath);
|
|
2913
2080
|
if (deleteCursorConfig(projectRoot)) {
|
|
2914
2081
|
console.log(`\u2713 Monitor config removed (${configRel})`);
|
|
2915
2082
|
}
|
|
2916
2083
|
} else {
|
|
2917
2084
|
const configPath = getCursorConfigPath(projectRoot);
|
|
2918
|
-
const configRel =
|
|
2085
|
+
const configRel = path11.relative(projectRoot, configPath);
|
|
2919
2086
|
console.log(`Monitor config retained at ${configRel}`);
|
|
2920
2087
|
}
|
|
2921
2088
|
console.log("");
|
|
@@ -2928,7 +2095,7 @@ async function uninstallCursor(opts) {
|
|
|
2928
2095
|
// src/monitor/plugins/cursor/status.ts
|
|
2929
2096
|
import * as fs12 from "fs";
|
|
2930
2097
|
import * as os5 from "os";
|
|
2931
|
-
import * as
|
|
2098
|
+
import * as path12 from "path";
|
|
2932
2099
|
async function getCursorStatus(opts) {
|
|
2933
2100
|
const projectRoot = opts?.projectRoot ?? process.cwd();
|
|
2934
2101
|
const homeDir = opts?.homeDir ?? os5.homedir();
|
|
@@ -3120,10 +2287,10 @@ function extractCursorMeta(payload) {
|
|
|
3120
2287
|
// src/monitor/plugins/cursor/pairing-state.ts
|
|
3121
2288
|
import * as fs13 from "fs";
|
|
3122
2289
|
import * as os6 from "os";
|
|
3123
|
-
import * as
|
|
2290
|
+
import * as path13 from "path";
|
|
3124
2291
|
var STATE_DIR_SEGMENTS2 = [".olakai", "cursor-pairings"];
|
|
3125
2292
|
function getPairingsDir(homeDir) {
|
|
3126
|
-
return
|
|
2293
|
+
return path13.join(homeDir, ...STATE_DIR_SEGMENTS2);
|
|
3127
2294
|
}
|
|
3128
2295
|
function sanitizeKeyFragment(value) {
|
|
3129
2296
|
return value.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -3134,7 +2301,7 @@ function getPairingKey(conversationId, generationId) {
|
|
|
3134
2301
|
return `${conv}__${sanitizeKeyFragment(generationId)}`;
|
|
3135
2302
|
}
|
|
3136
2303
|
function getPairingFile(conversationId, generationId, homeDir) {
|
|
3137
|
-
return
|
|
2304
|
+
return path13.join(
|
|
3138
2305
|
getPairingsDir(homeDir),
|
|
3139
2306
|
`${getPairingKey(conversationId, generationId)}.json`
|
|
3140
2307
|
);
|
|
@@ -3200,7 +2367,7 @@ function listPendingPrompts(homeDir = os6.homedir()) {
|
|
|
3200
2367
|
const result = [];
|
|
3201
2368
|
for (const name of files) {
|
|
3202
2369
|
if (!name.endsWith(".json")) continue;
|
|
3203
|
-
const filePath =
|
|
2370
|
+
const filePath = path13.join(dir, name);
|
|
3204
2371
|
try {
|
|
3205
2372
|
const raw = fs13.readFileSync(filePath, "utf-8");
|
|
3206
2373
|
const parsed = JSON.parse(raw);
|
|
@@ -3516,7 +2683,7 @@ function describeDetection(plugin, projectRoot) {
|
|
|
3516
2683
|
} catch {
|
|
3517
2684
|
}
|
|
3518
2685
|
try {
|
|
3519
|
-
const settings =
|
|
2686
|
+
const settings = path14.join(projectRoot, ".claude", "settings.json");
|
|
3520
2687
|
if (fs15.existsSync(settings)) {
|
|
3521
2688
|
return "found .claude/settings.json";
|
|
3522
2689
|
}
|
|
@@ -3604,26 +2771,34 @@ async function initCommand(options) {
|
|
|
3604
2771
|
try {
|
|
3605
2772
|
const resolved = resolveProfileName();
|
|
3606
2773
|
const profileName = resolved?.name ?? "default";
|
|
3607
|
-
|
|
3608
|
-
setHostOverride(host);
|
|
2774
|
+
let optedReAuth = false;
|
|
3609
2775
|
if (isTokenValid()) {
|
|
3610
2776
|
const file = readProfilesFile();
|
|
3611
2777
|
const existing = file.profiles[profileName];
|
|
3612
2778
|
const who = existing?.email ?? "unknown";
|
|
2779
|
+
const where = existing?.host ?? "(unknown host)";
|
|
3613
2780
|
if (options.nonInteractive) {
|
|
3614
2781
|
console.log(
|
|
3615
|
-
`Already authenticated as ${who} on ${
|
|
2782
|
+
`Already authenticated as ${who} on ${where} (profile: ${profileName}).`
|
|
3616
2783
|
);
|
|
3617
2784
|
return;
|
|
3618
2785
|
}
|
|
3619
2786
|
const answer = await promptUser(
|
|
3620
|
-
`Already authenticated as ${who} on ${
|
|
2787
|
+
`Already authenticated as ${who} on ${where} (profile: ${profileName}). Re-authenticate? [y/N]: `
|
|
3621
2788
|
);
|
|
3622
2789
|
if (answer.trim().toLowerCase() !== "y") {
|
|
3623
2790
|
console.log("Keeping existing credentials. Run 'olakai logout' to sign out.");
|
|
3624
2791
|
return;
|
|
3625
2792
|
}
|
|
2793
|
+
optedReAuth = true;
|
|
3626
2794
|
}
|
|
2795
|
+
const host = await resolveHost(
|
|
2796
|
+
profileName,
|
|
2797
|
+
options,
|
|
2798
|
+
interactive,
|
|
2799
|
+
optedReAuth
|
|
2800
|
+
);
|
|
2801
|
+
setHostOverride(host);
|
|
3627
2802
|
const email = await resolveEmail(options, interactive);
|
|
3628
2803
|
console.log(`
|
|
3629
2804
|
Contacting ${host} for ${email}...`);
|
|
@@ -3724,7 +2899,7 @@ function isSafeOpenUrl(raw) {
|
|
|
3724
2899
|
return false;
|
|
3725
2900
|
}
|
|
3726
2901
|
}
|
|
3727
|
-
async function resolveHost(profileName, options, interactive) {
|
|
2902
|
+
async function resolveHost(profileName, options, interactive, forceHostPrompt = false) {
|
|
3728
2903
|
const fromFlag = options.host?.trim() || process.env.OLAKAI_HOST?.trim();
|
|
3729
2904
|
if (fromFlag) {
|
|
3730
2905
|
return getBaseUrl();
|
|
@@ -3732,7 +2907,8 @@ async function resolveHost(profileName, options, interactive) {
|
|
|
3732
2907
|
const file = readProfilesFile();
|
|
3733
2908
|
const existing = file.profiles[profileName];
|
|
3734
2909
|
if (existing?.host) {
|
|
3735
|
-
|
|
2910
|
+
const shouldOfferSwitch = interactive && (forceHostPrompt || !isTokenValid());
|
|
2911
|
+
if (!shouldOfferSwitch) {
|
|
3736
2912
|
return existing.host;
|
|
3737
2913
|
}
|
|
3738
2914
|
const keep = await promptUser(
|
|
@@ -5360,7 +4536,7 @@ async function statusCommand(options) {
|
|
|
5360
4536
|
return;
|
|
5361
4537
|
}
|
|
5362
4538
|
if (plugin.id === "claude-code") {
|
|
5363
|
-
const { printClaudeCodeStatus } = await import("./status-
|
|
4539
|
+
const { printClaudeCodeStatus } = await import("./status-USHUUHK6.js");
|
|
5364
4540
|
await printClaudeCodeStatus({ projectRoot: process.cwd() });
|
|
5365
4541
|
return;
|
|
5366
4542
|
}
|
|
@@ -5376,6 +4552,24 @@ async function statusCommand(options) {
|
|
|
5376
4552
|
async function disableCommand(options) {
|
|
5377
4553
|
const toolId = await resolveToolFromOptions(options.tool, "disable");
|
|
5378
4554
|
const plugin = getPlugin(toolId);
|
|
4555
|
+
let agentIdToDelete;
|
|
4556
|
+
if (options.deleteAgent) {
|
|
4557
|
+
try {
|
|
4558
|
+
const report = await plugin.status({ projectRoot: process.cwd() });
|
|
4559
|
+
if (report.configured && report.agentId) {
|
|
4560
|
+
agentIdToDelete = report.agentId;
|
|
4561
|
+
} else {
|
|
4562
|
+
console.log(
|
|
4563
|
+
"No monitor config found in this workspace \u2014 nothing to delete remotely."
|
|
4564
|
+
);
|
|
4565
|
+
}
|
|
4566
|
+
} catch (err) {
|
|
4567
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4568
|
+
console.warn(
|
|
4569
|
+
`Couldn't read local monitor config to identify the agent (${msg}). Proceeding with local-only uninstall.`
|
|
4570
|
+
);
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
5379
4573
|
await runPluginAction(
|
|
5380
4574
|
plugin,
|
|
5381
4575
|
() => plugin.uninstall({
|
|
@@ -5383,6 +4577,22 @@ async function disableCommand(options) {
|
|
|
5383
4577
|
keepConfig: options.keepConfig
|
|
5384
4578
|
})
|
|
5385
4579
|
);
|
|
4580
|
+
if (agentIdToDelete) {
|
|
4581
|
+
const { deleteAgent: deleteAgent2 } = await import("./api-63UDJX5M.js");
|
|
4582
|
+
try {
|
|
4583
|
+
await deleteAgent2(agentIdToDelete);
|
|
4584
|
+
console.log(`\u2713 Remote agent ${agentIdToDelete} deleted.`);
|
|
4585
|
+
} catch (err) {
|
|
4586
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4587
|
+
console.error(
|
|
4588
|
+
`Local uninstall succeeded, but remote agent delete failed: ${msg}`
|
|
4589
|
+
);
|
|
4590
|
+
console.error(
|
|
4591
|
+
`You can retry by visiting the Olakai dashboard, or rerun 'olakai monitor disable --tool ${toolId} --delete-agent' (the local config is gone, so the agent id won't be re-discovered \u2014 use the dashboard).`
|
|
4592
|
+
);
|
|
4593
|
+
process.exit(1);
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
5386
4596
|
}
|
|
5387
4597
|
async function runPluginAction(plugin, fn) {
|
|
5388
4598
|
try {
|
|
@@ -5507,6 +4717,9 @@ function registerMonitorCommand(program2) {
|
|
|
5507
4717
|
).option(
|
|
5508
4718
|
"--keep-config",
|
|
5509
4719
|
"Keep the monitor config file (only remove hooks)"
|
|
4720
|
+
).option(
|
|
4721
|
+
"--delete-agent",
|
|
4722
|
+
"Also delete the remote agent on the Olakai backend. Useful when retiring a workspace permanently. Self-monitor owner or ADMIN only."
|
|
5510
4723
|
).action(disableCommand);
|
|
5511
4724
|
for (const plugin of listPlugins()) {
|
|
5512
4725
|
plugin.registerCommands?.(monitor);
|