contree-client 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.d.ts CHANGED
@@ -3,21 +3,24 @@
3
3
  import type { Profile } from "./profiles.js";
4
4
  import type { RequestSpec, ResponseData, RetryPolicy } from "./runtime.js";
5
5
  import type {
6
+ ClosableStreamRepr,
6
7
  DirectoryList,
7
8
  File,
8
9
  FileResponse,
9
10
  FileSpec,
10
11
  FilesListResponse,
12
+ GrepResult,
11
13
  Image,
12
14
  ImageImportRegistry,
13
15
  ImageListResponse,
16
+ InstanceNetworking,
14
17
  InstanceResourcesLimits,
18
+ InstanceResult,
15
19
  InstanceSpawnResponse,
16
20
  OperationEvent,
17
21
  OperationResponse,
18
22
  OperationStatus,
19
23
  OperationSummary,
20
- StreamRepr,
21
24
  WhoAmIResponse,
22
25
  } from "./models.js";
23
26
 
@@ -135,7 +138,8 @@ export declare class ContreeClient {
135
138
  uid?: number | undefined;
136
139
  gid?: number | undefined;
137
140
  resources_limits?: InstanceResourcesLimits | undefined;
138
- stdin?: StreamRepr | undefined;
141
+ networking?: InstanceNetworking | undefined;
142
+ stdin?: ClosableStreamRepr | undefined;
139
143
  timeout?: number | undefined;
140
144
  truncate_output_at?: number | undefined;
141
145
  files?: Record<string, FileSpec> | undefined;
@@ -163,6 +167,38 @@ export declare class ContreeClient {
163
167
  last_event_id?: number | null;
164
168
  },
165
169
  ): AsyncGenerator<OperationEvent>;
170
+ operationSubprocessCreate(
171
+ operationId: string,
172
+ command: string,
173
+ options?: {
174
+ args?: string[] | undefined;
175
+ shell?: boolean | undefined;
176
+ env?: Record<string, string> | null | undefined;
177
+ cwd?: string | undefined;
178
+ uid?: number | undefined;
179
+ gid?: number | undefined;
180
+ stdin?: ClosableStreamRepr | undefined;
181
+ truncate_output_at?: number | undefined;
182
+ },
183
+ ): Promise<number>;
184
+ operationSubprocess(
185
+ operationId: string,
186
+ spid: number,
187
+ ): Promise<InstanceResult>;
188
+ operationSubprocessKill(
189
+ operationId: string,
190
+ spid: number,
191
+ options?: { signal?: string | null },
192
+ ): Promise<null>;
193
+ operationSubprocessStdin(
194
+ operationId: string,
195
+ spid: number,
196
+ value: string,
197
+ options?: {
198
+ encoding?: "ascii" | "base64" | undefined;
199
+ close?: boolean | undefined;
200
+ },
201
+ ): Promise<null>;
166
202
  inspectFindImageByTag(tag: string): Promise<string>;
167
203
  inspectImage(imageUuid: string): Promise<Image>;
168
204
  inspectImageDownload(imageUuid: string, path: string): Promise<Uint8Array>;
@@ -177,5 +213,16 @@ export declare class ContreeClient {
177
213
  ): AsyncGenerator<Uint8Array>;
178
214
  checkImageArchive(imageUuid: string, path: string): Promise<boolean>;
179
215
  inspectImageList(imageUuid: string, path: string): Promise<DirectoryList>;
216
+ inspectImageGrep(
217
+ imageUuid: string,
218
+ pattern: string,
219
+ options?: {
220
+ path?: string | null;
221
+ glob?: string | null;
222
+ max_count?: number | null;
223
+ max_total?: number | null;
224
+ case?: "sensitive" | "insensitive" | "smart" | null;
225
+ },
226
+ ): Promise<GrepResult>;
180
227
  whoami(): Promise<WhoAmIResponse>;
181
228
  }
