@toshicloud/sdk 0.5.0 → 0.5.1

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 (71) hide show
  1. package/README.md +82 -40
  2. package/dist/client.d.ts +33 -2
  3. package/dist/client.js +28 -3
  4. package/dist/errors.d.ts +74 -22
  5. package/dist/errors.js +99 -27
  6. package/dist/gen/toshi/permission_pb.d.ts +207 -2
  7. package/dist/gen/toshi/permission_pb.js +54 -2
  8. package/dist/gen/toshi/workspace/v1/account_pb.d.ts +590 -0
  9. package/dist/gen/toshi/workspace/v1/account_pb.js +75 -0
  10. package/dist/gen/toshi/workspace/v1/artifacts_pb.d.ts +14 -8
  11. package/dist/gen/toshi/workspace/v1/artifacts_pb.js +9 -7
  12. package/dist/gen/toshi/workspace/v1/audit_events_pb.d.ts +191 -6
  13. package/dist/gen/toshi/workspace/v1/audit_events_pb.js +11 -1
  14. package/dist/gen/toshi/workspace/v1/channels_pb.d.ts +1194 -21
  15. package/dist/gen/toshi/workspace/v1/channels_pb.js +185 -39
  16. package/dist/gen/toshi/workspace/v1/connections_pb.d.ts +1481 -0
  17. package/dist/gen/toshi/workspace/v1/connections_pb.js +208 -0
  18. package/dist/gen/toshi/workspace/v1/discovery_pb.d.ts +291 -43
  19. package/dist/gen/toshi/workspace/v1/discovery_pb.js +79 -30
  20. package/dist/gen/toshi/workspace/v1/display_pb.d.ts +198 -15
  21. package/dist/gen/toshi/workspace/v1/display_pb.js +52 -2
  22. package/dist/gen/toshi/workspace/v1/domains_pb.d.ts +864 -0
  23. package/dist/gen/toshi/workspace/v1/domains_pb.js +152 -0
  24. package/dist/gen/toshi/workspace/v1/fillers_pb.d.ts +12 -4
  25. package/dist/gen/toshi/workspace/v1/fillers_pb.js +1 -1
  26. package/dist/gen/toshi/workspace/v1/grants_pb.d.ts +1297 -0
  27. package/dist/gen/toshi/workspace/v1/grants_pb.js +271 -0
  28. package/dist/gen/toshi/workspace/v1/hostjobs_pb.d.ts +315 -15
  29. package/dist/gen/toshi/workspace/v1/hostjobs_pb.js +10 -5
  30. package/dist/gen/toshi/workspace/v1/hosts_pb.d.ts +196 -7
  31. package/dist/gen/toshi/workspace/v1/hosts_pb.js +12 -7
  32. package/dist/gen/toshi/workspace/v1/images_pb.d.ts +1463 -0
  33. package/dist/gen/toshi/workspace/v1/images_pb.js +263 -0
  34. package/dist/gen/toshi/workspace/v1/products_pb.d.ts +99 -296
  35. package/dist/gen/toshi/workspace/v1/products_pb.js +7 -53
  36. package/dist/gen/toshi/workspace/v1/projects_pb.d.ts +1474 -99
  37. package/dist/gen/toshi/workspace/v1/projects_pb.js +150 -33
  38. package/dist/gen/toshi/workspace/v1/recordings_pb.d.ts +181 -14
  39. package/dist/gen/toshi/workspace/v1/recordings_pb.js +54 -14
  40. package/dist/gen/toshi/workspace/v1/roles_pb.d.ts +1994 -0
  41. package/dist/gen/toshi/workspace/v1/roles_pb.js +355 -0
  42. package/dist/gen/toshi/workspace/v1/secrets_pb.d.ts +314 -8
  43. package/dist/gen/toshi/workspace/v1/secrets_pb.js +42 -18
  44. package/dist/gen/toshi/workspace/v1/storage_providers_pb.d.ts +14 -0
  45. package/dist/gen/toshi/workspace/v1/storage_providers_pb.js +1 -1
  46. package/dist/gen/toshi/workspace/v1/tokens_pb.d.ts +434 -5
  47. package/dist/gen/toshi/workspace/v1/tokens_pb.js +65 -8
  48. package/dist/gen/toshi/workspace/v1/triggers_pb.d.ts +1099 -0
  49. package/dist/gen/toshi/workspace/v1/triggers_pb.js +185 -0
  50. package/dist/gen/toshi/workspace/v1/volumes_pb.d.ts +1052 -33
  51. package/dist/gen/toshi/workspace/v1/volumes_pb.js +169 -11
  52. package/dist/gen/toshi/workspace/v1/webhook_pb.d.ts +66 -8
  53. package/dist/gen/toshi/workspace/v1/webhook_pb.js +4 -4
  54. package/dist/gen/toshi/workspace/v1/workloads_pb.d.ts +2043 -54
  55. package/dist/gen/toshi/workspace/v1/workloads_pb.js +204 -54
  56. package/dist/gen/toshi/workspace/v1/workspace_pb.d.ts +6290 -937
  57. package/dist/gen/toshi/workspace/v1/workspace_pb.js +801 -88
  58. package/dist/gen/toshi/workspace/v1/workspace_service_pb.d.ts +2176 -630
  59. package/dist/gen/toshi/workspace/v1/workspace_service_pb.js +191 -74
  60. package/dist/index.d.ts +8 -1
  61. package/dist/index.js +8 -1
  62. package/dist/interceptors.d.ts +6 -2
  63. package/dist/interceptors.js +13 -8
  64. package/dist/retry.d.ts +18 -0
  65. package/dist/retry.js +225 -23
  66. package/dist/test/procedures.js +100 -5
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +1 -1
  69. package/dist/wait.d.ts +4 -0
  70. package/dist/wait.js +24 -1
  71. package/package.json +3 -2
package/README.md CHANGED
@@ -4,7 +4,7 @@ A TypeScript client for the Toshi Workspace API, `toshi.workspace.v1`,
4
4
  published as ES modules with type declarations.
5
5
 
