@flashbacktech/tsclient 0.4.33

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.cjs ADDED
@@ -0,0 +1,1805 @@
1
+ 'use strict';
2
+
3
+ // src/http/HttpError.ts
4
+ var HttpError = class extends Error {
5
+ constructor(status, statusText, body) {
6
+ const errorCode = extractErrorCode(body);
7
+ const message = extractMessage(body) ?? `${status} ${statusText}`;
8
+ super(message);
9
+ this.name = "HttpError";
10
+ this.status = status;
11
+ this.statusText = statusText;
12
+ this.errorCode = errorCode;
13
+ this.body = body;
14
+ }
15
+ };
16
+ function extractErrorCode(body) {
17
+ if (body && typeof body === "object") {
18
+ const code = body.error_code ?? body.errorCode;
19
+ if (typeof code === "string") return code;
20
+ }
21
+ return null;
22
+ }
23
+ function extractMessage(body) {
24
+ if (body && typeof body === "object") {
25
+ const msg = body.message ?? body.error;
26
+ if (typeof msg === "string") return msg;
27
+ }
28
+ return null;
29
+ }
30
+ var NotImplementedError = class extends Error {
31
+ constructor(method) {
32
+ super(`${method} is not implemented in the cloud-agent backend yet.`);
33
+ this.name = "NotImplementedError";
34
+ }
35
+ };
36
+
37
+ // src/http/BaseClient.ts
38
+ var BaseClient = class {
39
+ constructor(options) {
40
+ if (!options.baseUrl) throw new Error("CloudAgentClient: baseUrl is required");
41
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
42
+ this.getToken = options.getToken;
43
+ this.onUnauthorized = options.onUnauthorized;
44
+ this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
45
+ this.debug = options.debug ?? false;
46
+ this.defaultHeaders = options.defaultHeaders ?? {};
47
+ this.getImpersonateOrgId = options.getImpersonateOrgId;
48
+ }
49
+ get(path, options) {
50
+ return this.request("GET", path, options);
51
+ }
52
+ post(path, options) {
53
+ return this.request("POST", path, options);
54
+ }
55
+ put(path, options) {
56
+ return this.request("PUT", path, options);
57
+ }
58
+ patch(path, options) {
59
+ return this.request("PATCH", path, options);
60
+ }
61
+ delete(path, options) {
62
+ return this.request("DELETE", path, options);
63
+ }
64
+ async request(method, path, options = {}) {
65
+ const url = this.buildUrl(path, options.query);
66
+ const headers = { ...this.defaultHeaders, ...options.headers ?? {} };
67
+ if (!options.skipAuth && this.getToken) {
68
+ const token = await this.getToken();
69
+ if (token) headers["Authorization"] = `Bearer ${token}`;
70
+ }
71
+ if (!options.skipAuth && this.getImpersonateOrgId && !("X-Impersonate-Org-Id" in headers) && !("x-impersonate-org-id" in headers)) {
72
+ const target = await this.getImpersonateOrgId();
73
+ if (target) headers["X-Impersonate-Org-Id"] = target;
74
+ }
75
+ let body;
76
+ if (options.body !== void 0 && options.body !== null) {
77
+ if (options.body instanceof FormData) {
78
+ body = options.body;
79
+ } else {
80
+ if (!("Content-Type" in headers) && !("content-type" in headers)) {
81
+ headers["Content-Type"] = "application/json";
82
+ }
83
+ body = JSON.stringify(options.body);
84
+ }
85
+ }
86
+ if (this.debug) {
87
+ console.debug("[flashbacktech/tsclient] \u2192", method, url, options.body ?? "");
88
+ }
89
+ const res = await this.fetchImpl(url, { method, headers, body });
90
+ let parsed = null;
91
+ const text = await res.text();
92
+ if (text.length > 0) {
93
+ try {
94
+ parsed = JSON.parse(text);
95
+ } catch {
96
+ parsed = text;
97
+ }
98
+ }
99
+ if (this.debug) {
100
+ console.debug("[flashbacktech/tsclient] \u2190", res.status, url, parsed);
101
+ }
102
+ if (!res.ok) {
103
+ if (res.status === 401 && this.onUnauthorized) {
104
+ try {
105
+ await this.onUnauthorized();
106
+ } catch {
107
+ }
108
+ }
109
+ throw new HttpError(res.status, res.statusText, parsed);
110
+ }
111
+ return parsed;
112
+ }
113
+ buildUrl(path, query) {
114
+ const base = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
115
+ if (!query) return base;
116
+ const params = new URLSearchParams();
117
+ for (const [key, value] of Object.entries(query)) {
118
+ if (value === void 0 || value === null) continue;
119
+ if (Array.isArray(value)) {
120
+ for (const v of value) params.append(key, String(v));
121
+ } else {
122
+ params.set(key, String(value));
123
+ }
124
+ }
125
+ const qs = params.toString();
126
+ return qs ? `${base}?${qs}` : base;
127
+ }
128
+ };
129
+
130
+ // src/modules/auth/AuthClient.ts
131
+ var AuthClient = class {
132
+ constructor(http) {
133
+ this.http = http;
134
+ }
135
+ login(body) {
136
+ return this.http.post("/user/login", { body, skipAuth: true });
137
+ }
138
+ register(body) {
139
+ return this.http.post("/user/register", { body, skipAuth: true });
140
+ }
141
+ logout(refreshToken) {
142
+ return this.http.post("/user/logout", {
143
+ body: { refreshToken }
144
+ });
145
+ }
146
+ refresh(body) {
147
+ return this.http.post("/user/refresh", { body, skipAuth: true });
148
+ }
149
+ activate(body) {
150
+ return this.http.post("/user/activate", { body, skipAuth: true });
151
+ }
152
+ requestVerification(email) {
153
+ return this.http.post("/user/request-verification", {
154
+ body: { email },
155
+ skipAuth: true
156
+ });
157
+ }
158
+ requestPasswordReset(email) {
159
+ return this.http.post("/user/request-reset-password", {
160
+ body: { email },
161
+ skipAuth: true
162
+ });
163
+ }
164
+ resetPassword(body) {
165
+ return this.http.post("/user/reset-password", { body, skipAuth: true });
166
+ }
167
+ exchangeGoogle(body) {
168
+ return this.http.post("/auth/google/exchange", { body, skipAuth: true });
169
+ }
170
+ };
171
+
172
+ // src/common/envelope.ts
173
+ function unwrapData(payload) {
174
+ if (!payload || payload.data === void 0) {
175
+ throw new Error("Response envelope missing `data` field.");
176
+ }
177
+ return payload.data;
178
+ }
179
+
180
+ // src/modules/mfa/MfaClient.ts
181
+ var MfaClient = class {
182
+ constructor(http) {
183
+ this.http = http;
184
+ }
185
+ async getStatus() {
186
+ const res = await this.http.get("/mfa/status");
187
+ return unwrapData(res);
188
+ }
189
+ async getMethods() {
190
+ const res = await this.http.get("/mfa/methods");
191
+ return res.data?.methods ?? [];
192
+ }
193
+ setup(body) {
194
+ return this.http.post("/mfa/setup", { body });
195
+ }
196
+ verifySetup(body) {
197
+ return this.http.post("/mfa/verify-setup", { body });
198
+ }
199
+ enable(mfaType) {
200
+ return this.http.post("/mfa/enable", { body: { mfaType } });
201
+ }
202
+ disable(mfaType) {
203
+ return this.http.post("/mfa/disable", { body: { mfaType } });
204
+ }
205
+ setPrimary(mfaType) {
206
+ return this.http.post("/mfa/primary", { body: { mfaType } });
207
+ }
208
+ sendMagicLink() {
209
+ return this.http.post("/mfa/magic-link/send", {});
210
+ }
211
+ activateMagicLink(body) {
212
+ return this.http.post("/mfa/magic-link/activate", { body, skipAuth: true });
213
+ }
214
+ verifyLogin(body) {
215
+ return this.http.post("/mfa/verify-login", { body });
216
+ }
217
+ };
218
+
219
+ // src/modules/user/UserClient.ts
220
+ var UserClient = class {
221
+ constructor(http) {
222
+ this.http = http;
223
+ }
224
+ async getProfile() {
225
+ const res = await this.http.get("/me");
226
+ return unwrapData(res);
227
+ }
228
+ update(body) {
229
+ return this.http.put("/user", { body });
230
+ }
231
+ };
232
+
233
+ // src/modules/organization/OrganizationClient.ts
234
+ var OrganizationClient = class {
235
+ constructor(http) {
236
+ this.http = http;
237
+ }
238
+ async get(orgId) {
239
+ const res = await this.http.get(`/organization/${orgId}`);
240
+ return unwrapData(res);
241
+ }
242
+ async update(orgId, body) {
243
+ const res = await this.http.put(`/organization/${orgId}`, { body });
244
+ return unwrapData(res);
245
+ }
246
+ async listUsers() {
247
+ const res = await this.http.get("/organization/users");
248
+ return res.data ?? [];
249
+ }
250
+ updateUser(userId, body) {
251
+ return this.http.put(`/organization/users/${userId}`, { body });
252
+ }
253
+ /** Backend currently returns 501. Surfaced for forward-compat. */
254
+ createUser(body) {
255
+ return this.http.post("/organization/users", { body });
256
+ }
257
+ /** Backend currently returns 501. Surfaced for forward-compat. */
258
+ deleteUser(userId) {
259
+ return this.http.delete(`/organization/users/${userId}`);
260
+ }
261
+ /** Accept an org invite: verifies the one-time token and sets the user's password. */
262
+ acceptInvite(body) {
263
+ return this.http.post("/organization/invite/accept", { body });
264
+ }
265
+ /**
266
+ * Platform-admin only: list every org for the impersonation picker.
267
+ * The server returns 403 when the caller's home org is not the
268
+ * configured ADMIN_ORGANIZATION; render the picker only when the
269
+ * authenticated user has `isPlatformAdmin === true`.
270
+ */
271
+ async listAll() {
272
+ const res = await this.http.get("/orgs/list-all");
273
+ return res.data ?? [];
274
+ }
275
+ /**
276
+ * Platform-admin only: enable or disable the debug surface for an org.
277
+ * Returns the updated org entry with the new allowDebug value.
278
+ */
279
+ async setAllowDebug(orgId, allow) {
280
+ const res = await this.http.patch(`/orgs/${orgId}/allow-debug`, { body: { allow } });
281
+ return res.data;
282
+ }
283
+ };
284
+
285
+ // src/adapters/projectAdapter.ts
286
+ var projectAdapter = {
287
+ fromListResponse(payload) {
288
+ const list = payload?.workspaces ?? payload?.projects ?? [];
289
+ return list.map(normalize);
290
+ },
291
+ fromGetResponse(payload) {
292
+ const wire = payload;
293
+ const value = wire?.workspace ?? wire?.project ?? wire?.data;
294
+ if (!value) throw new Error("Project response missing body.");
295
+ return normalize(value);
296
+ },
297
+ normalize
298
+ };
299
+ function normalize(wire) {
300
+ return {
301
+ id: wire.id,
302
+ name: wire.name,
303
+ orgId: wire.orgId,
304
+ default: wire.default ?? false,
305
+ users: wire.users ?? []
306
+ };
307
+ }
308
+
309
+ // src/modules/projects/ProjectsClient.ts
310
+ var ProjectsClient = class {
311
+ constructor(http) {
312
+ this.http = http;
313
+ }
314
+ async list() {
315
+ const res = await this.http.get("/project");
316
+ return projectAdapter.fromListResponse(res);
317
+ }
318
+ async get(projectId) {
319
+ const res = await this.http.get(`/project/${projectId}`);
320
+ return projectAdapter.fromGetResponse(res);
321
+ }
322
+ async create(body) {
323
+ const res = await this.http.post("/project", { body });
324
+ return projectAdapter.fromGetResponse(res);
325
+ }
326
+ async update(projectId, body) {
327
+ const res = await this.http.put(`/project/${projectId}`, { body });
328
+ return projectAdapter.fromGetResponse(res);
329
+ }
330
+ delete(projectId) {
331
+ return this.http.delete(`/project/${projectId}`);
332
+ }
333
+ addMember(projectId, body) {
334
+ return this.http.post(`/project/${projectId}/users`, { body });
335
+ }
336
+ updateMemberRole(projectId, userId, body) {
337
+ return this.http.put(`/project/${projectId}/users/${userId}`, { body });
338
+ }
339
+ removeMember(projectId, userId) {
340
+ return this.http.delete(`/project/${projectId}/users/${userId}`);
341
+ }
342
+ };
343
+
344
+ // src/adapters/sandboxAdapter.ts
345
+ var sandboxAdapter = {
346
+ fromListResponse(payload) {
347
+ const list = payload?.repos ?? payload?.sandboxes ?? [];
348
+ return list.map(normalize2);
349
+ },
350
+ fromGetResponse(payload) {
351
+ const wire = payload;
352
+ const value = wire?.repo ?? wire?.sandbox ?? wire?.data;
353
+ if (!value) throw new Error("Sandbox response missing body.");
354
+ return normalize2(value);
355
+ },
356
+ normalize: normalize2
357
+ };
358
+ function normalize2(wire) {
359
+ return {
360
+ id: wire.id,
361
+ name: wire.name,
362
+ storageType: wire.storageType,
363
+ mode: wire.mode,
364
+ orgId: wire.orgId,
365
+ userId: wire.userId,
366
+ projectId: wire.projectId ?? wire.workspaceId ?? null,
367
+ createdAt: wire.createdAt,
368
+ disabled: wire.disabled ?? false
369
+ };
370
+ }
371
+
372
+ // src/modules/sandboxes/SandboxesClient.ts
373
+ var SandboxesClient = class {
374
+ constructor(http) {
375
+ this.http = http;
376
+ }
377
+ async list(query = {}) {
378
+ const res = await this.http.get("/sandbox", {
379
+ query: { projectId: query.projectId }
380
+ });
381
+ return sandboxAdapter.fromListResponse(res);
382
+ }
383
+ async get(sandboxId) {
384
+ const res = await this.http.get(`/sandbox/${sandboxId}`);
385
+ return sandboxAdapter.fromGetResponse(res);
386
+ }
387
+ async create(body) {
388
+ const res = await this.http.post("/sandbox", {
389
+ body: {
390
+ name: body.name,
391
+ projectId: body.projectId,
392
+ storageType: body.storageType ?? "S3",
393
+ mode: body.mode ?? "NORMAL"
394
+ }
395
+ });
396
+ return sandboxAdapter.fromGetResponse(res);
397
+ }
398
+ async update(sandboxId, body) {
399
+ const res = await this.http.put(`/sandbox/${sandboxId}`, { body });
400
+ return sandboxAdapter.fromGetResponse(res);
401
+ }
402
+ delete(sandboxId) {
403
+ return this.http.delete(`/sandbox/${sandboxId}`);
404
+ }
405
+ };
406
+
407
+ // src/modules/credentials/types.ts
408
+ function normalizeSandboxCredential(wire) {
409
+ return {
410
+ sandboxId: wire.sandboxId ?? wire.repoId ?? "",
411
+ credentialId: wire.credentialId,
412
+ provider: wire.provider,
413
+ label: wire.label,
414
+ displayHint: wire.displayHint ?? void 0,
415
+ createdAt: wire.createdAt
416
+ };
417
+ }
418
+ function normalizeGatewayToken(wire) {
419
+ return {
420
+ id: wire.id,
421
+ sandboxId: wire.sandboxId ?? wire.repoId ?? "",
422
+ label: wire.label,
423
+ createdAt: wire.createdAt
424
+ };
425
+ }
426
+
427
+ // src/modules/credentials/GatewayTokensClient.ts
428
+ var GatewayTokensClient = class {
429
+ constructor(http) {
430
+ this.http = http;
431
+ }
432
+ async list(sandboxId) {
433
+ const res = await this.http.get(
434
+ `/sandbox/${sandboxId}/gateway-token`
435
+ );
436
+ const list = res.tokens ?? res.data ?? [];
437
+ return list.map(normalizeGatewayToken);
438
+ }
439
+ /**
440
+ * create returns both the persisted token row and the plaintext bearer
441
+ * (`secret`). The bearer is shown only here; surface it to the user
442
+ * immediately. If they navigate away without copying, they have to
443
+ * delete the token and create a new one.
444
+ */
445
+ async create(sandboxId, body) {
446
+ const res = await this.http.post(`/sandbox/${sandboxId}/gateway-token`, { body });
447
+ if (!res.token || !res.secret) {
448
+ throw new Error("createGatewayToken response missing token or secret.");
449
+ }
450
+ return {
451
+ token: normalizeGatewayToken(res.token),
452
+ secret: res.secret
453
+ };
454
+ }
455
+ delete(sandboxId, tokenId) {
456
+ return this.http.delete(`/sandbox/${sandboxId}/gateway-token/${tokenId}`);
457
+ }
458
+ };
459
+
460
+ // src/modules/credentials/CredentialsClient.ts
461
+ var SandboxCredentialScope = class {
462
+ constructor(http) {
463
+ this.http = http;
464
+ }
465
+ async list(sandboxId) {
466
+ const res = await this.http.get(
467
+ `/sandbox/${sandboxId}/credential`
468
+ );
469
+ const list = res.items ?? res.data ?? [];
470
+ return list.map(normalizeSandboxCredential);
471
+ }
472
+ attach(sandboxId, credentialId) {
473
+ return this.http.post(`/sandbox/${sandboxId}/credential`, {
474
+ body: { credentialId }
475
+ });
476
+ }
477
+ detach(sandboxId, credentialId) {
478
+ return this.http.delete(`/sandbox/${sandboxId}/credential/${credentialId}`);
479
+ }
480
+ };
481
+ var CredentialsClient = class {
482
+ constructor(http) {
483
+ this.http = http;
484
+ this.sandbox = new SandboxCredentialScope(http);
485
+ this.gatewayTokens = new GatewayTokensClient(http);
486
+ }
487
+ async list(query) {
488
+ const res = await this.http.get(
489
+ "/credentials",
490
+ {
491
+ query: {
492
+ projectId: query.projectId,
493
+ provider: query.provider,
494
+ capability: query.capability
495
+ }
496
+ }
497
+ );
498
+ return res.credentials ?? res.data ?? [];
499
+ }
500
+ async create(body) {
501
+ const res = await this.http.post(
502
+ "/credentials",
503
+ { body }
504
+ );
505
+ const value = res.data ?? res.credential;
506
+ if (!value) throw new Error("createCredential response missing body.");
507
+ return value;
508
+ }
509
+ async update(credentialId, body) {
510
+ const res = await this.http.put(
511
+ `/credentials/${credentialId}`,
512
+ { body }
513
+ );
514
+ const value = res.credential ?? res.data;
515
+ if (!value) throw new Error("updateCredential response missing body.");
516
+ return value;
517
+ }
518
+ delete(credentialId) {
519
+ return this.http.delete(`/credentials/${credentialId}`);
520
+ }
521
+ /**
522
+ * providers fetches the server-side catalog. The tsclient ships its
523
+ * own catalog mirror in ./providers.ts that the frontend can use
524
+ * synchronously; this endpoint is the runtime source of truth and
525
+ * picks up customer extensions added server-side without a tsclient
526
+ * republish.
527
+ */
528
+ async providers() {
529
+ const res = await this.http.get("/providers");
530
+ return res.providers ?? res.data ?? [];
531
+ }
532
+ };
533
+
534
+ // src/modules/chat/ChatStream.ts
535
+ var STREAM_PATH = "/api/agentengine/v1/agent/chat/sessions";
536
+ async function openChatStream(deps, options) {
537
+ const mod = await import('event-source-polyfill');
538
+ const Polyfill = mod.EventSourcePolyfill ?? mod.default?.EventSourcePolyfill ?? mod.default;
539
+ if (!Polyfill) {
540
+ throw new Error("event-source-polyfill is required for client.chat.openStream(). Install it as a peer dep.");
541
+ }
542
+ const url = `${deps.baseUrl.replace(/\/$/, "")}${STREAM_PATH}/${options.conversationId}/stream`;
543
+ const headers = {
544
+ "X-Org-Id": options.scope.orgId,
545
+ "X-User-Id": options.scope.userId,
546
+ "X-Sandbox-Id": options.scope.sandboxId ?? options.scope.repoId ?? "",
547
+ "X-Repo-Id": options.scope.sandboxId ?? options.scope.repoId ?? ""
548
+ };
549
+ if (options.scope.projectId ?? options.scope.workspaceId) {
550
+ headers["X-Project-Id"] = options.scope.projectId ?? options.scope.workspaceId ?? "";
551
+ headers["X-Workspace-Id"] = options.scope.projectId ?? options.scope.workspaceId ?? "";
552
+ }
553
+ if (deps.getToken) {
554
+ const token = await deps.getToken();
555
+ if (token) headers["Authorization"] = `Bearer ${token}`;
556
+ }
557
+ if (options.lastEventId) headers["Last-Event-ID"] = options.lastEventId;
558
+ const es = new Polyfill(url, {
559
+ headers,
560
+ heartbeatTimeout: options.heartbeatTimeout ?? 6e4
561
+ });
562
+ const KNOWN_EVENTS = [
563
+ "phase",
564
+ "question",
565
+ "plan_draft",
566
+ "step_started",
567
+ "step_completed",
568
+ "step_failed",
569
+ "step_progress",
570
+ "narration",
571
+ "assistant_message",
572
+ "done",
573
+ "session_title",
574
+ "tool_call_started",
575
+ "tool_call_finished",
576
+ "llm_call_finished",
577
+ "tool_call_confirmation_required"
578
+ ];
579
+ const handleRaw = (raw) => {
580
+ try {
581
+ const parsed = JSON.parse(raw.data);
582
+ options.onEvent(parsed);
583
+ } catch {
584
+ }
585
+ };
586
+ for (const name of KNOWN_EVENTS) {
587
+ es.addEventListener(name, handleRaw);
588
+ }
589
+ if (options.onError) {
590
+ es.onerror = (event) => options.onError?.(event);
591
+ }
592
+ return { close: () => es.close() };
593
+ }
594
+
595
+ // src/modules/chat/ChatClient.ts
596
+ var BASE_PATH = "/api/agentengine/v1/agent/chat/sessions";
597
+ function scopeHeaders(scope) {
598
+ const sandboxId = scope.sandboxId ?? scope.repoId ?? "";
599
+ const projectId = scope.projectId ?? scope.workspaceId;
600
+ const headers = {
601
+ "X-Org-Id": scope.orgId,
602
+ "X-User-Id": scope.userId,
603
+ "X-Sandbox-Id": sandboxId,
604
+ "X-Repo-Id": sandboxId
605
+ };
606
+ if (projectId) {
607
+ headers["X-Project-Id"] = projectId;
608
+ headers["X-Workspace-Id"] = projectId;
609
+ }
610
+ return headers;
611
+ }
612
+ var ChatSessionsScope = class {
613
+ constructor(http) {
614
+ this.http = http;
615
+ }
616
+ create(scope, body) {
617
+ return this.http.post(BASE_PATH, {
618
+ body,
619
+ headers: scopeHeaders(scope)
620
+ });
621
+ }
622
+ list(scope, query = {}) {
623
+ return this.http.get(BASE_PATH, {
624
+ query,
625
+ headers: scopeHeaders(scope)
626
+ });
627
+ }
628
+ get(scope, conversationId) {
629
+ return this.http.get(`${BASE_PATH}/${conversationId}`, {
630
+ headers: scopeHeaders(scope)
631
+ });
632
+ }
633
+ delete(scope, conversationId) {
634
+ return this.http.delete(`${BASE_PATH}/${conversationId}`, {
635
+ headers: scopeHeaders(scope)
636
+ });
637
+ }
638
+ };
639
+ var ChatMessagesScope = class {
640
+ constructor(http) {
641
+ this.http = http;
642
+ }
643
+ post(scope, conversationId, body) {
644
+ return this.http.post(`${BASE_PATH}/${conversationId}/messages`, {
645
+ body,
646
+ headers: scopeHeaders(scope)
647
+ });
648
+ }
649
+ /**
650
+ * Interrupts an in-flight chat-loop turn. The engine cancels the turn's
651
+ * Go context and asks the sandbox service to kill any in-flight docker
652
+ * exec inside the per-session container (the container itself survives
653
+ * so the next turn keeps its filesystem state). Returns 404 when no
654
+ * turn matches `turnId` — covers the race where the user clicks Stop
655
+ * just after the turn already finished.
656
+ *
657
+ * The engine still emits a terminal `done` SSE event with
658
+ * status="context_cancelled" after this returns; the frontend should
659
+ * render its "Cancelled" marker on that event rather than on this
660
+ * response, so a reload mid-cancel still ends in the same UI state.
661
+ */
662
+ cancel(scope, conversationId, turnId) {
663
+ return this.http.post(
664
+ `${BASE_PATH}/${conversationId}/messages/${turnId}/cancel`,
665
+ { headers: scopeHeaders(scope) }
666
+ );
667
+ }
668
+ /**
669
+ * Deliver the user's allow/deny decision on an M3 confirmation modal
670
+ * (fired when the policy hook emits a
671
+ * `tool_call_confirmation_required` SSE event). The chat-loop has
672
+ * suspended waiting on this call; on allow it dispatches the tool, on
673
+ * deny it synthesises a "DENIED:" tool result so the model can react.
674
+ * Returns 404 when no pending confirmation exists for the given
675
+ * (turn, tool_call) — usually means the 4-minute confirmer timeout
676
+ * already elapsed.
677
+ */
678
+ confirmToolCall(scope, conversationId, turnId, toolCallId, body) {
679
+ return this.http.post(
680
+ `${BASE_PATH}/${conversationId}/messages/${turnId}/confirm/${toolCallId}`,
681
+ { body, headers: scopeHeaders(scope) }
682
+ );
683
+ }
684
+ };
685
+ var ChatQuestionsScope = class {
686
+ constructor(http) {
687
+ this.http = http;
688
+ }
689
+ answer(scope, conversationId, turnId, body) {
690
+ return this.http.post(
691
+ `${BASE_PATH}/${conversationId}/messages/${turnId}/answers`,
692
+ { body, headers: scopeHeaders(scope) }
693
+ );
694
+ }
695
+ };
696
+ var ChatDebugScope = class {
697
+ constructor(http) {
698
+ this.http = http;
699
+ }
700
+ getByConversation(scope, conversationId) {
701
+ return this.http.get(`${BASE_PATH}/${conversationId}/debug`, {
702
+ headers: scopeHeaders(scope)
703
+ });
704
+ }
705
+ };
706
+ var ChatSchedulesScope = class {
707
+ constructor(http) {
708
+ this.http = http;
709
+ }
710
+ repoPath(scope) {
711
+ const repoID = scope.sandboxId ?? scope.repoId ?? "";
712
+ return `/api/agentengine/v1/agent/repos/${repoID}/schedules`;
713
+ }
714
+ list(scope) {
715
+ return this.http.get(this.repoPath(scope), {
716
+ headers: scopeHeaders(scope)
717
+ });
718
+ }
719
+ create(scope, body) {
720
+ return this.http.post(this.repoPath(scope), {
721
+ body,
722
+ headers: scopeHeaders(scope)
723
+ });
724
+ }
725
+ get(scope, scheduleId) {
726
+ return this.http.get(`${this.repoPath(scope)}/${scheduleId}`, {
727
+ headers: scopeHeaders(scope)
728
+ });
729
+ }
730
+ update(scope, scheduleId, body) {
731
+ return this.http.put(`${this.repoPath(scope)}/${scheduleId}`, {
732
+ body,
733
+ headers: scopeHeaders(scope)
734
+ });
735
+ }
736
+ delete(scope, scheduleId) {
737
+ return this.http.delete(`${this.repoPath(scope)}/${scheduleId}`, {
738
+ headers: scopeHeaders(scope)
739
+ });
740
+ }
741
+ /**
742
+ * List recent chat sessions spawned by a schedule's cron tick — used
743
+ * by the SchedulesTab "Runs" panel to surface real audit trail
744
+ * instead of dumping the user into the global chat list.
745
+ *
746
+ * Each entry is a full ChatSession row; `.id` is the conversation id
747
+ * the dashboard navigates to (`/chat/:conversationId`). All rows are
748
+ * unattended (the scheduler is the only writer of `schedule_id`),
749
+ * which makes the chat session viewer render the "Scheduled run"
750
+ * ribbon and hide the composer.
751
+ *
752
+ * Server returns the 50 most-recent rows; no cursor pagination in
753
+ * v1 (deferred until the long-tail audit case proves real).
754
+ */
755
+ listRuns(scope, scheduleId) {
756
+ return this.http.get(
757
+ `${this.repoPath(scope)}/${scheduleId}/runs`,
758
+ { headers: scopeHeaders(scope) }
759
+ );
760
+ }
761
+ };
762
+ var ChatClient = class {
763
+ constructor(http, streamDeps) {
764
+ this.http = http;
765
+ this.streamDeps = streamDeps;
766
+ this.sessions = new ChatSessionsScope(http);
767
+ this.messages = new ChatMessagesScope(http);
768
+ this.questions = new ChatQuestionsScope(http);
769
+ this.debug = new ChatDebugScope(http);
770
+ this.schedules = new ChatSchedulesScope(http);
771
+ }
772
+ openStream(options) {
773
+ return openChatStream(this.streamDeps, options);
774
+ }
775
+ };
776
+
777
+ // src/modules/agentEngine/TemplatesClient.ts
778
+ var BASE_PATH2 = "/api/agentengine/v1/agent/templates";
779
+ var TemplatesClient = class {
780
+ constructor(http) {
781
+ this.http = http;
782
+ }
783
+ list(query = {}) {
784
+ return this.http.get(BASE_PATH2, { query });
785
+ }
786
+ create(body) {
787
+ return this.http.post(BASE_PATH2, { body });
788
+ }
789
+ delete(templateId) {
790
+ return this.http.delete(`${BASE_PATH2}/${templateId}`);
791
+ }
792
+ };
793
+
794
+ // src/modules/agentEngine/ScheduledTasksClient.ts
795
+ var BASE_PATH3 = "/api/agentengine/v1/agent/scheduled-tasks";
796
+ var ScheduledTasksClient = class {
797
+ constructor(http) {
798
+ this.http = http;
799
+ }
800
+ list(query) {
801
+ return this.http.get(BASE_PATH3, { query });
802
+ }
803
+ create(body) {
804
+ return this.http.post(BASE_PATH3, { body });
805
+ }
806
+ delete(taskId, orgId) {
807
+ return this.http.delete(`${BASE_PATH3}/${taskId}`, {
808
+ query: { org_id: orgId }
809
+ });
810
+ }
811
+ };
812
+
813
+ // src/modules/resources/ResourcesClient.ts
814
+ var ResourcesClient = class {
815
+ constructor(http) {
816
+ this.http = http;
817
+ }
818
+ getTree(repoId, query) {
819
+ const params = new URLSearchParams();
820
+ const search = query?.search?.trim();
821
+ if (search) params.set("search", search);
822
+ if (query?.showDefault === true) params.set("showDefault", "true");
823
+ const qs = params.size > 0 ? `?${params.toString()}` : "";
824
+ return this.http.get(`/repos/${repoId}/resources/tree${qs}`);
825
+ }
826
+ /**
827
+ * BFS-walks the dependency graph starting from `seedResourceIds` (or
828
+ * `seedNameTokens`) and returns the matching nodes + edges. Use this
829
+ * to render an inbound/outbound dependency view for a selected
830
+ * resource. The server caps the result at maxNodes (default 80,
831
+ * hard cap 200) and sets `truncated` when the cap was hit.
832
+ */
833
+ getSubgraph(repoId, request) {
834
+ return this.http.post(`/repos/${repoId}/resources/subgraph`, { body: request });
835
+ }
836
+ /**
837
+ * Returns recent scan history for a repo, optionally filtered by
838
+ * credential. Backed by CloudScanHistory rows written when the
839
+ * agent's discovery orchestrator releases a lease (or when the
840
+ * backend's cleanup job sweeps an expired one). Use this to render
841
+ * "last refreshed N min ago" badges and per-scope add/remove counts
842
+ * next to the resource tree.
843
+ */
844
+ listScans(repoId, query) {
845
+ const params = new URLSearchParams();
846
+ if (query?.credentialId) params.set("credentialId", query.credentialId);
847
+ if (query?.limit != null) params.set("limit", String(query.limit));
848
+ const qs = params.size > 0 ? `?${params.toString()}` : "";
849
+ return this.http.get(`/repos/${repoId}/scans${qs}`);
850
+ }
851
+ /**
852
+ * Peeks the active scan lease (if any) for the given credential. Used
853
+ * by the dashboard to display a "scan in progress" badge without
854
+ * attempting to acquire the lease. Returns `lease: null` when no
855
+ * scan is running. Expired leases are returned with their stale
856
+ * timestamps — callers should compare `expiresAt` to `Date.now()`
857
+ * before treating them as active.
858
+ */
859
+ peekScanLease(repoId, credentialId) {
860
+ const params = new URLSearchParams();
861
+ params.set("credentialId", credentialId);
862
+ return this.http.get(`/repos/${repoId}/scans/lease?${params.toString()}`);
863
+ }
864
+ /**
865
+ * Attempts to claim a scan lease. Returns the acquired lease on
866
+ * success. The server responds 409 with a `ScanInProgressError`-
867
+ * shaped body when another scan is already in flight — callers
868
+ * should catch the HttpError, parse the body, and surface a polite
869
+ * "another scan is running" notice rather than retry automatically.
870
+ *
871
+ * NOTE: routine use is the agent's job. The dashboard typically only
872
+ * calls peekScanLease + listScans; this method is exposed for power-
873
+ * user tooling and automated test harnesses.
874
+ */
875
+ acquireScanLease(repoId, request) {
876
+ return this.http.post(`/repos/${repoId}/scans/lease`, { body: request });
877
+ }
878
+ };
879
+
880
+ // src/modules/systemEvents/types.ts
881
+ function normalizeSystemEvent(wire) {
882
+ return {
883
+ id: wire.id,
884
+ timestamp: wire.timestamp,
885
+ contextId: wire.contextId,
886
+ context: wire.context,
887
+ event: wire.event,
888
+ orgId: wire.orgId,
889
+ userId: wire.userId,
890
+ userName: wire.userName,
891
+ userEmail: wire.userEmail,
892
+ projectId: wire.projectId ?? wire.workspaceId ?? null,
893
+ jsonData: wire.jsonData ?? null,
894
+ showUnread: wire.showUnread
895
+ };
896
+ }
897
+
898
+ // src/modules/systemEvents/SystemEventsClient.ts
899
+ var SystemEventsClient = class {
900
+ constructor(http) {
901
+ this.http = http;
902
+ }
903
+ async query(body) {
904
+ const wire = await this.http.post("/systemevent", { body });
905
+ return {
906
+ events: (wire.events ?? []).map(normalizeSystemEvent),
907
+ total: wire.total ?? 0,
908
+ skip: wire.skip ?? 0,
909
+ take: wire.take ?? 0
910
+ };
911
+ }
912
+ markRead(eventIdOrIds) {
913
+ const body = Array.isArray(eventIdOrIds) ? { ids: eventIdOrIds } : { id: eventIdOrIds };
914
+ return this.http.post("/systemevent/read", { body });
915
+ }
916
+ markUnread(eventIdOrIds) {
917
+ const body = Array.isArray(eventIdOrIds) ? { ids: eventIdOrIds } : { id: eventIdOrIds };
918
+ return this.http.delete("/systemevent/read", { body });
919
+ }
920
+ };
921
+
922
+ // src/modules/billing/SubscriptionsClient.ts
923
+ var SubscriptionsClient = class {
924
+ constructor(http) {
925
+ this.http = http;
926
+ }
927
+ async listCatalog() {
928
+ const res = await this.http.get("/subscriptions", { skipAuth: true });
929
+ return res.data ?? [];
930
+ }
931
+ async getMine() {
932
+ const res = await this.http.get("/subscriptions/my");
933
+ return res.data ?? null;
934
+ }
935
+ buy(body) {
936
+ return this.http.post("/subscriptions/buy", { body });
937
+ }
938
+ cancel() {
939
+ return this.http.post("/subscriptions/cancel", {});
940
+ }
941
+ async listPayments() {
942
+ const res = await this.http.get("/subscriptions/payments");
943
+ return res.data ?? [];
944
+ }
945
+ /** Stripe-hosted billing portal URL. */
946
+ async portal() {
947
+ const res = await this.http.post("/subscriptions/portal", {});
948
+ return unwrapData(res);
949
+ }
950
+ };
951
+
952
+ // src/modules/billing/CreditsClient.ts
953
+ var CreditsClient = class {
954
+ constructor(http) {
955
+ this.http = http;
956
+ }
957
+ async getBalance() {
958
+ const res = await this.http.get("/credits/balance");
959
+ return unwrapData(res);
960
+ }
961
+ listTransactions(query = {}) {
962
+ return this.http.get("/credits/transactions", { query });
963
+ }
964
+ /** Backend currently returns 501. Surfaced for forward-compat. */
965
+ async getMonthlyStats() {
966
+ const res = await this.http.get("/credits/stats/monthly");
967
+ return res.data ?? [];
968
+ }
969
+ };
970
+
971
+ // src/modules/usage/UsageClient.ts
972
+ var LEGACY_AGENT_PATH = "/api/agentengine/v1/usage";
973
+ var SCOPED_BASE = "/usage";
974
+ var UsageClient = class {
975
+ constructor(http) {
976
+ this.http = http;
977
+ }
978
+ // ── legacy ─────────────────────────────────────────────────────────────────
979
+ // Existing Settings → Usage page calls this. Kept for back-compat until the
980
+ // new dashboard fully replaces the JSON dump.
981
+ getSummary(query) {
982
+ return this.http.get(`${LEGACY_AGENT_PATH}/summary`, { query });
983
+ }
984
+ // ── new scoped surface ─────────────────────────────────────────────────────
985
+ // Hits the backend-side RBAC wrapper; the backend re-injects the scope id
986
+ // into the agent query so we don't have to.
987
+ org(query = {}) {
988
+ return this.http.get(`${SCOPED_BASE}/org`, { query });
989
+ }
990
+ project(projectId, query = {}) {
991
+ return this.http.get(`${SCOPED_BASE}/project/${encodeURIComponent(projectId)}`, { query });
992
+ }
993
+ sandbox(sandboxId, query = {}) {
994
+ return this.http.get(`${SCOPED_BASE}/sandbox/${encodeURIComponent(sandboxId)}`, { query });
995
+ }
996
+ chat(chatId, query = {}) {
997
+ return this.http.get(`${SCOPED_BASE}/chat/${encodeURIComponent(chatId)}`, { query });
998
+ }
999
+ // Each report variant exists so the dashboard chart can request hourly /
1000
+ // daily buckets without leaking scope routing into the caller.
1001
+ orgReport(query = {}) {
1002
+ return this.http.get(`${SCOPED_BASE}/org`, { query: { ...query, kind: "report" } });
1003
+ }
1004
+ projectReport(projectId, query = {}) {
1005
+ return this.http.get(`${SCOPED_BASE}/project/${encodeURIComponent(projectId)}`, { query: { ...query, kind: "report" } });
1006
+ }
1007
+ sandboxReport(sandboxId, query = {}) {
1008
+ return this.http.get(`${SCOPED_BASE}/sandbox/${encodeURIComponent(sandboxId)}`, { query: { ...query, kind: "report" } });
1009
+ }
1010
+ chatReport(chatId, query = {}) {
1011
+ return this.http.get(`${SCOPED_BASE}/chat/${encodeURIComponent(chatId)}`, { query: { ...query, kind: "report" } });
1012
+ }
1013
+ // Per-model breakdown — drives the table under the bar chart.
1014
+ orgByModel(query = {}) {
1015
+ return this.http.get(`${SCOPED_BASE}/org`, { query: { ...query, kind: "by_model" } });
1016
+ }
1017
+ projectByModel(projectId, query = {}) {
1018
+ return this.http.get(`${SCOPED_BASE}/project/${encodeURIComponent(projectId)}`, { query: { ...query, kind: "by_model" } });
1019
+ }
1020
+ sandboxByModel(sandboxId, query = {}) {
1021
+ return this.http.get(`${SCOPED_BASE}/sandbox/${encodeURIComponent(sandboxId)}`, { query: { ...query, kind: "by_model" } });
1022
+ }
1023
+ chatByModel(chatId, query = {}) {
1024
+ return this.http.get(`${SCOPED_BASE}/chat/${encodeURIComponent(chatId)}`, { query: { ...query, kind: "by_model" } });
1025
+ }
1026
+ };
1027
+
1028
+ // src/modules/budgets/BudgetsClient.ts
1029
+ var BASE = "/budgets";
1030
+ var BudgetsClient = class {
1031
+ constructor(http) {
1032
+ this.http = http;
1033
+ }
1034
+ // ── org ──
1035
+ getOrg() {
1036
+ return this.http.get(`${BASE}/org`);
1037
+ }
1038
+ updateOrg(body) {
1039
+ return this.http.put(`${BASE}/org`, { body });
1040
+ }
1041
+ // ── project (workspace) ──
1042
+ getProject(projectId) {
1043
+ return this.http.get(`${BASE}/project/${encodeURIComponent(projectId)}`);
1044
+ }
1045
+ updateProject(projectId, body) {
1046
+ return this.http.put(`${BASE}/project/${encodeURIComponent(projectId)}`, { body });
1047
+ }
1048
+ // ── sandbox (repo) ──
1049
+ getSandbox(sandboxId) {
1050
+ return this.http.get(`${BASE}/sandbox/${encodeURIComponent(sandboxId)}`);
1051
+ }
1052
+ updateSandbox(sandboxId, body) {
1053
+ return this.http.put(`${BASE}/sandbox/${encodeURIComponent(sandboxId)}`, { body });
1054
+ }
1055
+ // ── chat session ──
1056
+ getChat(chatId) {
1057
+ return this.http.get(`${BASE}/chat/${encodeURIComponent(chatId)}`);
1058
+ }
1059
+ updateChat(chatId, body) {
1060
+ return this.http.put(`${BASE}/chat/${encodeURIComponent(chatId)}`, { body });
1061
+ }
1062
+ // Resolves chat → sandbox → project → org and returns the first non-zero
1063
+ // per-chat USD cap plus the level it came from. Powers the chat header
1064
+ // budget badge — UI labels inherited values.
1065
+ getEffectiveChat(chatId) {
1066
+ return this.http.get(`${BASE}/chat/${encodeURIComponent(chatId)}/effective`);
1067
+ }
1068
+ // ── per-template / per-scheduled USD caps ──
1069
+ updateTemplateCostLimit(templateId, body) {
1070
+ return this.http.put(`${BASE}/template/${encodeURIComponent(templateId)}/cost_limit`, { body });
1071
+ }
1072
+ updateScheduleCostLimit(scheduleId, body) {
1073
+ return this.http.put(`${BASE}/schedule/${encodeURIComponent(scheduleId)}/cost_limit`, { body });
1074
+ }
1075
+ };
1076
+
1077
+ // src/client.ts
1078
+ var AgentEngineFacade = class {
1079
+ constructor(templates, scheduledTasks) {
1080
+ this.templates = templates;
1081
+ this.scheduledTasks = scheduledTasks;
1082
+ }
1083
+ };
1084
+ var BillingFacade = class {
1085
+ constructor(subscriptions, credits) {
1086
+ this.subscriptions = subscriptions;
1087
+ this.credits = credits;
1088
+ }
1089
+ };
1090
+ var CloudAgentClient = class {
1091
+ constructor(options) {
1092
+ this.http = new BaseClient(options);
1093
+ this.auth = new AuthClient(this.http);
1094
+ this.mfa = new MfaClient(this.http);
1095
+ this.user = new UserClient(this.http);
1096
+ this.organization = new OrganizationClient(this.http);
1097
+ this.projects = new ProjectsClient(this.http);
1098
+ this.sandboxes = new SandboxesClient(this.http);
1099
+ this.credentials = new CredentialsClient(this.http);
1100
+ this.chat = new ChatClient(this.http, { baseUrl: this.http.baseUrl, getToken: options.getToken });
1101
+ this.agentEngine = new AgentEngineFacade(
1102
+ new TemplatesClient(this.http),
1103
+ new ScheduledTasksClient(this.http)
1104
+ );
1105
+ this.resources = new ResourcesClient(this.http);
1106
+ this.systemEvents = new SystemEventsClient(this.http);
1107
+ this.billing = new BillingFacade(new SubscriptionsClient(this.http), new CreditsClient(this.http));
1108
+ this.usage = new UsageClient(this.http);
1109
+ this.budgets = new BudgetsClient(this.http);
1110
+ }
1111
+ };
1112
+
1113
+ // src/common/types.ts
1114
+ var ProviderType = /* @__PURE__ */ ((ProviderType2) => {
1115
+ ProviderType2["LOCAL"] = "LOCAL";
1116
+ ProviderType2["GOOGLE"] = "GOOGLE";
1117
+ ProviderType2["MICROSOFT"] = "MICROSOFT";
1118
+ ProviderType2["GITHUB"] = "GITHUB";
1119
+ ProviderType2["WEB3_STELLAR"] = "WEB3_STELLAR";
1120
+ return ProviderType2;
1121
+ })(ProviderType || {});
1122
+ var AccessType = /* @__PURE__ */ ((AccessType2) => {
1123
+ AccessType2["READ"] = "READ";
1124
+ AccessType2["WRITE"] = "WRITE";
1125
+ AccessType2["ADMIN"] = "ADMIN";
1126
+ return AccessType2;
1127
+ })(AccessType || {});
1128
+ var OrgRoles = /* @__PURE__ */ ((OrgRoles2) => {
1129
+ OrgRoles2[OrgRoles2["USER"] = 0] = "USER";
1130
+ OrgRoles2[OrgRoles2["BILLING"] = 1] = "BILLING";
1131
+ OrgRoles2[OrgRoles2["WORKSPACES"] = 2] = "WORKSPACES";
1132
+ OrgRoles2[OrgRoles2["ADMINISTRATORS"] = 254] = "ADMINISTRATORS";
1133
+ OrgRoles2[OrgRoles2["OWNER"] = 255] = "OWNER";
1134
+ return OrgRoles2;
1135
+ })(OrgRoles || {});
1136
+
1137
+ // src/modules/mfa/types.ts
1138
+ var MFAType = /* @__PURE__ */ ((MFAType2) => {
1139
+ MFAType2["GOOGLE_AUTH"] = "GOOGLE_AUTH";
1140
+ MFAType2["MAGIC_LINK"] = "MAGIC_LINK";
1141
+ MFAType2["PASSKEY"] = "PASSKEY";
1142
+ return MFAType2;
1143
+ })(MFAType || {});
1144
+
1145
+ // src/modules/credentials/providers.ts
1146
+ var CAP_STORAGE = 1;
1147
+ var CAP_AI = 2;
1148
+ var CAP_COMPUTE = 4;
1149
+ var CAP_VCS = 8;
1150
+ var CAP_GENERAL = 255;
1151
+ var PROVIDER_CATALOG = {
1152
+ AWS: {
1153
+ id: "AWS",
1154
+ displayName: "Amazon Web Services",
1155
+ capabilities: CAP_STORAGE,
1156
+ fields: [
1157
+ {
1158
+ path: "metadata.accessKeyId",
1159
+ label: "Access Key ID",
1160
+ type: "text",
1161
+ required: true,
1162
+ placeholder: "AKIAxxxxxxxxxxxxxxxx",
1163
+ help: "The AKIA-prefixed identifier from IAM. Stored in plain text \u2014 only the secret access key is encrypted."
1164
+ },
1165
+ {
1166
+ path: "secret",
1167
+ label: "Secret Access Key",
1168
+ type: "password",
1169
+ required: true,
1170
+ secret: true,
1171
+ help: "The 40-character secret. Encrypted at rest."
1172
+ },
1173
+ {
1174
+ path: "metadata.region",
1175
+ label: "Region",
1176
+ type: "text",
1177
+ required: false,
1178
+ placeholder: "us-east-1",
1179
+ help: "Default region for boto3 clients. Optional \u2014 leave blank to use the SDK default."
1180
+ },
1181
+ {
1182
+ path: "metadata.endpoint",
1183
+ label: "Endpoint (optional)",
1184
+ type: "text",
1185
+ required: false,
1186
+ placeholder: "https://s3.amazonaws.com",
1187
+ help: "Override for non-AWS S3-compatible endpoints (MinIO, Wasabi, etc.). Most users leave blank."
1188
+ }
1189
+ ]
1190
+ },
1191
+ BEDROCK: {
1192
+ id: "BEDROCK",
1193
+ displayName: "AWS Bedrock",
1194
+ capabilities: CAP_AI,
1195
+ fields: [
1196
+ {
1197
+ path: "metadata.accessKeyId",
1198
+ label: "Access Key ID",
1199
+ type: "text",
1200
+ required: true,
1201
+ placeholder: "AKIAxxxxxxxxxxxxxxxx",
1202
+ help: "IAM access key allowed to invoke Bedrock. Stored in plain text \u2014 only the secret access key is encrypted."
1203
+ },
1204
+ {
1205
+ path: "secret",
1206
+ label: "Secret Access Key",
1207
+ type: "password",
1208
+ required: true,
1209
+ secret: true,
1210
+ help: "The IAM secret. Encrypted at rest."
1211
+ },
1212
+ {
1213
+ path: "metadata.region",
1214
+ label: "Region",
1215
+ type: "text",
1216
+ required: false,
1217
+ placeholder: "us-east-1",
1218
+ help: "Default region for Bedrock Runtime. Optional \u2014 leave blank to use the SDK default."
1219
+ },
1220
+ {
1221
+ path: "metadata.endpoint",
1222
+ label: "Endpoint (optional)",
1223
+ type: "text",
1224
+ required: false,
1225
+ placeholder: "https://bedrock-runtime.us-east-1.amazonaws.com",
1226
+ help: "Override for VPC endpoints or testing. Most users leave blank."
1227
+ }
1228
+ ]
1229
+ },
1230
+ GCP: {
1231
+ id: "GCP",
1232
+ displayName: "Google Cloud",
1233
+ capabilities: CAP_STORAGE,
1234
+ fields: [
1235
+ {
1236
+ path: "secret",
1237
+ label: "Service Account JSON",
1238
+ type: "json",
1239
+ required: true,
1240
+ secret: true,
1241
+ placeholder: '{"type":"service_account","project_id":"\u2026","client_email":"\u2026","private_key":"\u2026"}',
1242
+ help: "Paste the service-account JSON, or drop the file you downloaded from the IAM console. The full document is encrypted; project_id and client_email are extracted automatically for display."
1243
+ }
1244
+ ]
1245
+ },
1246
+ AZURE: {
1247
+ id: "AZURE",
1248
+ displayName: "Microsoft Azure",
1249
+ capabilities: CAP_STORAGE,
1250
+ fields: [
1251
+ {
1252
+ path: "metadata.clientId",
1253
+ label: "Client ID (Application ID)",
1254
+ type: "text",
1255
+ required: true,
1256
+ placeholder: "00000000-0000-0000-0000-000000000000"
1257
+ },
1258
+ {
1259
+ path: "secret",
1260
+ label: "Client Secret",
1261
+ type: "password",
1262
+ required: true,
1263
+ secret: true,
1264
+ help: "The secret value generated when you registered the app. Encrypted at rest."
1265
+ },
1266
+ {
1267
+ path: "metadata.tenantId",
1268
+ label: "Tenant ID",
1269
+ type: "text",
1270
+ required: true,
1271
+ placeholder: "00000000-0000-0000-0000-000000000000"
1272
+ },
1273
+ {
1274
+ path: "metadata.subscriptionId",
1275
+ label: "Subscription ID",
1276
+ type: "text",
1277
+ required: false,
1278
+ placeholder: "00000000-0000-0000-0000-000000000000"
1279
+ }
1280
+ ]
1281
+ },
1282
+ SSH: {
1283
+ id: "SSH",
1284
+ displayName: "SSH",
1285
+ capabilities: CAP_COMPUTE,
1286
+ fields: [
1287
+ {
1288
+ path: "metadata.user",
1289
+ label: "User",
1290
+ type: "text",
1291
+ required: true,
1292
+ placeholder: "ubuntu",
1293
+ help: "Linux user the agent will SSH as."
1294
+ },
1295
+ {
1296
+ path: "metadata.host",
1297
+ label: "Host",
1298
+ type: "text",
1299
+ required: true,
1300
+ placeholder: "vps.example.com or * for wildcard",
1301
+ help: "Hostname or IP. Use '*' for a key that's valid across multiple hosts; the agent will require the prompt to name the target host."
1302
+ },
1303
+ {
1304
+ path: "metadata.port",
1305
+ label: "Port",
1306
+ type: "number",
1307
+ required: false,
1308
+ default: 22,
1309
+ placeholder: "22"
1310
+ },
1311
+ {
1312
+ path: "secret",
1313
+ label: "PEM Private Key",
1314
+ type: "pem",
1315
+ required: true,
1316
+ secret: true,
1317
+ placeholder: "-----BEGIN OPENSSH PRIVATE KEY-----\n\u2026\n-----END OPENSSH PRIVATE KEY-----",
1318
+ help: "Paste the PEM, or drop the private key file (e.g. id_ed25519). Passphrase-encrypted keys are rejected \u2014 the agent runs unattended."
1319
+ }
1320
+ ]
1321
+ },
1322
+ GITHUB: {
1323
+ id: "GITHUB",
1324
+ displayName: "GitHub",
1325
+ capabilities: CAP_VCS,
1326
+ fields: [
1327
+ {
1328
+ path: "secret",
1329
+ label: "Personal Access Token (PAT)",
1330
+ type: "password",
1331
+ required: true,
1332
+ secret: true,
1333
+ placeholder: "ghp_xxxxxxxxxxxxxxxxxxxxxxxx",
1334
+ help: "A classic or fine-grained PAT. Encrypted at rest. The first 7 characters are kept in metadata for display."
1335
+ },
1336
+ {
1337
+ path: "metadata.org",
1338
+ label: "Org (optional)",
1339
+ type: "text",
1340
+ required: false,
1341
+ placeholder: "my-github-org",
1342
+ help: "Restrict this credential to a single org if you scoped the PAT that way. Optional \u2014 leave blank for personal-account access."
1343
+ },
1344
+ {
1345
+ path: "metadata.repoSlugs",
1346
+ label: "Allowed Repos (optional)",
1347
+ type: "stringList",
1348
+ required: false,
1349
+ placeholder: "owner/repo",
1350
+ help: "Repos the agent is allowed to operate on with this credential, as 'owner/name'. Drives the awareness block injected into the planner. Leave empty to grant the full PAT scope (the agent will still only act on repos the user names in prompts)."
1351
+ },
1352
+ {
1353
+ path: "metadata.protectedBranches",
1354
+ label: "Protected Branches (optional)",
1355
+ type: "stringList",
1356
+ required: false,
1357
+ placeholder: "main",
1358
+ help: "Branches that require explicit user approval for write operations (push, merge, delete). 'main' / 'master' / 'release/*' are good defaults."
1359
+ }
1360
+ ]
1361
+ },
1362
+ GITLAB: {
1363
+ id: "GITLAB",
1364
+ displayName: "GitLab",
1365
+ capabilities: CAP_VCS,
1366
+ fields: [
1367
+ {
1368
+ path: "secret",
1369
+ label: "Access Token",
1370
+ type: "password",
1371
+ required: true,
1372
+ secret: true,
1373
+ placeholder: "glpat-xxxxxxxxxxxxxxxxxxxx",
1374
+ help: "A personal, project, or group access token. Encrypted at rest. The first 9 characters are kept in metadata for display."
1375
+ },
1376
+ {
1377
+ path: "metadata.endpoint",
1378
+ label: "Endpoint (optional)",
1379
+ type: "text",
1380
+ required: false,
1381
+ placeholder: "https://gitlab.example.com/api/v4",
1382
+ help: "Override for self-hosted GitLab. Leave blank to use https://gitlab.com/api/v4."
1383
+ },
1384
+ {
1385
+ path: "metadata.group",
1386
+ label: "Top-level Group (optional)",
1387
+ type: "text",
1388
+ required: false,
1389
+ placeholder: "my-gitlab-group",
1390
+ help: "Restrict this credential to a single top-level group if you scoped the token that way. Optional \u2014 leave blank for personal-account or wider scope."
1391
+ },
1392
+ {
1393
+ path: "metadata.repoSlugs",
1394
+ label: "Allowed Projects (optional)",
1395
+ type: "stringList",
1396
+ required: false,
1397
+ placeholder: "group/subgroup/project",
1398
+ help: "Project paths the agent is allowed to operate on with this credential. GitLab project paths can be arbitrarily deep (group/subgroup/.../project). Leave empty to grant the full token scope."
1399
+ },
1400
+ {
1401
+ path: "metadata.protectedBranches",
1402
+ label: "Protected Branches (optional)",
1403
+ type: "stringList",
1404
+ required: false,
1405
+ placeholder: "main",
1406
+ help: "Branches that require explicit user approval for write operations. 'main' / 'master' / 'release/*' are good defaults."
1407
+ }
1408
+ ]
1409
+ },
1410
+ AZURE_DEVOPS: {
1411
+ id: "AZURE_DEVOPS",
1412
+ displayName: "Azure DevOps",
1413
+ capabilities: CAP_VCS,
1414
+ fields: [
1415
+ {
1416
+ path: "secret",
1417
+ label: "Personal Access Token (PAT)",
1418
+ type: "password",
1419
+ required: true,
1420
+ secret: true,
1421
+ placeholder: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
1422
+ help: "An ADO PAT with the scopes you want the agent to use (Code, Build, Release, Work Items \u2026). Encrypted at rest. The first 7 characters are kept in metadata for display."
1423
+ },
1424
+ {
1425
+ path: "metadata.organization",
1426
+ label: "Organization",
1427
+ type: "text",
1428
+ required: true,
1429
+ placeholder: "my-ado-org",
1430
+ help: "The ADO organization name (the segment after dev.azure.com/, or the prefix in your-org.visualstudio.com)."
1431
+ },
1432
+ {
1433
+ path: "metadata.project",
1434
+ label: "Project (optional)",
1435
+ type: "text",
1436
+ required: false,
1437
+ placeholder: "Engineering",
1438
+ help: "Restrict this credential to a single ADO project. Optional \u2014 leave blank if the PAT spans multiple projects in the org."
1439
+ },
1440
+ {
1441
+ path: "metadata.endpoint",
1442
+ label: "Endpoint (optional)",
1443
+ type: "text",
1444
+ required: false,
1445
+ placeholder: "https://dev.azure.com",
1446
+ help: "Override for ADO Server (on-prem) deployments. Leave blank to use https://dev.azure.com."
1447
+ },
1448
+ {
1449
+ path: "metadata.repoSlugs",
1450
+ label: "Allowed Repos (optional)",
1451
+ type: "stringList",
1452
+ required: false,
1453
+ placeholder: "project/repo",
1454
+ help: "Repos the agent is allowed to operate on. Use 'project/repo' when 'Project' is blank, or just 'repo' when scoped to one project. Leave empty to grant the full PAT scope."
1455
+ },
1456
+ {
1457
+ path: "metadata.protectedBranches",
1458
+ label: "Protected Branches (optional)",
1459
+ type: "stringList",
1460
+ required: false,
1461
+ placeholder: "main",
1462
+ help: "Branches that require explicit user approval for write operations and pipeline triggers."
1463
+ }
1464
+ ]
1465
+ },
1466
+ OPENAI: {
1467
+ id: "OPENAI",
1468
+ displayName: "OpenAI",
1469
+ capabilities: CAP_AI,
1470
+ fields: [
1471
+ {
1472
+ path: "secret",
1473
+ label: "API Key",
1474
+ type: "password",
1475
+ required: true,
1476
+ secret: true,
1477
+ placeholder: "sk-\u2026",
1478
+ help: "Your OpenAI API key. Encrypted at rest. The first ~7 characters are kept in metadata for display."
1479
+ },
1480
+ {
1481
+ path: "metadata.endpoint",
1482
+ label: "Endpoint (optional)",
1483
+ type: "text",
1484
+ required: false,
1485
+ placeholder: "https://api.openai.com/v1",
1486
+ help: "Override for OpenAI-compatible endpoints (Azure OpenAI, self-hosted gateways). Most users leave blank."
1487
+ }
1488
+ ]
1489
+ },
1490
+ ANTHROPIC: {
1491
+ id: "ANTHROPIC",
1492
+ displayName: "Anthropic",
1493
+ capabilities: CAP_AI,
1494
+ fields: [
1495
+ {
1496
+ path: "secret",
1497
+ label: "API Key",
1498
+ type: "password",
1499
+ required: true,
1500
+ secret: true,
1501
+ placeholder: "sk-ant-\u2026",
1502
+ help: "Your Anthropic API key. Encrypted at rest. The first ~10 characters are kept in metadata for display."
1503
+ },
1504
+ {
1505
+ path: "metadata.endpoint",
1506
+ label: "Endpoint (optional)",
1507
+ type: "text",
1508
+ required: false,
1509
+ placeholder: "https://api.anthropic.com",
1510
+ help: "Override for Anthropic-compatible endpoints. Most users leave blank."
1511
+ }
1512
+ ]
1513
+ },
1514
+ GEMINI: {
1515
+ id: "GEMINI",
1516
+ displayName: "Google Gemini",
1517
+ capabilities: CAP_AI,
1518
+ fields: [
1519
+ {
1520
+ path: "secret",
1521
+ label: "API Key",
1522
+ type: "password",
1523
+ required: true,
1524
+ secret: true,
1525
+ placeholder: "AIza\u2026",
1526
+ help: "Your Gemini API key from Google AI Studio. Encrypted at rest. The first ~7 characters are kept in metadata for display."
1527
+ },
1528
+ {
1529
+ path: "metadata.endpoint",
1530
+ label: "Endpoint (optional)",
1531
+ type: "text",
1532
+ required: false,
1533
+ placeholder: "https://generativelanguage.googleapis.com",
1534
+ help: "Override for proxies or regional endpoints. Most users leave blank to use the default Gemini API base URL."
1535
+ }
1536
+ ]
1537
+ }
1538
+ };
1539
+ var PROVIDER_ORDER = ["AWS", "BEDROCK", "GCP", "AZURE", "SSH", "GITHUB", "GITLAB", "AZURE_DEVOPS", "OPENAI", "ANTHROPIC", "GEMINI"];
1540
+ function listProviders() {
1541
+ return PROVIDER_ORDER.flatMap((id) => PROVIDER_CATALOG[id] ? [PROVIDER_CATALOG[id]] : []);
1542
+ }
1543
+ function getProvider(id) {
1544
+ return PROVIDER_CATALOG[id];
1545
+ }
1546
+ var SECRET_PATH = "secret";
1547
+ var METADATA_PREFIX = "metadata.";
1548
+ function isSecretPath(path) {
1549
+ return path === SECRET_PATH;
1550
+ }
1551
+ function isMetadataPath(path) {
1552
+ return path.startsWith(METADATA_PREFIX);
1553
+ }
1554
+ function metadataKey(path) {
1555
+ return isMetadataPath(path) ? path.slice(METADATA_PREFIX.length) : void 0;
1556
+ }
1557
+ function getValueAt(secret, metadata, path) {
1558
+ if (path === SECRET_PATH) return secret;
1559
+ const k = metadataKey(path);
1560
+ return k ? metadata[k] : void 0;
1561
+ }
1562
+ function setValueAt(secret, metadata, path, value) {
1563
+ if (path === SECRET_PATH) return { secret: typeof value === "string" ? value : "", metadata };
1564
+ const k = metadataKey(path);
1565
+ if (!k) return { secret, metadata };
1566
+ const nextMeta = { ...metadata };
1567
+ if (value === void 0 || value === null || value === "") delete nextMeta[k];
1568
+ else nextMeta[k] = value;
1569
+ return { secret, metadata: nextMeta };
1570
+ }
1571
+ function validate(providerId, label, secret, metadata) {
1572
+ const spec = getProvider(providerId);
1573
+ if (!spec) {
1574
+ return `Unknown provider "${providerId}". Known providers: ${Object.keys(PROVIDER_CATALOG).join(", ")}`;
1575
+ }
1576
+ if (!label || label.trim() === "") {
1577
+ return "Label is required";
1578
+ }
1579
+ for (const f of spec.fields) {
1580
+ const present = isFieldPresent(secret, metadata, f.path);
1581
+ if (f.required && !present) {
1582
+ return `${spec.id}.${f.label} is required`;
1583
+ }
1584
+ if (!present) continue;
1585
+ const value = getValueAt(secret, metadata, f.path);
1586
+ const typeErr = typeCheck(f, value);
1587
+ if (typeErr) return typeErr;
1588
+ }
1589
+ const allowed = allowedMetadataKeys(spec);
1590
+ for (const k of Object.keys(metadata)) {
1591
+ if (k.startsWith("_")) {
1592
+ return `Metadata key "${k}" is reserved (the leading underscore is catalog-managed); remove it from the payload`;
1593
+ }
1594
+ if (!allowed.has(k)) {
1595
+ return `Metadata key "${k}" is not part of the ${spec.id} spec. Allowed keys: ${[...allowed].join(", ")}`;
1596
+ }
1597
+ }
1598
+ return null;
1599
+ }
1600
+ function isFieldPresent(secret, metadata, path) {
1601
+ const v = getValueAt(secret, metadata, path);
1602
+ if (v === void 0 || v === null) return false;
1603
+ if (typeof v === "string") return v.trim() !== "";
1604
+ if (Array.isArray(v)) return v.length > 0;
1605
+ return true;
1606
+ }
1607
+ function typeCheck(f, value) {
1608
+ switch (f.type) {
1609
+ case "text":
1610
+ case "password":
1611
+ case "textarea":
1612
+ return typeof value === "string" ? null : `${f.label} must be a string`;
1613
+ case "number":
1614
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0) return null;
1615
+ if (typeof value === "string" && /^\d+$/.test(value)) return null;
1616
+ return `${f.label} must be a non-negative integer`;
1617
+ case "json":
1618
+ if (typeof value !== "string") return `${f.label} must be a JSON string`;
1619
+ try {
1620
+ JSON.parse(value);
1621
+ return null;
1622
+ } catch {
1623
+ return `${f.label} is not valid JSON`;
1624
+ }
1625
+ case "pem":
1626
+ if (typeof value !== "string") return `${f.label} must be a PEM string`;
1627
+ return validatePEM(value, f.label);
1628
+ case "stringList": {
1629
+ if (!Array.isArray(value)) return `${f.label} must be a list of strings`;
1630
+ for (let i = 0; i < value.length; i++) {
1631
+ const item = value[i];
1632
+ if (typeof item !== "string") return `${f.label}[${i}] must be a string`;
1633
+ if (item.trim() === "") return `${f.label}[${i}] is empty`;
1634
+ }
1635
+ return null;
1636
+ }
1637
+ default:
1638
+ return `${f.label} has unsupported field type ${f.type}`;
1639
+ }
1640
+ }
1641
+ var PEM_BEGIN_RE = /-----BEGIN [^-]+-----/;
1642
+ var PEM_END_RE = /-----END [^-]+-----/;
1643
+ var PEM_PROC_TYPE_ENCRYPTED = /^Proc-Type:\s*\d+,\s*ENCRYPTED/m;
1644
+ var PEM_DEK_INFO = /^DEK-Info:/m;
1645
+ function validatePEM(s, label) {
1646
+ const t = s.trim();
1647
+ if (!t) return `${label}: empty PEM`;
1648
+ if (!PEM_BEGIN_RE.test(t) || !PEM_END_RE.test(t)) {
1649
+ return `${label}: no valid PEM block found (expected '-----BEGIN ... PRIVATE KEY-----')`;
1650
+ }
1651
+ if (PEM_PROC_TYPE_ENCRYPTED.test(t) || PEM_DEK_INFO.test(t)) {
1652
+ return `${label}: passphrase-encrypted PEM is not supported (the agent runs unattended)`;
1653
+ }
1654
+ return null;
1655
+ }
1656
+ function allowedMetadataKeys(spec) {
1657
+ const out = /* @__PURE__ */ new Set();
1658
+ for (const f of spec.fields) {
1659
+ const k = metadataKey(f.path);
1660
+ if (k) out.add(k);
1661
+ }
1662
+ switch (spec.id) {
1663
+ case "GCP":
1664
+ out.add("clientEmail");
1665
+ out.add("projectId");
1666
+ break;
1667
+ case "GITHUB":
1668
+ case "AZURE_DEVOPS":
1669
+ case "OPENAI":
1670
+ case "ANTHROPIC":
1671
+ case "GEMINI":
1672
+ out.add("tokenPrefix");
1673
+ break;
1674
+ case "GITLAB":
1675
+ out.add("tokenPrefix");
1676
+ out.add("host");
1677
+ break;
1678
+ }
1679
+ return out;
1680
+ }
1681
+ function computeDisplayHint(providerId, metadata) {
1682
+ const cached = metadata["_displayHint"];
1683
+ if (typeof cached === "string" && cached) return cached;
1684
+ switch (providerId) {
1685
+ case "AWS":
1686
+ case "BEDROCK": {
1687
+ const key = str(metadata["accessKeyId"]);
1688
+ const region = str(metadata["region"]);
1689
+ return shortPrefix(key, 8) + (region ? ` / ${region}` : "");
1690
+ }
1691
+ case "GCP": {
1692
+ const email = str(metadata["clientEmail"]);
1693
+ const proj = str(metadata["projectId"]);
1694
+ if (!email && !proj) return "";
1695
+ if (!email) return proj;
1696
+ if (!proj) return email;
1697
+ return `${email} / ${proj}`;
1698
+ }
1699
+ case "AZURE": {
1700
+ const client = str(metadata["clientId"]);
1701
+ const sub = str(metadata["subscriptionId"]);
1702
+ const head = shortPrefix(client, 8);
1703
+ return head + (sub ? ` / sub:${shortPrefix(sub, 8)}` : "");
1704
+ }
1705
+ case "SSH": {
1706
+ const user = str(metadata["user"]);
1707
+ const host = str(metadata["host"]);
1708
+ const port = metadata["port"];
1709
+ let portStr = "";
1710
+ if (typeof port === "number" && port > 0 && port !== 22) portStr = `:${port}`;
1711
+ else if (typeof port === "string" && /^\d+$/.test(port) && port !== "22") portStr = `:${port}`;
1712
+ if (!user && !host) return "";
1713
+ return `${user}@${host}${portStr}`.replace(/^@/, "");
1714
+ }
1715
+ case "GITHUB": {
1716
+ const prefix = str(metadata["tokenPrefix"]);
1717
+ const org = str(metadata["org"]);
1718
+ const head = prefix ? `${prefix}\u2026` : "";
1719
+ return [head, org].filter(Boolean).join(" / ");
1720
+ }
1721
+ case "GITLAB": {
1722
+ const prefix = str(metadata["tokenPrefix"]);
1723
+ const group = str(metadata["group"]);
1724
+ let host = str(metadata["host"]);
1725
+ if (!host) {
1726
+ const endpoint = str(metadata["endpoint"]);
1727
+ host = endpoint ? hostFromURL(endpoint) : "gitlab.com";
1728
+ }
1729
+ let out = prefix ? `${prefix}\u2026` : "";
1730
+ if (group) out = out ? `${out} / ${group}` : group;
1731
+ if (host && host !== "gitlab.com") out = out ? `${out} @ ${host}` : host;
1732
+ return out;
1733
+ }
1734
+ case "AZURE_DEVOPS": {
1735
+ const prefix = str(metadata["tokenPrefix"]);
1736
+ const org = str(metadata["organization"]);
1737
+ const project = str(metadata["project"]);
1738
+ let out = prefix ? `${prefix}\u2026` : "";
1739
+ if (org) {
1740
+ const orgSeg = project ? `${org}/${project}` : org;
1741
+ out = out ? `${out} / ${orgSeg}` : orgSeg;
1742
+ }
1743
+ return out;
1744
+ }
1745
+ case "OPENAI":
1746
+ case "ANTHROPIC":
1747
+ case "GEMINI": {
1748
+ const prefix = str(metadata["tokenPrefix"]);
1749
+ return prefix ? `${prefix}\u2026` : "";
1750
+ }
1751
+ default:
1752
+ return "";
1753
+ }
1754
+ }
1755
+ function str(v) {
1756
+ return typeof v === "string" ? v : "";
1757
+ }
1758
+ function shortPrefix(s, n) {
1759
+ if (!s) return "";
1760
+ if (s.length <= n) return s;
1761
+ return s.slice(0, n) + "\u2026";
1762
+ }
1763
+ function hostFromURL(raw) {
1764
+ let s = raw.trim();
1765
+ const schemeIdx = s.indexOf("://");
1766
+ if (schemeIdx >= 0) s = s.slice(schemeIdx + 3);
1767
+ const cut = s.search(/[/?#]/);
1768
+ if (cut >= 0) s = s.slice(0, cut);
1769
+ return s;
1770
+ }
1771
+
1772
+ // src/modules/budgets/types.ts
1773
+ var usdMicros = {
1774
+ toDollars: (micros) => micros / 1e6,
1775
+ fromDollars: (dollars) => Math.round(dollars * 1e6)
1776
+ };
1777
+
1778
+ exports.AccessType = AccessType;
1779
+ exports.BaseClient = BaseClient;
1780
+ exports.BudgetsClient = BudgetsClient;
1781
+ exports.CAP_AI = CAP_AI;
1782
+ exports.CAP_COMPUTE = CAP_COMPUTE;
1783
+ exports.CAP_GENERAL = CAP_GENERAL;
1784
+ exports.CAP_STORAGE = CAP_STORAGE;
1785
+ exports.CAP_VCS = CAP_VCS;
1786
+ exports.CloudAgentClient = CloudAgentClient;
1787
+ exports.HttpError = HttpError;
1788
+ exports.MFAType = MFAType;
1789
+ exports.NotImplementedError = NotImplementedError;
1790
+ exports.OrgRoles = OrgRoles;
1791
+ exports.PROVIDER_CATALOG = PROVIDER_CATALOG;
1792
+ exports.ProviderType = ProviderType;
1793
+ exports.ResourcesClient = ResourcesClient;
1794
+ exports.computeDisplayHint = computeDisplayHint;
1795
+ exports.getProvider = getProvider;
1796
+ exports.getValueAt = getValueAt;
1797
+ exports.isMetadataPath = isMetadataPath;
1798
+ exports.isSecretPath = isSecretPath;
1799
+ exports.listProviders = listProviders;
1800
+ exports.metadataKey = metadataKey;
1801
+ exports.setValueAt = setValueAt;
1802
+ exports.usdMicros = usdMicros;
1803
+ exports.validate = validate;
1804
+ //# sourceMappingURL=index.cjs.map
1805
+ //# sourceMappingURL=index.cjs.map