olakai-cli 0.6.2 → 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-PGRX347G.js → chunk-AVB4N2UN.js} +2 -303
- 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 +318 -1048
- package/dist/index.js.map +1 -1
- package/dist/{status-NK4PX2NS.js → status-USHUUHK6.js} +3 -2
- package/dist/status-USHUUHK6.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-PGRX347G.js.map +0 -1
- /package/dist/{status-NK4PX2NS.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-PGRX347G.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()) {
|
|
@@ -997,12 +284,7 @@ async function postHandshakeJson(url, body, options = {}) {
|
|
|
997
284
|
body: JSON.stringify(body)
|
|
998
285
|
});
|
|
999
286
|
} catch (err) {
|
|
1000
|
-
return
|
|
1001
|
-
kind: "error",
|
|
1002
|
-
code: "network_error",
|
|
1003
|
-
message: err instanceof Error ? err.message : "Network request failed",
|
|
1004
|
-
status: 0
|
|
1005
|
-
};
|
|
287
|
+
return buildNetworkError(err);
|
|
1006
288
|
}
|
|
1007
289
|
if (response.ok) {
|
|
1008
290
|
let data;
|
|
@@ -1024,7 +306,13 @@ async function postHandshakeJson(url, body, options = {}) {
|
|
|
1024
306
|
} catch {
|
|
1025
307
|
}
|
|
1026
308
|
const code = mapErrorCode(response.status, errBody);
|
|
1027
|
-
|
|
309
|
+
let fallbackPath = "";
|
|
310
|
+
try {
|
|
311
|
+
fallbackPath = new URL(url).pathname;
|
|
312
|
+
} catch {
|
|
313
|
+
fallbackPath = url;
|
|
314
|
+
}
|
|
315
|
+
const message = errBody.message || errBody.error || `Request to ${fallbackPath} failed with status ${response.status}`;
|
|
1028
316
|
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
1029
317
|
const envelope = {
|
|
1030
318
|
kind: "error",
|
|
@@ -1076,6 +364,28 @@ function mapErrorCode(status, body) {
|
|
|
1076
364
|
return "unknown_error";
|
|
1077
365
|
}
|
|
1078
366
|
}
|
|
367
|
+
function buildNetworkError(err) {
|
|
368
|
+
const base = err instanceof Error ? err.message : "Network request failed";
|
|
369
|
+
let causeCode;
|
|
370
|
+
let causeMessage;
|
|
371
|
+
if (err instanceof Error && err.cause && typeof err.cause === "object") {
|
|
372
|
+
const c = err.cause;
|
|
373
|
+
if (typeof c.code === "string" && c.code.length > 0) {
|
|
374
|
+
causeCode = c.code;
|
|
375
|
+
}
|
|
376
|
+
if (typeof c.message === "string" && c.message.length > 0) {
|
|
377
|
+
causeMessage = c.message;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const message = causeMessage && causeMessage !== base ? `${base}: ${causeMessage}` : base;
|
|
381
|
+
return {
|
|
382
|
+
kind: "error",
|
|
383
|
+
code: "network_error",
|
|
384
|
+
message,
|
|
385
|
+
status: 0,
|
|
386
|
+
...causeCode ? { causeCode } : {}
|
|
387
|
+
};
|
|
388
|
+
}
|
|
1079
389
|
function parseRetryAfter(header) {
|
|
1080
390
|
if (!header) return void 0;
|
|
1081
391
|
const asNumber2 = Number(header);
|
|
@@ -1110,7 +420,7 @@ function isInteractive() {
|
|
|
1110
420
|
|
|
1111
421
|
// src/monitor/detect-all.ts
|
|
1112
422
|
import * as fs15 from "fs";
|
|
1113
|
-
import * as
|
|
423
|
+
import * as path14 from "path";
|
|
1114
424
|
|
|
1115
425
|
// src/monitor/plugins/codex/paths.ts
|
|
1116
426
|
import * as os from "os";
|
|
@@ -1130,6 +440,7 @@ function getCodexSessionsDir() {
|
|
|
1130
440
|
|
|
1131
441
|
// src/monitor/plugins/claude-code/index.ts
|
|
1132
442
|
import * as fs4 from "fs";
|
|
443
|
+
import { spawnSync } from "child_process";
|
|
1133
444
|
|
|
1134
445
|
// src/monitor/plugin.ts
|
|
1135
446
|
var TOOL_IDS = [
|
|
@@ -1233,44 +544,98 @@ function shouldReportTurn(existing, currentUserTimestamp) {
|
|
|
1233
544
|
// src/monitor/plugins/claude-code/install.ts
|
|
1234
545
|
import * as fs2 from "fs";
|
|
1235
546
|
|
|
1236
|
-
// src/monitor/
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
);
|
|
1242
|
-
|
|
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];
|
|
1243
567
|
console.log(
|
|
1244
|
-
|
|
568
|
+
`
|
|
569
|
+
Found your existing ${opts.displayName} agent "${found.name}".`
|
|
1245
570
|
);
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
+
};
|
|
1253
600
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
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;
|
|
1269
634
|
}
|
|
1270
635
|
}
|
|
1271
636
|
|
|
1272
637
|
// src/monitor/plugins/claude-code/install.ts
|
|
1273
|
-
import
|
|
638
|
+
import path4 from "path";
|
|
1274
639
|
var CLAUDE_CODE_SOURCE = "claude-code";
|
|
1275
640
|
var CLAUDE_CODE_AGENT_SOURCE = "CLAUDE_CODE";
|
|
1276
641
|
var CLAUDE_CODE_AGENT_CATEGORY = "CODING";
|
|
@@ -1282,65 +647,19 @@ async function installClaudeCode(opts) {
|
|
|
1282
647
|
process.exit(1);
|
|
1283
648
|
}
|
|
1284
649
|
console.log("Setting up Claude Code monitoring for this workspace...\n");
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const agents = await listAgents();
|
|
1292
|
-
if (agents.length === 0) {
|
|
1293
|
-
console.log("No agents found. Creating a new one instead.\n");
|
|
1294
|
-
agent = await createNewAgent(dirName);
|
|
1295
|
-
} else {
|
|
1296
|
-
console.log("\nAvailable agents:");
|
|
1297
|
-
for (let i = 0; i < agents.length; i++) {
|
|
1298
|
-
console.log(
|
|
1299
|
-
` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`
|
|
1300
|
-
);
|
|
1301
|
-
}
|
|
1302
|
-
const selection = await promptUser(`
|
|
1303
|
-
Select agent (1-${agents.length}): `);
|
|
1304
|
-
const idx = parseInt(selection, 10) - 1;
|
|
1305
|
-
if (isNaN(idx) || idx < 0 || idx >= agents.length) {
|
|
1306
|
-
console.error("Invalid selection.");
|
|
1307
|
-
process.exit(1);
|
|
1308
|
-
}
|
|
1309
|
-
agent = await getAgent(agents[idx].id);
|
|
1310
|
-
if (!agent.apiKey?.key && !agent.apiKey?.isActive) {
|
|
1311
|
-
console.log(
|
|
1312
|
-
"\nThis agent has no active API key. Please create a new agent instead,"
|
|
1313
|
-
);
|
|
1314
|
-
console.log("or generate an API key via the Olakai dashboard.\n");
|
|
1315
|
-
const createNew = await promptUser("Create a new agent? (y/n) [y]: ");
|
|
1316
|
-
if (createNew.toLowerCase() !== "n") {
|
|
1317
|
-
agent = await createNewAgent(dirName);
|
|
1318
|
-
} else {
|
|
1319
|
-
process.exit(1);
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
} else {
|
|
1324
|
-
agent = await createNewAgent(dirName);
|
|
1325
|
-
}
|
|
650
|
+
const agent = await provisionSelfMonitorAgent({
|
|
651
|
+
projectRoot,
|
|
652
|
+
source: CLAUDE_CODE_AGENT_SOURCE,
|
|
653
|
+
displayName: "Claude Code",
|
|
654
|
+
category: CLAUDE_CODE_AGENT_CATEGORY
|
|
655
|
+
});
|
|
1326
656
|
const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
|
|
1327
|
-
|
|
657
|
+
const apiKey = agent.apiKey?.key;
|
|
1328
658
|
if (!apiKey) {
|
|
1329
|
-
console.
|
|
1330
|
-
"
|
|
659
|
+
console.error(
|
|
660
|
+
"Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
|
|
1331
661
|
);
|
|
1332
|
-
|
|
1333
|
-
if (!apiKey) {
|
|
1334
|
-
console.error("API key is required for monitoring.");
|
|
1335
|
-
process.exit(1);
|
|
1336
|
-
}
|
|
1337
|
-
await validatePastedApiKey({
|
|
1338
|
-
monitoringEndpoint,
|
|
1339
|
-
apiKey,
|
|
1340
|
-
expectedAgentId: agent.id,
|
|
1341
|
-
expectedAgentName: agent.name,
|
|
1342
|
-
expectedApiKeyId: agent.apiKey?.id ?? null
|
|
1343
|
-
});
|
|
662
|
+
process.exit(1);
|
|
1344
663
|
}
|
|
1345
664
|
const claudeDir = getClaudeDir(projectRoot);
|
|
1346
665
|
if (!fs2.existsSync(claudeDir)) {
|
|
@@ -1364,7 +683,7 @@ Select agent (1-${agents.length}): `);
|
|
|
1364
683
|
};
|
|
1365
684
|
writeClaudeCodeConfig(projectRoot, monitorConfig);
|
|
1366
685
|
const configPath = getClaudeCodeConfigPath(projectRoot);
|
|
1367
|
-
const configRel =
|
|
686
|
+
const configRel = path4.relative(projectRoot, configPath);
|
|
1368
687
|
console.log("");
|
|
1369
688
|
console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
|
|
1370
689
|
if (agent.apiKey?.key) {
|
|
@@ -1393,18 +712,6 @@ Select agent (1-${agents.length}): `);
|
|
|
1393
712
|
monitoringEndpoint
|
|
1394
713
|
};
|
|
1395
714
|
}
|
|
1396
|
-
async function createNewAgent(defaultName) {
|
|
1397
|
-
const nameInput = await promptUser(`Agent name [${defaultName}]: `);
|
|
1398
|
-
const agentName = nameInput || defaultName;
|
|
1399
|
-
return createAgent({
|
|
1400
|
-
name: agentName,
|
|
1401
|
-
description: `Claude Code local agent for ${agentName}`,
|
|
1402
|
-
role: "WORKER",
|
|
1403
|
-
createApiKey: true,
|
|
1404
|
-
category: CLAUDE_CODE_AGENT_CATEGORY,
|
|
1405
|
-
source: CLAUDE_CODE_AGENT_SOURCE
|
|
1406
|
-
});
|
|
1407
|
-
}
|
|
1408
715
|
async function uninstallClaudeCode(opts) {
|
|
1409
716
|
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
1410
717
|
const settingsPath = getSettingsPath(projectRoot);
|
|
@@ -1433,13 +740,13 @@ async function uninstallClaudeCode(opts) {
|
|
|
1433
740
|
}
|
|
1434
741
|
if (!opts.keepConfig) {
|
|
1435
742
|
const configPath = getClaudeCodeConfigPath(projectRoot);
|
|
1436
|
-
const configRel =
|
|
743
|
+
const configRel = path4.relative(projectRoot, configPath);
|
|
1437
744
|
if (deleteClaudeCodeConfig(projectRoot)) {
|
|
1438
745
|
console.log(`\u2713 Monitor config removed (${configRel})`);
|
|
1439
746
|
}
|
|
1440
747
|
} else {
|
|
1441
748
|
const configPath = getClaudeCodeConfigPath(projectRoot);
|
|
1442
|
-
const configRel =
|
|
749
|
+
const configRel = path4.relative(projectRoot, configPath);
|
|
1443
750
|
console.log(`Monitor config retained at ${configRel}`);
|
|
1444
751
|
}
|
|
1445
752
|
console.log("");
|
|
@@ -1813,21 +1120,37 @@ var claudeCodePlugin = {
|
|
|
1813
1120
|
if (fs4.existsSync(getLegacyClaudeMonitorConfigPath(projectRoot))) {
|
|
1814
1121
|
return true;
|
|
1815
1122
|
}
|
|
1816
|
-
return false;
|
|
1817
1123
|
} catch {
|
|
1818
|
-
return false;
|
|
1819
1124
|
}
|
|
1125
|
+
return detectClaudeBinaryOnPath();
|
|
1820
1126
|
}
|
|
1821
1127
|
};
|
|
1128
|
+
function detectClaudeBinaryOnPath() {
|
|
1129
|
+
try {
|
|
1130
|
+
const probe = spawnSync(
|
|
1131
|
+
process.platform === "win32" ? "where" : "which",
|
|
1132
|
+
["claude"],
|
|
1133
|
+
{
|
|
1134
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1135
|
+
timeout: 1e3
|
|
1136
|
+
}
|
|
1137
|
+
);
|
|
1138
|
+
if (probe.status === 0 && probe.stdout && probe.stdout.toString().trim()) {
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1822
1145
|
registerPlugin(claudeCodePlugin);
|
|
1823
1146
|
|
|
1824
1147
|
// src/monitor/plugins/codex/index.ts
|
|
1825
1148
|
import * as fs9 from "fs";
|
|
1826
|
-
import { spawnSync } from "child_process";
|
|
1149
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
1827
1150
|
|
|
1828
1151
|
// src/monitor/plugins/codex/install.ts
|
|
1829
1152
|
import * as fs6 from "fs";
|
|
1830
|
-
import * as
|
|
1153
|
+
import * as path6 from "path";
|
|
1831
1154
|
import * as TOML from "@iarna/toml";
|
|
1832
1155
|
|
|
1833
1156
|
// src/monitor/plugins/codex/hooks.ts
|
|
@@ -1903,7 +1226,7 @@ function hasOlakaiHooksInstalled(parsed) {
|
|
|
1903
1226
|
|
|
1904
1227
|
// src/monitor/plugins/codex/config.ts
|
|
1905
1228
|
import * as fs5 from "fs";
|
|
1906
|
-
import * as
|
|
1229
|
+
import * as path5 from "path";
|
|
1907
1230
|
function getCodexConfigPath2(projectRoot) {
|
|
1908
1231
|
return getMonitorConfigPath(projectRoot, "codex");
|
|
1909
1232
|
}
|
|
@@ -1919,7 +1242,7 @@ function loadCodexConfig(projectRoot) {
|
|
|
1919
1242
|
}
|
|
1920
1243
|
function writeCodexConfig(projectRoot, config) {
|
|
1921
1244
|
const filePath = getCodexConfigPath2(projectRoot);
|
|
1922
|
-
const dir =
|
|
1245
|
+
const dir = path5.dirname(filePath);
|
|
1923
1246
|
if (!fs5.existsSync(dir)) {
|
|
1924
1247
|
fs5.mkdirSync(dir, { recursive: true });
|
|
1925
1248
|
}
|
|
@@ -1952,65 +1275,19 @@ async function installCodex(opts) {
|
|
|
1952
1275
|
process.exit(1);
|
|
1953
1276
|
}
|
|
1954
1277
|
console.log("Setting up Codex CLI monitoring for this workspace...\n");
|
|
1955
|
-
const
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
const agents = await listAgents();
|
|
1962
|
-
if (agents.length === 0) {
|
|
1963
|
-
console.log("No agents found. Creating a new one instead.\n");
|
|
1964
|
-
agent = await createNewAgent2(dirName);
|
|
1965
|
-
} else {
|
|
1966
|
-
console.log("\nAvailable agents:");
|
|
1967
|
-
for (let i = 0; i < agents.length; i++) {
|
|
1968
|
-
console.log(
|
|
1969
|
-
` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`
|
|
1970
|
-
);
|
|
1971
|
-
}
|
|
1972
|
-
const selection = await promptUser(`
|
|
1973
|
-
Select agent (1-${agents.length}): `);
|
|
1974
|
-
const idx = parseInt(selection, 10) - 1;
|
|
1975
|
-
if (isNaN(idx) || idx < 0 || idx >= agents.length) {
|
|
1976
|
-
console.error("Invalid selection.");
|
|
1977
|
-
process.exit(1);
|
|
1978
|
-
}
|
|
1979
|
-
agent = await getAgent(agents[idx].id);
|
|
1980
|
-
if (!agent.apiKey?.key && !agent.apiKey?.isActive) {
|
|
1981
|
-
console.log(
|
|
1982
|
-
"\nThis agent has no active API key. Please create a new agent instead,"
|
|
1983
|
-
);
|
|
1984
|
-
console.log("or generate an API key via the Olakai dashboard.\n");
|
|
1985
|
-
const createNew = await promptUser("Create a new agent? (y/n) [y]: ");
|
|
1986
|
-
if (createNew.toLowerCase() !== "n") {
|
|
1987
|
-
agent = await createNewAgent2(dirName);
|
|
1988
|
-
} else {
|
|
1989
|
-
process.exit(1);
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1993
|
-
} else {
|
|
1994
|
-
agent = await createNewAgent2(dirName);
|
|
1995
|
-
}
|
|
1278
|
+
const agent = await provisionSelfMonitorAgent({
|
|
1279
|
+
projectRoot,
|
|
1280
|
+
source: CODEX_AGENT_SOURCE,
|
|
1281
|
+
displayName: "Codex CLI",
|
|
1282
|
+
category: CODEX_AGENT_CATEGORY
|
|
1283
|
+
});
|
|
1996
1284
|
const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
|
|
1997
|
-
|
|
1285
|
+
const apiKey = agent.apiKey?.key;
|
|
1998
1286
|
if (!apiKey) {
|
|
1999
|
-
console.
|
|
2000
|
-
"
|
|
1287
|
+
console.error(
|
|
1288
|
+
"Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
|
|
2001
1289
|
);
|
|
2002
|
-
|
|
2003
|
-
if (!apiKey) {
|
|
2004
|
-
console.error("API key is required for monitoring.");
|
|
2005
|
-
process.exit(1);
|
|
2006
|
-
}
|
|
2007
|
-
await validatePastedApiKey({
|
|
2008
|
-
monitoringEndpoint,
|
|
2009
|
-
apiKey,
|
|
2010
|
-
expectedAgentId: agent.id,
|
|
2011
|
-
expectedAgentName: agent.name,
|
|
2012
|
-
expectedApiKeyId: agent.apiKey?.id ?? null
|
|
2013
|
-
});
|
|
1290
|
+
process.exit(1);
|
|
2014
1291
|
}
|
|
2015
1292
|
const { configExisted: codexConfigExisted } = installCodexHooksConfig();
|
|
2016
1293
|
const monitorConfig = {
|
|
@@ -2023,7 +1300,7 @@ Select agent (1-${agents.length}): `);
|
|
|
2023
1300
|
};
|
|
2024
1301
|
writeCodexConfig(projectRoot, monitorConfig);
|
|
2025
1302
|
const configPath = getCodexConfigPath2(projectRoot);
|
|
2026
|
-
const configRel =
|
|
1303
|
+
const configRel = path6.relative(projectRoot, configPath);
|
|
2027
1304
|
console.log("");
|
|
2028
1305
|
console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
|
|
2029
1306
|
if (agent.apiKey?.key) {
|
|
@@ -2060,18 +1337,6 @@ Select agent (1-${agents.length}): `);
|
|
|
2060
1337
|
monitoringEndpoint
|
|
2061
1338
|
};
|
|
2062
1339
|
}
|
|
2063
|
-
async function createNewAgent2(defaultName) {
|
|
2064
|
-
const nameInput = await promptUser(`Agent name [${defaultName}]: `);
|
|
2065
|
-
const agentName = nameInput || defaultName;
|
|
2066
|
-
return createAgent({
|
|
2067
|
-
name: agentName,
|
|
2068
|
-
description: `Codex CLI local agent for ${agentName}`,
|
|
2069
|
-
role: "WORKER",
|
|
2070
|
-
createApiKey: true,
|
|
2071
|
-
category: CODEX_AGENT_CATEGORY,
|
|
2072
|
-
source: CODEX_AGENT_SOURCE
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
1340
|
function installCodexHooksConfig() {
|
|
2076
1341
|
const homeDir = getCodexHomeDir();
|
|
2077
1342
|
const configPath = getCodexConfigPath();
|
|
@@ -2099,13 +1364,13 @@ async function uninstallCodex(opts) {
|
|
|
2099
1364
|
}
|
|
2100
1365
|
if (!opts.keepConfig) {
|
|
2101
1366
|
const configPath = getCodexConfigPath2(projectRoot);
|
|
2102
|
-
const configRel =
|
|
1367
|
+
const configRel = path6.relative(projectRoot, configPath);
|
|
2103
1368
|
if (deleteCodexConfig(projectRoot)) {
|
|
2104
1369
|
console.log(`\u2713 Monitor config removed (${configRel})`);
|
|
2105
1370
|
}
|
|
2106
1371
|
} else {
|
|
2107
1372
|
const configPath = getCodexConfigPath2(projectRoot);
|
|
2108
|
-
const configRel =
|
|
1373
|
+
const configRel = path6.relative(projectRoot, configPath);
|
|
2109
1374
|
console.log(`Monitor config retained at ${configRel}`);
|
|
2110
1375
|
}
|
|
2111
1376
|
console.log("");
|
|
@@ -2143,7 +1408,7 @@ function readCodexConfigToml(configPath) {
|
|
|
2143
1408
|
}
|
|
2144
1409
|
}
|
|
2145
1410
|
function writeCodexConfigToml(configPath, data) {
|
|
2146
|
-
const dir =
|
|
1411
|
+
const dir = path6.dirname(configPath);
|
|
2147
1412
|
if (!fs6.existsSync(dir)) {
|
|
2148
1413
|
fs6.mkdirSync(dir, { recursive: true });
|
|
2149
1414
|
}
|
|
@@ -2152,7 +1417,7 @@ function writeCodexConfigToml(configPath, data) {
|
|
|
2152
1417
|
}
|
|
2153
1418
|
|
|
2154
1419
|
// src/monitor/plugins/codex/status.ts
|
|
2155
|
-
import
|
|
1420
|
+
import path7 from "path";
|
|
2156
1421
|
import * as fs7 from "fs";
|
|
2157
1422
|
async function getCodexStatus(opts) {
|
|
2158
1423
|
const projectRoot = opts?.projectRoot ?? process.cwd();
|
|
@@ -2192,7 +1457,7 @@ function isHooksBlockInstalled() {
|
|
|
2192
1457
|
|
|
2193
1458
|
// src/monitor/plugins/codex/transcript.ts
|
|
2194
1459
|
import * as fs8 from "fs";
|
|
2195
|
-
import * as
|
|
1460
|
+
import * as path8 from "path";
|
|
2196
1461
|
function emptyRollout() {
|
|
2197
1462
|
return {
|
|
2198
1463
|
prompt: "",
|
|
@@ -2232,7 +1497,7 @@ function findRolloutPathForSession(sessionId, sessionsDir = getCodexSessionsDir(
|
|
|
2232
1497
|
}
|
|
2233
1498
|
for (const entry of entries) {
|
|
2234
1499
|
if (filesScanned++ > merged.maxFiles) break;
|
|
2235
|
-
const full =
|
|
1500
|
+
const full = path8.join(dir, entry.name);
|
|
2236
1501
|
if (entry.isDirectory()) {
|
|
2237
1502
|
queue.push(full);
|
|
2238
1503
|
continue;
|
|
@@ -2526,7 +1791,7 @@ var codexPlugin = {
|
|
|
2526
1791
|
};
|
|
2527
1792
|
function detectCodexBinaryOnPath() {
|
|
2528
1793
|
try {
|
|
2529
|
-
const probe =
|
|
1794
|
+
const probe = spawnSync2(
|
|
2530
1795
|
process.platform === "win32" ? "where" : "which",
|
|
2531
1796
|
["codex"],
|
|
2532
1797
|
{
|
|
@@ -2550,11 +1815,11 @@ import * as os7 from "os";
|
|
|
2550
1815
|
// src/monitor/plugins/cursor/install.ts
|
|
2551
1816
|
import * as fs11 from "fs";
|
|
2552
1817
|
import * as os4 from "os";
|
|
2553
|
-
import * as
|
|
1818
|
+
import * as path11 from "path";
|
|
2554
1819
|
|
|
2555
1820
|
// src/monitor/plugins/cursor/config.ts
|
|
2556
1821
|
import * as fs10 from "fs";
|
|
2557
|
-
import * as
|
|
1822
|
+
import * as path9 from "path";
|
|
2558
1823
|
function getCursorConfigPath(projectRoot) {
|
|
2559
1824
|
return getMonitorConfigPath(projectRoot, "cursor");
|
|
2560
1825
|
}
|
|
@@ -2570,7 +1835,7 @@ function loadCursorConfig(projectRoot) {
|
|
|
2570
1835
|
}
|
|
2571
1836
|
function writeCursorConfig(projectRoot, config) {
|
|
2572
1837
|
const filePath = getCursorConfigPath(projectRoot);
|
|
2573
|
-
const dir =
|
|
1838
|
+
const dir = path9.dirname(filePath);
|
|
2574
1839
|
if (!fs10.existsSync(dir)) {
|
|
2575
1840
|
fs10.mkdirSync(dir, { recursive: true });
|
|
2576
1841
|
}
|
|
@@ -2593,14 +1858,14 @@ function deleteCursorConfig(projectRoot) {
|
|
|
2593
1858
|
|
|
2594
1859
|
// src/monitor/plugins/cursor/paths.ts
|
|
2595
1860
|
import * as os3 from "os";
|
|
2596
|
-
import * as
|
|
1861
|
+
import * as path10 from "path";
|
|
2597
1862
|
var CURSOR_DIR_NAME = ".cursor";
|
|
2598
1863
|
var CURSOR_HOOKS_FILE = "hooks.json";
|
|
2599
1864
|
function getCursorUserDir(homeDir = os3.homedir()) {
|
|
2600
|
-
return
|
|
1865
|
+
return path10.join(homeDir, CURSOR_DIR_NAME);
|
|
2601
1866
|
}
|
|
2602
1867
|
function getCursorHooksPath(homeDir = os3.homedir()) {
|
|
2603
|
-
return
|
|
1868
|
+
return path10.join(getCursorUserDir(homeDir), CURSOR_HOOKS_FILE);
|
|
2604
1869
|
}
|
|
2605
1870
|
|
|
2606
1871
|
// src/monitor/plugins/cursor/hooks-config.ts
|
|
@@ -2698,7 +1963,7 @@ function readJsonFileTolerant(filePath) {
|
|
|
2698
1963
|
}
|
|
2699
1964
|
}
|
|
2700
1965
|
function writeJsonFileWithDir(filePath, data) {
|
|
2701
|
-
const dir =
|
|
1966
|
+
const dir = path11.dirname(filePath);
|
|
2702
1967
|
if (!fs11.existsSync(dir)) {
|
|
2703
1968
|
fs11.mkdirSync(dir, { recursive: true });
|
|
2704
1969
|
}
|
|
@@ -2720,26 +1985,19 @@ async function installCursor(opts) {
|
|
|
2720
1985
|
`Activity from this workspace (${projectRoot}) will be associated with`
|
|
2721
1986
|
);
|
|
2722
1987
|
console.log("the agent you select below.\n");
|
|
2723
|
-
const
|
|
2724
|
-
|
|
1988
|
+
const agent = await provisionSelfMonitorAgent({
|
|
1989
|
+
projectRoot,
|
|
1990
|
+
source: CURSOR_AGENT_SOURCE,
|
|
1991
|
+
displayName: "Cursor",
|
|
1992
|
+
category: CURSOR_AGENT_CATEGORY
|
|
1993
|
+
});
|
|
2725
1994
|
const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
|
|
2726
|
-
|
|
1995
|
+
const apiKey = agent.apiKey?.key;
|
|
2727
1996
|
if (!apiKey) {
|
|
2728
|
-
console.
|
|
2729
|
-
"
|
|
1997
|
+
console.error(
|
|
1998
|
+
"Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
|
|
2730
1999
|
);
|
|
2731
|
-
|
|
2732
|
-
if (!apiKey) {
|
|
2733
|
-
console.error("API key is required for monitoring.");
|
|
2734
|
-
process.exit(1);
|
|
2735
|
-
}
|
|
2736
|
-
await validatePastedApiKey({
|
|
2737
|
-
monitoringEndpoint,
|
|
2738
|
-
apiKey,
|
|
2739
|
-
expectedAgentId: agent.id,
|
|
2740
|
-
expectedAgentName: agent.name,
|
|
2741
|
-
expectedApiKeyId: agent.apiKey?.id ?? null
|
|
2742
|
-
});
|
|
2000
|
+
process.exit(1);
|
|
2743
2001
|
}
|
|
2744
2002
|
const cursorDir = getCursorUserDir(homeDir);
|
|
2745
2003
|
if (!fs11.existsSync(cursorDir)) {
|
|
@@ -2759,8 +2017,8 @@ async function installCursor(opts) {
|
|
|
2759
2017
|
};
|
|
2760
2018
|
writeCursorConfig(projectRoot, monitorConfig);
|
|
2761
2019
|
const configPath = getCursorConfigPath(projectRoot);
|
|
2762
|
-
const configRel =
|
|
2763
|
-
const hooksDisplay = `~/${
|
|
2020
|
+
const configRel = path11.relative(projectRoot, configPath);
|
|
2021
|
+
const hooksDisplay = `~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`;
|
|
2764
2022
|
console.log("");
|
|
2765
2023
|
console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
|
|
2766
2024
|
if (agent.apiKey?.key) {
|
|
@@ -2787,57 +2045,6 @@ async function installCursor(opts) {
|
|
|
2787
2045
|
monitoringEndpoint
|
|
2788
2046
|
};
|
|
2789
2047
|
}
|
|
2790
|
-
async function chooseOrCreateAgent(defaultName) {
|
|
2791
|
-
const choice = await promptUser(
|
|
2792
|
-
"Create a new agent or use an existing one? (new/existing) [new]: "
|
|
2793
|
-
);
|
|
2794
|
-
if (choice.toLowerCase() === "existing" || choice.toLowerCase() === "e") {
|
|
2795
|
-
const agents = await listAgents();
|
|
2796
|
-
if (agents.length === 0) {
|
|
2797
|
-
console.log("No agents found. Creating a new one instead.\n");
|
|
2798
|
-
return createNewAgent3(defaultName);
|
|
2799
|
-
}
|
|
2800
|
-
console.log("\nAvailable agents:");
|
|
2801
|
-
for (let i = 0; i < agents.length; i++) {
|
|
2802
|
-
console.log(
|
|
2803
|
-
` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`
|
|
2804
|
-
);
|
|
2805
|
-
}
|
|
2806
|
-
const selection = await promptUser(`
|
|
2807
|
-
Select agent (1-${agents.length}): `);
|
|
2808
|
-
const idx = parseInt(selection, 10) - 1;
|
|
2809
|
-
if (isNaN(idx) || idx < 0 || idx >= agents.length) {
|
|
2810
|
-
console.error("Invalid selection.");
|
|
2811
|
-
process.exit(1);
|
|
2812
|
-
}
|
|
2813
|
-
const fetched = await getAgent(agents[idx].id);
|
|
2814
|
-
if (!fetched.apiKey?.key && !fetched.apiKey?.isActive) {
|
|
2815
|
-
console.log(
|
|
2816
|
-
"\nThis agent has no active API key. Please create a new agent instead,"
|
|
2817
|
-
);
|
|
2818
|
-
console.log("or generate an API key via the Olakai dashboard.\n");
|
|
2819
|
-
const createNew = await promptUser("Create a new agent? (y/n) [y]: ");
|
|
2820
|
-
if (createNew.toLowerCase() !== "n") {
|
|
2821
|
-
return createNewAgent3(defaultName);
|
|
2822
|
-
}
|
|
2823
|
-
process.exit(1);
|
|
2824
|
-
}
|
|
2825
|
-
return fetched;
|
|
2826
|
-
}
|
|
2827
|
-
return createNewAgent3(defaultName);
|
|
2828
|
-
}
|
|
2829
|
-
async function createNewAgent3(defaultName) {
|
|
2830
|
-
const nameInput = await promptUser(`Agent name [${defaultName}]: `);
|
|
2831
|
-
const agentName = nameInput || defaultName;
|
|
2832
|
-
return createAgent({
|
|
2833
|
-
name: agentName,
|
|
2834
|
-
description: `Cursor local agent for ${agentName}`,
|
|
2835
|
-
role: "WORKER",
|
|
2836
|
-
createApiKey: true,
|
|
2837
|
-
category: CURSOR_AGENT_CATEGORY,
|
|
2838
|
-
source: CURSOR_AGENT_SOURCE
|
|
2839
|
-
});
|
|
2840
|
-
}
|
|
2841
2048
|
async function uninstallCursor(opts) {
|
|
2842
2049
|
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
2843
2050
|
const homeDir = opts.homeDir ?? os4.homedir();
|
|
@@ -2862,20 +2069,20 @@ async function uninstallCursor(opts) {
|
|
|
2862
2069
|
writeJsonFileWithDir(hooksPath, cleaned);
|
|
2863
2070
|
}
|
|
2864
2071
|
console.log(
|
|
2865
|
-
`\u2713 Olakai hooks removed from ~/${
|
|
2072
|
+
`\u2713 Olakai hooks removed from ~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`
|
|
2866
2073
|
);
|
|
2867
2074
|
} else {
|
|
2868
2075
|
console.log("No Cursor hooks file found.");
|
|
2869
2076
|
}
|
|
2870
2077
|
if (!opts.keepConfig) {
|
|
2871
2078
|
const configPath = getCursorConfigPath(projectRoot);
|
|
2872
|
-
const configRel =
|
|
2079
|
+
const configRel = path11.relative(projectRoot, configPath);
|
|
2873
2080
|
if (deleteCursorConfig(projectRoot)) {
|
|
2874
2081
|
console.log(`\u2713 Monitor config removed (${configRel})`);
|
|
2875
2082
|
}
|
|
2876
2083
|
} else {
|
|
2877
2084
|
const configPath = getCursorConfigPath(projectRoot);
|
|
2878
|
-
const configRel =
|
|
2085
|
+
const configRel = path11.relative(projectRoot, configPath);
|
|
2879
2086
|
console.log(`Monitor config retained at ${configRel}`);
|
|
2880
2087
|
}
|
|
2881
2088
|
console.log("");
|
|
@@ -2888,7 +2095,7 @@ async function uninstallCursor(opts) {
|
|
|
2888
2095
|
// src/monitor/plugins/cursor/status.ts
|
|
2889
2096
|
import * as fs12 from "fs";
|
|
2890
2097
|
import * as os5 from "os";
|
|
2891
|
-
import * as
|
|
2098
|
+
import * as path12 from "path";
|
|
2892
2099
|
async function getCursorStatus(opts) {
|
|
2893
2100
|
const projectRoot = opts?.projectRoot ?? process.cwd();
|
|
2894
2101
|
const homeDir = opts?.homeDir ?? os5.homedir();
|
|
@@ -3080,10 +2287,10 @@ function extractCursorMeta(payload) {
|
|
|
3080
2287
|
// src/monitor/plugins/cursor/pairing-state.ts
|
|
3081
2288
|
import * as fs13 from "fs";
|
|
3082
2289
|
import * as os6 from "os";
|
|
3083
|
-
import * as
|
|
2290
|
+
import * as path13 from "path";
|
|
3084
2291
|
var STATE_DIR_SEGMENTS2 = [".olakai", "cursor-pairings"];
|
|
3085
2292
|
function getPairingsDir(homeDir) {
|
|
3086
|
-
return
|
|
2293
|
+
return path13.join(homeDir, ...STATE_DIR_SEGMENTS2);
|
|
3087
2294
|
}
|
|
3088
2295
|
function sanitizeKeyFragment(value) {
|
|
3089
2296
|
return value.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -3094,7 +2301,7 @@ function getPairingKey(conversationId, generationId) {
|
|
|
3094
2301
|
return `${conv}__${sanitizeKeyFragment(generationId)}`;
|
|
3095
2302
|
}
|
|
3096
2303
|
function getPairingFile(conversationId, generationId, homeDir) {
|
|
3097
|
-
return
|
|
2304
|
+
return path13.join(
|
|
3098
2305
|
getPairingsDir(homeDir),
|
|
3099
2306
|
`${getPairingKey(conversationId, generationId)}.json`
|
|
3100
2307
|
);
|
|
@@ -3160,7 +2367,7 @@ function listPendingPrompts(homeDir = os6.homedir()) {
|
|
|
3160
2367
|
const result = [];
|
|
3161
2368
|
for (const name of files) {
|
|
3162
2369
|
if (!name.endsWith(".json")) continue;
|
|
3163
|
-
const filePath =
|
|
2370
|
+
const filePath = path13.join(dir, name);
|
|
3164
2371
|
try {
|
|
3165
2372
|
const raw = fs13.readFileSync(filePath, "utf-8");
|
|
3166
2373
|
const parsed = JSON.parse(raw);
|
|
@@ -3476,7 +2683,7 @@ function describeDetection(plugin, projectRoot) {
|
|
|
3476
2683
|
} catch {
|
|
3477
2684
|
}
|
|
3478
2685
|
try {
|
|
3479
|
-
const settings =
|
|
2686
|
+
const settings = path14.join(projectRoot, ".claude", "settings.json");
|
|
3480
2687
|
if (fs15.existsSync(settings)) {
|
|
3481
2688
|
return "found .claude/settings.json";
|
|
3482
2689
|
}
|
|
@@ -3564,26 +2771,34 @@ async function initCommand(options) {
|
|
|
3564
2771
|
try {
|
|
3565
2772
|
const resolved = resolveProfileName();
|
|
3566
2773
|
const profileName = resolved?.name ?? "default";
|
|
3567
|
-
|
|
3568
|
-
setHostOverride(host);
|
|
2774
|
+
let optedReAuth = false;
|
|
3569
2775
|
if (isTokenValid()) {
|
|
3570
2776
|
const file = readProfilesFile();
|
|
3571
2777
|
const existing = file.profiles[profileName];
|
|
3572
2778
|
const who = existing?.email ?? "unknown";
|
|
2779
|
+
const where = existing?.host ?? "(unknown host)";
|
|
3573
2780
|
if (options.nonInteractive) {
|
|
3574
2781
|
console.log(
|
|
3575
|
-
`Already authenticated as ${who} on ${
|
|
2782
|
+
`Already authenticated as ${who} on ${where} (profile: ${profileName}).`
|
|
3576
2783
|
);
|
|
3577
2784
|
return;
|
|
3578
2785
|
}
|
|
3579
2786
|
const answer = await promptUser(
|
|
3580
|
-
`Already authenticated as ${who} on ${
|
|
2787
|
+
`Already authenticated as ${who} on ${where} (profile: ${profileName}). Re-authenticate? [y/N]: `
|
|
3581
2788
|
);
|
|
3582
2789
|
if (answer.trim().toLowerCase() !== "y") {
|
|
3583
2790
|
console.log("Keeping existing credentials. Run 'olakai logout' to sign out.");
|
|
3584
2791
|
return;
|
|
3585
2792
|
}
|
|
2793
|
+
optedReAuth = true;
|
|
3586
2794
|
}
|
|
2795
|
+
const host = await resolveHost(
|
|
2796
|
+
profileName,
|
|
2797
|
+
options,
|
|
2798
|
+
interactive,
|
|
2799
|
+
optedReAuth
|
|
2800
|
+
);
|
|
2801
|
+
setHostOverride(host);
|
|
3587
2802
|
const email = await resolveEmail(options, interactive);
|
|
3588
2803
|
console.log(`
|
|
3589
2804
|
Contacting ${host} for ${email}...`);
|
|
@@ -3684,7 +2899,7 @@ function isSafeOpenUrl(raw) {
|
|
|
3684
2899
|
return false;
|
|
3685
2900
|
}
|
|
3686
2901
|
}
|
|
3687
|
-
async function resolveHost(profileName, options, interactive) {
|
|
2902
|
+
async function resolveHost(profileName, options, interactive, forceHostPrompt = false) {
|
|
3688
2903
|
const fromFlag = options.host?.trim() || process.env.OLAKAI_HOST?.trim();
|
|
3689
2904
|
if (fromFlag) {
|
|
3690
2905
|
return getBaseUrl();
|
|
@@ -3692,7 +2907,8 @@ async function resolveHost(profileName, options, interactive) {
|
|
|
3692
2907
|
const file = readProfilesFile();
|
|
3693
2908
|
const existing = file.profiles[profileName];
|
|
3694
2909
|
if (existing?.host) {
|
|
3695
|
-
|
|
2910
|
+
const shouldOfferSwitch = interactive && (forceHostPrompt || !isTokenValid());
|
|
2911
|
+
if (!shouldOfferSwitch) {
|
|
3696
2912
|
return existing.host;
|
|
3697
2913
|
}
|
|
3698
2914
|
const keep = await promptUser(
|
|
@@ -3790,7 +3006,11 @@ We sent a 6-digit code to ${ctx.emailObfuscated} (expires in ${expiryMin} min).`
|
|
|
3790
3006
|
console.log("Codes are 6 digits. Try again.");
|
|
3791
3007
|
continue;
|
|
3792
3008
|
}
|
|
3793
|
-
|
|
3009
|
+
let result = await postHandshakeVerify({ email, code: trimmed });
|
|
3010
|
+
if (result.kind === "error" && result.code === "network_error") {
|
|
3011
|
+
console.log("(Network error verifying code \u2014 retrying once...)");
|
|
3012
|
+
result = await postHandshakeVerify({ email, code: trimmed });
|
|
3013
|
+
}
|
|
3794
3014
|
if (result.kind === "ok") {
|
|
3795
3015
|
const consentToken = result.data.consentToken;
|
|
3796
3016
|
await runExchange(consentToken, profileName, email);
|
|
@@ -3829,7 +3049,9 @@ We sent a 6-digit code to ${ctx.emailObfuscated} (expires in ${expiryMin} min).`
|
|
|
3829
3049
|
);
|
|
3830
3050
|
throw new InitAbortedError("", 1);
|
|
3831
3051
|
case "network_error":
|
|
3832
|
-
console.error(
|
|
3052
|
+
console.error(
|
|
3053
|
+
`Network error${result.causeCode ? ` (${result.causeCode})` : ""}: ${result.message}`
|
|
3054
|
+
);
|
|
3833
3055
|
throw new InitAbortedError("", 1);
|
|
3834
3056
|
default:
|
|
3835
3057
|
console.error(
|
|
@@ -3840,7 +3062,11 @@ We sent a 6-digit code to ${ctx.emailObfuscated} (expires in ${expiryMin} min).`
|
|
|
3840
3062
|
}
|
|
3841
3063
|
}
|
|
3842
3064
|
async function runExchange(consentToken, profileName, email) {
|
|
3843
|
-
|
|
3065
|
+
let result = await postHandshakeExchange({ consentToken });
|
|
3066
|
+
if (result.kind === "error" && result.code === "network_error") {
|
|
3067
|
+
console.log("(Network error exchanging consent \u2014 retrying once...)");
|
|
3068
|
+
result = await postHandshakeExchange({ consentToken });
|
|
3069
|
+
}
|
|
3844
3070
|
if (result.kind === "error") {
|
|
3845
3071
|
switch (result.code) {
|
|
3846
3072
|
case "invalid_consent":
|
|
@@ -3863,6 +3089,11 @@ async function runExchange(consentToken, profileName, email) {
|
|
|
3863
3089
|
"Olakai is temporarily unavailable. Try again in a minute."
|
|
3864
3090
|
);
|
|
3865
3091
|
break;
|
|
3092
|
+
case "network_error":
|
|
3093
|
+
console.error(
|
|
3094
|
+
`Network error exchanging consent${result.causeCode ? ` (${result.causeCode})` : ""}: ${result.message}`
|
|
3095
|
+
);
|
|
3096
|
+
break;
|
|
3866
3097
|
default:
|
|
3867
3098
|
console.error(`Exchange failed (${result.code}): ${result.message}`);
|
|
3868
3099
|
}
|
|
@@ -3897,7 +3128,9 @@ function handleHandshakeError(err) {
|
|
|
3897
3128
|
console.error("Olakai is temporarily unavailable. Try again in a minute.");
|
|
3898
3129
|
break;
|
|
3899
3130
|
case "network_error":
|
|
3900
|
-
console.error(
|
|
3131
|
+
console.error(
|
|
3132
|
+
`Network error contacting Olakai${err.causeCode ? ` (${err.causeCode})` : ""}: ${err.message}`
|
|
3133
|
+
);
|
|
3901
3134
|
break;
|
|
3902
3135
|
default:
|
|
3903
3136
|
console.error(`Handshake failed (${err.code}): ${err.message}`);
|
|
@@ -5303,7 +4536,7 @@ async function statusCommand(options) {
|
|
|
5303
4536
|
return;
|
|
5304
4537
|
}
|
|
5305
4538
|
if (plugin.id === "claude-code") {
|
|
5306
|
-
const { printClaudeCodeStatus } = await import("./status-
|
|
4539
|
+
const { printClaudeCodeStatus } = await import("./status-USHUUHK6.js");
|
|
5307
4540
|
await printClaudeCodeStatus({ projectRoot: process.cwd() });
|
|
5308
4541
|
return;
|
|
5309
4542
|
}
|
|
@@ -5319,6 +4552,24 @@ async function statusCommand(options) {
|
|
|
5319
4552
|
async function disableCommand(options) {
|
|
5320
4553
|
const toolId = await resolveToolFromOptions(options.tool, "disable");
|
|
5321
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
|
+
}
|
|
5322
4573
|
await runPluginAction(
|
|
5323
4574
|
plugin,
|
|
5324
4575
|
() => plugin.uninstall({
|
|
@@ -5326,6 +4577,22 @@ async function disableCommand(options) {
|
|
|
5326
4577
|
keepConfig: options.keepConfig
|
|
5327
4578
|
})
|
|
5328
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
|
+
}
|
|
5329
4596
|
}
|
|
5330
4597
|
async function runPluginAction(plugin, fn) {
|
|
5331
4598
|
try {
|
|
@@ -5450,6 +4717,9 @@ function registerMonitorCommand(program2) {
|
|
|
5450
4717
|
).option(
|
|
5451
4718
|
"--keep-config",
|
|
5452
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."
|
|
5453
4723
|
).action(disableCommand);
|
|
5454
4724
|
for (const plugin of listPlugins()) {
|
|
5455
4725
|
plugin.registerCommands?.(monitor);
|