@toshicloud/sdk 0.2.0 → 0.3.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 (53) hide show
  1. package/README.md +74 -18
  2. package/dist/client.d.ts +111 -18
  3. package/dist/client.js +82 -16
  4. package/dist/errors.d.ts +9 -1
  5. package/dist/errors.js +22 -0
  6. package/dist/execute.d.ts +3 -2
  7. package/dist/execute.js +3 -3
  8. package/dist/gen/toshi/workspace/v1/artifacts_pb.d.ts +25 -23
  9. package/dist/gen/toshi/workspace/v1/artifacts_pb.js +7 -7
  10. package/dist/gen/toshi/workspace/v1/audit_events_pb.d.ts +297 -0
  11. package/dist/gen/toshi/workspace/v1/audit_events_pb.js +37 -0
  12. package/dist/gen/toshi/workspace/v1/channels_pb.d.ts +2713 -0
  13. package/dist/gen/toshi/workspace/v1/channels_pb.js +336 -0
  14. package/dist/gen/toshi/workspace/v1/display_pb.d.ts +35 -11
  15. package/dist/gen/toshi/workspace/v1/display_pb.js +2 -2
  16. package/dist/gen/toshi/workspace/v1/fillers_pb.d.ts +920 -0
  17. package/dist/gen/toshi/workspace/v1/fillers_pb.js +94 -0
  18. package/dist/gen/toshi/workspace/v1/hostjobs_pb.d.ts +2048 -0
  19. package/dist/gen/toshi/workspace/v1/hostjobs_pb.js +226 -0
  20. package/dist/gen/toshi/workspace/v1/hosts_pb.d.ts +2135 -0
  21. package/dist/gen/toshi/workspace/v1/hosts_pb.js +301 -0
  22. package/dist/gen/toshi/workspace/v1/projects_pb.d.ts +157 -3
  23. package/dist/gen/toshi/workspace/v1/projects_pb.js +2 -2
  24. package/dist/gen/toshi/workspace/v1/recordings_pb.d.ts +76 -0
  25. package/dist/gen/toshi/workspace/v1/recordings_pb.js +1 -1
  26. package/dist/gen/toshi/workspace/v1/secrets_pb.d.ts +175 -23
  27. package/dist/gen/toshi/workspace/v1/secrets_pb.js +9 -9
  28. package/dist/gen/toshi/workspace/v1/storage_providers_pb.d.ts +717 -0
  29. package/dist/gen/toshi/workspace/v1/storage_providers_pb.js +74 -0
  30. package/dist/gen/toshi/workspace/v1/tokens_pb.d.ts +518 -0
  31. package/dist/gen/toshi/workspace/v1/tokens_pb.js +88 -0
  32. package/dist/gen/toshi/workspace/v1/volumes_pb.d.ts +39 -1
  33. package/dist/gen/toshi/workspace/v1/volumes_pb.js +2 -2
  34. package/dist/gen/toshi/workspace/v1/webhook_pb.d.ts +62 -18
  35. package/dist/gen/toshi/workspace/v1/webhook_pb.js +3 -3
  36. package/dist/gen/toshi/workspace/v1/workloads_pb.d.ts +2858 -0
  37. package/dist/gen/toshi/workspace/v1/workloads_pb.js +261 -0
  38. package/dist/gen/toshi/workspace/v1/workspace_pb.d.ts +1377 -2130
  39. package/dist/gen/toshi/workspace/v1/workspace_pb.js +235 -272
  40. package/dist/gen/toshi/workspace/v1/workspace_service_pb.d.ts +3719 -0
  41. package/dist/gen/toshi/workspace/v1/workspace_service_pb.js +270 -0
  42. package/dist/index.d.ts +14 -2
  43. package/dist/index.js +13 -1
  44. package/dist/output.d.ts +3 -2
  45. package/dist/output.js +2 -2
  46. package/dist/retry.js +116 -30
  47. package/dist/run.d.ts +10 -0
  48. package/dist/run.js +175 -0
  49. package/dist/wait.d.ts +10 -5
  50. package/dist/wait.js +23 -11
  51. package/dist/watch.d.ts +14 -6
  52. package/dist/watch.js +138 -85
  53. package/package.json +1 -1
package/README.md CHANGED
@@ -37,7 +37,7 @@ const toshi = createToshiClient({
37
37
  project: "default",
38
38
  });
39
39
 