package/lib/client.js CHANGED
@@ -216,11 +216,6 @@ export class ContreeClient {
216
216
  if (policy === null) {
217
217
  return await this._reconnecting(spec);
218
218
  }
219
- if (!spec.idempotent && !policy.retryUnsafe) {
220
- // a lost response after a non-idempotent request (POST) could
221
- // mean a second execution server-side
222
- return await this.request(spec);
223
- }
224
219
  if (
225
220
  typeof ReadableStream !== "undefined" &&
226
221
  spec.body instanceof ReadableStream
@@ -228,6 +223,13 @@ export class ContreeClient {
228
223
  // a stream cannot be replayed: single attempt
229
224
  return await this.request(spec);
230
225
  }
226
+ // a lost response after a non-idempotent request (POST) could
227
+ // mean a second execution server-side: never blind-retry unless
228
+ // the caller explicitly opted into that risk. 425 Too Early and
229
+ // 429 Too Many Requests are the exceptions - the backend's
230
+ // contract guarantees both mean the request was rejected before
231
+ // any processing, so replaying is always safe.
232
+ const replaySafe = spec.idempotent || policy.retryUnsafe;
231
233
  const delays = retryDelays(policy.delays);
232
234
  let attempts = 0;
233
235
  for (;;) {
@@ -239,6 +241,7 @@ export class ContreeClient {
239
241
  response = await this.request(spec);
240
242
  } catch (error) {
241
243
  if (
244
+ !replaySafe ||
242
245
  !this._transportRetryable(error) ||
243
246
  this._transportNonretryable(error) ||
244
247
  exhausted
@@ -251,6 +254,9 @@ export class ContreeClient {
251
254
  if (!policy.retryableStatus(response.status) || exhausted) {
252
255
  return response;
253
256
  }
257
+ if (!replaySafe && response.status !== 425 && response.status !== 429) {
258
+ return response;
259
+ }
254
260
  const retryAfter = retryAfterDelay(response);
255
261
  await sleep(retryAfter !== null ? retryAfter : delays.next().value);
256
262
  }
@@ -730,6 +736,43 @@ export class ContreeClient {
730
736
  }
731
737
  }
732
738
 
739
+ /** Spawn an additional subprocess inside a running instance (POST /operations/{operationId}/subprocesses) */
740
+ async operationSubprocessCreate(operationId, command, options = {}) {
741
+ const spec = operations.buildOperationSubprocessCreate(
742
+ operationId,
743
+ command,
744
+ options,
745
+ );
746
+ return operations.parseOperationSubprocessCreate(await this.call(spec));
747
+ }
748
+
749
+ /** Result of one subprocess, reconstructed from its events (GET /operations/{operationId}/subprocesses/{spid}) */
750
+ async operationSubprocess(operationId, spid) {
751
+ const spec = operations.buildOperationSubprocess(operationId, spid);
752
+ return operations.parseOperationSubprocess(await this.call(spec));
753
+ }
754
+
755
+ /** Kill one subprocess (DELETE /operations/{operationId}/subprocesses/{spid}) */
756
+ async operationSubprocessKill(operationId, spid, options = {}) {
757
+ const spec = operations.buildOperationSubprocessKill(
758
+ operationId,
759
+ spid,
760
+ options,
761
+ );
762
+ return operations.parseOperationSubprocessKill(await this.call(spec));
763
+ }
764
+
765
+ /** Write to a subprocess's stdin and/or close it (POST /operations/{operationId}/subprocesses/{spid}/stdin) */
766
+ async operationSubprocessStdin(operationId, spid, value, options = {}) {
767
+ const spec = operations.buildOperationSubprocessStdin(
768
+ operationId,
769
+ spid,
770
+ value,
771
+ options,
772
+ );
773
+ return operations.parseOperationSubprocessStdin(await this.call(spec));
774
+ }
775
+
733
776
  /** Find image by tag (GET /inspect/) */
734
777
  async inspectFindImageByTag(tag) {
735
778
  const spec = operations.buildInspectFindImageByTag(tag);
@@ -778,6 +821,12 @@ export class ContreeClient {
778
821
  return operations.parseInspectImageList(await this.call(spec));
779
822
  }
780
823
 
824
+ /** Search file contents in image (GET /inspect/{image_uuid}/grep) */
825
+ async inspectImageGrep(imageUuid, pattern, options = {}) {
826
+ const spec = operations.buildInspectImageGrep(imageUuid, pattern, options);
827
+ return operations.parseInspectImageGrep(await this.call(spec));
828
+ }
829
+
781
830
  /** Get current token information (GET /whoami) */
782
831
  async whoami() {
783
832
  const spec = operations.buildWhoami();
package/lib/models.d.ts CHANGED
@@ -78,6 +78,49 @@ export declare class DirectoryList {
78
78
  toWire(): Record<string, unknown>;
79
79
  }
80
80
 
81
+ export declare class GrepSubmatch {
82
+ text: string;
83
+ start: number;
84
+ end: number;
85
+ constructor(fields?: { text?: string; start?: number; end?: number });
86
+ static fromWire(data: Record<string, unknown>): GrepSubmatch;
87
+ toWire(): Record<string, unknown>;
88
+ }
89
+
90
+ export declare class GrepMatch {
91
+ path: string;
92
+ line_number: number;
93
+ absolute_offset: number;
94
+ line_text: string;
95
+ line_bytes: number;
96
+ submatches: GrepSubmatch[];
97
+ constructor(fields?: {
98
+ path?: string;
99
+ line_number?: number;
100
+ absolute_offset?: number;
101
+ line_text?: string;
102
+ line_bytes?: number;
103
+ submatches?: GrepSubmatch[];
104
+ });
105
+ static fromWire(data: Record<string, unknown>): GrepMatch;
106
+ toWire(): Record<string, unknown>;
107
+ }
108
+
109
+ export declare class GrepResult {
110
+ path: string;
111
+ patterns: string[];
112
+ matches: GrepMatch[];
113
+ truncated: boolean;
114
+ constructor(fields?: {
115
+ path?: string;
116
+ patterns?: string[];
117
+ matches?: GrepMatch[];
118
+ truncated?: boolean;
119
+ });
120
+ static fromWire(data: Record<string, unknown>): GrepResult;
121
+ toWire(): Record<string, unknown>;
122
+ }
123
+
81
124
  export declare class ImageImportRegistryCredentials {
82
125
  username?: string;
83
126
  password?: string;
@@ -186,6 +229,19 @@ export declare class StreamRepr {
186
229
  static fromText(value: string): StreamRepr;
187
230
  }
188
231
 
232
+ export declare class ClosableStreamRepr {
233
+ value: string;
234
+ encoding?: "ascii" | "base64";
235
+ close?: boolean;
236
+ constructor(fields?: {
237
+ value?: string;
238
+ encoding?: "ascii" | "base64" | null;
239
+ close?: boolean | null;
240
+ });
241
+ static fromWire(data: Record<string, unknown>): ClosableStreamRepr;
242
+ toWire(): Record<string, unknown>;
243
+ }
244
+
189
245
  export declare class InstanceResourcesLimits {
190
246
  max_layer_bytes?: number;
191
247
  constructor(fields?: { max_layer_bytes?: number | null });
@@ -193,6 +249,38 @@ export declare class InstanceResourcesLimits {
193
249
  toWire(): Record<string, unknown>;
194
250
  }
195
251
 
252
+ export declare class InstanceNetworking {
253
+ enabled?: boolean;
254
+ constructor(fields?: { enabled?: boolean | null });
255
+ static fromWire(data: Record<string, unknown>): InstanceNetworking;
256
+ toWire(): Record<string, unknown>;
257
+ }
258
+
259
+ export declare class ExecSpec {
260
+ command: string;
261
+ args?: string[];
262
+ shell?: boolean;
263
+ env?: Record<string, string> | null;
264
+ cwd?: string;
265
+ uid?: number;
266
+ gid?: number;
267
+ stdin?: ClosableStreamRepr;
268
+ truncate_output_at?: number;
269
+ constructor(fields?: {
270
+ command?: string;
271
+ args?: string[] | null;
272
+ shell?: boolean | null;
273
+ env?: Record<string, string> | null;
274
+ cwd?: string | null;
275
+ uid?: number | null;
276
+ gid?: number | null;
277
+ stdin?: ClosableStreamRepr | null;
278
+ truncate_output_at?: number | null;
279
+ });
280
+ static fromWire(data: Record<string, unknown>): ExecSpec;
281
+ toWire(): Record<string, unknown>;
282
+ }
283
+
196
284
  export declare class FileSpec {
197
285
  uuid?: string;
198
286
  uid?: number;
@@ -221,7 +309,8 @@ export declare class InstanceSpawnRequest {
221
309
  uid?: number;
222
310
  gid?: number;
223
311
  resources_limits?: InstanceResourcesLimits;
224
- stdin?: StreamRepr;
312
+ networking?: InstanceNetworking;
313
+ stdin?: ClosableStreamRepr;
225
314
  timeout?: number;
226
315
  truncate_output_at?: number;
227
316
  files?: Record<string, FileSpec>;
@@ -238,7 +327,8 @@ export declare class InstanceSpawnRequest {
238
327
  uid?: number | null;
239
328
  gid?: number | null;
240
329
  resources_limits?: InstanceResourcesLimits | null;
241
- stdin?: StreamRepr | null;
330
+ networking?: InstanceNetworking | null;
331
+ stdin?: ClosableStreamRepr | null;
242
332
  timeout?: number | null;
243
333
  truncate_output_at?: number | null;
244
334
  files?: Record<string, FileSpec> | null;
@@ -335,7 +425,8 @@ export declare class InstanceSpawnResponse {
335
425
  uid?: number;
336
426
  gid?: number;
337
427
  resources_limits?: InstanceResourcesLimits;
338
- stdin?: StreamRepr;
428
+ networking?: InstanceNetworking;
429
+ stdin?: ClosableStreamRepr;
339
430
  timeout?: number;
340
431
  truncate_output_at?: number;
341
432
  disposable?: boolean;
@@ -354,7 +445,8 @@ export declare class InstanceSpawnResponse {
354
445
  uid?: number | null;
355
446
  gid?: number | null;
356
447
  resources_limits?: InstanceResourcesLimits | null;
357
- stdin?: StreamRepr | null;
448
+ networking?: InstanceNetworking | null;
449
+ stdin?: ClosableStreamRepr | null;
358
450
  timeout?: number | null;
359
451
  truncate_output_at?: number | null;
360
452
  disposable?: boolean | null;
@@ -407,7 +499,8 @@ export declare class OperationInstanceMetadata {
407
499
  uid?: number;
408
500
  gid?: number;
409
501
  resources_limits?: InstanceResourcesLimits;
410
- stdin?: StreamRepr;
502
+ networking?: InstanceNetworking;
503
+ stdin?: ClosableStreamRepr;
411
504
  timeout?: number;
412
505
  truncate_output_at?: number;
413
506
  files?: Record<string, FileSpec>;
@@ -425,7 +518,8 @@ export declare class OperationInstanceMetadata {
425
518
  uid?: number | null;
426
519
  gid?: number | null;
427
520
  resources_limits?: InstanceResourcesLimits | null;
428
- stdin?: StreamRepr | null;
521
+ networking?: InstanceNetworking | null;
522
+ stdin?: ClosableStreamRepr | null;
429
523
  timeout?: number | null;
430
524
  truncate_output_at?: number | null;
431
525
  files?: Record<string, FileSpec> | null;
@@ -668,6 +762,7 @@ export declare class EventDataExit {
668
762
  timed_out: boolean;
669
763
  duration_ms: number;
670
764
  resources: EventResources;
765
+ core_dump?: boolean;
671
766
  constructor(fields?: {
672
767
  pid?: number;
673
768
  code?: number;
@@ -675,6 +770,7 @@ export declare class EventDataExit {
675
770
  timed_out?: boolean;
676
771
  duration_ms?: number;
677
772
  resources?: EventResources;
773
+ core_dump?: boolean | null;
678
774
  });
679
775
  static fromWire(data: Record<string, unknown>): EventDataExit;
680
776
  toWire(): Record<string, unknown>;
package/lib/models.js CHANGED
@@ -157,6 +157,122 @@ export class DirectoryList {
157
157
  }
158
158
  }
159
159
 
160
+ export class GrepSubmatch {
161
+ constructor(fields = {}) {
162
+ this.text = fields.text;
163
+ this.start = fields.start;
164
+ this.end = fields.end;
165
+ }
166
+
167
+ static fromWire(data) {
168
+ return new GrepSubmatch({
169
+ text: data["text"],
170
+ start: data["start"],
171
+ end: data["end"],
172
+ });
173
+ }
174
+
175
+ toWire() {
176
+ const data = {};
177
+ if (this.text !== undefined) {
178
+ data["text"] = this.text;
179
+ }
180
+ if (this.start !== undefined) {
181
+ data["start"] = this.start;
182
+ }
183
+ if (this.end !== undefined) {
184
+ data["end"] = this.end;
185
+ }
186
+ return data;
187
+ }
188
+ }
189
+
190
+ export class GrepMatch {
191
+ constructor(fields = {}) {
192
+ this.path = fields.path;
193
+ this.line_number = fields.line_number;
194
+ this.absolute_offset = fields.absolute_offset;
195
+ this.line_text = fields.line_text;
196
+ this.line_bytes = fields.line_bytes;
197
+ this.submatches = fields.submatches;
198
+ }
199
+
200
+ static fromWire(data) {
201
+ return new GrepMatch({
202
+ path: data["path"],
203
+ line_number: data["line_number"],
204
+ absolute_offset: data["absolute_offset"],
205
+ line_text: data["line_text"],
206
+ line_bytes: data["line_bytes"],
207
+ submatches: data["submatches"].map((item) => GrepSubmatch.fromWire(item)),
208
+ });
209
+ }
210
+
211
+ toWire() {
212
+ const data = {};
213
+ if (this.path !== undefined) {
214
+ data["path"] = this.path;
215
+ }
216
+ if (this.line_number !== undefined) {
217
+ data["line_number"] = this.line_number;
218
+ }
219
+ if (this.absolute_offset !== undefined) {
220
+ data["absolute_offset"] = this.absolute_offset;
221
+ }
222
+ if (this.line_text !== undefined) {
223
+ data["line_text"] = this.line_text;
224
+ }
225
+ if (this.line_bytes !== undefined) {
226
+ data["line_bytes"] = this.line_bytes;
227
+ }
228
+ if (this.submatches !== undefined) {
229
+ data["submatches"] =
230
+ this.submatches === null
231
+ ? null
232
+ : this.submatches.map((item) => item.toWire());
233
+ }
234
+ return data;
235
+ }
236
+ }
237
+
238
+ export class GrepResult {
239
+ constructor(fields = {}) {
240
+ this.path = fields.path;
241
+ this.patterns = fields.patterns;
242
+ this.matches = fields.matches;
243
+ this.truncated = fields.truncated;
244
+ }
245
+
246
+ static fromWire(data) {
247
+ return new GrepResult({
248
+ path: data["path"],
249
+ patterns: data["patterns"],
250
+ matches: data["matches"].map((item) => GrepMatch.fromWire(item)),
251
+ truncated: data["truncated"],
252
+ });
253
+ }
254
+
255
+ toWire() {
256
+ const data = {};
257
+ if (this.path !== undefined) {
258
+ data["path"] = this.path;
259
+ }
260
+ if (this.patterns !== undefined) {
261
+ data["patterns"] = this.patterns;
262
+ }
263
+ if (this.matches !== undefined) {
264
+ data["matches"] =
265
+ this.matches === null
266
+ ? null
267
+ : this.matches.map((item) => item.toWire());
268
+ }
269
+ if (this.truncated !== undefined) {
270
+ data["truncated"] = this.truncated;
271
+ }
272
+ return data;
273
+ }
274
+ }
275
+
160
276
  export class ImageImportRegistryCredentials {
161
277
  constructor(fields = {}) {
162
278
  this.username = fields.username;
@@ -464,6 +580,37 @@ export class StreamRepr {
464
580
  }
465
581
  }
466
582
 
583
+ /** Stdin payload. Unlike output streams it is never truncated; */
584
+ export class ClosableStreamRepr {
585
+ constructor(fields = {}) {
586
+ this.value = fields.value;
587
+ this.encoding = fields.encoding;
588
+ this.close = fields.close;
589
+ }
590
+
591
+ static fromWire(data) {
592
+ return new ClosableStreamRepr({
593
+ value: data["value"],
594
+ encoding: data["encoding"],
595
+ close: data["close"],
596
+ });
597
+ }
598
+
599
+ toWire() {
600
+ const data = {};
601
+ if (this.value !== undefined) {
602
+ data["value"] = this.value;
603
+ }
604
+ if (this.encoding !== undefined) {
605
+ data["encoding"] = this.encoding;
606
+ }
607
+ if (this.close !== undefined) {
608
+ data["close"] = this.close;
609
+ }
610
+ return data;
611
+ }
612
+ }
613
+
467
614
  export class InstanceResourcesLimits {
468
615
  constructor(fields = {}) {
469
616
  this.max_layer_bytes = fields.max_layer_bytes;
@@ -484,6 +631,90 @@ export class InstanceResourcesLimits {
484
631
  }
485
632
  }
486
633
 
634
+ export class InstanceNetworking {
635
+ constructor(fields = {}) {
636
+ this.enabled = fields.enabled;
637
+ }
638
+
639
+ static fromWire(data) {
640
+ return new InstanceNetworking({
641
+ enabled: data["enabled"],
642
+ });
643
+ }
644
+
645
+ toWire() {
646
+ const data = {};
647
+ if (this.enabled !== undefined) {
648
+ data["enabled"] = this.enabled;
649
+ }
650
+ return data;
651
+ }
652
+ }
653
+
654
+ /** Process-execution surface shared between `InstanceSpawnRequest` */
655
+ export class ExecSpec {
656
+ constructor(fields = {}) {
657
+ this.command = fields.command;
658
+ this.args = fields.args;
659
+ this.shell = fields.shell;
660
+ this.env = fields.env;
661
+ this.cwd = fields.cwd;
662
+ this.uid = fields.uid;
663
+ this.gid = fields.gid;
664
+ this.stdin = fields.stdin;
665
+ this.truncate_output_at = fields.truncate_output_at;
666
+ }
667
+
668
+ static fromWire(data) {
669
+ return new ExecSpec({
670
+ command: data["command"],
671
+ args: data["args"],
672
+ shell: data["shell"],
673
+ env: data["env"],
674
+ cwd: data["cwd"],
675
+ uid: data["uid"],
676
+ gid: data["gid"],
677
+ stdin:
678
+ data["stdin"] == null
679
+ ? data["stdin"]
680
+ : ClosableStreamRepr.fromWire(data["stdin"]),
681
+ truncate_output_at: data["truncate_output_at"],
682
+ });
683
+ }
684
+
685
+ toWire() {
686
+ const data = {};
687
+ if (this.command !== undefined) {
688
+ data["command"] = this.command;
689
+ }
690
+ if (this.args !== undefined) {
691
+ data["args"] = this.args;
692
+ }
693
+ if (this.shell !== undefined) {
694
+ data["shell"] = this.shell;
695
+ }
696
+ if (this.env !== undefined) {
697
+ data["env"] = this.env;
698
+ }
699
+ if (this.cwd !== undefined) {
700
+ data["cwd"] = this.cwd;
701
+ }
702
+ if (this.uid !== undefined) {
703
+ data["uid"] = this.uid;
704
+ }
705
+ if (this.gid !== undefined) {
706
+ data["gid"] = this.gid;
707
+ }
708
+ if (this.stdin !== undefined) {
709
+ data["stdin"] = this.stdin === null ? null : this.stdin.toWire();
710
+ }
711
+ if (this.truncate_output_at !== undefined) {
712
+ data["truncate_output_at"] = this.truncate_output_at;
713
+ }
714
+ return data;
715
+ }
716
+ }
717
+
487
718
  export class FileSpec {
488
719
  constructor(fields = {}) {
489
720
  this.uuid = fields.uuid;
@@ -538,6 +769,7 @@ export class InstanceSpawnRequest {
538
769
  this.uid = fields.uid;
539
770
  this.gid = fields.gid;
540
771
  this.resources_limits = fields.resources_limits;
772
+ this.networking = fields.networking;
541
773
  this.stdin = fields.stdin;
542
774
  this.timeout = fields.timeout;
543
775
  this.truncate_output_at = fields.truncate_output_at;
@@ -561,10 +793,14 @@ export class InstanceSpawnRequest {
561
793
  data["resources_limits"] == null
562
794
  ? data["resources_limits"]
563
795
  : InstanceResourcesLimits.fromWire(data["resources_limits"]),
796
+ networking:
797
+ data["networking"] == null
798
+ ? data["networking"]
799
+ : InstanceNetworking.fromWire(data["networking"]),
564
800
  stdin:
565
801
  data["stdin"] == null
566
802
  ? data["stdin"]
567
- : StreamRepr.fromWire(data["stdin"]),
803
+ : ClosableStreamRepr.fromWire(data["stdin"]),
568
804
  timeout: data["timeout"],
569
805
  truncate_output_at: data["truncate_output_at"],
570
806
  files:
@@ -618,6 +854,10 @@ export class InstanceSpawnRequest {
618
854
  data["resources_limits"] =
619
855
  this.resources_limits === null ? null : this.resources_limits.toWire();
620
856
  }
857
+ if (this.networking !== undefined) {
858
+ data["networking"] =
859
+ this.networking === null ? null : this.networking.toWire();
860
+ }
621
861
  if (this.stdin !== undefined) {
622
862
  data["stdin"] = this.stdin === null ? null : this.stdin.toWire();
623
863
  }
@@ -850,6 +1090,7 @@ export class InstanceSpawnResponse {
850
1090
  this.uid = fields.uid;
851
1091
  this.gid = fields.gid;
852
1092
  this.resources_limits = fields.resources_limits;
1093
+ this.networking = fields.networking;
853
1094
  this.stdin = fields.stdin;
854
1095
  this.timeout = fields.timeout;
855
1096
  this.truncate_output_at = fields.truncate_output_at;
@@ -875,10 +1116,14 @@ export class InstanceSpawnResponse {
875
1116
  data["resources_limits"] == null
876
1117
  ? data["resources_limits"]
877
1118
  : InstanceResourcesLimits.fromWire(data["resources_limits"]),
1119
+ networking:
1120
+ data["networking"] == null
1121
+ ? data["networking"]
1122
+ : InstanceNetworking.fromWire(data["networking"]),
878
1123
  stdin:
879
1124
  data["stdin"] == null
880
1125
  ? data["stdin"]
881
- : StreamRepr.fromWire(data["stdin"]),
1126
+ : ClosableStreamRepr.fromWire(data["stdin"]),
882
1127
  timeout: data["timeout"],
883
1128
  truncate_output_at: data["truncate_output_at"],
884
1129
  disposable: data["disposable"],
@@ -937,6 +1182,10 @@ export class InstanceSpawnResponse {
937
1182
  data["resources_limits"] =
938
1183
  this.resources_limits === null ? null : this.resources_limits.toWire();
939
1184
  }
1185
+ if (this.networking !== undefined) {
1186
+ data["networking"] =
1187
+ this.networking === null ? null : this.networking.toWire();
1188
+ }
940
1189
  if (this.stdin !== undefined) {
941
1190
  data["stdin"] = this.stdin === null ? null : this.stdin.toWire();
942
1191
  }
@@ -1051,6 +1300,7 @@ export class OperationInstanceMetadata {
1051
1300
  this.uid = fields.uid;
1052
1301
  this.gid = fields.gid;
1053
1302
  this.resources_limits = fields.resources_limits;
1303
+ this.networking = fields.networking;
1054
1304
  this.stdin = fields.stdin;
1055
1305
  this.timeout = fields.timeout;
1056
1306
  this.truncate_output_at = fields.truncate_output_at;
@@ -1075,10 +1325,14 @@ export class OperationInstanceMetadata {
1075
1325
  data["resources_limits"] == null
1076
1326
  ? data["resources_limits"]
1077
1327
  : InstanceResourcesLimits.fromWire(data["resources_limits"]),
1328
+ networking:
1329
+ data["networking"] == null
1330
+ ? data["networking"]
1331
+ : InstanceNetworking.fromWire(data["networking"]),
1078
1332
  stdin:
1079
1333
  data["stdin"] == null
1080
1334
  ? data["stdin"]
1081
- : StreamRepr.fromWire(data["stdin"]),
1335
+ : ClosableStreamRepr.fromWire(data["stdin"]),
1082
1336
  timeout: data["timeout"],
1083
1337
  truncate_output_at: data["truncate_output_at"],
1084
1338
  files:
@@ -1136,6 +1390,10 @@ export class OperationInstanceMetadata {
1136
1390
  data["resources_limits"] =
1137
1391
  this.resources_limits === null ? null : this.resources_limits.toWire();
1138
1392
  }
1393
+ if (this.networking !== undefined) {
1394
+ data["networking"] =
1395
+ this.networking === null ? null : this.networking.toWire();
1396
+ }
1139
1397
  if (this.stdin !== undefined) {
1140
1398
  data["stdin"] = this.stdin === null ? null : this.stdin.toWire();
1141
1399
  }
@@ -1788,6 +2046,7 @@ export class EventDataExit {
1788
2046
  this.timed_out = fields.timed_out;
1789
2047
  this.duration_ms = fields.duration_ms;
1790
2048
  this.resources = fields.resources;
2049
+ this.core_dump = fields.core_dump;
1791
2050
  }
1792
2051
 
1793
2052
  static fromWire(data) {
@@ -1798,6 +2057,7 @@ export class EventDataExit {
1798
2057
  timed_out: data["timed_out"],
1799
2058
  duration_ms: data["duration_ms"],
1800
2059
  resources: EventResources.fromWire(data["resources"]),
2060
+ core_dump: data["core_dump"],
1801
2061
  });
1802
2062
  }
1803
2063
 
@@ -1822,6 +2082,9 @@ export class EventDataExit {
1822
2082
  data["resources"] =
1823
2083
  this.resources === null ? null : this.resources.toWire();
1824
2084
  }
2085
+ if (this.core_dump !== undefined) {
2086
+ data["core_dump"] = this.core_dump;
2087
+ }
1825
2088
  return data;
1826
2089
  }
1827
2090
  }
@@ -3,20 +3,23 @@
3
3
  import type { RequestSpec, ResponseData } from "./runtime.js";
4
4
 
5
5
  import type {
6
+ ClosableStreamRepr,
6
7
  DirectoryList,
7
8
  File,
8
9
  FileResponse,
9
10
  FileSpec,
10
11
  FilesListResponse,
12
+ GrepResult,
11
13
  Image,
12
14
  ImageImportRegistry,
13
15
  ImageListResponse,
16
+ InstanceNetworking,
14
17
  InstanceResourcesLimits,
18
+ InstanceResult,
15
19
  InstanceSpawnResponse,
16
20
  OperationResponse,
17
21
  OperationStatus,
18
22
  OperationSummary,
19
- StreamRepr,
20
23
  WhoAmIResponse,
21
24
  } from "./models.js";
22
25
 
@@ -94,7 +97,8 @@ export declare function buildSpawnInstance(
94
97
  uid?: number | undefined;
95
98
  gid?: number | undefined;
96
99
  resources_limits?: InstanceResourcesLimits | undefined;
97
- stdin?: StreamRepr | undefined;
100
+ networking?: InstanceNetworking | undefined;
101
+ stdin?: ClosableStreamRepr | undefined;
98
102
  timeout?: number | undefined;
99
103
  truncate_output_at?: number | undefined;
100
104
  files?: Record<string, FileSpec> | undefined;
@@ -141,6 +145,58 @@ export declare function buildIterOperationEvents(
141
145
  },
142
146
  ): RequestSpec;
143
147
 
148
+ export declare function buildOperationSubprocessCreate(
149
+ operationId: string,
150
+ command: string,
151
+ options?: {
152
+ args?: string[] | undefined;
153
+ shell?: boolean | undefined;
154
+ env?: Record<string, string> | null | undefined;
155
+ cwd?: string | undefined;
156
+ uid?: number | undefined;
157
+ gid?: number | undefined;
158
+ stdin?: ClosableStreamRepr | undefined;
159
+ truncate_output_at?: number | undefined;
160
+ },
161
+ ): RequestSpec;
162
+
163
+ export declare function parseOperationSubprocessCreate(
164
+ response: ResponseData,
165
+ ): number;
166
+
167
+ export declare function buildOperationSubprocess(
168
+ operationId: string,
169
+ spid: number,
170
+ ): RequestSpec;
171
+
172
+ export declare function parseOperationSubprocess(
173
+ response: ResponseData,
174
+ ): InstanceResult;
175
+
176
+ export declare function buildOperationSubprocessKill(
177
+ operationId: string,
178
+ spid: number,
179
+ options?: { signal?: string | null },
180
+ ): RequestSpec;
181
+
182
+ export declare function parseOperationSubprocessKill(
183
+ response: ResponseData,
184
+ ): null;
185
+
186
+ export declare function buildOperationSubprocessStdin(
187
+ operationId: string,
188
+ spid: number,
189
+ value: string,
190
+ options?: {
191
+ encoding?: "ascii" | "base64" | undefined;
192
+ close?: boolean | undefined;
193
+ },
194
+ ): RequestSpec;
195
+
196
+ export declare function parseOperationSubprocessStdin(
197
+ response: ResponseData,
198
+ ): null;
199
+
144
200
  export declare function buildInspectFindImageByTag(tag: string): RequestSpec;
145
201
 
146
202
  export declare function parseInspectFindImageByTag(
@@ -188,6 +244,22 @@ export declare function parseInspectImageList(
188
244
  response: ResponseData,
189
245
  ): DirectoryList;
190
246
 
247
+ export declare function buildInspectImageGrep(
248
+ imageUuid: string,
249
+ pattern: string,
250
+ options?: {
251
+ path?: string | null;
252
+ glob?: string | null;
253
+ max_count?: number | null;
254
+ max_total?: number | null;
255
+ case?: "sensitive" | "insensitive" | "smart" | null;
256
+ },
257
+ ): RequestSpec;
258
+
259
+ export declare function parseInspectImageGrep(
260
+ response: ResponseData,
261
+ ): GrepResult;
262
+
191
263
  export declare function buildWhoami(): RequestSpec;
192
264
 
193
265
  export declare function parseWhoami(response: ResponseData): WhoAmIResponse;
package/lib/operations.js CHANGED
@@ -9,13 +9,17 @@ import {
9
9
  } from "./runtime.js";
10
10
 
11
11
  import {
12
+ ClosableStreamRepr,
12
13
  DirectoryList,
14
+ ExecSpec,
13
15
  File,
14
16
  FileResponse,
15
17
  FilesListResponse,
18
+ GrepResult,
16
19
  Image,
17
20
  ImageImportRequest,
18
21
  ImageListResponse,
22
+ InstanceResult,
19
23
  InstanceSpawnRequest,
20
24
  InstanceSpawnResponse,
21
25
  OperationResponse,
@@ -111,11 +115,7 @@ export function parseUpdateImageTag(response) {
111
115
 
112
116
  /** Build the request for `POST /images/import`. */
113
117
  export function buildImportImage(registry, { tag, timeout } = {}) {
114
- const payload = new ImageImportRequest({
115
- registry: registry,
116
- tag,
117
- timeout,
118
- }).toWire();
118
+ const payload = new ImageImportRequest({ registry, tag, timeout }).toWire();
119
119
  return {
120
120
  method: "POST",
121
121
  path: `/images/import`,
@@ -235,6 +235,7 @@ export function buildSpawnInstance(
235
235
  uid,
236
236
  gid,
237
237
  resources_limits,
238
+ networking,
238
239
  stdin,
239
240
  timeout,
240
241
  truncate_output_at,
@@ -242,8 +243,8 @@ export function buildSpawnInstance(
242
243
  } = {},
243
244
  ) {
244
245
  const payload = new InstanceSpawnRequest({
245
- command: command,
246
- image: image,
246
+ command,
247
+ image,
247
248
  disposable,
248
249
  hostname,
249
250
  args,
@@ -254,6 +255,7 @@ export function buildSpawnInstance(
254
255
  uid,
255
256
  gid,
256
257
  resources_limits,
258
+ networking,
257
259
  stdin,
258
260
  timeout,
259
261
  truncate_output_at,
@@ -386,6 +388,115 @@ export function buildIterOperationEvents(
386
388
  };
387
389
  }
388
390
 
391
+ /** Build the request for `POST /operations/{operationId}/subprocesses`. */
392
+ export function buildOperationSubprocessCreate(
393
+ operationId,
394
+ command,
395
+ { args, shell, env, cwd, uid, gid, stdin, truncate_output_at } = {},
396
+ ) {
397
+ const payload = new ExecSpec({
398
+ operation_id: operationId,
399
+ command,
400
+ args,
401
+ shell,
402
+ env,
403
+ cwd,
404
+ uid,
405
+ gid,
406
+ stdin,
407
+ truncate_output_at,
408
+ }).toWire();
409
+ return {
410
+ method: "POST",
411
+ path: `/operations/${quotePath(operationId)}/subprocesses`,
412
+ idempotent: false,
413
+ body: JSON.stringify(payload),
414
+ contentType: "application/json",
415
+ };
416
+ }
417
+
418
+ /** Parse the response of `POST /operations/{operationId}/subprocesses`. */
419
+ export function parseOperationSubprocessCreate(response) {
420
+ if (response.status >= 200 && response.status < 300) {
421
+ return Number(jsonObject(response)["spid"]);
422
+ }
423
+ throw errorForResponse(response);
424
+ }
425
+
426
+ /** Build the request for `GET /operations/{operationId}/subprocesses/{spid}`. */
427
+ export function buildOperationSubprocess(operationId, spid) {
428
+ return {
429
+ method: "GET",
430
+ path: `/operations/${quotePath(operationId)}/subprocesses/${quotePath(spid)}`,
431
+ idempotent: true,
432
+ };
433
+ }
434
+
435
+ /** Parse the response of `GET /operations/{operationId}/subprocesses/{spid}`. */
436
+ export function parseOperationSubprocess(response) {
437
+ if (response.status >= 200 && response.status < 300) {
438
+ return InstanceResult.fromWire(jsonObject(response));
439
+ }
440
+ throw errorForResponse(response);
441
+ }
442
+
443
+ /** Build the request for `DELETE /operations/{operationId}/subprocesses/{spid}`. */
444
+ export function buildOperationSubprocessKill(
445
+ operationId,
446
+ spid,
447
+ { signal = null } = {},
448
+ ) {
449
+ const query = {};
450
+ if (signal != null) {
451
+ query["signal"] = signal;
452
+ }
453
+ return {
454
+ method: "DELETE",
455
+ path: `/operations/${quotePath(operationId)}/subprocesses/${quotePath(spid)}`,
456
+ idempotent: true,
457
+ query,
458
+ };
459
+ }
460
+
461
+ /** Parse the response of `DELETE /operations/{operationId}/subprocesses/{spid}`. */
462
+ export function parseOperationSubprocessKill(response) {
463
+ if (response.status >= 200 && response.status < 300) {
464
+ return null;
465
+ }
466
+ throw errorForResponse(response);
467
+ }
468
+
469
+ /** Build the request for `POST /operations/{operationId}/subprocesses/{spid}/stdin`. */
470
+ export function buildOperationSubprocessStdin(
471
+ operationId,
472
+ spid,
473
+ value,
474
+ { encoding, close } = {},
475
+ ) {
476
+ const payload = new ClosableStreamRepr({
477
+ operation_id: operationId,
478
+ spid,
479
+ value,
480
+ encoding,
481
+ close,
482
+ }).toWire();
483
+ return {
484
+ method: "POST",
485
+ path: `/operations/${quotePath(operationId)}/subprocesses/${quotePath(spid)}/stdin`,
486
+ idempotent: false,
487
+ body: JSON.stringify(payload),
488
+ contentType: "application/json",
489
+ };
490
+ }
491
+
492
+ /** Parse the response of `POST /operations/{operationId}/subprocesses/{spid}/stdin`. */
493
+ export function parseOperationSubprocessStdin(response) {
494
+ if (response.status >= 200 && response.status < 300) {
495
+ return null;
496
+ }
497
+ throw errorForResponse(response);
498
+ }
499
+
389
500
  /** Build the request for `GET /inspect/`. */
390
501
  export function buildInspectFindImageByTag(tag) {
391
502
  const query = {};
@@ -529,6 +640,51 @@ export function parseInspectImageList(response) {
529
640
  throw errorForResponse(response);
530
641
  }
531
642
 
643
+ /** Build the request for `GET /inspect/{image_uuid}/grep`. */
644
+ export function buildInspectImageGrep(
645
+ imageUuid,
646
+ pattern,
647
+ {
648
+ path = null,
649
+ glob = null,
650
+ max_count = null,
651
+ max_total = null,
652
+ case: case_ = null,
653
+ } = {},
654
+ ) {
655
+ const query = {};
656
+ query["pattern"] = pattern;
657
+ if (path != null) {
658
+ query["path"] = path;
659
+ }
660
+ if (glob != null) {
661
+ query["glob"] = glob;
662
+ }
663
+ if (max_count != null) {
664
+ query["max_count"] = String(max_count);
665
+ }
666
+ if (max_total != null) {
667
+ query["max_total"] = String(max_total);
668
+ }
669
+ if (case_ != null) {
670
+ query["case"] = case_;
671
+ }
672
+ return {
673
+ method: "GET",
674
+ path: `/inspect/${quotePath(imageUuid)}/grep`,
675
+ idempotent: true,
676
+ query,
677
+ };
678
+ }
679
+
680
+ /** Parse the response of `GET /inspect/{image_uuid}/grep`. */
681
+ export function parseInspectImageGrep(response) {
682
+ if (response.status >= 200 && response.status < 300) {
683
+ return GrepResult.fromWire(jsonObject(response));
684
+ }
685
+ throw errorForResponse(response);
686
+ }
687
+
532
688
  /** Build the request for `GET /whoami`. */
533
689
  export function buildWhoami() {
534
690
  return { method: "GET", path: `/whoami`, idempotent: true };
package/lib/runtime.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
 
15
15
  export const CHUNK_SIZE = 65536;
16
16
 
17
- export const PACKAGE_VERSION = "0.1.3";
17
+ export const PACKAGE_VERSION = "0.2.0";
18
18
  export const UA_PRODUCT = `contree-client-js/${PACKAGE_VERSION}`;
19
19
 
20
20
  const NODE_VERSION =
@@ -47,10 +47,15 @@ export function* retryDelays(delays = RETRY_DELAYS) {
47
47
  }
48
48
  }
49
49
 
50
- /** Opt-in retries for transient failures of buffered requests. */
50
+ /** Opt-in retries for transient failures of buffered requests.
51
+ *
52
+ * 425 (Too Early) and 429 (Too Many Requests) are a backend contract:
53
+ * both mean the request was rejected before any processing, so
54
+ * replaying them is always safe - even for a POST the caller hasn't
55
+ * opted into unsafe retries for (see `call()`). */
51
56
  export class RetryPolicy {
52
57
  constructor({
53
- statuses = [410, 425],
58
+ statuses = [410, 425, 429],
54
59
  serverErrors = true,
55
60
  delays = RETRY_DELAYS,
56
61
  maxAttempts = 10,
package/lib/specInfo.js CHANGED
@@ -5,4 +5,4 @@ export const DEFAULT_BASE_URL = "https://api.tokenfactory.nebius.com/sandboxes";
5
5
  // sha256 of the exact OpenAPI document this package was built
6
6
  // from - the build input provenance
7
7
  export const SPEC_SHA256 =
8
- "3782df855d7ae14556e221f4f37a67fb6aeb65121396cb009a5e7a0995abd06b";
8
+ "bf17dbf27d28bebe3e23b295835bbdb2927ab9de5e9ae581af675e90986acc56";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contree-client",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "JavaScript client for the Contree API, generated from the OpenAPI spec",
5
5
  "homepage": "https://contree.dev/",
6
6
  "repository": {