@dharta/cli 0.6.77 → 0.6.78

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.
Files changed (293) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +215 -11
  3. package/dist/agent.js +3 -2
  4. package/dist/agent.js.map +1 -1
  5. package/dist/api-error.js +6 -6
  6. package/dist/api-error.js.map +1 -1
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.js +860 -149
  9. package/dist/cli.js.map +1 -1
  10. package/dist/command-tree.d.ts +3 -0
  11. package/dist/command-tree.js +10 -0
  12. package/dist/command-tree.js.map +1 -0
  13. package/dist/commands/agents.d.ts +54 -17
  14. package/dist/commands/agents.js +246 -136
  15. package/dist/commands/agents.js.map +1 -1
  16. package/dist/commands/analytics.js +6 -5
  17. package/dist/commands/analytics.js.map +1 -1
  18. package/dist/commands/api.js +71 -86
  19. package/dist/commands/api.js.map +1 -1
  20. package/dist/commands/appearance.js +5 -6
  21. package/dist/commands/appearance.js.map +1 -1
  22. package/dist/commands/artifact.js +7 -6
  23. package/dist/commands/artifact.js.map +1 -1
  24. package/dist/commands/audit-log.js +2 -1
  25. package/dist/commands/audit-log.js.map +1 -1
  26. package/dist/commands/auth.js +26 -6
  27. package/dist/commands/auth.js.map +1 -1
  28. package/dist/commands/caps.js +11 -15
  29. package/dist/commands/caps.js.map +1 -1
  30. package/dist/commands/deploy.js +25 -10
  31. package/dist/commands/deploy.js.map +1 -1
  32. package/dist/commands/dev.d.ts +10 -1
  33. package/dist/commands/dev.js +56 -25
  34. package/dist/commands/dev.js.map +1 -1
  35. package/dist/commands/doctor.d.ts +2 -0
  36. package/dist/commands/doctor.js +60 -0
  37. package/dist/commands/doctor.js.map +1 -1
  38. package/dist/commands/embed-keys.js +4 -13
  39. package/dist/commands/embed-keys.js.map +1 -1
  40. package/dist/commands/env.js +4 -7
  41. package/dist/commands/env.js.map +1 -1
  42. package/dist/commands/files.js +3 -4
  43. package/dist/commands/files.js.map +1 -1
  44. package/dist/commands/fleet.d.ts +129 -0
  45. package/dist/commands/fleet.js +408 -0
  46. package/dist/commands/fleet.js.map +1 -0
  47. package/dist/commands/genui.d.ts +2 -0
  48. package/dist/commands/genui.js +53 -0
  49. package/dist/commands/genui.js.map +1 -0
  50. package/dist/commands/git-credential.js +2 -3
  51. package/dist/commands/git-credential.js.map +1 -1
  52. package/dist/commands/kartas.d.ts +9 -0
  53. package/dist/commands/kartas.js +33 -11
  54. package/dist/commands/kartas.js.map +1 -1
  55. package/dist/commands/keys.d.ts +17 -18
  56. package/dist/commands/keys.js +108 -19
  57. package/dist/commands/keys.js.map +1 -1
  58. package/dist/commands/login.js +2 -1
  59. package/dist/commands/login.js.map +1 -1
  60. package/dist/commands/members.d.ts +10 -0
  61. package/dist/commands/members.js +20 -6
  62. package/dist/commands/members.js.map +1 -1
  63. package/dist/commands/model-connections.d.ts +44 -0
  64. package/dist/commands/model-connections.js +435 -0
  65. package/dist/commands/model-connections.js.map +1 -0
  66. package/dist/commands/open.d.ts +2 -2
  67. package/dist/commands/open.js +4 -3
  68. package/dist/commands/open.js.map +1 -1
  69. package/dist/commands/org.d.ts +9 -0
  70. package/dist/commands/org.js +42 -0
  71. package/dist/commands/org.js.map +1 -0
  72. package/dist/commands/organization.d.ts +16 -0
  73. package/dist/commands/organization.js +118 -0
  74. package/dist/commands/organization.js.map +1 -0
  75. package/dist/commands/parity.js +1 -3
  76. package/dist/commands/parity.js.map +1 -1
  77. package/dist/commands/repos.d.ts +4 -0
  78. package/dist/commands/repos.js +17 -0
  79. package/dist/commands/repos.js.map +1 -0
  80. package/dist/commands/rollback.js +2 -3
  81. package/dist/commands/rollback.js.map +1 -1
  82. package/dist/commands/schedules.js +26 -21
  83. package/dist/commands/schedules.js.map +1 -1
  84. package/dist/commands/scopes.js +8 -1
  85. package/dist/commands/scopes.js.map +1 -1
  86. package/dist/commands/sessions.js +9 -3
  87. package/dist/commands/sessions.js.map +1 -1
  88. package/dist/commands/setup.js +5 -4
  89. package/dist/commands/setup.js.map +1 -1
  90. package/dist/commands/update.d.ts +1 -0
  91. package/dist/commands/update.js +24 -11
  92. package/dist/commands/update.js.map +1 -1
  93. package/dist/commands/webhooks.d.ts +4 -1
  94. package/dist/commands/webhooks.js +21 -16
  95. package/dist/commands/webhooks.js.map +1 -1
  96. package/dist/completions.js +24 -13
  97. package/dist/completions.js.map +1 -1
  98. package/dist/config.d.ts +12 -0
  99. package/dist/config.js +121 -7
  100. package/dist/config.js.map +1 -1
  101. package/dist/context.d.ts +2 -0
  102. package/dist/context.js +23 -7
  103. package/dist/context.js.map +1 -1
  104. package/dist/docs/cli-reference.d.ts +2 -0
  105. package/dist/docs/cli-reference.js +191 -0
  106. package/dist/docs/cli-reference.js.map +1 -0
  107. package/dist/exit-codes.d.ts +25 -0
  108. package/dist/exit-codes.js +107 -0
  109. package/dist/exit-codes.js.map +1 -0
  110. package/dist/genui.d.ts +178 -0
  111. package/dist/genui.js +1130 -0
  112. package/dist/genui.js.map +1 -0
  113. package/dist/harnesses.js +2 -1
  114. package/dist/harnesses.js.map +1 -1
  115. package/dist/jq.d.ts +4 -0
  116. package/dist/jq.js +91 -0
  117. package/dist/jq.js.map +1 -0
  118. package/dist/login-offer.js +6 -2
  119. package/dist/login-offer.js.map +1 -1
  120. package/dist/manifest.d.ts +12 -0
  121. package/dist/manifest.js +126 -0
  122. package/dist/manifest.js.map +1 -1
  123. package/dist/model-auth.d.ts +29 -0
  124. package/dist/model-auth.js +117 -0
  125. package/dist/model-auth.js.map +1 -0
  126. package/dist/model-settings.d.ts +23 -0
  127. package/dist/model-settings.js +186 -0
  128. package/dist/model-settings.js.map +1 -0
  129. package/dist/money.js +2 -1
  130. package/dist/money.js.map +1 -1
  131. package/dist/output.d.ts +6 -0
  132. package/dist/output.js +54 -0
  133. package/dist/output.js.map +1 -1
  134. package/dist/runtime.d.ts +1 -1
  135. package/dist/runtime.js +1 -1
  136. package/dist/scaffold.js +2 -1
  137. package/dist/scaffold.js.map +1 -1
  138. package/dist/validators.js +17 -11
  139. package/dist/validators.js.map +1 -1
  140. package/dist/version-check.d.ts +1 -1
  141. package/dist/version-check.js +1 -1
  142. package/dist/version-check.js.map +1 -1
  143. package/node_modules/@dharta/contracts/LICENSE +21 -0
  144. package/node_modules/@dharta/contracts/README.md +45 -0
  145. package/node_modules/@dharta/contracts/artifact-runtime.json +750 -0
  146. package/node_modules/@dharta/contracts/dist/activity.d.ts +62 -0
  147. package/node_modules/@dharta/contracts/dist/activity.d.ts.map +1 -0
  148. package/node_modules/@dharta/contracts/dist/activity.js +178 -0
  149. package/node_modules/@dharta/contracts/dist/activity.js.map +1 -0
  150. package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.d.ts +20 -0
  151. package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.d.ts.map +1 -0
  152. package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.js +53 -0
  153. package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.js.map +1 -0
  154. package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.d.ts +37 -0
  155. package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.d.ts.map +1 -0
  156. package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.js +5107 -0
  157. package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.js.map +1 -0
  158. package/node_modules/@dharta/contracts/dist/artifact-runtime.d.ts +154 -0
  159. package/node_modules/@dharta/contracts/dist/artifact-runtime.d.ts.map +1 -0
  160. package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.d.ts +1670 -0
  161. package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.d.ts.map +1 -0
  162. package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.js +1903 -0
  163. package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.js.map +1 -0
  164. package/node_modules/@dharta/contracts/dist/artifact-runtime.js +377 -0
  165. package/node_modules/@dharta/contracts/dist/artifact-runtime.js.map +1 -0
  166. package/node_modules/@dharta/contracts/dist/index.d.ts +6 -0
  167. package/node_modules/@dharta/contracts/dist/index.d.ts.map +1 -0
  168. package/node_modules/@dharta/contracts/dist/index.js +6 -0
  169. package/node_modules/@dharta/contracts/dist/index.js.map +1 -0
  170. package/node_modules/@dharta/contracts/dist/model-auth.d.ts +31 -0
  171. package/node_modules/@dharta/contracts/dist/model-auth.d.ts.map +1 -0
  172. package/node_modules/@dharta/contracts/dist/model-auth.js +45 -0
  173. package/node_modules/@dharta/contracts/dist/model-auth.js.map +1 -0
  174. package/node_modules/@dharta/contracts/dist/tasks.d.ts +44 -0
  175. package/node_modules/@dharta/contracts/dist/tasks.d.ts.map +1 -0
  176. package/node_modules/@dharta/contracts/dist/tasks.js +127 -0
  177. package/node_modules/@dharta/contracts/dist/tasks.js.map +1 -0
  178. package/node_modules/@dharta/contracts/dist/wire-events.d.ts +71 -0
  179. package/node_modules/@dharta/contracts/dist/wire-events.d.ts.map +1 -0
  180. package/node_modules/@dharta/contracts/dist/wire-events.js +48 -0
  181. package/node_modules/@dharta/contracts/dist/wire-events.js.map +1 -0
  182. package/node_modules/@dharta/contracts/model-auth-capabilities-v1.json +168 -0
  183. package/node_modules/@dharta/contracts/model-auth-reasons-v1.json +38 -0
  184. package/node_modules/@dharta/contracts/package.json +44 -0
  185. package/node_modules/@dharta/contracts/src/activity.ts +215 -0
  186. package/node_modules/@dharta/contracts/src/artifact-runtime-formats.ts +54 -0
  187. package/node_modules/@dharta/contracts/src/artifact-runtime-validators.generated.ts +29 -0
  188. package/node_modules/@dharta/contracts/src/artifact-runtime.generated.ts +2093 -0
  189. package/node_modules/@dharta/contracts/src/artifact-runtime.ts +520 -0
  190. package/node_modules/@dharta/contracts/src/index.ts +5 -0
  191. package/node_modules/@dharta/contracts/src/model-auth.ts +82 -0
  192. package/node_modules/@dharta/contracts/src/tasks.ts +142 -0
  193. package/node_modules/@dharta/contracts/src/wire-events.ts +150 -0
  194. package/node_modules/@dharta/contracts/wire-events.json +29 -0
  195. package/node_modules/@dharta/sdk/LICENSE +1 -1
  196. package/node_modules/@dharta/sdk/README.md +22 -8
  197. package/node_modules/@dharta/sdk/dist/errors.d.ts +9 -7
  198. package/node_modules/@dharta/sdk/dist/errors.d.ts.map +1 -1
  199. package/node_modules/@dharta/sdk/dist/errors.js +36 -18
  200. package/node_modules/@dharta/sdk/dist/errors.js.map +1 -1
  201. package/node_modules/@dharta/sdk/dist/fleet.d.ts +69 -0
  202. package/node_modules/@dharta/sdk/dist/fleet.d.ts.map +1 -0
  203. package/node_modules/@dharta/sdk/dist/fleet.js +240 -0
  204. package/node_modules/@dharta/sdk/dist/fleet.js.map +1 -0
  205. package/node_modules/@dharta/sdk/dist/http.d.ts +6 -2
  206. package/node_modules/@dharta/sdk/dist/http.d.ts.map +1 -1
  207. package/node_modules/@dharta/sdk/dist/http.js +71 -12
  208. package/node_modules/@dharta/sdk/dist/http.js.map +1 -1
  209. package/node_modules/@dharta/sdk/dist/idempotency.d.ts +12 -0
  210. package/node_modules/@dharta/sdk/dist/idempotency.d.ts.map +1 -0
  211. package/node_modules/@dharta/sdk/dist/idempotency.js +72 -0
  212. package/node_modules/@dharta/sdk/dist/idempotency.js.map +1 -0
  213. package/node_modules/@dharta/sdk/dist/index.d.ts +20 -6
  214. package/node_modules/@dharta/sdk/dist/index.d.ts.map +1 -1
  215. package/node_modules/@dharta/sdk/dist/index.js +27 -4
  216. package/node_modules/@dharta/sdk/dist/index.js.map +1 -1
  217. package/node_modules/@dharta/sdk/dist/resources.d.ts +108 -13
  218. package/node_modules/@dharta/sdk/dist/resources.d.ts.map +1 -1
  219. package/node_modules/@dharta/sdk/dist/resources.js +403 -57
  220. package/node_modules/@dharta/sdk/dist/resources.js.map +1 -1
  221. package/node_modules/@dharta/sdk/dist/types.d.ts +436 -16
  222. package/node_modules/@dharta/sdk/dist/types.d.ts.map +1 -1
  223. package/node_modules/@dharta/sdk/dist/types.js +31 -2
  224. package/node_modules/@dharta/sdk/dist/types.js.map +1 -1
  225. package/node_modules/@dharta/sdk/package.json +9 -3
  226. package/node_modules/@dharta/sdk/src/errors.ts +41 -18
  227. package/node_modules/@dharta/sdk/src/fleet.ts +339 -0
  228. package/node_modules/@dharta/sdk/src/http.ts +110 -17
  229. package/node_modules/@dharta/sdk/src/idempotency.ts +101 -0
  230. package/node_modules/@dharta/sdk/src/index.ts +50 -11
  231. package/node_modules/@dharta/sdk/src/resources.ts +549 -68
  232. package/node_modules/@dharta/sdk/src/types.ts +629 -20
  233. package/package.json +16 -6
  234. package/src/agent.ts +3 -2
  235. package/src/api-error.ts +6 -5
  236. package/src/cli.ts +978 -163
  237. package/src/command-tree.ts +13 -0
  238. package/src/commands/agents.ts +365 -167
  239. package/src/commands/analytics.ts +6 -5
  240. package/src/commands/api.ts +73 -92
  241. package/src/commands/appearance.ts +5 -6
  242. package/src/commands/artifact.ts +7 -6
  243. package/src/commands/audit-log.ts +2 -1
  244. package/src/commands/auth.ts +25 -5
  245. package/src/commands/caps.ts +11 -15
  246. package/src/commands/deploy.ts +18 -4
  247. package/src/commands/dev.ts +86 -25
  248. package/src/commands/doctor.ts +69 -0
  249. package/src/commands/embed-keys.ts +4 -14
  250. package/src/commands/env.ts +4 -7
  251. package/src/commands/files.ts +3 -4
  252. package/src/commands/fleet.ts +672 -0
  253. package/src/commands/genui.ts +59 -0
  254. package/src/commands/git-credential.ts +2 -3
  255. package/src/commands/kartas.ts +45 -11
  256. package/src/commands/keys.ts +152 -35
  257. package/src/commands/login.ts +2 -1
  258. package/src/commands/members.ts +31 -6
  259. package/src/commands/model-connections.ts +547 -0
  260. package/src/commands/open.ts +5 -4
  261. package/src/commands/org.ts +57 -0
  262. package/src/commands/organization.ts +153 -0
  263. package/src/commands/parity.ts +1 -3
  264. package/src/commands/repos.ts +23 -0
  265. package/src/commands/rollback.ts +2 -3
  266. package/src/commands/schedules.ts +33 -22
  267. package/src/commands/scopes.ts +8 -1
  268. package/src/commands/sessions.ts +9 -3
  269. package/src/commands/setup.ts +5 -4
  270. package/src/commands/update.ts +28 -12
  271. package/src/commands/webhooks.ts +30 -19
  272. package/src/completions.ts +25 -13
  273. package/src/config.ts +141 -9
  274. package/src/context.ts +34 -7
  275. package/src/docs/cli-reference.ts +206 -0
  276. package/src/exit-codes.ts +122 -0
  277. package/src/genui.ts +1398 -0
  278. package/src/harnesses.ts +2 -1
  279. package/src/jq.ts +109 -0
  280. package/src/login-offer.ts +6 -2
  281. package/src/manifest.ts +134 -0
  282. package/src/model-auth.ts +173 -0
  283. package/src/model-settings.ts +222 -0
  284. package/src/money.ts +5 -1
  285. package/src/output.ts +60 -0
  286. package/src/runtime.ts +1 -1
  287. package/src/scaffold.ts +2 -1
  288. package/src/validators.ts +18 -11
  289. package/src/version-check.ts +1 -1
  290. package/dist/commands/model-keys.d.ts +0 -22
  291. package/dist/commands/model-keys.js +0 -222
  292. package/dist/commands/model-keys.js.map +0 -1
  293. package/src/commands/model-keys.ts +0 -278