40
- const { projects } = await toshi.projects.listProjects({});
40
+ const { projects } = await toshi.project.listProjects({});
41
41
  ```
42
42
 
43
43
  In a browser, with `@connectrpc/connect-web`:
@@ -70,6 +70,27 @@ server and any CA come from the caller.
70
70
  5, 200 ms and 10 s, with full jitter. `retry: false` turns off retries
71
71
  and watch reconnects.
72
72
 
73
+ ### The client's shape
74
+
75
+ The client has one field per Workspace API service: `project`, `secret`,
76
+ `webhook`, `display`, `channel`, `recording`, `volume`, `artifact`,
77
+ `artifactUpload`, `workspace`, `hostJob`, `host`, `token`, `auditEvent`,
78
+ `filler` and `storageProvider`, each holding that service's generated
79
+ client.
80
+ `workload` holds `WorkloadService`'s calls, grouped by the type they
81
+ name: `workload.jobs`, `workload.sandboxes` and `workload.services`, one
82
+ method for each call (`create`, `get`, `list`, `update`, `delete`,
83
+ `wait`, `watch`, and `cancel` on a job, `execute` on a sandbox, `start`,
84
+ `stop`, `setMode`, `listRuns`, `wake`, `hold` and `release` on a service). The calls that
85
+ span the three types sit on `workload` itself: `listEvents`, `watchEvents`,
86
+ `getOperation` and `waitOperation`. `run` sits at the top of the client, as
87
+ `toshi run` sits at the top of the CLI.
88
+
89
+ ```ts
90
+ const { jobs } = await toshi.workload.jobs.list({ pageSize: 10 });
91
+ const job = await toshi.workload.jobs.get({ id: jobs[0].id });
92
+ ```
93
+
73
94
  ### Retries
74
95
 
75
96
  The client retries a read, and a write that carries a request key, when
@@ -85,22 +106,34 @@ from a server error.
85
106
 
86
107
  ### Waiting and watching
87
108
 
88
- - `waitWorkload(req)` and `waitOperation(req)` ask again until the
89
- workload reaches the state or the operation is done.
90
- - `watchWorkloads(req)`, `watchEvents(req)` and `watchOutput(req)`
91
- resolve to a watch you read with `for await`. A watch opens its stream
92
- when you start reading and reconnects after a connection failure from
93
- where it stopped: `cursor` for workloads and events, `offsets` (bytes,
94
- as `bigint`) for output. With no cursor, `watchWorkloads` lists the
95
- workloads first, and `snapshot` holds them once its promise resolves;
96
- `watchEvents` yields the past events page by page before the live ones.
97
- `close()` or leaving the loop ends the stream. The output watch ends at
98
- the output's end.
109
+ - `workload.jobs.wait(req)`, `workload.sandboxes.wait(req)` and
110
+ `workload.services.wait(req)` ask again until the workload reaches the
111
+ state; `workload.waitOperation(req)` asks until the operation is done.
112
+ - `workload.getOperation(req)` returns one operation by id: a call that
113
+ takes long hands one back, and this asks for it again.
114
+ - `workload.listEvents(req)` returns the events after a cursor, oldest
115
+ first, a page at a time, for a client that polls or catches up after
116
+ downtime; `workload.watchEvents(req)` is the live form.
117
+ - `workload.jobs.watch(req)`, `workload.sandboxes.watch(req)`,
118
+ `workload.services.watch(req)`, `workload.watchEvents(req)` and
119
+ `workload.jobs.watchOutput(req)` resolve to a watch you read with
120
+ `for await`. A watch opens its stream when you start reading and
121
+ reconnects after a connection failure from where it stopped: `cursor`
122
+ for the typed watches and events, `offsets` (bytes, as `bigint`) for
123
+ output. With no cursor, a typed watch lists that type first, and
124
+ `snapshot` holds them once its promise resolves; `watchEvents`
125
+ yields the past events page by page before the live ones. `close()` or
126
+ leaving the loop ends the stream. The output watch ends at the output's
127
+ end.
128
+ - Every watch yields the stream's responses: a job watch yields
129
+ `WatchJobsResponse` (`job`, `cursor`, `type`), an event watch yields
130
+ `WatchEventsResponse` (`event`), and the output watch yields
131
+ `WatchOutputResponse` (`stdout`, `stderr` or `end`).
99
132
 
100
133
  ```ts
