@cardstack/boxel-cli 0.12.0-unstable.12 → 0.12.0-unstable.14

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 (58) hide show
  1. package/bundled-test-harness/assets/{ai-module-creation-test-D9FuBmi8.js → ai-module-creation-test-BuOdKEfn.js} +1 -1
  2. package/bundled-test-harness/assets/{app-B6ALqYQD.js → app-DFQ4JqlP.js} +2 -2
  3. package/bundled-test-harness/assets/{binary-upload-dedupe-test-eI1yGxdL.js → binary-upload-dedupe-test-jbrJQUz0.js} +1 -1
  4. package/bundled-test-harness/assets/{can-invoke-test-5enpsRYF.js → can-invoke-test-DoWzbZ8k.js} +1 -1
  5. package/bundled-test-harness/assets/{card-chooser-paging-test-CdtsXIpu.js → card-chooser-paging-test-VRmsX6sY.js} +1 -1
  6. package/bundled-test-harness/assets/{card-chooser-test-CpUK3_Su.js → card-chooser-test-CgrV_vWi.js} +1 -1
  7. package/bundled-test-harness/assets/{card-def-field-def-relationships-test-Z1O-t73y.js → card-def-field-def-relationships-test-BHQoFm4_.js} +1 -1
  8. package/bundled-test-harness/assets/{card-delete-test-DI16JAzu.js → card-delete-test-DCmoRd03.js} +1 -1
  9. package/bundled-test-harness/assets/{codeblocks-test-DEaQeMcn.js → codeblocks-test-DQU6dnQH.js} +1 -1
  10. package/bundled-test-harness/assets/{compaction-test-DliwzHTd.js → compaction-test-B7JuwjQ7.js} +1 -1
  11. package/bundled-test-harness/assets/{container-BHHKErdI.js → container-CwRfjGxi.js} +1 -1
  12. package/bundled-test-harness/assets/{create-listing-modal-test-BZiD6UL9.js → create-listing-modal-test-CxkuYwnv.js} +1 -1
  13. package/bundled-test-harness/assets/{debug-message-test-D7VEbny3.js → debug-message-test-CyrcsddQ.js} +1 -1
  14. package/bundled-test-harness/assets/{fallback-banner-live-delete-test-DwXohU1S.js → fallback-banner-live-delete-test-Bxxt9EBl.js} +1 -1
  15. package/bundled-test-harness/assets/{fallback-banner-test-jEmNTOeh.js → fallback-banner-test-BzmDOq0F.js} +1 -1
  16. package/bundled-test-harness/assets/{fallback-models-test-BfgkQJ8l.js → fallback-models-test-mZvUg3zI.js} +1 -1
  17. package/bundled-test-harness/assets/{file-attachment-test-CaX44AfG.js → file-attachment-test-ybs1HRMC.js} +1 -1
  18. package/bundled-test-harness/assets/{general-test-zz1QVTIv.js → general-test-jB4EQTh8.js} +1 -1
  19. package/bundled-test-harness/assets/{main-CYiHxOxo.js → main-uvgWNg1U.js} +1 -1
  20. package/bundled-test-harness/assets/{model-cost-tier-test-DcEeQJwF.js → model-cost-tier-test-BPCl0EeO.js} +1 -1
  21. package/bundled-test-harness/assets/{operator-mode-card-chooser-test-DYSf_9jJ.js → operator-mode-card-chooser-test-D8RfBsEc.js} +1 -1
  22. package/bundled-test-harness/assets/{operator-mode-links-test-BA3gjmzY.js → operator-mode-links-test-DmpvnpHI.js} +1 -1
  23. package/bundled-test-harness/assets/{operator-mode-test-C6iSqlfM.js → operator-mode-test-BeCWE8qH.js} +1 -1
  24. package/bundled-test-harness/assets/{operator-mode-ui-test-BFGw2suP.js → operator-mode-ui-test-D0k0pr1Z.js} +1 -1
  25. package/bundled-test-harness/assets/{overlay-menu-items-test-Ch351wNL.js → overlay-menu-items-test-l4-oOOi5.js} +1 -1
  26. package/bundled-test-harness/assets/{panel-B8BaMllL.js → panel-8nNZbvzg.js} +16 -4
  27. package/bundled-test-harness/assets/{past-sessions-test-DoVlENnJ.js → past-sessions-test-DrJ_h58w.js} +1 -1
  28. package/bundled-test-harness/assets/{permission-checking-realm-test-BrBeo81O.js → permission-checking-realm-test-CL_nySH1.js} +1 -1
  29. package/bundled-test-harness/assets/{quick-look-test-InFXyTqv.js → quick-look-test-DMfAGvgd.js} +1 -1
  30. package/bundled-test-harness/assets/{realm-api-keys-test-Bgg-gi56.js → realm-api-keys-test-DVkuepc8.js} +1 -1
  31. package/bundled-test-harness/assets/{realm-config-policy-standing-test-BIEwUQYA.js → realm-config-policy-standing-test-DXS8kYVC.js} +1 -1
  32. package/bundled-test-harness/assets/{realm-config-routing-rule-edit-test-CDj8rdvZ.js → realm-config-routing-rule-edit-test-CPTifvXx.js} +1 -1
  33. package/bundled-test-harness/assets/{realm-config-settings-test-Csi-H8bJ.js → realm-config-settings-test-Civ3ZVxT.js} +1 -1
  34. package/bundled-test-harness/assets/{reasoning-test-MNcXjMV3.js → reasoning-test-DYSQdtDZ.js} +1 -1
  35. package/bundled-test-harness/assets/{room-message-test-DxMyOu8L.js → room-message-test-CSbclniG.js} +1 -1
  36. package/bundled-test-harness/assets/{run-realm-code-test-DTXPkVu_.js → run-realm-code-test-B9KJIaYx.js} +81 -0
  37. package/bundled-test-harness/assets/{scrolling-test-6-Soq39G.js → scrolling-test-nDHEhgzp.js} +2 -2
  38. package/bundled-test-harness/assets/{sending-test-DXOw4MlE.js → sending-test-CjrOIeGV.js} +1 -1
  39. package/bundled-test-harness/assets/{skills-test-BiUE1x5l.js → skills-test-Be4crUyO.js} +1 -1
  40. package/bundled-test-harness/assets/{store-test-Cbf2xZ40.js → store-test-BCyLVXZY.js} +1 -1
  41. package/bundled-test-harness/assets/{tests-Blos5s0A.js → tests-DbU4zMsp.js} +43 -43
  42. package/bundled-test-harness/assets/{text-input-validator-test-BxG8Dc2i.js → text-input-validator-test-DZuVdEKj.js} +1 -1
  43. package/bundled-test-harness/assets/{to-device-streaming-test-CGuKaubC.js → to-device-streaming-test-C3i5WmnX.js} +1 -1
  44. package/bundled-test-harness/assets/{token-usage-test-DJJmXyD8.js → token-usage-test-1AWgVaXO.js} +1 -1
  45. package/bundled-test-harness/assets/{tools-test-DxDvBhPf.js → tools-test-BSmxEyfO.js} +1 -1
  46. package/bundled-test-harness/assets/{turn-settling-test-Co2Dmnzm.js → turn-settling-test-S3ROiZu9.js} +1 -1
  47. package/bundled-test-harness/index.html +5 -5
  48. package/bundled-test-harness/tests/index.html +5 -5
  49. package/bundled-types/host-app/lib/secure-sign-in.ts +5 -3
  50. package/bundled-types/host-app/services/matrix-service.ts +3 -2
  51. package/bundled-types/host-app/services/tool-service.ts +36 -5
  52. package/dist/index.js +290 -290
  53. package/package.json +1 -1
  54. package/src/commands/keys/approve.ts +2 -1
  55. package/src/commands/keys/index.ts +48 -3
  56. package/src/commands/keys/manage.ts +257 -21
  57. package/src/lib/key-bindings.ts +2 -1
  58. package/src/lib/user-keys.ts +174 -4