@@ -1,13 +1,38 @@
1
1
  import type { HttpClient } from "./http.js";
2
+ import type { MutationOptions } from "./idempotency.js";
2
3
  import type {
4
+ AgentChangeResponse,
5
+ AgentCodeUpdateParams,
6
+ AgentCodeUpdateResponse,
7
+ AgentCreateParams,
8
+ AgentCreateResponse,
9
+ AgentDeployResponse,
10
+ AgentFromTemplateParams,
11
+ AgentFromTemplateResponse,
12
+ AgentLinkGithubResponse,
3
13
  ApiKey,
14
+ ApiKeyBulkAction,
15
+ ApiKeyBulkUpdateResponse,
4
16
  ApiKeyCreateResponse,
17
+ ApiKeyRotateResponse,
18
+ ApiKeyRotationGracePeriod,
5
19
  ApiKeyScope,
20
+ ApiKeyStateResponse,
6
21
  BillingStatus,
7
- ModelKey,
8
- ModelKeyCreateParams,
9
- ModelKeyProvider,
22
+ GithubRepository,
23
+ KartaShareCreateResponse,
24
+ MemberInvitation,
25
+ ModelConnection,
26
+ ModelConnectionCreateParams,
27
+ ModelConnectionOffers,
28
+ ModelConnectionOffersUpdateParams,
29
+ ModelConnectionProvider,
30
+ ModelConnectionRotateParams,
31
+ OrganizationModelSettings,
32
+ OrganizationModelSettingsUpdateParams,
33
+ ReleaseRetryResponse,
10
34
  UsageSummary,
35
+ WebhookDelivery,
11
36
  WebhookEndpoint,
12
37
  WebhookEndpointCreateResponse,
13
38
  WebhookEventType,
@@ -17,14 +42,180 @@ interface ListEnvelope<T> {
17
42
  data: T[];
18
43
  }
19
44
 
20
- interface ModelKeyEnvelope {
21
- model_key: ModelKey;
45
+ interface ModelConnectionEnvelope {
46
+ model_connection: ModelConnection;
22
47
  }
23
48
 
24
49
  interface WebhookEndpointEnvelope {
25
50
  webhook_endpoint: WebhookEndpoint;
26
51
  }
27
52
 
53
+ export interface ApiKeyCreateParams {
54
+ expiresAt?: string;
55
+ }
56
+
57
+ // Per-call options every mutating method accepts.
58
+ //
59
+ // `idempotencyKey` is what makes an ambiguous network failure recoverable. A
60
+ // request that times out may or may not have been performed; retrying it with
61
+ // the SAME key lets the server tell the two apart - it replays the first
62
+ // response instead of performing the mutation twice. Without one, each call
63
+ // mints a fresh key, which protects a caller from its own duplicate calls but
64
+ // cannot help a retry, because the retry looks like a new request.
65
+ //
66
+ // The agent control plane (RFC 0048 P4). Reads live on the CLI's own client
67
+ // for now; these are the mutations, and every one of them is idempotent
68
+ // server-side, so each carries an Idempotency-Key - minted per call unless the
69
+ // caller supplies one through `MutationOptions`.
70
+ export class AgentsResource {
71
+ constructor(private readonly http: HttpClient) {}
72
+
73
+ // Two forks, as the console has. With `githubRepositoryId` this LINKS that
74
+ // installed repository as the source of truth and answers a source_repo (the
75
+ // agent appears after the sync); without it, it creates an empty hosted
76
+ // agent. Use `isAgentLinkGithubResponse` to tell the two answers apart.
77
+ //
78
+ // The key matters on the GitHub fork: linking is NOT idempotent by
79
+ // construction the way the hosted fork is - a repeat re-links and re-syncs,
80
+ // bumping the sync attempt and queueing another mirror job - so a retry on a
81
+ // dropped connection would deploy twice without one. After a 503
82
+ // (`sync_not_queued`) the same key is the right one to retry with: the server
83
+ // never stores a 5xx, so the retry re-drives the sync.
84
+ async create(
85
+ params: AgentCreateParams = {},
86
+ options: MutationOptions = {},
87
+ ): Promise<AgentCreateResponse | AgentLinkGithubResponse> {
88
+ const body: Record<string, unknown> = {};
89
+ if (params.name !== undefined) body.name = params.name;
90
+ if (params.slug !== undefined) body.slug = params.slug;
91
+ if (params.githubRepositoryId !== undefined) {
92
+ body.github_repository_id = params.githubRepositoryId;
93
+ }
94
+ if (params.deployBranch !== undefined) body.deploy_branch = params.deployBranch;
95
+ return this.http.request<AgentCreateResponse | AgentLinkGithubResponse>({
96
+ method: "POST",
97
+ path: "/api/agents",
98
+ idempotencyKey: options.idempotencyKey,
99
+ body,
100
+ });
101
+ }
102
+
103
+ // Changes the DISPLAY name. The slug is the agent's identity in URLs,
104
+ // deploys and every other endpoint, and never changes here.
105
+ async rename(slug: string, name: string, options: MutationOptions = {}): Promise<AgentChangeResponse> {
106
+ return this.http.request<AgentChangeResponse>({
107
+ method: "PATCH",
108
+ path: `/api/agents/${encodeURIComponent(slug)}/name`,
109
+ idempotencyKey: options.idempotencyKey,
110
+ body: { name },
111
+ });
112
+ }
113
+
114
+ // A cost setting: it decides whether the agent's kartas write to the durable
115
+ // store, which is billed per GB and never garbage-collected. It takes effect
116
+ // on the agent's next release; stored data is untouched either way.
117
+ async setDurableWorkspace(
118
+ slug: string,
119
+ enabled: boolean,
120
+ options: MutationOptions = {},
121
+ ): Promise<AgentChangeResponse> {
122
+ return this.http.request<AgentChangeResponse>({
123
+ method: "PATCH",
124
+ path: `/api/agents/${encodeURIComponent(slug)}/durable_workspace`,
125
+ idempotencyKey: options.idempotencyKey,
126
+ body: { enabled },
127
+ });
128
+ }
129
+
130
+ // Creates the agent AND publishes release v1 from a built-in template, so it
131
+ // serves without a first push. 202 while v1 builds; the response says so.
132
+ async createFromTemplate(
133
+ params: AgentFromTemplateParams,
134
+ options: MutationOptions = {},
135
+ ): Promise<AgentFromTemplateResponse> {
136
+ const body: Record<string, unknown> = { template: params.template, name: params.name };
137
+ if (params.slug !== undefined) body.slug = params.slug;
138
+ if (params.description !== undefined) body.description = params.description;
139
+ if (params.harness !== undefined) body.harness = params.harness;
140
+ if (params.harnessVersion !== undefined) body.harness_version = params.harnessVersion;
141
+ return this.http.request<AgentFromTemplateResponse>({
142
+ method: "POST",
143
+ path: "/api/agents/from_template",
144
+ idempotencyKey: options.idempotencyKey,
145
+ body,
146
+ });
147
+ }
148
+
149
+ // Re-syncs the agent's source repository, which rebuilds it at deploy-branch
150
+ // HEAD. Answers when the sync is QUEUED, not when the release is built. A 503
151
+ // (`sync_not_queued`) is retryable under the SAME key: a 5xx is never stored,
152
+ // so the retry re-drives the sync.
153
+ async deploy(slug: string, options: MutationOptions = {}): Promise<AgentDeployResponse> {
154
+ return this.http.request<AgentDeployResponse>({
155
+ method: "POST",
156
+ path: `/api/agents/${encodeURIComponent(slug)}/deploy`,
157
+ idempotencyKey: options.idempotencyKey,
158
+ });
159
+ }
160
+
161
+ // Publishes the edited files as release vN+1. `baseVersion` is optimistic
162
+ // concurrency: a save against a stale version is a 409, never a clobber.
163
+ async updateCode(
164
+ slug: string,
165
+ params: AgentCodeUpdateParams,
166
+ options: MutationOptions = {},
167
+ ): Promise<AgentCodeUpdateResponse> {
168
+ return this.http.request<AgentCodeUpdateResponse>({
169
+ method: "PUT",
170
+ path: `/api/agents/${encodeURIComponent(slug)}/code`,
171
+ idempotencyKey: options.idempotencyKey,
172
+ body: { files: params.files, base_version: params.baseVersion },
173
+ });
174
+ }
175
+ }
176
+
177
+ // The ids `agents.create({ githubRepositoryId })` accepts: the repositories
178
+ // the Dharta GitHub App is installed on for this organization.
179
+ export class GithubRepositoriesResource {
180
+ constructor(private readonly http: HttpClient) {}
181
+
182
+ async list(): Promise<{ data: GithubRepository[] }> {
183
+ return this.http.request<{ data: GithubRepository[] }>({
184
+ method: "GET",
185
+ path: "/api/github_repositories",
186
+ });
187
+ }
188
+ }
189
+
190
+ export class ReleasesResource {
191
+ constructor(private readonly http: HttpClient) {}
192
+
193
+ // Republishes a FAILED browser-created release as release vN+1.
194
+ //
195
+ // The version is validated, not repaired. Stripping a leading `v` turned
196
+ // "vv2" into "v2" and "v0" into "0", so a typo reached the server as a
197
+ // DIFFERENT release id than the caller wrote. One optional `v`, then a
198
+ // positive integer with no leading zero - the same shape the server accepts.
199
+ async retry(
200
+ slug: string,
201
+ version: number | string,
202
+ options: MutationOptions = {},
203
+ ): Promise<ReleaseRetryResponse> {
204
+ const raw = String(version);
205
+ if (!/^v?[1-9]\d*$/.test(raw)) {
206
+ throw new Error(
207
+ `DhartaSDK: release version must be a positive integer, optionally prefixed with 'v'; got '${raw}'.`,
208
+ );
209
+ }
210
+ const normalized = raw.replace(/^v/, "");
211
+ return this.http.request<ReleaseRetryResponse>({
212
+ method: "POST",
213
+ path: `/api/agents/${encodeURIComponent(slug)}/releases/${encodeURIComponent(normalized)}/retry`,
214
+ idempotencyKey: options.idempotencyKey,
215
+ });
216
+ }
217
+ }
218
+
28
219
  export class ApiKeysResource {
29
220
  constructor(private readonly http: HttpClient) {}
30
221
 
@@ -33,70 +224,239 @@ export class ApiKeysResource {
33
224
  return res.data;
34
225
  }
35
226
 
36
- async create(name: string, scopes: ApiKeyScope[]): Promise<ApiKeyCreateResponse> {
227
+ // `expiresAt` is an ISO 8601 timestamp. Sensitive scopes
228
+ // (ApiKey::FORCED_EXPIRY_SCOPES server-side) require one within 90 days, and
229
+ // the server rejects the mint without it.
230
+ async create(
231
+ name: string,
232
+ scopes: ApiKeyScope[],
233
+ params: ApiKeyCreateParams = {},
234
+ options: MutationOptions = {},
235
+ ): Promise<ApiKeyCreateResponse> {
236
+ const body: Record<string, unknown> = { name, scopes };
237
+ if (params.expiresAt !== undefined) body.expires_at = params.expiresAt;
37
238
  return this.http.request<ApiKeyCreateResponse>({
38
239
  method: "POST",
39
240
  path: "/api_keys",
40
- headers: idempotencyHeaders(),
41
- body: { name, scopes },
241
+ idempotencyKey: options.idempotencyKey,
242
+ body,
42
243
  });
43
244
  }
44
245
 
45
- async rename(id: string, name: string): Promise<ApiKey> {
246
+ // Every single-key method takes the numeric `ApiKey.id` the API answers, and
247
+ // accepts the string form for a caller that read the id out of argv or JSON.
248
+ async rename(id: number | string, name: string, options: MutationOptions = {}): Promise<ApiKey> {
46
249
  const res = await this.http.request<{ api_key: ApiKey }>({
47
250
  method: "PATCH",
48
- path: `/api_keys/${encodeURIComponent(id)}`,
49
- headers: idempotencyHeaders(),
251
+ path: `/api_keys/${encodeURIComponent(String(id))}`,
252
+ idempotencyKey: options.idempotencyKey,
50
253
  body: { name },
51
254
  });
52
255
  return res.api_key;
53
256
  }
54
257
 
55
- async revoke(id: string): Promise<void> {
258
+ async revoke(id: number | string, options: MutationOptions = {}): Promise<void> {
259
+ await this.http.request<void>({
260
+ method: "DELETE",
261
+ path: `/api_keys/${encodeURIComponent(String(id))}`,
262
+ idempotencyKey: options.idempotencyKey,
263
+ });
264
+ }
265
+
266
+ // Mints a replacement carrying this key's scopes, mode, IP allowlist and
267
+ // limits, then retires the original: immediately with `gracePeriod: "now"`,
268
+ // otherwise at the end of the window (24 hours by default). The replacement's
269
+ // secret is returned once and is never readable again.
270
+ async rotate(
271
+ id: number | string,
272
+ params: { gracePeriod?: ApiKeyRotationGracePeriod } = {},
273
+ options: MutationOptions = {},
274
+ ): Promise<ApiKeyRotateResponse> {
275
+ const body: Record<string, unknown> = {};
276
+ if (params.gracePeriod !== undefined) body.grace_period = params.gracePeriod;
277
+ return this.http.request<ApiKeyRotateResponse>({
278
+ method: "POST",
279
+ path: `/api_keys/${encodeURIComponent(String(id))}/rotate`,
280
+ idempotencyKey: options.idempotencyKey,
281
+ body,
282
+ });
283
+ }
284
+
285
+ // `changed` is false when the key was already disabled, which is a no-op
286
+ // rather than an error.
287
+ async disable(id: number | string, options: MutationOptions = {}): Promise<ApiKeyStateResponse> {
288
+ return this.http.request<ApiKeyStateResponse>({
289
+ method: "POST",
290
+ path: `/api_keys/${encodeURIComponent(String(id))}/disable`,
291
+ idempotencyKey: options.idempotencyKey,
292
+ });
293
+ }
294
+
295
+ async enable(id: number | string, options: MutationOptions = {}): Promise<ApiKeyStateResponse> {
296
+ return this.http.request<ApiKeyStateResponse>({
297
+ method: "POST",
298
+ path: `/api_keys/${encodeURIComponent(String(id))}/enable`,
299
+ idempotencyKey: options.idempotencyKey,
300
+ });
301
+ }
302
+
303
+ // Atomic: every key changes or none does. Keys the action cannot apply to
304
+ // come back in `skipped` rather than failing the request.
305
+ async bulkUpdate(
306
+ action: ApiKeyBulkAction,
307
+ ids: number[],
308
+ options: MutationOptions = {},
309
+ ): Promise<ApiKeyBulkUpdateResponse> {
310
+ return this.http.request<ApiKeyBulkUpdateResponse>({
311
+ method: "POST",
312
+ path: "/api_keys/bulk_update",
313
+ idempotencyKey: options.idempotencyKey,
314
+ body: { action_name: action, ids },
315
+ });
316
+ }
317
+ }
318
+
319
+ // Only the share link lives here. The rest of the karta surface predates the
320
+ // SDK resource pattern and is reached through `HttpClient` directly; moving it
321
+ // is a separate change from adding an endpoint.
322
+ export class KartasResource {
323
+ constructor(private readonly http: HttpClient) {}
324
+
325
+ // Mints an origin-fenced publishable key pinned to this karta. Anyone with
326
+ // the link gets their own session against the same durable workspace and
327
+ // spends from its budget, so the secret and the link are returned once and
328
+ // are never readable again.
329
+ async createShare(kartaId: string, options: MutationOptions = {}): Promise<KartaShareCreateResponse> {
330
+ return this.http.request<KartaShareCreateResponse>({
331
+ method: "POST",
332
+ path: `/api/kartas/${encodeURIComponent(kartaId)}/share`,
333
+ idempotencyKey: options.idempotencyKey,
334
+ });
335
+ }
336
+
337
+ async revokeShare(
338
+ kartaId: string,
339
+ shareId: string | number,
340
+ options: MutationOptions = {},
341
+ ): Promise<void> {
56
342
  await this.http.request<void>({
57
343
  method: "DELETE",
58
- path: `/api_keys/${encodeURIComponent(id)}`,
59
- headers: idempotencyHeaders(),
344
+ path: `/api/kartas/${encodeURIComponent(kartaId)}/shares/${encodeURIComponent(String(shareId))}`,
345
+ idempotencyKey: options.idempotencyKey,
60
346
  });
61
347
  }
62
348
  }
63
349
 
64
- export class ModelKeysResource {
350
+ export class ModelConnectionsResource {
65
351
  constructor(private readonly http: HttpClient) {}
66
352
 
67
- async list(): Promise<ModelKey[]> {
68
- const res = await this.http.request<ListEnvelope<ModelKey>>({ path: "/model_keys" });
353
+ async list(): Promise<ModelConnection[]> {
354
+ const res = await this.http.request<ListEnvelope<ModelConnection>>({ path: "/model_connections" });
69
355
  return res.data;
70
356
  }
71
357
 
72
- async create(provider: ModelKeyProvider, plaintext: string, name?: string): Promise<ModelKey>;
73
- async create(params: ModelKeyCreateParams): Promise<ModelKey>;
74
358
  async create(
75
- providerOrParams: ModelKeyProvider | ModelKeyCreateParams,
76
- plaintext?: string,
359
+ provider: ModelConnectionProvider,
360
+ plaintext: string,
77
361
  name?: string,
78
- ): Promise<ModelKey> {
79
- const body = typeof providerOrParams === "string"
80
- ? { provider: providerOrParams, plaintext, name }
362
+ options?: MutationOptions,
363
+ ): Promise<ModelConnection>;
364
+ async create(params: ModelConnectionCreateParams, options?: MutationOptions): Promise<ModelConnection>;
365
+ async create(
366
+ providerOrParams: ModelConnectionProvider | ModelConnectionCreateParams,
367
+ plaintextOrOptions?: string | MutationOptions,
368
+ name?: string,
369
+ maybeOptions?: MutationOptions,
370
+ ): Promise<ModelConnection> {
371
+ const positional = typeof providerOrParams === "string";
372
+ const body = positional
373
+ ? { provider: providerOrParams, plaintext: plaintextOrOptions as string, name }
81
374
  : providerOrParams;
82
- const res = await this.http.request<ModelKeyEnvelope>({
375
+ const options = (positional ? maybeOptions : (plaintextOrOptions as MutationOptions)) ?? {};
376
+ const res = await this.http.request<ModelConnectionEnvelope>({
83
377
  method: "POST",
84
- path: "/model_keys",
85
- headers: idempotencyHeaders(),
378
+ path: "/model_connections",
379
+ idempotencyKey: options.idempotencyKey,
86
380
  body,
87
381
  });
88
- return res.model_key;
382
+ return res.model_connection;
383
+ }
384
+
385
+ // Replace a connector's stored credential. Address the connector by its
386
+ // numeric id or its slug.
387
+ async rotate(
388
+ idOrSlug: string | number,
389
+ params: ModelConnectionRotateParams,
390
+ options: MutationOptions = {},
391
+ ): Promise<ModelConnection> {
392
+ const res = await this.http.request<ModelConnectionEnvelope>({
393
+ method: "PATCH",
394
+ path: `/model_connections/${encodeURIComponent(String(idOrSlug))}`,
395
+ idempotencyKey: options.idempotencyKey,
396
+ body: params,
397
+ });
398
+ return res.model_connection;
399
+ }
400
+
401
+ // List the models the connector can serve, with the enabled set and the
402
+ // connector default.
403
+ async models(idOrSlug: string | number): Promise<ModelConnectionOffers> {
404
+ return this.http.request<ModelConnectionOffers>({
405
+ path: `/model_connections/${encodeURIComponent(String(idOrSlug))}/models`,
406
+ });
407
+ }
408
+
409
+ // Replace the whole enabled set and the connector default.
410
+ async configureModels(
411
+ idOrSlug: string | number,
412
+ params: ModelConnectionOffersUpdateParams,
413
+ options: MutationOptions = {},
414
+ ): Promise<ModelConnectionOffers> {
415
+ return this.http.request<ModelConnectionOffers>({
416
+ method: "PUT",
417
+ path: `/model_connections/${encodeURIComponent(String(idOrSlug))}/models`,
418
+ idempotencyKey: options.idempotencyKey,
419
+ body: params,
420
+ });
89
421
  }
90
422
 
91
- async revoke(id: string | number): Promise<void> {
423
+ async revoke(id: string | number, options: MutationOptions = {}): Promise<void> {
92
424
  await this.http.request<void>({
93
425
  method: "DELETE",
94
- path: `/model_keys/${encodeURIComponent(String(id))}`,
95
- headers: idempotencyHeaders(),
426
+ path: `/model_connections/${encodeURIComponent(String(id))}`,
427
+ idempotencyKey: options.idempotencyKey,
96
428
  });
97
429
  }
98
430
  }
99
431
 
432
+ interface OrganizationModelSettingsEnvelope {
433
+ model_settings: OrganizationModelSettings;
434
+ }
435
+
436
+ export class OrganizationModelSettingsResource {
437
+ constructor(private readonly http: HttpClient) {}
438
+
439
+ async get(): Promise<OrganizationModelSettings> {
440
+ const res = await this.http.request<OrganizationModelSettingsEnvelope>({
441
+ path: "/api/organization/model_settings",
442
+ });
443
+ return res.model_settings;
444
+ }
445
+
446
+ async update(
447
+ params: OrganizationModelSettingsUpdateParams,
448
+ options: MutationOptions = {},
449
+ ): Promise<OrganizationModelSettings> {
450
+ const res = await this.http.request<OrganizationModelSettingsEnvelope>({
451
+ method: "PATCH",
452
+ path: "/api/organization/model_settings",
453
+ idempotencyKey: options.idempotencyKey,
454
+ body: params,
455
+ });
456
+ return res.model_settings;
457
+ }
458
+ }
459
+
100
460
  export interface WebhookEndpointCreateParams {
101
461
  url: string;
102
462
  name?: string;
@@ -109,6 +469,15 @@ export interface WebhookEndpointUpdateParams {
109
469
  event_types?: WebhookEventType[];
110
470
  }
111
471
 
472
+ // Reads take `webhooks:read`. Every mutation here takes `webhooks:write`, which
473
+ // is a tenant-administration scope: the key must carry an expiry within 90 days.
474
+ // A destination is an egress trust root, not a delivery detail - any URL is
475
+ // accepted, and an endpoint created with no `event_types` receives every event
476
+ // type, which carries billing, API-key, session and turn data.
477
+ // The ids are integers on the wire (WebhookEndpoint.id is a number), so
478
+ // `create(...).webhook_endpoint.id` feeds straight into these. `string` is
479
+ // still accepted because an id read back out of a config file or an argv is a
480
+ // string, and the only thing that reaches the URL is its decimal form.
112
481
  export class WebhookEndpointsResource {
113
482
  constructor(private readonly http: HttpClient) {}
114
483
 
@@ -117,39 +486,177 @@ export class WebhookEndpointsResource {
117
486
  return res.data;
118
487
  }
119
488
 
120
- async create(params: WebhookEndpointCreateParams): Promise<WebhookEndpointCreateResponse> {
489
+ async create(
490
+ params: WebhookEndpointCreateParams,
491
+ options: MutationOptions = {},
492
+ ): Promise<WebhookEndpointCreateResponse> {
121
493
  return this.http.request<WebhookEndpointCreateResponse>({
122
494
  method: "POST",
123
495
  path: "/api/webhook_endpoints",
124
- headers: idempotencyHeaders(),
496
+ idempotencyKey: options.idempotencyKey,
125
497
  body: params,
126
498
  });
127
499
  }
128
500
 
129
- async update(id: string, params: WebhookEndpointUpdateParams): Promise<WebhookEndpoint> {
501
+ async update(
502
+ id: string | number,
503
+ params: WebhookEndpointUpdateParams,
504
+ options: MutationOptions = {},
505
+ ): Promise<WebhookEndpoint> {
130
506
  const res = await this.http.request<WebhookEndpointEnvelope>({
131
507
  method: "PATCH",
132
- path: `/api/webhook_endpoints/${encodeURIComponent(id)}`,
133
- headers: idempotencyHeaders(),
508
+ path: `/api/webhook_endpoints/${encodeURIComponent(String(id))}`,
509
+ idempotencyKey: options.idempotencyKey,
134
510
  body: params,
135
511
  });
136
512
  return res.webhook_endpoint;
137
513
  }
138
514
 
139
- async enable(id: string): Promise<WebhookEndpoint> {
515
+ async enable(id: string | number, options: MutationOptions = {}): Promise<WebhookEndpoint> {
140
516
  const res = await this.http.request<WebhookEndpointEnvelope>({
141
517
  method: "POST",
142
- path: `/api/webhook_endpoints/${encodeURIComponent(id)}/enable`,
143
- headers: idempotencyHeaders(),
518
+ path: `/api/webhook_endpoints/${encodeURIComponent(String(id))}/enable`,
519
+ idempotencyKey: options.idempotencyKey,
144
520
  });
145
521
  return res.webhook_endpoint;
146
522
  }
147
523
 
148
- async delete(id: string): Promise<void> {
524
+ // Queues one probe through the production delivery path: the same delivery
525
+ // row, the same signature, the same retries. The result is the QUEUED
526
+ // delivery, not its outcome, which lands on the delivery row later.
527
+ async test(id: string | number, options: MutationOptions = {}): Promise<WebhookDelivery> {
528
+ const res = await this.http.request<{ delivery: WebhookDelivery }>({
529
+ method: "POST",
530
+ path: `/api/webhook_endpoints/${encodeURIComponent(String(id))}/test`,
531
+ idempotencyKey: options.idempotencyKey,
532
+ });
533
+ return res.delivery;
534
+ }
535
+
536
+ async delete(id: string | number, options: MutationOptions = {}): Promise<void> {
537
+ await this.http.request<void>({
538
+ method: "DELETE",
539
+ path: `/api/webhook_endpoints/${encodeURIComponent(String(id))}`,
540
+ idempotencyKey: options.idempotencyKey,
541
+ });
542
+ }
543
+ }
544
+
545
+ export class MembersResource {
546
+ constructor(private readonly http: HttpClient) {}
547
+
548
+ // Pending invitations only. An accepted invitation is history, so the server
549
+ // answers 404 rather than silently doing nothing.
550
+ async revokeInvitation(id: string | number, options: MutationOptions = {}): Promise<void> {
149
551
  await this.http.request<void>({
150
552
  method: "DELETE",
151
- path: `/api/webhook_endpoints/${encodeURIComponent(id)}`,
152
- headers: idempotencyHeaders(),
553
+ path: `/api/members/invitations/${encodeURIComponent(String(id))}`,
554
+ idempotencyKey: options.idempotencyKey,
555
+ });
556
+ }
557
+
558
+ // Re-arms the expiry window and mails the invitee again, keeping the token
559
+ // the original email carried. The idempotency key is what stops a retry
560
+ // sending a second email.
561
+ async resendInvitation(
562
+ id: string | number,
563
+ options: MutationOptions = {},
564
+ ): Promise<MemberInvitation> {
565
+ const res = await this.http.request<{ invitation: MemberInvitation }>({
566
+ method: "POST",
567
+ path: `/api/members/invitations/${encodeURIComponent(String(id))}/resend`,
568
+ idempotencyKey: options.idempotencyKey,
569
+ });
570
+ return res.invitation;
571
+ }
572
+ }
573
+
574
+ export interface OrganizationIdentity {
575
+ slug: string;
576
+ name: string;
577
+ }
578
+
579
+ export interface OrganizationRenameResult {
580
+ organization: OrganizationIdentity;
581
+ changed: boolean;
582
+ }
583
+
584
+ export interface SupportAccessResult {
585
+ support_access: { allowed: boolean };
586
+ changed: boolean;
587
+ }
588
+
589
+ export interface TranscriptAccessResult {
590
+ transcript_access: { enabled: boolean };
591
+ changed: boolean;
592
+ }
593
+
594
+ export class OrganizationResource {
595
+ readonly modelSettings: OrganizationModelSettingsResource;
596
+
597
+ constructor(private readonly http: HttpClient) {
598
+ this.modelSettings = new OrganizationModelSettingsResource(http);
599
+ }
600
+
601
+ // Display name only. The slug is the API and audit identity and cannot be
602
+ // changed. Renaming to the current name returns `changed: false`.
603
+ async rename(name: string, options: MutationOptions = {}): Promise<OrganizationRenameResult> {
604
+ return this.http.request<OrganizationRenameResult>({
605
+ method: "PATCH",
606
+ path: "/api/organization",
607
+ idempotencyKey: options.idempotencyKey,
608
+ body: { name },
609
+ });
610
+ }
611
+
612
+ // Owner-only: needs `organization:support_access:write` on a key created by
613
+ // a current owner. Free-plan organizations cannot change the setting.
614
+ async setSupportAccess(
615
+ allowed: boolean,
616
+ options: MutationOptions = {},
617
+ ): Promise<SupportAccessResult> {
618
+ return this.http.request<SupportAccessResult>({
619
+ method: "PATCH",
620
+ path: "/api/organization/support_access",
621
+ idempotencyKey: options.idempotencyKey,
622
+ body: { allowed },
623
+ });
624
+ }
625
+
626
+ // Whether API keys may read end-user transcript content. Same owner-only
627
+ // gate; off by default.
628
+ async setTranscriptAccess(
629
+ enabled: boolean,
630
+ options: MutationOptions = {},
631
+ ): Promise<TranscriptAccessResult> {
632
+ return this.http.request<TranscriptAccessResult>({
633
+ method: "PATCH",
634
+ path: "/api/organization/support_access/transcripts",
635
+ idempotencyKey: options.idempotencyKey,
636
+ body: { enabled },
637
+ });
638
+ }
639
+ }
640
+
641
+ export interface SourceRepoDisconnectResult {
642
+ source_repo: { slug: string; deploy_branch: string };
643
+ github_repository: string | null;
644
+ changed: boolean;
645
+ }
646
+
647
+ export class SourceReposResource {
648
+ constructor(private readonly http: HttpClient) {}
649
+
650
+ // Stops pushes to the linked GitHub repository from producing releases.
651
+ // Current releases stay live. Already disconnected returns `changed: false`.
652
+ async disconnectGithub(
653
+ slug: string,
654
+ options: MutationOptions = {},
655
+ ): Promise<SourceRepoDisconnectResult> {
656
+ return this.http.request<SourceRepoDisconnectResult>({
657
+ method: "DELETE",
658
+ path: `/api/source_repos/${encodeURIComponent(slug)}/github`,
659
+ idempotencyKey: options.idempotencyKey,
153
660
  });
154
661
  }
155
662
  }
@@ -169,29 +676,3 @@ export class BillingResource {
169
676
  return this.http.request<BillingStatus>({ path: "/api/billing" });
170
677
  }
171
678
  }
172
-
173
- function idempotencyHeaders(): Record<string, string> {
174
- return { "Idempotency-Key": randomIdempotencyKey() };
175
- }
176
-
177
- function randomIdempotencyKey(): string {
178
- if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID();
179
- const bytes = new Uint8Array(16);
180
- if (globalThis.crypto?.getRandomValues) {
181
- globalThis.crypto.getRandomValues(bytes);
182
- } else {
183
- for (let i = 0; i < bytes.length; i += 1) {
184
- bytes[i] = Math.floor(Math.random() * 256);
185
- }
186
- }
187
- bytes[6] = (bytes[6] & 0x0f) | 0x40;
188
- bytes[8] = (bytes[8] & 0x3f) | 0x80;
189
- const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0"));
190
- return [
191
- hex.slice(0, 4).join(""),
192
- hex.slice(4, 6).join(""),
193
- hex.slice(6, 8).join(""),
194
- hex.slice(8, 10).join(""),
195
- hex.slice(10, 16).join(""),
196
- ].join("-");
197
- }