101
- const watch = await toshi.watchEvents({ workloadId: id });
102
- for await (const event of watch) {
103
- console.log(event.name);
134
+ const watch = await toshi.workload.watchEvents({ workloadId: id });
135
+ for await (const { event } of watch) {
136
+ console.log(event?.name);
104
137
  }
105
138
  // watch.cursor resumes a later watch after the last event read.
106
139
  ```
@@ -109,8 +142,8 @@ Every helper takes `{ signal }`, an `AbortSignal` that ends it.
109
142
 
110
143
  ### Running a command
111
144
 
112
- `execute(start)` runs a command and resolves once the server has started
113
- it. The process is one stream of output chunks, `{ stream: "stdout" |
145
+ `workload.sandboxes.execute(start)` runs a command in a running sandbox
146
+ and resolves once the server has started it. The process is one stream of output chunks, `{ stream: "stdout" |
114
147
  "stderr", data: Uint8Array }`. `write` sends standard input in frames of
115
148
  at most 64 KiB and resolves once the stream has taken the last one; await
116
149
  it before the next `write`, which is refused while one is pending, so the
@@ -119,7 +152,10 @@ after a pending write, and a later `write` is refused. `wait` settles when
119
152
  the command ends.
120
153
 
121
154
  ```ts
122
- const proc = await toshi.execute({ workloadId: id, command: { argv: ["cat"] } });
155
+ const proc = await toshi.workload.sandboxes.execute({
156
+ sandboxId: id,
157
+ command: { argv: ["cat"] },
158
+ });
123
159
  await proc.write("hello\n");
124
160
  await proc.closeStdin();
125
161
  for await (const chunk of proc) {
@@ -141,6 +177,26 @@ Execute needs a bidirectional stream: `@connectrpc/connect-node` with
141
177
  call. Over a transport without HTTP/2, the call fails with the
142
178
  transport's or the server's error.
143
179
 
180
+ ### Running a job
181
+
182
+ `run(req)` creates a job with a `CreateJobRequest` and resolves to the
183
+ job and its output, which is what `toshi run` does: `CreateJob`, then
184
+ `WatchOutput`. It yields output chunks like an execution, and `wait()`
185
+ resolves when the job exits 0 and rejects with `JobExitError` otherwise.
186
+
187
+ ```ts
188
+ const run = await toshi.run({
189
+ spec: { command: { argv: ["./test.sh"] } },
190
+ });
191
+ for await (const chunk of run) {
192
+ process[chunk.stream].write(chunk.data);
193
+ }
194
+ await run.wait(); // rejects with JobExitError on a non-zero exit
195
+ ```
196
+
197
+ Stopping the loop stops only the output stream: the job keeps running,
198
+ and `workload.jobs.cancel` ends it.
199
+
144
200
  ## Generated code
145
201
 
146
202
  `src/gen/toshi/workspace/v1/` is generated by
package/dist/client.d.ts CHANGED
@@ -1,11 +1,24 @@
1
1
  import type { MessageInitShape } from "@bufbuild/protobuf";
2
2
  import type { Client, Interceptor, Transport } from "@connectrpc/connect";
3
+ import { ArtifactService, ArtifactUploadService } from "./gen/toshi/workspace/v1/artifacts_pb.js";
4
+ import { AuditEventService } from "./gen/toshi/workspace/v1/audit_events_pb.js";
5
+ import { ChannelService } from "./gen/toshi/workspace/v1/channels_pb.js";
3
6
  import { DisplayService } from "./gen/toshi/workspace/v1/display_pb.js";
4
- import { ExecuteStartSchema, WaitOperationRequestSchema, WaitWorkloadRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema, WatchWorkloadsRequestSchema, WorkspaceService } from "./gen/toshi/workspace/v1/workspace_pb.js";
5
- import type { Event, Operation, WaitWorkloadResponse, WatchOutputResponse, WatchWorkloadsResponse, Workload } from "./gen/toshi/workspace/v1/workspace_pb.js";
7
+ import { FillerService } from "./gen/toshi/workspace/v1/fillers_pb.js";
8
+ import { HostJobService } from "./gen/toshi/workspace/v1/hostjobs_pb.js";
9
+ import { HostService } from "./gen/toshi/workspace/v1/hosts_pb.js";
6
10
  import { ProjectService } from "./gen/toshi/workspace/v1/projects_pb.js";
11
+ import { RecordingService } from "./gen/toshi/workspace/v1/recordings_pb.js";
7
12
  import { SecretService } from "./gen/toshi/workspace/v1/secrets_pb.js";
13
+ import { StorageProviderService } from "./gen/toshi/workspace/v1/storage_providers_pb.js";
14
+ import { TokenService } from "./gen/toshi/workspace/v1/tokens_pb.js";
15
+ import { VolumeService } from "./gen/toshi/workspace/v1/volumes_pb.js";
8
16
  import { WebhookService } from "./gen/toshi/workspace/v1/webhook_pb.js";
17
+ import { WorkspaceService } from "./gen/toshi/workspace/v1/workspace_service_pb.js";
18
+ 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";
19
+ 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";
20
+ import { ExecuteStartSchema, GetOperationRequestSchema, HoldServiceRequestSchema, ListEventsRequestSchema, ListServiceRunsRequestSchema, ReleaseServiceRequestSchema, SetServiceModeRequestSchema, StartServiceRequestSchema, StopServiceRequestSchema, WaitOperationRequestSchema, WakeServiceRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema } from "./gen/toshi/workspace/v1/workspace_pb.js";
21
+ import type { GetOperationResponse, HoldServiceResponse, Job, ListEventsResponse, ListServiceRunsResponse, Operation, ReleaseServiceResponse, Sandbox, Service, SetServiceModeResponse, StartServiceResponse, StopServiceResponse, WakeServiceResponse, WatchEventsResponse, WatchOutputResponse } from "./gen/toshi/workspace/v1/workspace_pb.js";
9
22
  import type { RetryPolicy } from "./retry.js";
10
23
  /** How the client is configured; the caller builds the transport. */
11
24
  export interface ToshiOptions {
@@ -21,32 +34,103 @@ export interface ToshiOptions {
21
34
  export interface CallOptions {
22
35
  signal?: AbortSignal;
23
36
  }
24
- /** The five generated clients plus the helpers that live on top of them. */
37
+ /** One client per Workspace API service, plus the helpers that live on top of them. */
25
38
  export interface ToshiClient {
39
+ readonly project: Client<typeof ProjectService>;
40
+ readonly secret: Client<typeof SecretService>;
41
+ readonly webhook: Client<typeof WebhookService>;
42
+ readonly display: Client<typeof DisplayService>;
43
+ readonly channel: Client<typeof ChannelService>;
44
+ readonly recording: Client<typeof RecordingService>;
45
+ readonly volume: Client<typeof VolumeService>;
46
+ readonly artifact: Client<typeof ArtifactService>;
47
+ readonly artifactUpload: Client<typeof ArtifactUploadService>;
48
+ readonly workload: WorkloadClient;
26
49
  readonly workspace: Client<typeof WorkspaceService>;
27
- readonly projects: Client<typeof ProjectService>;
28
- readonly secrets: Client<typeof SecretService>;
29
- readonly webhooks: Client<typeof WebhookService>;
30
- readonly displays: Client<typeof DisplayService>;
31
- waitWorkload(req: MessageInitShape<typeof WaitWorkloadRequestSchema>, opts?: CallOptions): Promise<WaitWorkloadResponse>;
50
+ readonly hostJob: Client<typeof HostJobService>;
51
+ readonly host: Client<typeof HostService>;
52
+ readonly token: Client<typeof TokenService>;
53
+ readonly auditEvent: Client<typeof AuditEventService>;
54
+ readonly filler: Client<typeof FillerService>;
55
+ readonly storageProvider: Client<typeof StorageProviderService>;
56
+ run(req: MessageInitShape<typeof CreateJobRequestSchema>, opts?: CallOptions): Promise<JobRun>;
57
+ }
58
+ /** WorkloadService's calls, grouped by the type they name. */
59
+ export interface WorkloadClient {
60
+ readonly jobs: JobsClient;
61
+ readonly sandboxes: SandboxesClient;
62
+ readonly services: ServicesClient;
32
63
  waitOperation(req: MessageInitShape<typeof WaitOperationRequestSchema>, opts?: CallOptions): Promise<Operation>;
33
- watchWorkloads(req: MessageInitShape<typeof WatchWorkloadsRequestSchema>, opts?: CallOptions): Promise<WorkloadWatch>;
64
+ /** Lists the events after a cursor, a page at a time; `watchEvents` is the live form. */
65
+ listEvents(req: MessageInitShape<typeof ListEventsRequestSchema>, opts?: CallOptions): Promise<ListEventsResponse>;
34
66
  watchEvents(req: MessageInitShape<typeof WatchEventsRequestSchema>, opts?: CallOptions): Promise<EventWatch>;
67
+ /** Returns one operation: the id a long call hands back, asked for again. */
68
+ getOperation(req: MessageInitShape<typeof GetOperationRequestSchema>, opts?: CallOptions): Promise<GetOperationResponse>;
69
+ }
70
+ /** A job's calls: one method for each of WorkloadService's job calls. */
71
+ export interface JobsClient {
72
+ create(req: MessageInitShape<typeof CreateJobRequestSchema>, opts?: CallOptions): Promise<CreateJobResponse>;
73
+ get(req: MessageInitShape<typeof GetJobRequestSchema>, opts?: CallOptions): Promise<GetJobResponse>;
74
+ list(req: MessageInitShape<typeof ListJobsRequestSchema>, opts?: CallOptions): Promise<ListJobsResponse>;
75
+ update(req: MessageInitShape<typeof UpdateJobRequestSchema>, opts?: CallOptions): Promise<UpdateJobResponse>;
76
+ delete(req: MessageInitShape<typeof DeleteJobRequestSchema>, opts?: CallOptions): Promise<DeleteJobResponse>;
77
+ cancel(req: MessageInitShape<typeof CancelJobRequestSchema>, opts?: CallOptions): Promise<CancelJobResponse>;
78
+ wait(req: MessageInitShape<typeof WaitJobRequestSchema>, opts?: CallOptions): Promise<WaitJobResponse>;
79
+ watch(req: MessageInitShape<typeof WatchJobsRequestSchema>, opts?: CallOptions): Promise<JobWatch>;
35
80
  watchOutput(req: MessageInitShape<typeof WatchOutputRequestSchema>, opts?: CallOptions): Promise<OutputWatch>;
81
+ }
82
+ /** A sandbox's calls: one method for each of WorkloadService's sandbox calls. */
83
+ export interface SandboxesClient {
84
+ create(req: MessageInitShape<typeof CreateSandboxRequestSchema>, opts?: CallOptions): Promise<CreateSandboxResponse>;
85
+ get(req: MessageInitShape<typeof GetSandboxRequestSchema>, opts?: CallOptions): Promise<GetSandboxResponse>;
86
+ list(req: MessageInitShape<typeof ListSandboxesRequestSchema>, opts?: CallOptions): Promise<ListSandboxesResponse>;
87
+ update(req: MessageInitShape<typeof UpdateSandboxRequestSchema>, opts?: CallOptions): Promise<UpdateSandboxResponse>;
88
+ delete(req: MessageInitShape<typeof DeleteSandboxRequestSchema>, opts?: CallOptions): Promise<DeleteSandboxResponse>;
89
+ wait(req: MessageInitShape<typeof WaitSandboxRequestSchema>, opts?: CallOptions): Promise<WaitSandboxResponse>;
90
+ watch(req: MessageInitShape<typeof WatchSandboxesRequestSchema>, opts?: CallOptions): Promise<SandboxWatch>;
36
91
  execute(start: MessageInitShape<typeof ExecuteStartSchema>, opts?: CallOptions): Promise<Process>;
37
92
  }
38
- /** A live stream of workload changes (watch.go:40-60). */
39
- export interface WorkloadWatch extends AsyncIterable<WatchWorkloadsResponse> {
93
+ /** A service's calls: one method for each of WorkloadService's service calls. */
94
+ export interface ServicesClient {
95
+ create(req: MessageInitShape<typeof CreateServiceRequestSchema>, opts?: CallOptions): Promise<CreateServiceResponse>;
96
+ get(req: MessageInitShape<typeof GetServiceRequestSchema>, opts?: CallOptions): Promise<GetServiceResponse>;
97
+ list(req: MessageInitShape<typeof ListServicesRequestSchema>, opts?: CallOptions): Promise<ListServicesResponse>;
98
+ update(req: MessageInitShape<typeof UpdateServiceRequestSchema>, opts?: CallOptions): Promise<UpdateServiceResponse>;
99
+ delete(req: MessageInitShape<typeof DeleteServiceRequestSchema>, opts?: CallOptions): Promise<DeleteServiceResponse>;
100
+ wait(req: MessageInitShape<typeof WaitServiceRequestSchema>, opts?: CallOptions): Promise<WaitServiceResponse>;
101
+ watch(req: MessageInitShape<typeof WatchServicesRequestSchema>, opts?: CallOptions): Promise<ServiceWatch>;
102
+ start(req: MessageInitShape<typeof StartServiceRequestSchema>, opts?: CallOptions): Promise<StartServiceResponse>;
103
+ stop(req: MessageInitShape<typeof StopServiceRequestSchema>, opts?: CallOptions): Promise<StopServiceResponse>;
104
+ setMode(req: MessageInitShape<typeof SetServiceModeRequestSchema>, opts?: CallOptions): Promise<SetServiceModeResponse>;
105
+ release(req: MessageInitShape<typeof ReleaseServiceRequestSchema>, opts?: CallOptions): Promise<ReleaseServiceResponse>;
106
+ wake(req: MessageInitShape<typeof WakeServiceRequestSchema>, opts?: CallOptions): Promise<WakeServiceResponse>;
107
+ hold(req: MessageInitShape<typeof HoldServiceRequestSchema>, opts?: CallOptions): Promise<HoldServiceResponse>;
108
+ listRuns(req: MessageInitShape<typeof ListServiceRunsRequestSchema>, opts?: CallOptions): Promise<ListServiceRunsResponse>;
109
+ }
110
+ /** A live stream of a job's changes. */
111
+ export interface JobWatch extends AsyncIterable<WatchJobsResponse> {
112
+ readonly cursor: string;
113
+ readonly snapshot: Job[] | undefined;
114
+ close(): void;
115
+ }
116
+ /** A live stream of a sandbox's changes. */
117
+ export interface SandboxWatch extends AsyncIterable<WatchSandboxesResponse> {
118
+ readonly cursor: string;
119
+ readonly snapshot: Sandbox[] | undefined;
120
+ close(): void;
121
+ }
122
+ /** A live stream of a service's changes. */
123
+ export interface ServiceWatch extends AsyncIterable<WatchServicesResponse> {
40
124
  readonly cursor: string;
41
- readonly snapshot: Workload[] | undefined;
125
+ readonly snapshot: Service[] | undefined;
42
126
  close(): void;
43
127
  }
44
- /** A live stream of events (watch.go:261-460). */
45
- export interface EventWatch extends AsyncIterable<Event> {
128
+ /** A live stream of events; every message is the stream's response. */
129
+ export interface EventWatch extends AsyncIterable<WatchEventsResponse> {
46
130
  readonly cursor: string;
47
131
  close(): void;
48
132
  }
49
- /** A live stream of a job's output (output.go). */
133
+ /** A live stream of a job's output. */
50
134
  export interface OutputWatch extends AsyncIterable<WatchOutputResponse> {
51
135
  readonly offsets: {
52
136
  readonly stdout: bigint;
@@ -54,7 +138,7 @@ export interface OutputWatch extends AsyncIterable<WatchOutputResponse> {
54
138
  };
55
139
  close(): void;
56
140
  }
57
- /** One chunk of an execution's output. */
141
+ /** One chunk of output. */
58
142
  export type OutputChunk = {
59
143
  stream: "stdout";
60
144
  data: Uint8Array;
@@ -62,7 +146,7 @@ export type OutputChunk = {
62
146
  stream: "stderr";
63
147
  data: Uint8Array;
64
148
  };
65
- /** A running execution (execute.go). */
149
+ /** A running execution. */
66
150
  export interface Process extends AsyncIterable<OutputChunk> {
67
151
  readonly executionId: string;
68
152
  readonly requestKey: string;
@@ -70,9 +154,18 @@ export interface Process extends AsyncIterable<OutputChunk> {
70
154
  closeStdin(): Promise<void>;
71
155
  wait(): Promise<void>;
72
156
  }
157
+ /** A job started by `run`: its output as it comes, then its outcome. */
158
+ export interface JobRun extends AsyncIterable<OutputChunk> {
159
+ readonly job: Job;
160
+ wait(): Promise<void>;
161
+ }
73
162
  /**
74
163
  * Creates the client: one normalized retry setting, the auth, scope, key and
75
164
  * retry interceptors (auth outermost, retry innermost), one transport for every
76
- * client and helper (client.go:40-93).
165
+ * client and helper.
166
+ *
167
+ * Since 0.2.0 the workload calls are grouped by the type they name under
168
+ * `workload`; every other service, WorkspaceService's included, is handed out
169
+ * as one client.
77
170
  */
78
171
  export declare function createToshiClient(options: ToshiOptions): ToshiClient;
package/dist/client.js CHANGED
@@ -1,20 +1,37 @@
1
1
  import { create } from "@bufbuild/protobuf";
2
2
  import { createClient } from "@connectrpc/connect";
3
3
  import { executeCommand } from "./execute.js";
4
+ import { ArtifactService, ArtifactUploadService } from "./gen/toshi/workspace/v1/artifacts_pb.js";
5
+ import { AuditEventService } from "./gen/toshi/workspace/v1/audit_events_pb.js";
6
+ import { ChannelService } from "./gen/toshi/workspace/v1/channels_pb.js";
4
7
  import { DisplayService } from "./gen/toshi/workspace/v1/display_pb.js";
5
- import { ExecuteStartSchema, WaitOperationRequestSchema, WaitWorkloadRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema, WatchWorkloadsRequestSchema, WorkspaceService, } from "./gen/toshi/workspace/v1/workspace_pb.js";
8
+ import { FillerService } from "./gen/toshi/workspace/v1/fillers_pb.js";
9
+ import { HostJobService } from "./gen/toshi/workspace/v1/hostjobs_pb.js";
10
+ import { HostService } from "./gen/toshi/workspace/v1/hosts_pb.js";
6
11
  import { ProjectService } from "./gen/toshi/workspace/v1/projects_pb.js";
12
+ import { RecordingService } from "./gen/toshi/workspace/v1/recordings_pb.js";
7
13
  import { SecretService } from "./gen/toshi/workspace/v1/secrets_pb.js";
14
+ import { StorageProviderService } from "./gen/toshi/workspace/v1/storage_providers_pb.js";
15
+ import { TokenService } from "./gen/toshi/workspace/v1/tokens_pb.js";
16
+ import { VolumeService } from "./gen/toshi/workspace/v1/volumes_pb.js";
8
17
  import { WebhookService } from "./gen/toshi/workspace/v1/webhook_pb.js";
18
+ import { WorkspaceService } from "./gen/toshi/workspace/v1/workspace_service_pb.js";
19
+ 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";
20
+ import { ExecuteStartSchema, GetOperationRequestSchema, HoldServiceRequestSchema, ListEventsRequestSchema, ListServiceRunsRequestSchema, ReleaseServiceRequestSchema, SetServiceModeRequestSchema, StartServiceRequestSchema, StopServiceRequestSchema, WaitOperationRequestSchema, WakeServiceRequestSchema, WatchEventsRequestSchema, WatchOutputRequestSchema, } from "./gen/toshi/workspace/v1/workspace_pb.js";
9
21
  import { openOutputWatch } from "./output.js";
10
22
  import { authInterceptor, keyInterceptor, retryInterceptor, scopeInterceptor } from "./interceptors.js";
11
23
  import { normalizeRetry } from "./retry.js";
12
- import { openEventWatch, openWorkloadWatch } from "./watch.js";
13
- import { waitOperation, waitWorkload } from "./wait.js";
24
+ import { openEventWatch, openJobWatch, openSandboxWatch, openServiceWatch } from "./watch.js";
25
+ import { runJob } from "./run.js";
26
+ import { waitJob, waitOperation, waitSandbox, waitService } from "./wait.js";
14
27
  /**
15
28
  * Creates the client: one normalized retry setting, the auth, scope, key and
16
29
  * retry interceptors (auth outermost, retry innermost), one transport for every
17
- * client and helper (client.go:40-93).
30
+ * client and helper.
31
+ *
32
+ * Since 0.2.0 the workload calls are grouped by the type they name under
33
+ * `workload`; every other service, WorkspaceService's included, is handed out
34
+ * as one client.
18
35
  */
19
36
  export function createToshiClient(options) {
20
37
  const retry = normalizeRetry(options.retry);
@@ -25,18 +42,67 @@ export function createToshiClient(options) {
25
42
  retryInterceptor(retry),
26
43
  ];
27
44
  const transport = options.transport(interceptors);
28
- const workspace = createClient(WorkspaceService, transport);
45
+ const workloadService = createClient(WorkloadService, transport);
29
46
  return {
30
- workspace,
31
- projects: createClient(ProjectService, transport),
32
- secrets: createClient(SecretService, transport),
33
- webhooks: createClient(WebhookService, transport),
34
- displays: createClient(DisplayService, transport),
35
- waitWorkload: (req, opts) => waitWorkload(workspace, create(WaitWorkloadRequestSchema, req), opts),
36
- waitOperation: (req, opts) => waitOperation(workspace, create(WaitOperationRequestSchema, req), opts),
37
- watchWorkloads: (req, opts) => openWorkloadWatch(workspace, retry, create(WatchWorkloadsRequestSchema, req), opts),
38
- watchEvents: (req, opts) => openEventWatch(workspace, retry, create(WatchEventsRequestSchema, req), opts),
39
- watchOutput: (req, opts) => openOutputWatch(workspace, retry, create(WatchOutputRequestSchema, req), opts),
40
- execute: (start, opts) => executeCommand(workspace, start, opts),
47
+ project: createClient(ProjectService, transport),
48
+ secret: createClient(SecretService, transport),
49
+ webhook: createClient(WebhookService, transport),
50
+ display: createClient(DisplayService, transport),
51
+ channel: createClient(ChannelService, transport),
52
+ recording: createClient(RecordingService, transport),
53
+ volume: createClient(VolumeService, transport),
54
+ artifact: createClient(ArtifactService, transport),
55
+ artifactUpload: createClient(ArtifactUploadService, transport),
56
+ workload: {
57
+ jobs: {
58
+ create: (req, opts) => workloadService.createJob(req, opts),
59
+ get: (req, opts) => workloadService.getJob(req, opts),
60
+ list: (req, opts) => workloadService.listJobs(req, opts),
61
+ update: (req, opts) => workloadService.updateJob(req, opts),
62
+ delete: (req, opts) => workloadService.deleteJob(req, opts),
63
+ cancel: (req, opts) => workloadService.cancelJob(req, opts),
64
+ wait: (req, opts) => waitJob(workloadService, create(WaitJobRequestSchema, req), opts),
65
+ watch: (req, opts) => openJobWatch(workloadService, retry, create(WatchJobsRequestSchema, req), opts),
66
+ watchOutput: (req, opts) => openOutputWatch(workloadService, retry, create(WatchOutputRequestSchema, req), opts),
67
+ },
68
+ sandboxes: {
69
+ create: (req, opts) => workloadService.createSandbox(req, opts),
70
+ get: (req, opts) => workloadService.getSandbox(req, opts),
71
+ list: (req, opts) => workloadService.listSandboxes(req, opts),
72
+ update: (req, opts) => workloadService.updateSandbox(req, opts),
73
+ delete: (req, opts) => workloadService.deleteSandbox(req, opts),
74
+ wait: (req, opts) => waitSandbox(workloadService, create(WaitSandboxRequestSchema, req), opts),
75
+ watch: (req, opts) => openSandboxWatch(workloadService, retry, create(WatchSandboxesRequestSchema, req), opts),
76
+ execute: (start, opts) => executeCommand(workloadService, start, opts),
77
+ },
78
+ services: {
79
+ create: (req, opts) => workloadService.createService(req, opts),
80
+ get: (req, opts) => workloadService.getService(req, opts),
81
+ list: (req, opts) => workloadService.listServices(req, opts),
82
+ update: (req, opts) => workloadService.updateService(req, opts),
83
+ delete: (req, opts) => workloadService.deleteService(req, opts),
84
+ wait: (req, opts) => waitService(workloadService, create(WaitServiceRequestSchema, req), opts),
85
+ watch: (req, opts) => openServiceWatch(workloadService, retry, create(WatchServicesRequestSchema, req), opts),
86
+ start: (req, opts) => workloadService.startService(req, opts),
87
+ stop: (req, opts) => workloadService.stopService(req, opts),
88
+ setMode: (req, opts) => workloadService.setServiceMode(req, opts),
89
+ release: (req, opts) => workloadService.releaseService(req, opts),
90
+ wake: (req, opts) => workloadService.wakeService(req, opts),
91
+ hold: (req, opts) => workloadService.holdService(req, opts),
92
+ listRuns: (req, opts) => workloadService.listServiceRuns(req, opts),
93
+ },
94
+ waitOperation: (req, opts) => waitOperation(workloadService, create(WaitOperationRequestSchema, req), opts),
95
+ listEvents: (req, opts) => workloadService.listEvents(req, opts),
96
+ watchEvents: (req, opts) => openEventWatch(workloadService, retry, create(WatchEventsRequestSchema, req), opts),
97
+ getOperation: (req, opts) => workloadService.getOperation(req, opts),
98
+ },
99
+ run: (req, opts) => runJob(workloadService, retry, create(CreateJobRequestSchema, req), opts),
100
+ workspace: createClient(WorkspaceService, transport),
101
+ hostJob: createClient(HostJobService, transport),
102
+ host: createClient(HostService, transport),
103
+ token: createClient(TokenService, transport),
104
+ auditEvent: createClient(AuditEventService, transport),
105
+ filler: createClient(FillerService, transport),
106
+ storageProvider: createClient(StorageProviderService, transport),
41
107
  };
42
108
  }
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ExecutionExit } from "./gen/toshi/workspace/v1/workspace_pb.js";
1
+ import type { ExecutionExit, OutputEnd } from "./gen/toshi/workspace/v1/workspace_pb.js";
2
2
  /** A command that ended non-zero, or was cancelled before it had a status (errors.go:9-26). */
3
3
  export declare class ExitError extends Error {
4
4
  /** The execution the exit belongs to. */
@@ -7,6 +7,14 @@ export declare class ExitError extends Error {
7
7
  readonly exit: ExecutionExit;
8
8
  constructor(executionId: string, exit: ExecutionExit);
9
9
  }
10
+ /** A job `run` started that did not exit 0. */
11
+ export declare class JobExitError extends Error {
12
+ /** The job's id. */
13
+ readonly jobId: string;
14
+ /** The job's end-of-output message. */
15
+ readonly end: OutputEnd;
16
+ constructor(jobId: string, end: OutputEnd);
17
+ }
10
18
  /**
11
19
  * An Execute stream that ended before its exit message; stdin cannot be replayed
12
20
  * (errors.go:28-49). The cause is the error that ended the stream.
package/dist/errors.js CHANGED
@@ -11,6 +11,28 @@ export class ExitError extends Error {
11
11
  this.exit = exit;
12
12
  }
13
13
  }
14
+ /** A job `run` started that did not exit 0. */
15
+ export class JobExitError extends Error {
16
+ /** The job's id. */
17
+ jobId;
18
+ /** The job's end-of-output message. */
19
+ end;
20
+ constructor(jobId, end) {
21
+ super(jobMessage(jobId, end));
22
+ this.name = "JobExitError";
23
+ this.jobId = jobId;
24
+ this.end = end;
25
+ }
26
+ }
27
+ function jobMessage(jobId, end) {
28
+ if (end.cancelled) {
29
+ return `toshi: job ${jobId} was cancelled`;
30
+ }
31
+ if (end.exitCode === undefined) {
32
+ return `toshi: job ${jobId} ended without an exit status`;
33
+ }
34
+ return `toshi: job ${jobId} exited with status ${end.exitCode}`;
35
+ }
14
36
  function exitMessage(executionId, exit) {
15
37
  if (exit.cancelled) {
16
38
  return `toshi: execution ${executionId} was cancelled`;
package/dist/execute.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { MessageInitShape } from "@bufbuild/protobuf";
2
2
  import type { Client } from "@connectrpc/connect";
3
3
  import type { CallOptions, Process } from "./client.js";
4
- import { ExecuteStartSchema, WorkspaceService } from "./gen/toshi/workspace/v1/workspace_pb.js";
4
+ import { ExecuteStartSchema } from "./gen/toshi/workspace/v1/workspace_pb.js";
5
+ import type { WorkloadService } from "./gen/toshi/workspace/v1/workloads_pb.js";
5
6
  /** Starts a command and returns its process; the only export of this module. */
6
- export declare function executeCommand(workspace: Client<typeof WorkspaceService>, start: MessageInitShape<typeof ExecuteStartSchema> | undefined, opts?: CallOptions): Promise<Process>;
7
+ export declare function executeCommand(service: Client<typeof WorkloadService>, start: MessageInitShape<typeof ExecuteStartSchema> | undefined, opts?: CallOptions): Promise<Process>;
package/dist/execute.js CHANGED
@@ -3,12 +3,12 @@ import { Code, ConnectError } from "@connectrpc/connect";
3
3
  import { createWritableIterable } from "@connectrpc/connect/protocol";
4
4
  import { ExitError, StreamLostError } from "./errors.js";
5
5
  import { isStreamLoss } from "./retry.js";
6
- import { ExecuteRequestSchema, ExecuteStartSchema, WorkspaceService, } from "./gen/toshi/workspace/v1/workspace_pb.js";
6
+ import { ExecuteRequestSchema, ExecuteStartSchema, } from "./gen/toshi/workspace/v1/workspace_pb.js";
7
7
  // The largest standard-input frame Execute sends (execute.go:14-16).
8
8
  const STDIN_CHUNK = 64 << 10;
9
9
  const BROWSER_MESSAGE = 'toshi: Execute needs a Node transport over HTTP/2 (@connectrpc/connect-node with httpVersion "2"); a browser cannot open a bidirectional stream';
10
10
  /** Starts a command and returns its process; the only export of this module. */
11
- export async function executeCommand(workspace, start, opts) {
11
+ export async function executeCommand(service, start, opts) {
12
12
  // G3: the browser check runs before any message or transport work, and reads
13
13
  // process only through globalThis so src/ needs no Node import.
14
14
  const nodeProcess = globalThis.process;
@@ -43,7 +43,7 @@ export async function executeCommand(workspace, start, opts) {
43
43
  const finishHandshake = () => {
44
44
  signal?.removeEventListener("abort", onAbort);
45
45
  };
46
- const responses = workspace.execute(input, { signal: abort.signal });
46
+ const responses = service.execute(input, { signal: abort.signal });
47
47
  const responseIterator = responses[Symbol.asyncIterator]();
48
48
  // Send the start and read the answer together: the request stream only moves
49
49
  // while the call is running. The created message is the one the interceptors
@@ -649,9 +649,9 @@ export declare const GetArtifactResponseSchema: GenMessage<GetArtifactResponse,
649
649
  jsonType: GetArtifactResponseJson;
650
650
  }>;
651
651
  /**
652
- * @generated from message toshi.workspace.v1.GetDownloadURLRequest
652
+ * @generated from message toshi.workspace.v1.GetArtifactDownloadURLRequest
653
653
  */
654
- export type GetDownloadURLRequest = Message<"toshi.workspace.v1.GetDownloadURLRequest"> & {
654
+ export type GetArtifactDownloadURLRequest = Message<"toshi.workspace.v1.GetArtifactDownloadURLRequest"> & {
655
655
  /**
656
656
  * @generated from field: string artifact_id = 1;
657
657
  */
@@ -671,9 +671,9 @@ export type GetDownloadURLRequest = Message<"toshi.workspace.v1.GetDownloadURLRe
671
671
  ttlSeconds: bigint;
672
672
  };
673
673
  /**
674
- * @generated from message toshi.workspace.v1.GetDownloadURLRequest
674
+ * @generated from message toshi.workspace.v1.GetArtifactDownloadURLRequest
675
675
  */
676
- export type GetDownloadURLRequestJson = {
676
+ export type GetArtifactDownloadURLRequestJson = {
677
677
  /**
678
678
  * @generated from field: string artifact_id = 1;
679
679
  */
@@ -693,16 +693,16 @@ export type GetDownloadURLRequestJson = {
693
693
  ttlSeconds?: string;
694
694
  };
695
695
  /**
696
- * Describes the message toshi.workspace.v1.GetDownloadURLRequest.
697
- * Use `create(GetDownloadURLRequestSchema)` to create a new message.
696
+ * Describes the message toshi.workspace.v1.GetArtifactDownloadURLRequest.
697
+ * Use `create(GetArtifactDownloadURLRequestSchema)` to create a new message.
698
698
  */
699
- export declare const GetDownloadURLRequestSchema: GenMessage<GetDownloadURLRequest, {
700
- jsonType: GetDownloadURLRequestJson;
699
+ export declare const GetArtifactDownloadURLRequestSchema: GenMessage<GetArtifactDownloadURLRequest, {
700
+ jsonType: GetArtifactDownloadURLRequestJson;
701
701
  }>;
702
702
  /**
703
- * @generated from message toshi.workspace.v1.GetDownloadURLResponse
703
+ * @generated from message toshi.workspace.v1.GetArtifactDownloadURLResponse
704
704
  */
705
- export type GetDownloadURLResponse = Message<"toshi.workspace.v1.GetDownloadURLResponse"> & {
705
+ export type GetArtifactDownloadURLResponse = Message<"toshi.workspace.v1.GetArtifactDownloadURLResponse"> & {
706
706
  /**
707
707
  * @generated from field: string url = 1;
708
708
  */
@@ -723,9 +723,9 @@ export type GetDownloadURLResponse = Message<"toshi.workspace.v1.GetDownloadURLR
723
723
  dataKey: Uint8Array;
724
724
  };
725
725
  /**
726
- * @generated from message toshi.workspace.v1.GetDownloadURLResponse
726
+ * @generated from message toshi.workspace.v1.GetArtifactDownloadURLResponse
727
727
  */
728
- export type GetDownloadURLResponseJson = {
728
+ export type GetArtifactDownloadURLResponseJson = {
729
729
  /**
730
730
  * @generated from field: string url = 1;
731
731
  */
@@ -746,11 +746,11 @@ export type GetDownloadURLResponseJson = {
746
746
  dataKey?: string;
747
747
  };
748
748
  /**
749
- * Describes the message toshi.workspace.v1.GetDownloadURLResponse.
750
- * Use `create(GetDownloadURLResponseSchema)` to create a new message.
749
+ * Describes the message toshi.workspace.v1.GetArtifactDownloadURLResponse.
750
+ * Use `create(GetArtifactDownloadURLResponseSchema)` to create a new message.
751
751
  */
752
- export declare const GetDownloadURLResponseSchema: GenMessage<GetDownloadURLResponse, {
753
- jsonType: GetDownloadURLResponseJson;
752
+ export declare const GetArtifactDownloadURLResponseSchema: GenMessage<GetArtifactDownloadURLResponse, {
753
+ jsonType: GetArtifactDownloadURLResponseJson;
754
754
  }>;
755
755
  /**
756
756
  * ArtifactUploadService is how a job uploads what it recorded:
@@ -809,6 +809,8 @@ export declare const ArtifactService: GenService<{
809
809
  * ListArtifacts returns the workspace's artifacts, oldest first, by
810
810
  * project, environment, workload, execution and kind.
811
811
  *
812
+ * A page holds 50 artifacts when page_size is zero, and 200 at most.
813
+ *
812
814
  * @generated from rpc toshi.workspace.v1.ArtifactService.ListArtifacts
813
815
  */
814
816
  listArtifacts: {
@@ -827,15 +829,15 @@ export declare const ArtifactService: GenService<{
827
829
  output: typeof GetArtifactResponseSchema;
828
830
  };
829
831
  /**
830
- * GetDownloadURL returns a signed URL that expires, and the artifact's
831
- * data key when it is envelope encrypted. An artifact that is not
832
- * complete fails with FAILED_PRECONDITION.
832
+ * GetArtifactDownloadURL returns a signed URL that expires, and the
833
+ * artifact's data key when it is envelope encrypted. An artifact that is
834
+ * not complete fails with FAILED_PRECONDITION.
833
835
  *
834
- * @generated from rpc toshi.workspace.v1.ArtifactService.GetDownloadURL
836
+ * @generated from rpc toshi.workspace.v1.ArtifactService.GetArtifactDownloadURL
835
837
  */
836
- getDownloadURL: {
838
+ getArtifactDownloadURL: {
837
839
  methodKind: "unary";
838
- input: typeof GetDownloadURLRequestSchema;
839
- output: typeof GetDownloadURLResponseSchema;
840
+ input: typeof GetArtifactDownloadURLRequestSchema;
841
+ output: typeof GetArtifactDownloadURLResponseSchema;
840
842
  };
841
843
  }>;