6
6
  ```sh
7
- npm install @toshicloud/sdk @connectrpc/connect
7
+ npm install @toshicloud/sdk @bufbuild/protobuf @connectrpc/connect
8
8
  ```
9
9
 
10
10
  The browser entry needs a transport such as `@connectrpc/connect-web`; the
@@ -68,13 +68,16 @@ entry reads `TOSHI_SERVER`, `TOSHI_TOKEN`, `TOSHI_TOKEN_FILE` and
68
68
  `environment` fields when they are empty. A value in the request wins.
69
69
  `environment` is left empty on a request with `shared: true`.
70
70
  - `retry`: `{ maxAttempts, initialBackoffMs, maxBackoffMs }`, by default
71
- 5, 200 ms and 10 s, with full jitter. `retry: false` turns off retries
72
- and watch reconnects.
71
+ 5, 200 ms and 10 s, with full jitter. Reads, and the calls whose request
72
+ takes a `request_key`, are retried; a stream and every other call is not.
73
+ `retry: false` turns off retries and watch reconnects.
73
74
  - `defaultTimeoutMs`: bounds each unary call except the Wait calls,
74
75
  which their request's timeout bounds; streams ignore it.
75
76
 
76
77
  A call may set `timeoutMs` instead: it bounds the whole call, retries
77
78
  included, and on a watch it bounds the open. It composes with `signal`.
79
+ `ToshiOptions` is the object the client takes, and `CallOptions` the second
80
+ argument of each call, which carries `signal` and `timeoutMs`.
78
81
 
79
82
  ### Node options
80
83
 
@@ -135,13 +138,16 @@ picked up without a new client.
135
138
  The client has one field per Workspace API service: `project`, `secret`,
136
139
  `webhook`, `display`, `channel`, `recording`, `volume`, `artifact`,
137
140
  `artifactUpload`, `workspace`, `hostJob`, `host`, `token`, `auditEvent`,
138
- `filler`, `storageProvider`, `product` and `discovery`, each holding that
139
- service's generated client.
141
+ `filler`, `grant`, `storageProvider`, `product`, `discovery`, `connection`,
142
+ `role`, `trigger`, `account`, `image` and `domain`, each holding that service's
143
+ generated client.
140
144
  `workload` holds `WorkloadService`'s calls, grouped by the type they
141
145
  name: `workload.jobs`, `workload.sandboxes` and `workload.services`, one
142
- method for each call (`create`, `get`, `list`, `update`, `delete`,
143
- `wait`, `watch`, and `cancel` on a job, `execute` on a sandbox, `start`,
144
- `stop`, `setMode`, `listRuns`, `wake`, `hold` and `release` on a service). The calls that
146
+ method for each call (`create`, `get`, `list`, `update`, `delete`, `wait`,
147
+ `watch`, and `cancel` on a job, `execute`, `start` and `stop` on a sandbox,
148
+ `start`, `stop`, `setMode`, `listRuns`, `wake`, `hold`, `release`,
149
+ `listRevisions`, `getRevision`, `rollback` and `listActivations` on a
150
+ service). The calls that
145
151
  span the three types sit on `workload` itself: `listEvents`, `watchEvents`,
146
152
  `getOperation` and `waitOperation`. `run` sits at the top of the client, as
147
153
  `toshi run` sits at the top of the CLI.
@@ -156,7 +162,8 @@ console.log(job?.id);
156
162
 
157
163
  ### Lists
158
164
 
159
- `workload.jobs.all`, `workload.sandboxes.all`, `workload.services.all`,
165
+ `workload.jobs.all`, `workload.sandboxes.all`,
166
+ `workload.sandboxes.allPreviewShareLinks`, `workload.services.all`,
160
167
  `workload.services.allRuns` and `workload.allEvents` take the request their
161
168
  `list` method takes and yield every item, one page after another. Each
162
169
  response's `nextPageToken` becomes the next request's `pageToken`, and a
