olakai-cli 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,98 @@ function shouldReportTurn(existing, currentUserTimestamp) {
1257
544
  // src/monitor/plugins/claude-code/install.ts
1258
545
  import * as fs2 from "fs";
1259
546
 
1260
- // src/monitor/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
+ category: opts.category,
610
+ source: opts.source
611
+ });
612
+ } catch (err) {
613
+ const message = err instanceof Error ? err.message : String(err);
614
+ if (message.toLowerCase().includes("already exists") || message.toLowerCase().includes("conflict")) {
615
+ console.log(
616
+ `
617
+ An agent named "${agentName}" already exists on this account (created by someone else or an admin).`
618
+ );
619
+ const retry = await promptUser("Try a different name: ");
620
+ if (!retry.trim()) {
621
+ console.error("No name provided. Aborting.");
622
+ process.exit(1);
623
+ }
624
+ return createAgent({
625
+ name: retry.trim(),
626
+ description: `${opts.displayName} local agent for ${retry.trim()}`,
627
+ role: "WORKER",
628
+ createApiKey: true,
629
+ category: opts.category,
630
+ source: opts.source
631
+ });
632
+ }
633
+ throw err;
1293
634
  }
1294
635
  }
1295
636
 
1296
637
  // src/monitor/plugins/claude-code/install.ts
1297
- import path3 from "path";
638
+ import path4 from "path";
1298
639
  var CLAUDE_CODE_SOURCE = "claude-code";
1299
640
  var CLAUDE_CODE_AGENT_SOURCE = "CLAUDE_CODE";
1300
641
  var CLAUDE_CODE_AGENT_CATEGORY = "CODING";
@@ -1306,65 +647,19 @@ async function installClaudeCode(opts) {
1306
647
  process.exit(1);
1307
648
  }
1308
649
  console.log("Setting up Claude Code monitoring for this workspace...\n");
1309
- const 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
- }
650
+ const agent = await provisionSelfMonitorAgent({
651
+ projectRoot,
652
+ source: CLAUDE_CODE_AGENT_SOURCE,
653
+ displayName: "Claude Code",
654
+ category: CLAUDE_CODE_AGENT_CATEGORY
655
+ });
1350
656
  const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
1351
- let apiKey = agent.apiKey?.key;
657
+ const apiKey = agent.apiKey?.key;
1352
658
  if (!apiKey) {
1353
- console.log(
1354
- "\nThe API key for this agent is not available (it is only shown once at creation)."
659
+ console.error(
660
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
1355
661
  );
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
- });
662
+ process.exit(1);
1368
663
  }
1369
664
  const claudeDir = getClaudeDir(projectRoot);
1370
665
  if (!fs2.existsSync(claudeDir)) {
@@ -1388,7 +683,7 @@ Select agent (1-${agents.length}): `);
1388
683
  };
1389
684
  writeClaudeCodeConfig(projectRoot, monitorConfig);
1390
685
  const configPath = getClaudeCodeConfigPath(projectRoot);
1391
- const configRel = path3.relative(projectRoot, configPath);
686
+ const configRel = path4.relative(projectRoot, configPath);
1392
687
  console.log("");
1393
688
  console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
1394
689
  if (agent.apiKey?.key) {
@@ -1417,18 +712,6 @@ Select agent (1-${agents.length}): `);
1417
712
  monitoringEndpoint
1418
713
  };
1419
714
  }