@@ -95,13 +95,40 @@ export interface UserKeyMetadata {
95
95
  oauth?: OAuthKeyDescription;
96
96
  // Only a google-service-account key has one.
97
97
  serviceAccount?: ServiceAccountDetails;
98
+ // The limits in force, defaults filled in.
99
+ limits?: KeyLimits;
98
100
  createdAt: string;
99
101
  updatedAt: string;
100
102
  // Whether its owner lets people who aren't signed in use it, wherever a
101
- // realm's policy lets them make the call, and the optional cap on their
102
- // calls a day, all together, in every realm it is shared into.
103
+ // realm's policy lets them make the call, and the optional caps on their
104
+ // calls a day and a month, all together, in every realm it is shared into.
103
105
  anonymous?: boolean;
104
106
  anonymousDailyCap?: number | null;
107
+ anonymousMonthlyCap?: number | null;
108
+ }
109
+
110
+ // A key's anonymous caps as a clause that ends a sentence about people who
111
+ // aren't signed in: ", up to 100 calls a day and 1000 a month all together",
112
+ // or nothing where the key has neither.
113
+ export function anonymousCapsClause({
114
+ anonymousDailyCap,
115
+ anonymousMonthlyCap,
116
+ }: {
117
+ anonymousDailyCap?: number | null;
118
+ anonymousMonthlyCap?: number | null;
119
+ }): string {
120
+ let daily = anonymousDailyCap ?? null;
121
+ let monthly = anonymousMonthlyCap ?? null;
122
+ if (daily === null && monthly === null) {
123
+ return '';
124
+ }
125
+ let caps =
126
+ daily !== null && monthly !== null
127
+ ? `${daily} calls a day and ${monthly} a month`
128
+ : daily !== null
129
+ ? `${daily} calls a day`
130
+ : `${monthly} calls a month`;
131
+ return `, up to ${caps} all together`;
105
132
  }
