olakai-cli 0.6.3 → 0.6.6

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/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
- writeClaudeCodeConfig,
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 path13 from "path";
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,96 @@ 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/validate-pasted-key.ts
1261
- async function validatePastedApiKey(opts) {
1262
- const validation = await validateMonitoringApiKey(
1263
- opts.monitoringEndpoint,
1264
- opts.apiKey
1265
- );
1266
- if (validation === null) {
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
- "\u26A0 Could not verify the pasted API key (network or endpoint unreachable). Continuing."
568
+ `
569
+ Found your existing ${opts.displayName} agent "${found.name}".`
1269
570
  );
1270
- return;
1271
- }
1272
- if (opts.expectedApiKeyId === null) {
1273
- return;
1274
- }
1275
- if (validation.apiKeyId === opts.expectedApiKeyId) {
1276
- return;
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
- console.log("");
1279
- console.log(
1280
- `\u26A0 The pasted API key does not belong to "${opts.expectedAgentName}" (${opts.expectedAgentId}).`
1281
- );
1282
- console.log(
1283
- ` It resolves to a different agent on this account, so monitoring events would be`
1284
- );
1285
- console.log(
1286
- ` attributed to that other agent \u2014 not "${opts.expectedAgentName}".`
1287
- );
1288
- console.log("");
1289
- const proceed = await promptUser("Use the pasted key anyway? (y/n) [n]: ");
1290
- if (proceed.trim().toLowerCase() !== "y") {
1291
- console.log("Aborted. Re-run init with the correct key for this agent.");
1292
- process.exit(1);
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
+ source: opts.source
610
+ });
611
+ } catch (err) {
612
+ const message = err instanceof Error ? err.message : String(err);
613
+ if (message.toLowerCase().includes("already exists") || message.toLowerCase().includes("conflict")) {
614
+ console.log(
615
+ `
616
+ An agent named "${agentName}" already exists on this account (created by someone else or an admin).`
617
+ );
618
+ const retry = await promptUser("Try a different name: ");
619
+ if (!retry.trim()) {
620
+ console.error("No name provided. Aborting.");
621
+ process.exit(1);
622
+ }
623
+ return createAgent({
624
+ name: retry.trim(),
625
+ description: `${opts.displayName} local agent for ${retry.trim()}`,
626
+ role: "WORKER",
627
+ createApiKey: true,
628
+ source: opts.source
629
+ });
630
+ }
631
+ throw err;
1293
632
  }
1294
633
  }
1295
634
 
1296
635
  // src/monitor/plugins/claude-code/install.ts
1297
- import path3 from "path";
636
+ import path4 from "path";
1298
637
  var CLAUDE_CODE_SOURCE = "claude-code";
1299
638
  var CLAUDE_CODE_AGENT_SOURCE = "CLAUDE_CODE";
1300
639
  var CLAUDE_CODE_AGENT_CATEGORY = "CODING";
@@ -1306,65 +645,19 @@ async function installClaudeCode(opts) {
1306
645
  process.exit(1);
1307
646
  }
1308
647
  console.log("Setting up Claude Code monitoring for this workspace...\n");
1309
- const dirName = path3.basename(projectRoot);
1310
- let agent;
1311
- const choice = await promptUser(
1312
- "Create a new agent or use an existing one? (new/existing) [new]: "
1313
- );
1314
- if (choice.toLowerCase() === "existing" || choice.toLowerCase() === "e") {
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
- }
648
+ const agent = await provisionSelfMonitorAgent({
649
+ projectRoot,
650
+ source: CLAUDE_CODE_AGENT_SOURCE,
651
+ displayName: "Claude Code",
652
+ category: CLAUDE_CODE_AGENT_CATEGORY
653
+ });
1350
654
  const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
1351
- let apiKey = agent.apiKey?.key;
655
+ const apiKey = agent.apiKey?.key;
1352
656
  if (!apiKey) {
1353
- console.log(
1354
- "\nThe API key for this agent is not available (it is only shown once at creation)."
657
+ console.error(
658
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
1355
659
  );
1356
- apiKey = await promptUser("Paste the API key for this agent: ");
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
- });
660
+ process.exit(1);
1368
661
  }