1420
- async function createNewAgent(defaultName) {
1421
- const nameInput = await promptUser(`Agent name [${defaultName}]: `);
1422
- const agentName = nameInput || defaultName;
1423
- return createAgent({
1424
- name: agentName,
1425
- description: `Claude Code local agent for ${agentName}`,
1426
- role: "WORKER",
1427
- createApiKey: true,
1428
- category: CLAUDE_CODE_AGENT_CATEGORY,
1429
- source: CLAUDE_CODE_AGENT_SOURCE
1430
- });
1431
- }
1432
715
  async function uninstallClaudeCode(opts) {
1433
716
  const projectRoot = opts.projectRoot ?? process.cwd();
1434
717
  const settingsPath = getSettingsPath(projectRoot);
@@ -1457,13 +740,13 @@ async function uninstallClaudeCode(opts) {
1457
740
  }
1458
741
  if (!opts.keepConfig) {
1459
742
  const configPath = getClaudeCodeConfigPath(projectRoot);
1460
- const configRel = path3.relative(projectRoot, configPath);
743
+ const configRel = path4.relative(projectRoot, configPath);
1461
744
  if (deleteClaudeCodeConfig(projectRoot)) {
1462
745
  console.log(`\u2713 Monitor config removed (${configRel})`);
1463
746
  }
1464
747
  } else {
1465
748
  const configPath = getClaudeCodeConfigPath(projectRoot);
1466
- const configRel = path3.relative(projectRoot, configPath);
749
+ const configRel = path4.relative(projectRoot, configPath);
1467
750
  console.log(`Monitor config retained at ${configRel}`);
1468
751
  }
1469
752
  console.log("");
@@ -1867,7 +1150,7 @@ import { spawnSync as spawnSync2 } from "child_process";
1867
1150
 
1868
1151
  // src/monitor/plugins/codex/install.ts
1869
1152
  import * as fs6 from "fs";
1870
- import * as path5 from "path";
1153
+ import * as path6 from "path";
1871
1154
  import * as TOML from "@iarna/toml";
1872
1155
 
1873
1156
  // src/monitor/plugins/codex/hooks.ts
@@ -1943,7 +1226,7 @@ function hasOlakaiHooksInstalled(parsed) {
1943
1226
 
1944
1227
  // src/monitor/plugins/codex/config.ts
1945
1228
  import * as fs5 from "fs";
1946
- import * as path4 from "path";
1229
+ import * as path5 from "path";
1947
1230
  function getCodexConfigPath2(projectRoot) {
1948
1231
  return getMonitorConfigPath(projectRoot, "codex");
1949
1232
  }
@@ -1959,7 +1242,7 @@ function loadCodexConfig(projectRoot) {
1959
1242
  }
1960
1243
  function writeCodexConfig(projectRoot, config) {
1961
1244
  const filePath = getCodexConfigPath2(projectRoot);
1962
- const dir = path4.dirname(filePath);
1245
+ const dir = path5.dirname(filePath);
1963
1246
  if (!fs5.existsSync(dir)) {
1964
1247
  fs5.mkdirSync(dir, { recursive: true });
1965
1248
  }
@@ -1992,65 +1275,19 @@ async function installCodex(opts) {
1992
1275
  process.exit(1);
1993
1276
  }
1994
1277
  console.log("Setting up Codex CLI monitoring for this workspace...\n");
1995
- const 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
- }
1278
+ const agent = await provisionSelfMonitorAgent({
1279
+ projectRoot,
1280
+ source: CODEX_AGENT_SOURCE,
1281
+ displayName: "Codex CLI",
1282
+ category: CODEX_AGENT_CATEGORY
1283
+ });
2036
1284
  const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
2037
- let apiKey = agent.apiKey?.key;
1285
+ const apiKey = agent.apiKey?.key;
2038
1286
  if (!apiKey) {
2039
- console.log(
2040
- "\nThe API key for this agent is not available (it is only shown once at creation)."
1287
+ console.error(
1288
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
2041
1289
  );
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
- });
1290
+ process.exit(1);
2054
1291
  }
2055
1292
  const { configExisted: codexConfigExisted } = installCodexHooksConfig();
2056
1293
  const monitorConfig = {
@@ -2063,7 +1300,7 @@ Select agent (1-${agents.length}): `);
2063
1300
  };
2064
1301
  writeCodexConfig(projectRoot, monitorConfig);
2065
1302
  const configPath = getCodexConfigPath2(projectRoot);
2066
- const configRel = path5.relative(projectRoot, configPath);
1303
+ const configRel = path6.relative(projectRoot, configPath);
2067
1304
  console.log("");
2068
1305
  console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
2069
1306
  if (agent.apiKey?.key) {
@@ -2100,18 +1337,6 @@ Select agent (1-${agents.length}): `);
2100
1337
  monitoringEndpoint
2101
1338
  };
2102
1339
  }