@@ -190,6 +197,9 @@ for await (const project of paginate(
190
197
  fields. Every page is a call of its own, so `opts` goes to each one, and
191
198
  `timeoutMs` bounds a page and not the whole iteration. An error throws once
192
199
  and ends the iteration; leaving the loop sends no further request.
200
+ `PageRequest` is a list request, which may name the page to read first with
201
+ `pageToken`, and `PageResponse` a list response, which names the page after
202
+ it with `nextPageToken`.
193
203
 
194
204
  ### Retries
195
205
 
@@ -199,18 +209,35 @@ connection reset or destroyed stream that `@connectrpc/connect-node`
199
209
  reports, or a stream the server refused before reading it
200
210
  (`REFUSED_STREAM`). A write that takes a request key gets a random one when the
201
211
  request has none, so the server applies a retried write once. Other
202
- writes are never retried. An error the server sends, including
203
- `Aborted`, `InvalidArgument` and `ResourceExhausted`, reaches the caller
204
- unchanged. So does any connection failure the client cannot tell apart
205
- from a server error.
212
+ writes are never retried.
213
+
214
+ A `ResourceExhausted` refusal that names how long to wait, as the API's
215
+ rate limit does, is retried the same way. The wait comes from its
216
+ `google.rpc.RetryInfo` detail, else from its `Retry-After` header. So is an
217
+ HTTP 429 with a `Retry-After`, which reaches the client as `Unavailable`.
218
+ The client waits the longer of that wait and the backoff, never more than
219
+ `maxBackoffMs`, and stops when the call's signal aborts. A
220
+ `ResourceExhausted` that names no wait, such as a plan's limit, reaches the
221
+ caller unchanged, as do every other error the server sends, including
222
+ `Aborted` and `InvalidArgument`, and any connection failure the client
223
+ cannot tell apart from a server error.
224
+
225
+ `RetryPolicy` says how often and how long a call is retried, `maxAttempts`
226
+ counting the first try; `defaultRetryPolicy` is the default — five
227
+ attempts, a 200 ms initial backoff and a 10 s cap.
206
228
 
207
229
  ### Waiting and watching
208
230
 
209
231
  - `workload.jobs.wait(req)`, `workload.sandboxes.wait(req)` and
210
232
  `workload.services.wait(req)` ask again until the workload reaches the
211
- state; `workload.waitOperation(req)` asks until the operation is done.
233
+ state; `workload.waitOperation(req)` asks until the operation is done. A
234
+ call starts at least a second, and at most 1.2 seconds, after the start of
235
+ the one before, and a signal ends a pause with its own error.
212
236
  - `workload.getOperation(req)` returns one operation by id: a call that
213
237
  takes long hands one back, and this asks for it again.
238
+ - An event's payload says what happened: a trigger's `TriggerResult` names
239
+ the key's `reason` and its workload, and with `process_exited` or
240
+ `setup_failed` it carries the sandbox's `exitCode` and `message`.
214
241
  - `workload.listEvents(req)` returns the events after a cursor, oldest
215
242
  first, a page at a time, for a client that polls or catches up after
216
243
  downtime; `workload.watchEvents(req)` is the live form.
@@ -307,44 +334,55 @@ and `workload.jobs.cancel` ends it.
307
334
 
308
335
  ## Products that are off
309
336
 
310
- A call for a product that is off for the workspace, or that turns on an
311
- outside product without accepting the access it asks for, rejects with a
337
+ A call for a product that is off for the workspace rejects with a
312
338
  `ProductError`, `wait`, the watches and `execute` included.
313
339
  `isProductError(err)` tells it apart. It is still a `ConnectError` with the
314
340
  same `code`, and it has the product's ID (`product`), the `reason`
315
- (`ProductReason.NotInstalled`, `Disabled`, `NotEntitled`, `TurnedOff` or
316
- `AccessNotAccepted`), the setting that turns the product on (`feature`,
317
- such as `runners.enabled`), how far a product the deployment disables has
318
- wound down (`state`) and, for `AccessNotAccepted`, the access to accept
319
- (`access`).
341
+ (`ProductReason.Unavailable`), the input that turned the product off
342
+ (`productCause`: `build`, `operator`, `plan` or `workspace`) and, for a
343
+ product the deployment turns off, how far it has wound down (`state`:
344
+ `no_new_work`, `draining` or `stopped`).
320
345
 
321
346
  <!-- examples/product-error.ts -->
322
347
 
323
348
  ```ts
349
+ import { isProductError } from "@toshicloud/sdk";
324
350
 
325
- // Turning an outside product on installs it, and the API asks the caller to
326
- // accept the access it wants first.
327
- const enable = { id: "acme_ci", enabled: true, acceptedAccess: [] as string[] };
351
+ import { toshi } from "./client.js";
352
+
353
+ // Turn a product on for the workspace.
354
+ const enable = { id: "acme_ci", enabled: true };
328
355
  try {
329
- try {
330
- await toshi.product.updateProduct(enable);
331
- } catch (err) {
332
- if (!isProductError(err) || err.reason !== ProductReason.AccessNotAccepted) {
333
- throw err;
334
- }
335
- console.log(err.product, err.reason, err.access);
336
- // Accept exactly the access the product asks for.
337
- await toshi.product.updateProduct({ ...enable, acceptedAccess: err.access });
338
- }
356
+ await toshi.product.updateProduct(enable);
339
357
  } catch (err) {
340
358
  if (!isProductError(err)) {
341
359
  throw err;
342
360
  }
343
- // Off for the workspace: say which product and why.
344
- console.log(err.product, err.reason, err.feature);
361
+ // Off for the workspace: which product, the input that turns it off, and,
362
+ // for the deployment's config, how far it has wound down.
363
+ console.log(err.product, err.productCause, err.state);
345
364
  }
346
365
  ```
347
366
 
367
+ ## Refusals with a reason
368
+
369
+ A refusal that is not a product's rejects with a `RefusalError`;
370
+ `isRefusalError(err)` tells it apart. It is still a `ConnectError` with the
371
+ same `code`, and its `reason` is one of `RefusalReason`:
372
+ `RefusalReason.ImageInUse` when an image a workload or a revision still uses
373
+ cannot be deleted, `RefusalReason.ImageNotReady` when a job, a sandbox or a
374
+ service revision names a workspace image with no ready version,
375
+ `RefusalReason.PlanExcludesImages` when the plan has no images, `RefusalReason.LastIdentity` when the caller's last identity cannot
376
+ be unlinked, and so on for images, volumes, roles and display sessions.
377
+
378
+ `workload.services.rollback` asks the revision's registry whether it still
379
+ serves the revision's image before it selects it, and refuses with
380
+ `RefusalReason.ImageNotServed` when the registry answered that it does not
381
+ serve the image, or `RefusalReason.ImageCheckFailed` when it could not say.
382
+ A revision's `imageCheck` carries the last answer: its `outcome`, the
383
+ registry's `status` and `message`, and when it was asked, `checkedAt`.
384
+ Reading a revision asks no registry.
385
+
348
386
  ## Testing with `@toshicloud/sdk/test`
349
387
 
350
388
  `createFake()` serves the whole Workspace API in memory: no server, no port,
@@ -384,14 +422,17 @@ in `proto/toshi/workspace/v1` of the
384
422
  `proto/toshi/permission.proto`, which they import. The generated code is committed and imports
385
423
  only `@bufbuild/protobuf`, so installing the package needs no generator.
386
424
 
387
- `pin.env` names the toshi-api commit (or, once one exists, the release
388
- tag) the code is generated from. `mise.toml` pins the versions of Node,
389
- pnpm, `buf` and `protoc-gen-es`.
425
+ `pin.env` names the toshi-api commit the code is generated from:
426
+ `2184ae8440834bb5b91991b3e4d62d14ce9dd15a` today, the commit of the
427
+ release tag `v0.4.1`. `mise.toml` pins the versions of Node, pnpm and `buf`;
428
+ `package.json` pins `protoc-gen-es` at the same version as the
429
+ `@bufbuild/protobuf` development dependency.
390
430
 
391
431
  To regenerate:
392
432
 
393
433
  ```sh
394
434
  mise install
435
+ pnpm install --frozen-lockfile
395
436
  mise exec -- scripts/generate.sh
396
437
  ```
397
438
 
@@ -405,7 +446,8 @@ and fails if the result differs from what is committed
405
446
  The package's major version follows the API's: `1.x` will serve
406
447
  `toshi.workspace.v1`, and a `toshi.workspace.v2` gets a new major
407
448
  version. Until a Toshi release serves the API, the package stays at
408
- `0.x` and is generated from a pinned commit.
449
+ `0.x` and is generated from a pinned commit. `VERSION` is the package
450
+ version, the one `package.json` carries.
409
451
 
410
452
  ## Publishing
411
453
 
package/dist/client.d.ts CHANGED
@@ -1,26 +1,35 @@
1
1
  import type { MessageInitShape } from "@bufbuild/protobuf";
2
2
  import type { Client, Interceptor, Transport } from "@connectrpc/connect";
3
+ import { AccountService } from "./gen/toshi/workspace/v1/account_pb.js";
3
4
  import { ArtifactService, ArtifactUploadService } from "./gen/toshi/workspace/v1/artifacts_pb.js";
4
5
  import { AuditEventService } from "./gen/toshi/workspace/v1/audit_events_pb.js";
5
6
  import { ChannelService } from "./gen/toshi/workspace/v1/channels_pb.js";
7
+ import { ConnectionService } from "./gen/toshi/workspace/v1/connections_pb.js";
6
8
  import { DiscoveryService } from "./gen/toshi/workspace/v1/discovery_pb.js";
7
9
  import { DisplayService } from "./gen/toshi/workspace/v1/display_pb.js";
10
+ import { DomainService } from "./gen/toshi/workspace/v1/domains_pb.js";
8
11
  import { FillerService } from "./gen/toshi/workspace/v1/fillers_pb.js";
12
+ import { GrantService } from "./gen/toshi/workspace/v1/grants_pb.js";
13
+ import { ImageService } from "./gen/toshi/workspace/v1/images_pb.js";
9
14
  import { HostJobService } from "./gen/toshi/workspace/v1/hostjobs_pb.js";
10
15
  import { HostService } from "./gen/toshi/workspace/v1/hosts_pb.js";
11
16
  import { ProductService } from "./gen/toshi/workspace/v1/products_pb.js";
12
17
  import { ProjectService } from "./gen/toshi/workspace/v1/projects_pb.js";
13
18
  import { RecordingService } from "./gen/toshi/workspace/v1/recordings_pb.js";
19
+ import { RoleService } from "./gen/toshi/workspace/v1/roles_pb.js";
14
20
  import { SecretService } from "./gen/toshi/workspace/v1/secrets_pb.js";
15
21
  import { StorageProviderService } from "./gen/toshi/workspace/v1/storage_providers_pb.js";
16
22
  import { TokenService } from "./gen/toshi/workspace/v1/tokens_pb.js";
23
+ import { TriggerService } from "./gen/toshi/workspace/v1/triggers_pb.js";
17
24
  import { VolumeService } from "./gen/toshi/workspace/v1/volumes_pb.js";
18
25
  import { WebhookService } from "./gen/toshi/workspace/v1/webhook_pb.js";
19
26
  import { WorkspaceService } from "./gen/toshi/workspace/v1/workspace_service_pb.js";
27
+ import { CreatePreviewShareLinkRequestSchema, ListPreviewShareLinksRequestSchema, RevokePreviewShareLinkRequestSchema, StartSandboxRequestSchema, StopSandboxRequestSchema } from "./gen/toshi/workspace/v1/workspace_pb.js";
28
+ import type { CreatePreviewShareLinkResponse, ListPreviewShareLinksResponse, RevokePreviewShareLinkResponse, StartSandboxResponse, StopSandboxResponse } from "./gen/toshi/workspace/v1/workspace_pb.js";
20
29
  import { CancelJobRequestSchema, CreateJobRequestSchema, CreateSandboxRequestSchema, CreateServiceRequestSchema, DeleteJobRequestSchema, DeleteSandboxRequestSchema, DeleteServiceRequestSchema, GetJobRequestSchema, GetSandboxRequestSchema, GetServiceRequestSchema, ListJobsRequestSchema, ListSandboxesRequestSchema, ListServicesRequestSchema, UpdateJobRequestSchema, UpdateSandboxRequestSchema, UpdateServiceRequestSchema, WaitJobRequestSchema, WaitSandboxRequestSchema, WaitServiceRequestSchema, WatchJobsRequestSchema, WatchSandboxesRequestSchema, WatchServicesRequestSchema } from "./gen/toshi/workspace/v1/workloads_pb.js";
21
30
  import type { CancelJobResponse, CreateJobResponse, CreateSandboxResponse, CreateServiceResponse, DeleteJobResponse, DeleteSandboxResponse, DeleteServiceResponse, GetJobResponse, GetSandboxResponse, GetServiceResponse, ListJobsResponse, ListSandboxesResponse, ListServicesResponse, UpdateJobResponse, UpdateSandboxResponse, UpdateServiceResponse, WaitJobResponse, WaitSandboxResponse, WaitServiceResponse, WatchJobsResponse, WatchSandboxesResponse, WatchServicesResponse } from "./gen/toshi/workspace/v1/workloads_pb.js";
22
- import { ExecuteStartSchema, GetOperationRequestSchema, HoldServiceRequestSchema, ListEventsRequestSchema, ListServiceRunsRequestSchema, ReleaseServiceRequestSchema, SetServiceModeRequestSchema, StartServiceRequestSchema, StopServiceRequestSchema, WaitOperationRequestSchema, WakeServiceRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema } from "./gen/toshi/workspace/v1/workspace_pb.js";
23
- import type { Event, GetOperationResponse, HoldServiceResponse, Job, ListEventsResponse, ListServiceRunsResponse, Operation, ReleaseServiceResponse, Sandbox, Service, ServiceRun, SetServiceModeResponse, StartServiceResponse, StopServiceResponse, WakeServiceResponse, WatchEventsResponse, WatchOutputResponse } from "./gen/toshi/workspace/v1/workspace_pb.js";
31
+ import { ExecuteStartSchema, GetOperationRequestSchema, GetServiceRevisionRequestSchema, HoldServiceRequestSchema, ListEventsRequestSchema, ListServiceActivationsRequestSchema, ListServiceRevisionsRequestSchema, ListServiceRunsRequestSchema, ReleaseServiceRequestSchema, RollbackServiceRequestSchema, SetServiceModeRequestSchema, StartServiceRequestSchema, StopServiceRequestSchema, WaitOperationRequestSchema, WakeServiceRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema } from "./gen/toshi/workspace/v1/workspace_pb.js";
32
+ import type { Event, GetOperationResponse, GetServiceRevisionResponse, HoldServiceResponse, Job, ListEventsResponse, ListServiceActivationsResponse, ListServiceRevisionsResponse, ListServiceRunsResponse, Operation, PreviewShareLink, ReleaseServiceResponse, RollbackServiceResponse, Sandbox, Service, ServiceRun, SetServiceModeResponse, StartServiceResponse, StopServiceResponse, WakeServiceResponse, WatchEventsResponse, WatchOutputResponse } from "./gen/toshi/workspace/v1/workspace_pb.js";
24
33
  import type { RetryPolicy } from "./retry.js";
25
34
  /** How the client is configured; the caller builds the transport. */
26
35
  export interface ToshiOptions {
@@ -65,9 +74,16 @@ export interface ToshiClient {
65
74
  readonly token: Client<typeof TokenService>;
66
75
  readonly auditEvent: Client<typeof AuditEventService>;
67
76
  readonly filler: Client<typeof FillerService>;
77
+ readonly grant: Client<typeof GrantService>;
68
78
  readonly storageProvider: Client<typeof StorageProviderService>;
69
79
  readonly product: Client<typeof ProductService>;
70
80
  readonly discovery: Client<typeof DiscoveryService>;
81
+ readonly connection: Client<typeof ConnectionService>;
82
+ readonly role: Client<typeof RoleService>;
83
+ readonly trigger: Client<typeof TriggerService>;
84
+ readonly account: Client<typeof AccountService>;
85
+ readonly image: Client<typeof ImageService>;
86
+ readonly domain: Client<typeof DomainService>;
71
87
  run(req: MessageInitShape<typeof CreateJobRequestSchema>, opts?: CallOptions): Promise<JobRun>;
72
88
  }
73
89
  /** WorkloadService's calls, grouped by the type they name. */
@@ -107,9 +123,16 @@ export interface SandboxesClient {
107
123
  all(req: MessageInitShape<typeof ListSandboxesRequestSchema>, opts?: CallOptions): AsyncIterable<Sandbox>;
108
124
  update(req: MessageInitShape<typeof UpdateSandboxRequestSchema>, opts?: CallOptions): Promise<UpdateSandboxResponse>;
109
125
  delete(req: MessageInitShape<typeof DeleteSandboxRequestSchema>, opts?: CallOptions): Promise<DeleteSandboxResponse>;
126
+ stop(req: MessageInitShape<typeof StopSandboxRequestSchema>, opts?: CallOptions): Promise<StopSandboxResponse>;
127
+ start(req: MessageInitShape<typeof StartSandboxRequestSchema>, opts?: CallOptions): Promise<StartSandboxResponse>;
110
128
  wait(req: MessageInitShape<typeof WaitSandboxRequestSchema>, opts?: CallOptions): Promise<WaitSandboxResponse>;
111
129
  watch(req: MessageInitShape<typeof WatchSandboxesRequestSchema>, opts?: CallOptions): Promise<SandboxWatch>;
112
130
  execute(start: MessageInitShape<typeof ExecuteStartSchema>, opts?: CallOptions): Promise<Process>;
131
+ createPreviewShareLink(req: MessageInitShape<typeof CreatePreviewShareLinkRequestSchema>, opts?: CallOptions): Promise<CreatePreviewShareLinkResponse>;
132
+ listPreviewShareLinks(req: MessageInitShape<typeof ListPreviewShareLinksRequestSchema>, opts?: CallOptions): Promise<ListPreviewShareLinksResponse>;
133
+ /** Yields every share link that matches every filter that is set, following the page token from one page to the next. */
134
+ allPreviewShareLinks(req: MessageInitShape<typeof ListPreviewShareLinksRequestSchema>, opts?: CallOptions): AsyncIterable<PreviewShareLink>;
135
+ revokePreviewShareLink(req: MessageInitShape<typeof RevokePreviewShareLinkRequestSchema>, opts?: CallOptions): Promise<RevokePreviewShareLinkResponse>;
113
136
  }
114
137
  /** A service's calls: one method for each of WorkloadService's service calls. */
115
138
  export interface ServicesClient {
@@ -131,6 +154,14 @@ export interface ServicesClient {
131
154
  listRuns(req: MessageInitShape<typeof ListServiceRunsRequestSchema>, opts?: CallOptions): Promise<ListServiceRunsResponse>;
132
155
  /** Yields every run listRuns returns, the newest first, following the page token from one page to the next. */
133
156
  allRuns(req: MessageInitShape<typeof ListServiceRunsRequestSchema>, opts?: CallOptions): AsyncIterable<ServiceRun>;
157
+ /** Returns a service's revisions, oldest first. */
158
+ listRevisions(req: MessageInitShape<typeof ListServiceRevisionsRequestSchema>, opts?: CallOptions): Promise<ListServiceRevisionsResponse>;
159
+ /** Returns one revision of a service. */
160
+ getRevision(req: MessageInitShape<typeof GetServiceRevisionRequestSchema>, opts?: CallOptions): Promise<GetServiceRevisionResponse>;
161
+ /** Selects a revision the service already has as its current one. */
162
+ rollback(req: MessageInitShape<typeof RollbackServiceRequestSchema>, opts?: CallOptions): Promise<RollbackServiceResponse>;
163
+ /** Returns a service's activations, oldest first. */
164
+ listActivations(req: MessageInitShape<typeof ListServiceActivationsRequestSchema>, opts?: CallOptions): Promise<ListServiceActivationsResponse>;
134
165
  }
135
166
  /** A live stream of a job's changes. */
136
167
  export interface JobWatch extends AsyncIterable<WatchJobsResponse> {
package/dist/client.js CHANGED
@@ -1,28 +1,36 @@
1
1
  import { create } from "@bufbuild/protobuf";
2
2
  import { createClient } from "@connectrpc/connect";
3
3
  import { executeCommand } from "./execute.js";
4
+ import { AccountService } from "./gen/toshi/workspace/v1/account_pb.js";
4
5
  import { ArtifactService, ArtifactUploadService } from "./gen/toshi/workspace/v1/artifacts_pb.js";
5
6
  import { AuditEventService } from "./gen/toshi/workspace/v1/audit_events_pb.js";
6
7
  import { ChannelService } from "./gen/toshi/workspace/v1/channels_pb.js";
8
+ import { ConnectionService } from "./gen/toshi/workspace/v1/connections_pb.js";
7
9
  import { DiscoveryService } from "./gen/toshi/workspace/v1/discovery_pb.js";
8
10
  import { DisplayService } from "./gen/toshi/workspace/v1/display_pb.js";
11
+ import { DomainService } from "./gen/toshi/workspace/v1/domains_pb.js";
9
12
  import { FillerService } from "./gen/toshi/workspace/v1/fillers_pb.js";
13
+ import { GrantService } from "./gen/toshi/workspace/v1/grants_pb.js";
14
+ import { ImageService } from "./gen/toshi/workspace/v1/images_pb.js";
10
15
  import { HostJobService } from "./gen/toshi/workspace/v1/hostjobs_pb.js";
11
16
  import { HostService } from "./gen/toshi/workspace/v1/hosts_pb.js";
12
17
  import { ProductService } from "./gen/toshi/workspace/v1/products_pb.js";
13
18
  import { ProjectService } from "./gen/toshi/workspace/v1/projects_pb.js";
14
19
  import { RecordingService } from "./gen/toshi/workspace/v1/recordings_pb.js";
20
+ import { RoleService } from "./gen/toshi/workspace/v1/roles_pb.js";
15
21
  import { SecretService } from "./gen/toshi/workspace/v1/secrets_pb.js";
16
22
  import { StorageProviderService } from "./gen/toshi/workspace/v1/storage_providers_pb.js";
17
23
  import { TokenService } from "./gen/toshi/workspace/v1/tokens_pb.js";
24
+ import { TriggerService } from "./gen/toshi/workspace/v1/triggers_pb.js";
18
25
  import { VolumeService } from "./gen/toshi/workspace/v1/volumes_pb.js";
19
26
  import { WebhookService } from "./gen/toshi/workspace/v1/webhook_pb.js";
20
27
  import { WorkspaceService } from "./gen/toshi/workspace/v1/workspace_service_pb.js";
28
+ import { CreatePreviewShareLinkRequestSchema, ListPreviewShareLinksRequestSchema, RevokePreviewShareLinkRequestSchema, StartSandboxRequestSchema, StopSandboxRequestSchema, } from "./gen/toshi/workspace/v1/workspace_pb.js";
21
29
  import { CancelJobRequestSchema, CreateJobRequestSchema, CreateSandboxRequestSchema, CreateServiceRequestSchema, DeleteJobRequestSchema, DeleteSandboxRequestSchema, DeleteServiceRequestSchema, GetJobRequestSchema, GetSandboxRequestSchema, GetServiceRequestSchema, ListJobsRequestSchema, ListSandboxesRequestSchema, ListServicesRequestSchema, UpdateJobRequestSchema, UpdateSandboxRequestSchema, UpdateServiceRequestSchema, WaitJobRequestSchema, WaitSandboxRequestSchema, WaitServiceRequestSchema, WatchJobsRequestSchema, WatchSandboxesRequestSchema, WatchServicesRequestSchema, WorkloadService, } from "./gen/toshi/workspace/v1/workloads_pb.js";
22
- import { ExecuteStartSchema, GetOperationRequestSchema, HoldServiceRequestSchema, ListEventsRequestSchema, ListServiceRunsRequestSchema, ReleaseServiceRequestSchema, SetServiceModeRequestSchema, StartServiceRequestSchema, StopServiceRequestSchema, WaitOperationRequestSchema, WakeServiceRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema, } from "./gen/toshi/workspace/v1/workspace_pb.js";
30
+ import { ExecuteStartSchema, GetOperationRequestSchema, GetServiceRevisionRequestSchema, HoldServiceRequestSchema, ListEventsRequestSchema, ListServiceActivationsRequestSchema, ListServiceRevisionsRequestSchema, ListServiceRunsRequestSchema, ReleaseServiceRequestSchema, RollbackServiceRequestSchema, SetServiceModeRequestSchema, StartServiceRequestSchema, StopServiceRequestSchema, WaitOperationRequestSchema, WakeServiceRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema, } from "./gen/toshi/workspace/v1/workspace_pb.js";
23
31
  import { openOutputWatch } from "./output.js";
24
32
  import { paginate } from "./paginate.js";
25
- import { authInterceptor, keyInterceptor, productInterceptor, retryInterceptor, scopeInterceptor, } from "./interceptors.js";
33
+ import { authInterceptor, keyInterceptor, refusalInterceptor, retryInterceptor, scopeInterceptor, } from "./interceptors.js";
26
34
  import { normalizeRetry } from "./retry.js";
27
35
  import { withDefaultUnaryTimeout } from "./timeout.js";
28
36
  import { openEventWatch, openJobWatch, openSandboxWatch, openServiceWatch } from "./watch.js";
@@ -41,7 +49,7 @@ import { waitJob, waitOperation, waitSandbox, waitService } from "./wait.js";
41
49
  export function createToshiClient(options) {
42
50
  const retry = normalizeRetry(options.retry);
43
51
  const interceptors = [
44
- productInterceptor(),
52
+ refusalInterceptor(),
45
53
  scopeInterceptor(options.project ?? "", options.environment ?? ""),
46
54
  keyInterceptor(),
47
55
  retryInterceptor(retry),
@@ -79,9 +87,15 @@ export function createToshiClient(options) {
79
87
  all: (req, opts) => paginate(req, workloadService.listSandboxes, (resp) => resp.sandboxes, opts),
80
88
  update: (req, opts) => workloadService.updateSandbox(req, opts),
81
89
  delete: (req, opts) => workloadService.deleteSandbox(req, opts),
90
+ stop: (req, opts) => workloadService.stopSandbox(req, opts),
91
+ start: (req, opts) => workloadService.startSandbox(req, opts),
82
92
  wait: (req, opts) => waitSandbox(workloadService, create(WaitSandboxRequestSchema, req), opts),
83
93
  watch: (req, opts) => openSandboxWatch(workloadService, retry, create(WatchSandboxesRequestSchema, req), opts),
84
94
  execute: (start, opts) => executeCommand(workloadService, start, opts),
95
+ createPreviewShareLink: (req, opts) => workloadService.createPreviewShareLink(req, opts),
96
+ listPreviewShareLinks: (req, opts) => workloadService.listPreviewShareLinks(req, opts),
97
+ allPreviewShareLinks: (req, opts) => paginate(req, workloadService.listPreviewShareLinks, (resp) => resp.shareLinks, opts),
98
+ revokePreviewShareLink: (req, opts) => workloadService.revokePreviewShareLink(req, opts),
85
99
  },
86
100
  services: {
87
101
  create: (req, opts) => workloadService.createService(req, opts),
@@ -100,6 +114,10 @@ export function createToshiClient(options) {
100
114
  hold: (req, opts) => workloadService.holdService(req, opts),
101
115
  listRuns: (req, opts) => workloadService.listServiceRuns(req, opts),
102
116
  allRuns: (req, opts) => paginate(req, workloadService.listServiceRuns, (resp) => resp.runs, opts),
117
+ listRevisions: (req, opts) => workloadService.listServiceRevisions(req, opts),
118
+ getRevision: (req, opts) => workloadService.getServiceRevision(req, opts),
119
+ rollback: (req, opts) => workloadService.rollbackService(req, opts),
120
+ listActivations: (req, opts) => workloadService.listServiceActivations(req, opts),
103
121
  },
104
122
  waitOperation: (req, opts) => waitOperation(workloadService, create(WaitOperationRequestSchema, req), opts),
105
123
  listEvents: (req, opts) => workloadService.listEvents(req, opts),
@@ -114,8 +132,15 @@ export function createToshiClient(options) {
114
132
  token: createClient(TokenService, transport),
115
133
  auditEvent: createClient(AuditEventService, transport),
116
134
  filler: createClient(FillerService, transport),
135
+ grant: createClient(GrantService, transport),
117
136
  storageProvider: createClient(StorageProviderService, transport),
118
137
  product: createClient(ProductService, transport),
119
138
  discovery: createClient(DiscoveryService, transport),
139
+ connection: createClient(ConnectionService, transport),
140
+ role: createClient(RoleService, transport),
141
+ trigger: createClient(TriggerService, transport),
142
+ account: createClient(AccountService, transport),
143
+ image: createClient(ImageService, transport),
144
+ domain: createClient(DomainService, transport),
120
145
  };
121
146
  }
package/dist/errors.d.ts CHANGED
@@ -28,29 +28,64 @@ export declare class StreamLostError extends Error {
28
28
  constructor(executionId: string, requestKey: string, cause?: unknown);
29
29
  }
30
30
  /** Why the API refused a call for a product: the reason of the refusal's ErrorInfo detail. */
31
- export type ProductReason = "PRODUCT_NOT_INSTALLED" | "PRODUCT_DISABLED" | "PRODUCT_NOT_ENTITLED" | "PRODUCT_TURNED_OFF" | "ACCESS_NOT_ACCEPTED";
31
+ export type ProductReason = "PRODUCT_UNAVAILABLE";
32
32
  /** The reasons a call is refused for a product. */
33
33
  export declare const ProductReason: {
34
- /** The deployment's build does not include the product; the code is Unimplemented. */
35
- readonly NotInstalled: "PRODUCT_NOT_INSTALLED";
36
- /** The deployment turned the product off; the code is FailedPrecondition. */
37
- readonly Disabled: "PRODUCT_DISABLED";
38
- /** The workspace's entitlements leave the product out; the code is PermissionDenied. */
39
- readonly NotEntitled: "PRODUCT_NOT_ENTITLED";
40
34
  /**
41
- * The workspace's settings turn the product off, or an outside product's
42
- * installation is turned off; the code is FailedPrecondition.
35
+ * The product is off for the workspace; the code is FailedPrecondition, and
36
+ * `cause` says which input turned it off.
43
37
  */
44
- readonly TurnedOff: "PRODUCT_TURNED_OFF";
45
- /**
46
- * Turning an outside product on installs it, and the call did not accept the
47
- * access it asks for; the code is FailedPrecondition.
48
- */
49
- readonly AccessNotAccepted: "ACCESS_NOT_ACCEPTED";
38
+ readonly Unavailable: "PRODUCT_UNAVAILABLE";
39
+ };
40
+ /** The input that turned a product off: a ProductError's `cause`. */
41
+ export type ProductCause = "build" | "operator" | "plan" | "workspace";
42
+ /** The inputs that turn a product off. */
43
+ export declare const ProductCause: {
44
+ /** The deployment's build does not include the product. */
45
+ readonly Build: "build";
46
+ /** The deployment's config turns the product off. */
47
+ readonly Operator: "operator";
48
+ /** The workspace's plan leaves the product out. */
49
+ readonly Plan: "plan";
50
+ /** The workspace's settings turn the product off. */
51
+ readonly Workspace: "workspace";
52
+ };
53
+ /**
54
+ * Why the API refused a call and named the reason: the reason of the
55
+ * refusal's ErrorInfo detail, for a refusal that is not a product's.
56
+ */
57
+ export type RefusalReason = "DISPLAY_INPUT_NOT_ENABLED" | "IMAGE_BUILD_IN_PROGRESS" | "IMAGE_CHECK_FAILED" | "IMAGE_IN_USE" | "IMAGE_NAME_TAKEN" | "IMAGE_NOT_READY" | "IMAGE_NOT_SERVED" | "LAST_IDENTITY" | "LAST_MEMBER" | "LAST_OWNER" | "PERMISSION_MISSING" | "PLAN_EXCLUDES_IMAGES" | "TOSHI_LICENSED_WINDOWS";
58
+ /** The reasons the API names on a refusal that is not a product's. */
59
+ export declare const RefusalReason: {
60
+ /** The Windows VM takes no display input; the code is FailedPrecondition. */
61
+ readonly DisplayInputNotEnabled: "DISPLAY_INPUT_NOT_ENABLED";
62
+ /** The image already has a build running; the code is FailedPrecondition. */
63
+ readonly ImageBuildInProgress: "IMAGE_BUILD_IN_PROGRESS";
64
+ /** The registry of the revision's image could not say whether it serves the digest; the code is FailedPrecondition. */
65
+ readonly ImageCheckFailed: "IMAGE_CHECK_FAILED";
66
+ /** A workload or a service revision still uses the image; the code is FailedPrecondition. */
67
+ readonly ImageInUse: "IMAGE_IN_USE";
68
+ /** Another live image of the workspace has the name; the code is AlreadyExists. */
69
+ readonly ImageNameTaken: "IMAGE_NAME_TAKEN";
70
+ /** A workspace image named by a job, a sandbox or a service revision has no ready version; the code is FailedPrecondition. */
71
+ readonly ImageNotReady: "IMAGE_NOT_READY";
72
+ /** The revision's registry answered that it does not serve the image digest; the code is FailedPrecondition. */
73
+ readonly ImageNotServed: "IMAGE_NOT_SERVED";
74
+ /** The call would remove the caller's last identity; the code is FailedPrecondition. */
75
+ readonly LastIdentity: "LAST_IDENTITY";
76
+ /** The call would remove the workspace's last member; the code is FailedPrecondition. */
77
+ readonly LastMember: "LAST_MEMBER";
78
+ /** The call would leave the workspace without an owner; the code is FailedPrecondition. */
79
+ readonly LastOwner: "LAST_OWNER";
80
+ /** A role change names a permission the caller does not hold where the role applies; the code is PermissionDenied. */
81
+ readonly PermissionMissing: "PERMISSION_MISSING";
82
+ /** The workspace's plan does not include images; the code is FailedPrecondition. */
83
+ readonly PlanExcludesImages: "PLAN_EXCLUDES_IMAGES";
84
+ /** A Windows job Toshi licenses opens no display session; the code is FailedPrecondition. */
85
+ readonly ToshiLicensedWindows: "TOSHI_LICENSED_WINDOWS";
50
86
  };
51
87
  /**
52
- * The API's refusal of a call for a product: one that is off for the
53
- * workspace, or an outside product whose access the call did not accept.
88
+ * The API's refusal of a call for a product that is off for the workspace.
54
89
  * Every call the client makes throws it in place of the ConnectError, wait,
55
90
  * watch and execute included. It is a ConnectError, so `instanceof
56
91
  * ConnectError` and `code` read as before; test for it with
@@ -60,16 +95,33 @@ export declare class ProductError extends ConnectError {
60
95
  /** The product's ID, such as `runners`. */
61
96
  readonly product: string;
62
97
  readonly reason: ProductReason;
63
- /** The setting that turns the product on, such as `runners.enabled`; empty for ACCESS_NOT_ACCEPTED. */
64
- readonly feature: string;
65
- /** How far a product the deployment disables has wound down: `no_new_work`, `draining` or `stopped`; empty otherwise. */
98
+ /** The input that turned the product off, the refusal's `cause` metadata: `build`, `operator`, `plan` or `workspace`. */
99
+ readonly productCause: ProductCause;
100
+ /** How far a product the deployment turns off has wound down: `no_new_work`, `draining` or `stopped`; empty otherwise. */
66
101
  readonly state: string;
67
- /** The access an outside product asks for, for ACCESS_NOT_ACCEPTED: accept exactly this list to install it. */
68
- readonly access: string[];
69
102
  constructor(err: ConnectError, reason: ProductReason, metadata: ReadonlyMap<string, string>);
70
103
  static [Symbol.hasInstance](v: unknown): boolean;
71
104
  }
72
105
  /** Reports whether err is a product's refusal. */
73
106
  export declare function isProductError(err: unknown): err is ProductError;
107
+ /**
108
+ * The API's refusal that names a reason: an image still in use, a plan
109
+ * without images, a last identity, a rollback whose revision's image is gone,
110
+ * and the rest of {@link RefusalReason}. Every call the client makes throws
111
+ * it in place of the ConnectError. It is a ConnectError, so `instanceof
112
+ * ConnectError` and `code` read as before; test for it with
113
+ * {@link isRefusalError}.
114
+ */
115
+ export declare class RefusalError extends ConnectError {
116
+ readonly reason: RefusalReason;
117
+ constructor(err: ConnectError, reason: RefusalReason);
118
+ static [Symbol.hasInstance](v: unknown): boolean;
119
+ }
120
+ /** Reports whether err is a refusal that names a reason this module knows. */
121
+ export declare function isRefusalError(err: unknown): err is RefusalError;
74
122
  /** err as a ProductError when it is a product's refusal, and err unchanged otherwise. */
75
123
  export declare function asProductError(err: unknown): unknown;
124
+ /** err as a RefusalError when it is a refusal that names a reason this module knows, and err unchanged otherwise. */
125
+ export declare function asRefusalError(err: unknown): unknown;
126
+ /** err as one of this module's typed errors when the API's ErrorInfo detail names a reason it knows, and err unchanged otherwise. */
127
+ export declare function asTypedError(err: unknown): unknown;