1369
662
  const claudeDir = getClaudeDir(projectRoot);
1370
663
  if (!fs2.existsSync(claudeDir)) {
@@ -1388,7 +681,7 @@ Select agent (1-${agents.length}): `);
1388
681
  };
1389
682
  writeClaudeCodeConfig(projectRoot, monitorConfig);
1390
683
  const configPath = getClaudeCodeConfigPath(projectRoot);
1391
- const configRel = path3.relative(projectRoot, configPath);
684
+ const configRel = path4.relative(projectRoot, configPath);
1392
685
  console.log("");
1393
686
  console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
1394
687
  if (agent.apiKey?.key) {
@@ -1417,18 +710,6 @@ Select agent (1-${agents.length}): `);
1417
710
  monitoringEndpoint
1418
711
  };
1419
712
  }
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
713
  async function uninstallClaudeCode(opts) {
1433
714
  const projectRoot = opts.projectRoot ?? process.cwd();
1434
715
  const settingsPath = getSettingsPath(projectRoot);
@@ -1457,13 +738,13 @@ async function uninstallClaudeCode(opts) {
1457
738
  }
1458
739
  if (!opts.keepConfig) {
1459
740
  const configPath = getClaudeCodeConfigPath(projectRoot);
1460
- const configRel = path3.relative(projectRoot, configPath);
741
+ const configRel = path4.relative(projectRoot, configPath);
1461
742
  if (deleteClaudeCodeConfig(projectRoot)) {
1462
743
  console.log(`\u2713 Monitor config removed (${configRel})`);
1463
744
  }
1464
745
  } else {
1465
746
  const configPath = getClaudeCodeConfigPath(projectRoot);
1466
- const configRel = path3.relative(projectRoot, configPath);
747
+ const configRel = path4.relative(projectRoot, configPath);
1467
748
  console.log(`Monitor config retained at ${configRel}`);
1468
749
  }
1469
750
  console.log("");
@@ -1867,7 +1148,7 @@ import { spawnSync as spawnSync2 } from "child_process";
1867
1148
 
1868
1149
  // src/monitor/plugins/codex/install.ts
1869
1150
  import * as fs6 from "fs";
1870
- import * as path5 from "path";
1151
+ import * as path6 from "path";
1871
1152
  import * as TOML from "@iarna/toml";
1872
1153
 
1873
1154
  // src/monitor/plugins/codex/hooks.ts
@@ -1943,7 +1224,7 @@ function hasOlakaiHooksInstalled(parsed) {
1943
1224
 
1944
1225
  // src/monitor/plugins/codex/config.ts
1945
1226
  import * as fs5 from "fs";
1946
- import * as path4 from "path";
1227
+ import * as path5 from "path";
1947
1228
  function getCodexConfigPath2(projectRoot) {
1948
1229
  return getMonitorConfigPath(projectRoot, "codex");
1949
1230
  }
@@ -1959,7 +1240,7 @@ function loadCodexConfig(projectRoot) {
1959
1240
  }
1960
1241
  function writeCodexConfig(projectRoot, config) {
1961
1242
  const filePath = getCodexConfigPath2(projectRoot);
1962
- const dir = path4.dirname(filePath);
1243
+ const dir = path5.dirname(filePath);
1963
1244
  if (!fs5.existsSync(dir)) {
1964
1245
  fs5.mkdirSync(dir, { recursive: true });
1965
1246
  }
@@ -1992,65 +1273,19 @@ async function installCodex(opts) {
1992
1273
  process.exit(1);
1993
1274
  }
1994
1275
  console.log("Setting up Codex CLI monitoring for this workspace...\n");
1995
- const dirName = path5.basename(projectRoot);
1996
- let agent;
1997
- const choice = await promptUser(
1998
- "Create a new agent or use an existing one? (new/existing) [new]: "
1999
- );
2000
- if (choice.toLowerCase() === "existing" || choice.toLowerCase() === "e") {
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
- }
1276
+ const agent = await provisionSelfMonitorAgent({
1277
+ projectRoot,
1278
+ source: CODEX_AGENT_SOURCE,
1279
+ displayName: "Codex CLI",
1280
+ category: CODEX_AGENT_CATEGORY
1281
+ });
2036
1282
  const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
2037
- let apiKey = agent.apiKey?.key;
1283
+ const apiKey = agent.apiKey?.key;
2038
1284
  if (!apiKey) {
2039
- console.log(
2040
- "\nThe API key for this agent is not available (it is only shown once at creation)."
1285
+ console.error(
1286
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
2041
1287
  );
2042
- apiKey = await promptUser("Paste the API key for this agent: ");
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
- });
1288
+ process.exit(1);
2054
1289
  }
2055
1290
  const { configExisted: codexConfigExisted } = installCodexHooksConfig();
2056
1291
  const monitorConfig = {
@@ -2063,7 +1298,7 @@ Select agent (1-${agents.length}): `);
2063
1298
  };
2064
1299
  writeCodexConfig(projectRoot, monitorConfig);
2065
1300
  const configPath = getCodexConfigPath2(projectRoot);
2066
- const configRel = path5.relative(projectRoot, configPath);
1301
+ const configRel = path6.relative(projectRoot, configPath);
2067
1302
  console.log("");
2068
1303
  console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
2069
1304
  if (agent.apiKey?.key) {
@@ -2100,18 +1335,6 @@ Select agent (1-${agents.length}): `);
2100
1335
  monitoringEndpoint
2101
1336
  };
2102
1337
  }
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
1338
  function installCodexHooksConfig() {
2116
1339
  const homeDir = getCodexHomeDir();
2117
1340
  const configPath = getCodexConfigPath();
@@ -2139,13 +1362,13 @@ async function uninstallCodex(opts) {
2139
1362
  }
2140
1363
  if (!opts.keepConfig) {
2141
1364
  const configPath = getCodexConfigPath2(projectRoot);
2142
- const configRel = path5.relative(projectRoot, configPath);
1365
+ const configRel = path6.relative(projectRoot, configPath);
2143
1366
  if (deleteCodexConfig(projectRoot)) {
2144
1367
  console.log(`\u2713 Monitor config removed (${configRel})`);
2145
1368
  }
2146
1369
  } else {
2147
1370
  const configPath = getCodexConfigPath2(projectRoot);
2148
- const configRel = path5.relative(projectRoot, configPath);
1371
+ const configRel = path6.relative(projectRoot, configPath);
2149
1372
  console.log(`Monitor config retained at ${configRel}`);
2150
1373
  }
2151
1374
  console.log("");
@@ -2183,7 +1406,7 @@ function readCodexConfigToml(configPath) {
2183
1406
  }
2184
1407
  }