2103
- async function createNewAgent2(defaultName) {
2104
- const nameInput = await promptUser(`Agent name [${defaultName}]: `);
2105
- const agentName = nameInput || defaultName;
2106
- return createAgent({
2107
- name: agentName,
2108
- description: `Codex CLI local agent for ${agentName}`,
2109
- role: "WORKER",
2110
- createApiKey: true,
2111
- category: CODEX_AGENT_CATEGORY,
2112
- source: CODEX_AGENT_SOURCE
2113
- });
2114
- }
2115
1340
  function installCodexHooksConfig() {
2116
1341
  const homeDir = getCodexHomeDir();
2117
1342
  const configPath = getCodexConfigPath();
@@ -2139,13 +1364,13 @@ async function uninstallCodex(opts) {
2139
1364
  }
2140
1365
  if (!opts.keepConfig) {
2141
1366
  const configPath = getCodexConfigPath2(projectRoot);
2142
- const configRel = path5.relative(projectRoot, configPath);
1367
+ const configRel = path6.relative(projectRoot, configPath);
2143
1368
  if (deleteCodexConfig(projectRoot)) {
2144
1369
  console.log(`\u2713 Monitor config removed (${configRel})`);
2145
1370
  }
2146
1371
  } else {
2147
1372
  const configPath = getCodexConfigPath2(projectRoot);
2148
- const configRel = path5.relative(projectRoot, configPath);
1373
+ const configRel = path6.relative(projectRoot, configPath);
2149
1374
  console.log(`Monitor config retained at ${configRel}`);
2150
1375
  }
2151
1376
  console.log("");
@@ -2183,7 +1408,7 @@ function readCodexConfigToml(configPath) {
2183
1408
  }
2184
1409
  }