106
133
 
107
134
  export interface KeyShare {
@@ -118,6 +145,9 @@ export interface KeyShare {
118
145
  suspensionReason: string | null;
119
146
  suspendedBy: string | null;
120
147
  suspendedAt: string | null;
148
+ // The limits in force, defaults filled in; listed for the key's owner and
149
+ // the realm's direct writers.
150
+ limits?: KeyLimits;
121
151
  createdAt: string;
122
152
  updatedAt: string;
123
153
  // In a realm's listing: the operations the share is approved for there,
@@ -125,6 +155,22 @@ export interface KeyShare {
125
155
  operations?: KeyShareOperation[];
126
156
  }
127
157
 
158
+ // How much of a key interactive calls may spend: a token bucket per caller
159
+ // (`rateBurst` calls at once, refilled at `rateRefillPerMinute`) and the calls
160
+ // the share or key serves in a UTC day and month, across every caller.
161
+ export interface KeyLimits {
162
+ rateBurst: number;
163
+ rateRefillPerMinute: number;
164
+ dailyBudget: number;
165
+ monthlyBudget: number;
166
+ }
167
+
168
+ // A change to some of a key's limits: a limit left out keeps what it was, and
169
+ // null puts it back to the platform's default.
170
+ export type KeyLimitChanges = Partial<{
171
+ [limit in keyof KeyLimits]: number | null;
172
+ }>;
173
+
128
174
  export interface KeyShareOperation {
129
175
  bindingId: string;
130
176
  type: { module: string; name: string };
@@ -135,6 +181,50 @@ export interface KeyShareOperation {
135
181
  destination?: string;
136
182
  }
137
183
 
184
+ // What a share or a personal key has served, as the realm server records it.
185
+ // The counts cover `since` (the start of the UTC month) until now; the
186
+ // budgets' `used` is what the limits count, interactive calls charged this
187
+ // UTC day and month.
188
+ export interface KeyUsage {
189
+ id: string;
190
+ limits: KeyLimits;
191
+ budgets: {
192
+ day: KeyBudgetUse;
193
+ month: KeyBudgetUse;
194
+ };
195
+ since: string;
196
+ calls: number;
197
+ lastUse: {
198
+ at: string;
199
+ caller: string | null;
200
+ operation: string;
201
+ outcome: 'sent' | 'refused' | 'failed';
202
+ // Set when the caller is someone the reader can't see in the realm.
203
+ withheld?: boolean;
204
+ } | null;
205
+ // A null caller is a call with no session; one marked `withheld` is every
206
+ // caller the reader can't see in the realm, counted together.
207
+ topCallers: { caller: string | null; calls: number; withheld?: boolean }[];
208
+ refusals: { code: string; count: number }[];
209
+ vendorOutcomes: { status: number; count: number; outcome?: string }[];
210
+ grants: {
211
+ id: string;
212
+ host: string;
213
+ destination: string;
214
+ state: 'active' | 'rejected';
215
+ rejectedAt: string | null;
216
+ lastForbiddenAt: string | null;
217
+ }[];
218
+ // Whether the reader may change the limits: only the key's owner may.
219
+ canEditLimits: boolean;
220
+ }
221
+
222
+ export interface KeyBudgetUse {
223
+ used: number;
224
+ budget: number;
225
+ resetsAt: string;
226
+ }
227
+
138
228
  export interface KeyWriteTarget {
139
229
  action: KeyWriteAction;
140
230
  keyId?: string;
@@ -246,6 +336,7 @@ export async function addUserKey(
246
336
  ticket,
247
337
  anonymous,
248
338
  anonymousDailyCap,
339
+ anonymousMonthlyCap,
249
340
  ...entered
250
341
  }: {
251
342
  name: string;
@@ -253,6 +344,7 @@ export async function addUserKey(
253
344
  ticket?: string;
254
345
  anonymous?: boolean;
255
346
  anonymousDailyCap?: number;
347
+ anonymousMonthlyCap?: number;
256
348
  } & EnteredValue,
257
349
  fetchFn: FetchFn = fetch,
258
350
  ): Promise<UserKeyMetadata> {
@@ -275,6 +367,7 @@ export async function addUserKey(
275
367
  ...valueAttributes(entered),
276
368
  ...(anonymous ? { anonymous: true } : {}),
277
369
  ...(anonymousDailyCap !== undefined ? { anonymousDailyCap } : {}),
370
+ ...(anonymousMonthlyCap !== undefined ? { anonymousMonthlyCap } : {}),
278
371
  },
279
372
  },
280
373
  meta: { ticket },
@@ -311,7 +404,8 @@ export async function realmOpensToAnonymous(
311
404
  }
312
405
 
313
406
  // Sets whether people who aren't signed in may use one of the user's keys,
314
- // and the optional cap on their calls a day. Takes a secure session.
407
+ // and the optional caps on their calls a day and a month. A cap left out is
408
+ // kept, and null removes it. Takes a secure session.
315
409
  export async function setUserKeyAnonymousUse(
316
410
  realmServerUrl: string,
317
411
  token: string,
@@ -319,7 +413,14 @@ export async function setUserKeyAnonymousUse(
319
413
  keyId,
320
414
  anonymous,
321
415
  anonymousDailyCap,
322
- }: { keyId: string; anonymous: boolean; anonymousDailyCap?: number },
416
+ anonymousMonthlyCap,
417
+ }: {
418
+ keyId: string;
419
+ anonymous: boolean;
420
+ // A cap left out is kept, and null removes it.
421
+ anonymousDailyCap?: number | null;
422
+ anonymousMonthlyCap?: number | null;
423
+ },
323
424
  fetchFn: FetchFn = fetch,
324
425
  ): Promise<UserKeyMetadata> {
325
426
  let json = await realmServerRequest(
@@ -335,6 +436,9 @@ export async function setUserKeyAnonymousUse(
335
436
  attributes: {
336
437
  anonymous,
337
438
  ...(anonymousDailyCap !== undefined ? { anonymousDailyCap } : {}),
439
+ ...(anonymousMonthlyCap !== undefined
440
+ ? { anonymousMonthlyCap }
441
+ : {}),
338
442
  },
339
443
  },
340
444
  },
@@ -629,3 +733,69 @@ export async function completeOAuthConnect(
629
733
  );
630
734
  return json.data.attributes;
631
735
  }
736
+
737
+ // The user's own share labelled `label` in `realm`, or else the realm's share
738
+ // of that label, for a direct writer of the realm to read or to be refused
739
+ // changing.
740
+ export async function findShare(
741
+ realmServerUrl: string,
742
+ token: string,
743
+ { realm, label }: { realm: string; label: string },
744
+ fetchFn: FetchFn = fetch,
745
+ ): Promise<KeyShare> {
746
+ let canonical = canonicalRealmUrl(realm);
747
+ let own = (await listOwnShares(realmServerUrl, token, {}, fetchFn)).find(
748
+ (share) => share.realm === canonical && share.label === label,
749
+ );
750
+ if (own) {
751
+ return own;
752
+ }
753
+ return findByLabel(
754
+ await listRealmShares(realmServerUrl, token, realm, fetchFn),
755
+ realm,
756
+ label,
757
+ );
758
+ }
759
+
760
+ // Sets some of a share's or a personal key's limits, which only the key's
761
+ // owner may, with a session signed in on the secure origin. Answers the
762
+ // limits in force afterwards, defaults filled in.
763
+ export async function setKeyLimits(
764
+ realmServerUrl: string,
765
+ token: string,
766
+ target: { shareId: string } | { keyId: string },
767
+ changes: KeyLimitChanges,
768
+ fetchFn: FetchFn = fetch,
769
+ ): Promise<KeyLimits> {
770
+ let path =
771
+ 'shareId' in target
772
+ ? `_key-shares/${encodeURIComponent(target.shareId)}/limits`
773
+ : `_user-keys/${encodeURIComponent(target.keyId)}/limits`;
774
+ let json = await realmServerRequest(realmServerUrl, path, {
775
+ method: 'PUT',
776
+ token,
777
+ fetchFn,
778
+ body: { data: { type: 'key-limits', attributes: changes } },
779
+ });
780
+ return json.data.attributes as KeyLimits;
781
+ }
782
+
783
+ // What a share or a personal key has served. A share's usage is for the
784
+ // realm's direct writers, a personal key's for its owner; anyone else is
785
+ // answered 404, as for no such share or key.
786
+ export async function getKeyUsage(
787
+ realmServerUrl: string,
788
+ token: string,
789
+ target: { shareId: string } | { keyId: string },
790
+ fetchFn: FetchFn = fetch,
791
+ ): Promise<KeyUsage> {
792
+ let path =
793
+ 'shareId' in target
794
+ ? `_key-shares/${encodeURIComponent(target.shareId)}/usage`
795
+ : `_user-keys/${encodeURIComponent(target.keyId)}/usage`;
796
+ let json = await realmServerRequest(realmServerUrl, path, {
797
+ token,
798
+ fetchFn,
799
+ });
800
+ return fromResource<KeyUsage>(json.data);
801
+ }