2185
1408
  function writeCodexConfigToml(configPath, data) {
2186
- const dir = path5.dirname(configPath);
1409
+ const dir = path6.dirname(configPath);
2187
1410
  if (!fs6.existsSync(dir)) {
2188
1411
  fs6.mkdirSync(dir, { recursive: true });
2189
1412
  }
@@ -2192,7 +1415,7 @@ function writeCodexConfigToml(configPath, data) {
2192
1415
  }
2193
1416
 
2194
1417
  // src/monitor/plugins/codex/status.ts
2195
- import path6 from "path";
1418
+ import path7 from "path";
2196
1419
  import * as fs7 from "fs";
2197
1420
  async function getCodexStatus(opts) {
2198
1421
  const projectRoot = opts?.projectRoot ?? process.cwd();
@@ -2232,7 +1455,7 @@ function isHooksBlockInstalled() {
2232
1455
 
2233
1456
  // src/monitor/plugins/codex/transcript.ts
2234
1457
  import * as fs8 from "fs";
2235
- import * as path7 from "path";
1458
+ import * as path8 from "path";
2236
1459
  function emptyRollout() {
2237
1460
  return {
2238
1461
  prompt: "",
@@ -2272,7 +1495,7 @@ function findRolloutPathForSession(sessionId, sessionsDir = getCodexSessionsDir(
2272
1495
  }
2273
1496
  for (const entry of entries) {
2274
1497
  if (filesScanned++ > merged.maxFiles) break;
2275
- const full = path7.join(dir, entry.name);
1498
+ const full = path8.join(dir, entry.name);
2276
1499
  if (entry.isDirectory()) {
2277
1500
  queue.push(full);
2278
1501
  continue;
@@ -2590,11 +1813,11 @@ import * as os7 from "os";
2590
1813
  // src/monitor/plugins/cursor/install.ts
2591
1814
  import * as fs11 from "fs";
2592
1815
  import * as os4 from "os";
2593
- import * as path10 from "path";
1816
+ import * as path11 from "path";
2594
1817
 
2595
1818
  // src/monitor/plugins/cursor/config.ts
2596
1819
  import * as fs10 from "fs";
2597
- import * as path8 from "path";
1820
+ import * as path9 from "path";
2598
1821
  function getCursorConfigPath(projectRoot) {
2599
1822
  return getMonitorConfigPath(projectRoot, "cursor");
2600
1823
  }
@@ -2610,7 +1833,7 @@ function loadCursorConfig(projectRoot) {
2610
1833
  }
2611
1834
  function writeCursorConfig(projectRoot, config) {
2612
1835
  const filePath = getCursorConfigPath(projectRoot);
2613
- const dir = path8.dirname(filePath);
1836
+ const dir = path9.dirname(filePath);
2614
1837
  if (!fs10.existsSync(dir)) {
2615
1838
  fs10.mkdirSync(dir, { recursive: true });
2616
1839
  }
@@ -2633,14 +1856,14 @@ function deleteCursorConfig(projectRoot) {
2633
1856
 
2634
1857
  // src/monitor/plugins/cursor/paths.ts
2635
1858
  import * as os3 from "os";
2636
- import * as path9 from "path";
1859
+ import * as path10 from "path";
2637
1860
  var CURSOR_DIR_NAME = ".cursor";
2638
1861
  var CURSOR_HOOKS_FILE = "hooks.json";
2639
1862
  function getCursorUserDir(homeDir = os3.homedir()) {
2640
- return path9.join(homeDir, CURSOR_DIR_NAME);
1863
+ return path10.join(homeDir, CURSOR_DIR_NAME);
2641
1864
  }
2642
1865
  function getCursorHooksPath(homeDir = os3.homedir()) {
2643
- return path9.join(getCursorUserDir(homeDir), CURSOR_HOOKS_FILE);
1866
+ return path10.join(getCursorUserDir(homeDir), CURSOR_HOOKS_FILE);
2644
1867
  }
2645
1868
 
2646
1869
  // src/monitor/plugins/cursor/hooks-config.ts
@@ -2738,7 +1961,7 @@ function readJsonFileTolerant(filePath) {
2738
1961
  }
2739
1962
  }
2740
1963
  function writeJsonFileWithDir(filePath, data) {
2741
- const dir = path10.dirname(filePath);
1964
+ const dir = path11.dirname(filePath);
2742
1965
  if (!fs11.existsSync(dir)) {
2743
1966
  fs11.mkdirSync(dir, { recursive: true });
2744
1967
  }
@@ -2760,26 +1983,19 @@ async function installCursor(opts) {
2760
1983
  `Activity from this workspace (${projectRoot}) will be associated with`
2761
1984
  );
2762
1985
  console.log("the agent you select below.\n");
2763
- const dirName = path10.basename(projectRoot);
2764
- const agent = await chooseOrCreateAgent(dirName);
1986
+ const agent = await provisionSelfMonitorAgent({
1987
+ projectRoot,
1988
+ source: CURSOR_AGENT_SOURCE,
1989
+ displayName: "Cursor",
1990
+ category: CURSOR_AGENT_CATEGORY
1991
+ });
2765
1992
  const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
2766
- let apiKey = agent.apiKey?.key;
1993
+ const apiKey = agent.apiKey?.key;
2767
1994
  if (!apiKey) {
2768
- console.log(
2769
- "\nThe API key for this agent is not available (it is only shown once at creation)."
1995
+ console.error(
1996
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
2770
1997
  );
2771
- apiKey = await promptUser("Paste the API key for this agent: ");
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
- });
1998
+ process.exit(1);
2783
1999
  }
2784
2000
  const cursorDir = getCursorUserDir(homeDir);
2785
2001
  if (!fs11.existsSync(cursorDir)) {
@@ -2799,8 +2015,8 @@ async function installCursor(opts) {
2799
2015
  };
2800
2016
  writeCursorConfig(projectRoot, monitorConfig);
2801
2017
  const configPath = getCursorConfigPath(projectRoot);
2802
- const configRel = path10.relative(projectRoot, configPath);
2803
- const hooksDisplay = `~/${path10.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`;
2018
+ const configRel = path11.relative(projectRoot, configPath);
2019
+ const hooksDisplay = `~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`;
2804
2020
  console.log("");
2805
2021
  console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
2806
2022
  if (agent.apiKey?.key) {
@@ -2827,57 +2043,6 @@ async function installCursor(opts) {
2827
2043
  monitoringEndpoint
2828
2044
  };
2829
2045
  }
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
2046
  async function uninstallCursor(opts) {
2882
2047
  const projectRoot = opts.projectRoot ?? process.cwd();
2883
2048
  const homeDir = opts.homeDir ?? os4.homedir();
@@ -2902,20 +2067,20 @@ async function uninstallCursor(opts) {
2902
2067
  writeJsonFileWithDir(hooksPath, cleaned);
2903
2068
  }
2904
2069
  console.log(
2905
- `\u2713 Olakai hooks removed from ~/${path10.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`
2070
+ `\u2713 Olakai hooks removed from ~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`
2906
2071
  );
2907
2072
  } else {
2908
2073
  console.log("No Cursor hooks file found.");
2909
2074
  }
2910
2075
  if (!opts.keepConfig) {
2911
2076
  const configPath = getCursorConfigPath(projectRoot);
2912
- const configRel = path10.relative(projectRoot, configPath);
2077
+ const configRel = path11.relative(projectRoot, configPath);
2913
2078
  if (deleteCursorConfig(projectRoot)) {
2914
2079
  console.log(`\u2713 Monitor config removed (${configRel})`);
2915
2080
  }
2916
2081
  } else {
2917
2082
  const configPath = getCursorConfigPath(projectRoot);
2918
- const configRel = path10.relative(projectRoot, configPath);
2083
+ const configRel = path11.relative(projectRoot, configPath);
2919
2084
  console.log(`Monitor config retained at ${configRel}`);
2920
2085
  }
2921
2086
  console.log("");
@@ -2928,7 +2093,7 @@ async function uninstallCursor(opts) {
2928
2093
  // src/monitor/plugins/cursor/status.ts
2929
2094
  import * as fs12 from "fs";
2930
2095
  import * as os5 from "os";
2931
- import * as path11 from "path";
2096
+ import * as path12 from "path";
2932
2097
  async function getCursorStatus(opts) {
2933
2098
  const projectRoot = opts?.projectRoot ?? process.cwd();
2934
2099
  const homeDir = opts?.homeDir ?? os5.homedir();
@@ -3120,10 +2285,10 @@ function extractCursorMeta(payload) {
3120
2285
  // src/monitor/plugins/cursor/pairing-state.ts
3121
2286
  import * as fs13 from "fs";
3122
2287
  import * as os6 from "os";
3123
- import * as path12 from "path";
2288
+ import * as path13 from "path";
3124
2289
  var STATE_DIR_SEGMENTS2 = [".olakai", "cursor-pairings"];
3125
2290
  function getPairingsDir(homeDir) {
3126
- return path12.join(homeDir, ...STATE_DIR_SEGMENTS2);
2291
+ return path13.join(homeDir, ...STATE_DIR_SEGMENTS2);
3127
2292
  }
3128
2293
  function sanitizeKeyFragment(value) {
3129
2294
  return value.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -3134,7 +2299,7 @@ function getPairingKey(conversationId, generationId) {
3134
2299
  return `${conv}__${sanitizeKeyFragment(generationId)}`;
3135
2300
  }
3136
2301
  function getPairingFile(conversationId, generationId, homeDir) {
3137
- return path12.join(
2302
+ return path13.join(
3138
2303
  getPairingsDir(homeDir),
3139
2304
  `${getPairingKey(conversationId, generationId)}.json`
3140
2305
  );
@@ -3200,7 +2365,7 @@ function listPendingPrompts(homeDir = os6.homedir()) {
3200
2365
  const result = [];
3201
2366
  for (const name of files) {
3202
2367
  if (!name.endsWith(".json")) continue;
3203
- const filePath = path12.join(dir, name);
2368
+ const filePath = path13.join(dir, name);
3204
2369
  try {
3205
2370
  const raw = fs13.readFileSync(filePath, "utf-8");
3206
2371
  const parsed = JSON.parse(raw);
@@ -3516,7 +2681,7 @@ function describeDetection(plugin, projectRoot) {
3516
2681
  } catch {
3517
2682
  }
3518
2683
  try {
3519
- const settings = path13.join(projectRoot, ".claude", "settings.json");
2684
+ const settings = path14.join(projectRoot, ".claude", "settings.json");
3520
2685
  if (fs15.existsSync(settings)) {
3521
2686
  return "found .claude/settings.json";
3522
2687
  }
@@ -3604,26 +2769,34 @@ async function initCommand(options) {
3604
2769
  try {
3605
2770
  const resolved = resolveProfileName();
3606
2771
  const profileName = resolved?.name ?? "default";
3607
- const host = await resolveHost(profileName, options, interactive);
3608
- setHostOverride(host);
2772
+ let optedReAuth = false;
3609
2773
  if (isTokenValid()) {
3610
2774
  const file = readProfilesFile();
3611
2775
  const existing = file.profiles[profileName];
3612
2776
  const who = existing?.email ?? "unknown";
2777
+ const where = existing?.host ?? "(unknown host)";
3613
2778
  if (options.nonInteractive) {
3614
2779
  console.log(
3615
- `Already authenticated as ${who} on ${host} (profile: ${profileName}).`
2780
+ `Already authenticated as ${who} on ${where} (profile: ${profileName}).`
3616
2781
  );
3617
2782
  return;
3618
2783
  }
3619
2784
  const answer = await promptUser(
3620
- `Already authenticated as ${who} on ${host} (profile: ${profileName}). Re-authenticate? [y/N]: `
2785
+ `Already authenticated as ${who} on ${where} (profile: ${profileName}). Re-authenticate? [y/N]: `
3621
2786
  );
3622
2787
  if (answer.trim().toLowerCase() !== "y") {
3623
2788
  console.log("Keeping existing credentials. Run 'olakai logout' to sign out.");
3624
2789
  return;
3625
2790
  }
2791
+ optedReAuth = true;
3626
2792
  }
2793
+ const host = await resolveHost(
2794
+ profileName,
2795
+ options,
2796
+ interactive,
2797
+ optedReAuth
2798
+ );
2799
+ setHostOverride(host);
3627
2800
  const email = await resolveEmail(options, interactive);
3628
2801
  console.log(`
3629
2802
  Contacting ${host} for ${email}...`);
@@ -3724,7 +2897,7 @@ function isSafeOpenUrl(raw) {
3724
2897
  return false;
3725
2898
  }
3726
2899
  }
3727
- async function resolveHost(profileName, options, interactive) {
2900
+ async function resolveHost(profileName, options, interactive, forceHostPrompt = false) {
3728
2901
  const fromFlag = options.host?.trim() || process.env.OLAKAI_HOST?.trim();
3729
2902
  if (fromFlag) {
3730
2903
  return getBaseUrl();
@@ -3732,7 +2905,8 @@ async function resolveHost(profileName, options, interactive) {
3732
2905
  const file = readProfilesFile();
3733
2906
  const existing = file.profiles[profileName];
3734
2907
  if (existing?.host) {
3735
- if (!interactive || isTokenValid()) {
2908
+ const shouldOfferSwitch = interactive && (forceHostPrompt || !isTokenValid());
2909
+ if (!shouldOfferSwitch) {
3736
2910
  return existing.host;
3737
2911
  }
3738
2912
  const keep = await promptUser(
@@ -5360,7 +4534,7 @@ async function statusCommand(options) {
5360
4534
  return;
5361
4535
  }
5362
4536
  if (plugin.id === "claude-code") {
5363
- const { printClaudeCodeStatus } = await import("./status-CAHO5FHI.js");
4537
+ const { printClaudeCodeStatus } = await import("./status-USHUUHK6.js");
5364
4538
  await printClaudeCodeStatus({ projectRoot: process.cwd() });
5365
4539
  return;
5366
4540
  }
@@ -5376,6 +4550,24 @@ async function statusCommand(options) {
5376
4550
  async function disableCommand(options) {
5377
4551
  const toolId = await resolveToolFromOptions(options.tool, "disable");
5378
4552
  const plugin = getPlugin(toolId);
4553
+ let agentIdToDelete;
4554
+ if (options.deleteAgent) {
4555
+ try {
4556
+ const report = await plugin.status({ projectRoot: process.cwd() });
4557
+ if (report.configured && report.agentId) {
4558
+ agentIdToDelete = report.agentId;
4559
+ } else {
4560
+ console.log(
4561
+ "No monitor config found in this workspace \u2014 nothing to delete remotely."
4562
+ );
4563
+ }
4564
+ } catch (err) {
4565
+ const msg = err instanceof Error ? err.message : String(err);
4566
+ console.warn(
4567
+ `Couldn't read local monitor config to identify the agent (${msg}). Proceeding with local-only uninstall.`
4568
+ );
4569
+ }
4570
+ }
5379
4571
  await runPluginAction(
5380
4572
  plugin,
5381
4573
  () => plugin.uninstall({
@@ -5383,6 +4575,22 @@ async function disableCommand(options) {
5383
4575
  keepConfig: options.keepConfig
5384
4576
  })
5385
4577
  );
4578
+ if (agentIdToDelete) {
4579
+ const { deleteAgent: deleteAgent2 } = await import("./api-63UDJX5M.js");
4580
+ try {
4581
+ await deleteAgent2(agentIdToDelete);
4582
+ console.log(`\u2713 Remote agent ${agentIdToDelete} deleted.`);
4583
+ } catch (err) {
4584
+ const msg = err instanceof Error ? err.message : String(err);
4585
+ console.error(
4586
+ `Local uninstall succeeded, but remote agent delete failed: ${msg}`
4587
+ );
4588
+ console.error(
4589
+ `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).`
4590
+ );
4591
+ process.exit(1);
4592
+ }
4593
+ }
5386
4594
  }
5387
4595
  async function runPluginAction(plugin, fn) {
5388
4596
  try {
@@ -5507,6 +4715,9 @@ function registerMonitorCommand(program2) {
5507
4715
  ).option(
5508
4716
  "--keep-config",
5509
4717
  "Keep the monitor config file (only remove hooks)"
4718
+ ).option(
4719
+ "--delete-agent",
4720
+ "Also delete the remote agent on the Olakai backend. Useful when retiring a workspace permanently. Self-monitor owner or ADMIN only."
5510
4721
  ).action(disableCommand);
5511
4722
  for (const plugin of listPlugins()) {
5512
4723
  plugin.registerCommands?.(monitor);