2185
1410
  function writeCodexConfigToml(configPath, data) {
2186
- const dir = path5.dirname(configPath);
1411
+ const dir = path6.dirname(configPath);
2187
1412
  if (!fs6.existsSync(dir)) {
2188
1413
  fs6.mkdirSync(dir, { recursive: true });
2189
1414
  }
@@ -2192,7 +1417,7 @@ function writeCodexConfigToml(configPath, data) {
2192
1417
  }
2193
1418
 
2194
1419
  // src/monitor/plugins/codex/status.ts
2195
- import path6 from "path";
1420
+ import path7 from "path";
2196
1421
  import * as fs7 from "fs";
2197
1422
  async function getCodexStatus(opts) {
2198
1423
  const projectRoot = opts?.projectRoot ?? process.cwd();
@@ -2232,7 +1457,7 @@ function isHooksBlockInstalled() {
2232
1457
 
2233
1458
  // src/monitor/plugins/codex/transcript.ts
2234
1459
  import * as fs8 from "fs";
2235
- import * as path7 from "path";
1460
+ import * as path8 from "path";
2236
1461
  function emptyRollout() {
2237
1462
  return {
2238
1463
  prompt: "",
@@ -2272,7 +1497,7 @@ function findRolloutPathForSession(sessionId, sessionsDir = getCodexSessionsDir(
2272
1497
  }
2273
1498
  for (const entry of entries) {
2274
1499
  if (filesScanned++ > merged.maxFiles) break;
2275
- const full = path7.join(dir, entry.name);
1500
+ const full = path8.join(dir, entry.name);
2276
1501
  if (entry.isDirectory()) {
2277
1502
  queue.push(full);
2278
1503
  continue;
@@ -2590,11 +1815,11 @@ import * as os7 from "os";
2590
1815
  // src/monitor/plugins/cursor/install.ts
2591
1816
  import * as fs11 from "fs";
2592
1817
  import * as os4 from "os";
2593
- import * as path10 from "path";
1818
+ import * as path11 from "path";
2594
1819
 
2595
1820
  // src/monitor/plugins/cursor/config.ts
2596
1821
  import * as fs10 from "fs";
2597
- import * as path8 from "path";
1822
+ import * as path9 from "path";
2598
1823
  function getCursorConfigPath(projectRoot) {
2599
1824
  return getMonitorConfigPath(projectRoot, "cursor");
2600
1825
  }
@@ -2610,7 +1835,7 @@ function loadCursorConfig(projectRoot) {
2610
1835
  }
2611
1836
  function writeCursorConfig(projectRoot, config) {
2612
1837
  const filePath = getCursorConfigPath(projectRoot);
2613
- const dir = path8.dirname(filePath);
1838
+ const dir = path9.dirname(filePath);
2614
1839
  if (!fs10.existsSync(dir)) {
2615
1840
  fs10.mkdirSync(dir, { recursive: true });
2616
1841
  }
@@ -2633,14 +1858,14 @@ function deleteCursorConfig(projectRoot) {
2633
1858
 
2634
1859
  // src/monitor/plugins/cursor/paths.ts
2635
1860
  import * as os3 from "os";
2636
- import * as path9 from "path";
1861
+ import * as path10 from "path";
2637
1862
  var CURSOR_DIR_NAME = ".cursor";
2638
1863
  var CURSOR_HOOKS_FILE = "hooks.json";
2639
1864
  function getCursorUserDir(homeDir = os3.homedir()) {
2640
- return path9.join(homeDir, CURSOR_DIR_NAME);
1865
+ return path10.join(homeDir, CURSOR_DIR_NAME);
2641
1866
  }
2642
1867
  function getCursorHooksPath(homeDir = os3.homedir()) {
2643
- return path9.join(getCursorUserDir(homeDir), CURSOR_HOOKS_FILE);
1868
+ return path10.join(getCursorUserDir(homeDir), CURSOR_HOOKS_FILE);
2644
1869
  }
2645
1870
 
2646
1871
  // src/monitor/plugins/cursor/hooks-config.ts
@@ -2738,7 +1963,7 @@ function readJsonFileTolerant(filePath) {
2738
1963
  }
2739
1964
  }
2740
1965
  function writeJsonFileWithDir(filePath, data) {
2741
- const dir = path10.dirname(filePath);
1966
+ const dir = path11.dirname(filePath);
2742
1967
  if (!fs11.existsSync(dir)) {
2743
1968
  fs11.mkdirSync(dir, { recursive: true });
2744
1969
  }
@@ -2760,26 +1985,19 @@ async function installCursor(opts) {
2760
1985
  `Activity from this workspace (${projectRoot}) will be associated with`
2761
1986
  );
2762
1987
  console.log("the agent you select below.\n");
2763
- const dirName = path10.basename(projectRoot);
2764
- const agent = await chooseOrCreateAgent(dirName);
1988
+ const agent = await provisionSelfMonitorAgent({
1989
+ projectRoot,
1990
+ source: CURSOR_AGENT_SOURCE,
1991
+ displayName: "Cursor",
1992
+ category: CURSOR_AGENT_CATEGORY
1993
+ });
2765
1994
  const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
2766
- let apiKey = agent.apiKey?.key;
1995
+ const apiKey = agent.apiKey?.key;
2767
1996
  if (!apiKey) {
2768
- console.log(
2769
- "\nThe API key for this agent is not available (it is only shown once at creation)."
1997
+ console.error(
1998
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
2770
1999
  );
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
- });
2000
+ process.exit(1);
2783
2001
  }
2784
2002
  const cursorDir = getCursorUserDir(homeDir);
2785
2003
  if (!fs11.existsSync(cursorDir)) {
@@ -2799,8 +2017,8 @@ async function installCursor(opts) {
2799
2017
  };
2800
2018
  writeCursorConfig(projectRoot, monitorConfig);
2801
2019
  const configPath = getCursorConfigPath(projectRoot);
2802
- const configRel = path10.relative(projectRoot, configPath);
2803
- const hooksDisplay = `~/${path10.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`;
2020
+ const configRel = path11.relative(projectRoot, configPath);
2021
+ const hooksDisplay = `~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`;
2804
2022
  console.log("");
2805
2023
  console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
2806
2024
  if (agent.apiKey?.key) {
@@ -2827,57 +2045,6 @@ async function installCursor(opts) {
2827
2045
  monitoringEndpoint
2828
2046
  };
2829
2047
  }
2830
- async function chooseOrCreateAgent(defaultName) {
2831
- const choice = await promptUser(
2832
- "Create a new agent or use an existing one? (new/existing) [new]: "
2833
- );
2834
- if (choice.toLowerCase() === "existing" || choice.toLowerCase() === "e") {
2835
- const agents = await listAgents();
2836
- if (agents.length === 0) {
2837
- console.log("No agents found. Creating a new one instead.\n");
2838
- return createNewAgent3(defaultName);
2839
- }
2840
- console.log("\nAvailable agents:");
2841
- for (let i = 0; i < agents.length; i++) {
2842
- console.log(
2843
- ` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`
2844
- );
2845
- }
2846
- const selection = await promptUser(`
2847
- Select agent (1-${agents.length}): `);
2848
- const idx = parseInt(selection, 10) - 1;
2849
- if (isNaN(idx) || idx < 0 || idx >= agents.length) {
2850
- console.error("Invalid selection.");
2851
- process.exit(1);
2852
- }
2853
- const fetched = await getAgent(agents[idx].id);
2854
- if (!fetched.apiKey?.key && !fetched.apiKey?.isActive) {
2855
- console.log(
2856
- "\nThis agent has no active API key. Please create a new agent instead,"
2857
- );
2858
- console.log("or generate an API key via the Olakai dashboard.\n");
2859
- const createNew = await promptUser("Create a new agent? (y/n) [y]: ");
2860
- if (createNew.toLowerCase() !== "n") {
2861
- return createNewAgent3(defaultName);
2862
- }
2863
- process.exit(1);
2864
- }
2865
- return fetched;
2866
- }
2867
- return createNewAgent3(defaultName);
2868
- }
2869
- async function createNewAgent3(defaultName) {
2870
- const nameInput = await promptUser(`Agent name [${defaultName}]: `);
2871
- const agentName = nameInput || defaultName;
2872
- return createAgent({
2873
- name: agentName,
2874
- description: `Cursor local agent for ${agentName}`,
2875
- role: "WORKER",
2876
- createApiKey: true,
2877
- category: CURSOR_AGENT_CATEGORY,
2878
- source: CURSOR_AGENT_SOURCE
2879
- });
2880
- }
2881
2048
  async function uninstallCursor(opts) {
2882
2049
  const projectRoot = opts.projectRoot ?? process.cwd();
2883
2050
  const homeDir = opts.homeDir ?? os4.homedir();
@@ -2902,20 +2069,20 @@ async function uninstallCursor(opts) {
2902
2069
  writeJsonFileWithDir(hooksPath, cleaned);
2903
2070
  }
2904
2071
  console.log(
2905
- `\u2713 Olakai hooks removed from ~/${path10.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`
2072
+ `\u2713 Olakai hooks removed from ~/${path11.join(CURSOR_DIR_NAME, CURSOR_HOOKS_FILE)}`
2906
2073
  );
2907
2074
  } else {
2908
2075
  console.log("No Cursor hooks file found.");
2909
2076
  }
2910
2077
  if (!opts.keepConfig) {
2911
2078
  const configPath = getCursorConfigPath(projectRoot);
2912
- const configRel = path10.relative(projectRoot, configPath);
2079
+ const configRel = path11.relative(projectRoot, configPath);
2913
2080
  if (deleteCursorConfig(projectRoot)) {
2914
2081
  console.log(`\u2713 Monitor config removed (${configRel})`);
2915
2082
  }
2916
2083
  } else {
2917
2084
  const configPath = getCursorConfigPath(projectRoot);
2918
- const configRel = path10.relative(projectRoot, configPath);
2085
+ const configRel = path11.relative(projectRoot, configPath);
2919
2086
  console.log(`Monitor config retained at ${configRel}`);
2920
2087
  }
2921
2088
  console.log("");
@@ -2928,7 +2095,7 @@ async function uninstallCursor(opts) {
2928
2095
  // src/monitor/plugins/cursor/status.ts
2929
2096
  import * as fs12 from "fs";
2930
2097
  import * as os5 from "os";
2931
- import * as path11 from "path";
2098
+ import * as path12 from "path";
2932
2099
  async function getCursorStatus(opts) {
2933
2100
  const projectRoot = opts?.projectRoot ?? process.cwd();
2934
2101
  const homeDir = opts?.homeDir ?? os5.homedir();
@@ -3120,10 +2287,10 @@ function extractCursorMeta(payload) {
3120
2287
  // src/monitor/plugins/cursor/pairing-state.ts
3121
2288
  import * as fs13 from "fs";
3122
2289
  import * as os6 from "os";
3123
- import * as path12 from "path";
2290
+ import * as path13 from "path";
3124
2291
  var STATE_DIR_SEGMENTS2 = [".olakai", "cursor-pairings"];
3125
2292
  function getPairingsDir(homeDir) {
3126
- return path12.join(homeDir, ...STATE_DIR_SEGMENTS2);
2293
+ return path13.join(homeDir, ...STATE_DIR_SEGMENTS2);
3127
2294
  }
3128
2295
  function sanitizeKeyFragment(value) {
3129
2296
  return value.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -3134,7 +2301,7 @@ function getPairingKey(conversationId, generationId) {
3134
2301
  return `${conv}__${sanitizeKeyFragment(generationId)}`;
3135
2302
  }
3136
2303
  function getPairingFile(conversationId, generationId, homeDir) {
3137
- return path12.join(
2304
+ return path13.join(
3138
2305
  getPairingsDir(homeDir),
3139
2306
  `${getPairingKey(conversationId, generationId)}.json`
3140
2307
  );
@@ -3200,7 +2367,7 @@ function listPendingPrompts(homeDir = os6.homedir()) {
3200
2367
  const result = [];
3201
2368
  for (const name of files) {
3202
2369
  if (!name.endsWith(".json")) continue;
3203
- const filePath = path12.join(dir, name);
2370
+ const filePath = path13.join(dir, name);
3204
2371
  try {
3205
2372
  const raw = fs13.readFileSync(filePath, "utf-8");
3206
2373
  const parsed = JSON.parse(raw);
@@ -3516,7 +2683,7 @@ function describeDetection(plugin, projectRoot) {
3516
2683
  } catch {
3517
2684
  }
3518
2685
  try {
3519
- const settings = path13.join(projectRoot, ".claude", "settings.json");
2686
+ const settings = path14.join(projectRoot, ".claude", "settings.json");
3520
2687
  if (fs15.existsSync(settings)) {
3521
2688
  return "found .claude/settings.json";
3522
2689
  }
@@ -3604,26 +2771,34 @@ async function initCommand(options) {
3604
2771
  try {
3605
2772
  const resolved = resolveProfileName();
3606
2773
  const profileName = resolved?.name ?? "default";
3607
- const host = await resolveHost(profileName, options, interactive);
3608
- setHostOverride(host);
2774
+ let optedReAuth = false;
3609
2775
  if (isTokenValid()) {
3610
2776
  const file = readProfilesFile();
3611
2777
  const existing = file.profiles[profileName];
3612
2778
  const who = existing?.email ?? "unknown";
2779
+ const where = existing?.host ?? "(unknown host)";
3613
2780
  if (options.nonInteractive) {
3614
2781
  console.log(
3615
- `Already authenticated as ${who} on ${host} (profile: ${profileName}).`
2782
+ `Already authenticated as ${who} on ${where} (profile: ${profileName}).`
3616
2783
  );
3617
2784
  return;
3618
2785
  }
3619
2786
  const answer = await promptUser(
3620
- `Already authenticated as ${who} on ${host} (profile: ${profileName}). Re-authenticate? [y/N]: `
2787
+ `Already authenticated as ${who} on ${where} (profile: ${profileName}). Re-authenticate? [y/N]: `
3621
2788
  );
3622
2789
  if (answer.trim().toLowerCase() !== "y") {
3623
2790
  console.log("Keeping existing credentials. Run 'olakai logout' to sign out.");
3624
2791
  return;
3625
2792
  }
2793
+ optedReAuth = true;
3626
2794
  }
2795
+ const host = await resolveHost(
2796
+ profileName,
2797
+ options,
2798
+ interactive,
2799
+ optedReAuth
2800
+ );
2801
+ setHostOverride(host);
3627
2802
  const email = await resolveEmail(options, interactive);
3628
2803
  console.log(`
3629
2804
  Contacting ${host} for ${email}...`);
@@ -3724,7 +2899,7 @@ function isSafeOpenUrl(raw) {
3724
2899
  return false;
3725
2900
  }
3726
2901
  }
3727
- async function resolveHost(profileName, options, interactive) {
2902
+ async function resolveHost(profileName, options, interactive, forceHostPrompt = false) {
3728
2903
  const fromFlag = options.host?.trim() || process.env.OLAKAI_HOST?.trim();
3729
2904
  if (fromFlag) {
3730
2905
  return getBaseUrl();
@@ -3732,7 +2907,8 @@ async function resolveHost(profileName, options, interactive) {
3732
2907
  const file = readProfilesFile();
3733
2908
  const existing = file.profiles[profileName];
3734
2909
  if (existing?.host) {
3735
- if (!interactive || isTokenValid()) {
2910
+ const shouldOfferSwitch = interactive && (forceHostPrompt || !isTokenValid());
2911
+ if (!shouldOfferSwitch) {
3736
2912
  return existing.host;
3737
2913
  }
3738
2914
  const keep = await promptUser(
@@ -5360,7 +4536,7 @@ async function statusCommand(options) {
5360
4536
  return;
5361
4537
  }
5362
4538
  if (plugin.id === "claude-code") {
5363
- const { printClaudeCodeStatus } = await import("./status-CAHO5FHI.js");
4539
+ const { printClaudeCodeStatus } = await import("./status-USHUUHK6.js");
5364
4540
  await printClaudeCodeStatus({ projectRoot: process.cwd() });
5365
4541
  return;
5366
4542
  }
@@ -5376,6 +4552,24 @@ async function statusCommand(options) {
5376
4552
  async function disableCommand(options) {
5377
4553
  const toolId = await resolveToolFromOptions(options.tool, "disable");
5378
4554
  const plugin = getPlugin(toolId);
4555
+ let agentIdToDelete;
4556
+ if (options.deleteAgent) {
4557
+ try {
4558
+ const report = await plugin.status({ projectRoot: process.cwd() });
4559
+ if (report.configured && report.agentId) {
4560
+ agentIdToDelete = report.agentId;
4561
+ } else {
4562
+ console.log(
4563
+ "No monitor config found in this workspace \u2014 nothing to delete remotely."
4564
+ );
4565
+ }
4566
+ } catch (err) {
4567
+ const msg = err instanceof Error ? err.message : String(err);
4568
+ console.warn(
4569
+ `Couldn't read local monitor config to identify the agent (${msg}). Proceeding with local-only uninstall.`
4570
+ );
4571
+ }
4572
+ }
5379
4573
  await runPluginAction(
5380
4574
  plugin,
5381
4575
  () => plugin.uninstall({
@@ -5383,6 +4577,22 @@ async function disableCommand(options) {
5383
4577
  keepConfig: options.keepConfig
5384
4578
  })
5385
4579
  );
4580
+ if (agentIdToDelete) {
4581
+ const { deleteAgent: deleteAgent2 } = await import("./api-63UDJX5M.js");
4582
+ try {
4583
+ await deleteAgent2(agentIdToDelete);
4584
+ console.log(`\u2713 Remote agent ${agentIdToDelete} deleted.`);
4585
+ } catch (err) {
4586
+ const msg = err instanceof Error ? err.message : String(err);
4587
+ console.error(
4588
+ `Local uninstall succeeded, but remote agent delete failed: ${msg}`
4589
+ );
4590
+ console.error(
4591
+ `You can retry by visiting the Olakai dashboard, or rerun 'olakai monitor disable --tool ${toolId} --delete-agent' (the local config is gone, so the agent id won't be re-discovered \u2014 use the dashboard).`
4592
+ );
4593
+ process.exit(1);
4594
+ }
4595
+ }
5386
4596
  }
5387
4597
  async function runPluginAction(plugin, fn) {
5388
4598
  try {
@@ -5507,6 +4717,9 @@ function registerMonitorCommand(program2) {
5507
4717
  ).option(
5508
4718
  "--keep-config",
5509
4719
  "Keep the monitor config file (only remove hooks)"
4720
+ ).option(
4721
+ "--delete-agent",
4722
+ "Also delete the remote agent on the Olakai backend. Useful when retiring a workspace permanently. Self-monitor owner or ADMIN only."
5510
4723
  ).action(disableCommand);
5511
4724
  for (const plugin of listPlugins()) {
5512
4725
  plugin.registerCommands?.(monitor);