@archildata/client 0.8.6 → 0.8.8

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/README.md CHANGED
@@ -20,7 +20,7 @@ npm install @archildata/client
20
20
 
21
21
  An Archil "disk" is a filesystem that sits in front of your cloud storage. You create one by telling Archil which bucket to use. This doesn't move or copy your data — Archil reads and writes directly to your bucket.
22
22
 
23
- You also need to authorize a mount token on the disk. This is a separate credential from your API key it's what clients use to connect to the disk's data plane. Pick any string as the principal (it's just an identifier).
23
+ A mount token is automatically generated when you create a disk; it is recommended to save it.
24
24
 
25
25
  ```typescript
26
26
  import { Archil } from '@archildata/client/api';
@@ -30,25 +30,19 @@ const archil = new Archil({
30
30
  region: 'aws-us-east-1',
31
31
  });
32
32
 
33
- const MOUNT_TOKEN = 'my-secret-mount-token';
34
-
35
- const disk = await archil.disks.create({
33
+ const { disk, token } = await archil.disks.create({
36
34
  name: 'my-disk',
37
35
  mounts: [{
38
36
  type: 's3',
39
37
  bucketName: 'my-bucket',
40
38
  accessKeyId: 'AKIA...',
41
39
  secretAccessKey: '...',
42
- }],
43
- authMethods: [{
44
- type: 'token',
45
- principal: MOUNT_TOKEN,
46
- nickname: 'my-mount-token',
47
- tokenSuffix: MOUNT_TOKEN.slice(-4),
40
+ bucketPrefix: 'data/', // optional — only expose a subfolder of the bucket
48
41
  }],
49
42
  });
50
43
 
51
44
  console.log(`Created disk: ${disk.organization}/${disk.name}`);
45
+ console.log(`Mount token: ${token}`); // save this — it won't be shown again
52
46
  ```
53
47
 
54
48
  You only do this once. After that, the disk is available by name whenever you want to connect.
@@ -61,10 +55,10 @@ Install `@archildata/just-bash` to get a shell that runs against your disk:
61
55
  npm install @archildata/just-bash
62
56
  ```
63
57
 
64
- Then connect using the mount token you authorized above:
58
+ Then connect using the mount token from step 3:
65
59
 
66
60
  ```bash
67
- ARCHIL_TOKEN=my-secret-mount-token npx @archildata/just-bash aws-us-east-1 myaccount/my-disk
61
+ ARCHIL_DISK_TOKEN=<your-mount-token> npx @archildata/just-bash aws-us-east-1 myaccount/my-disk
68
62
  ```
69
63
 
70
64
  This drops you into an interactive shell. The files you see are the contents of your S3 bucket:
@@ -94,7 +88,7 @@ import { Bash } from 'just-bash';
94
88
  const client = await ArchilClient.connect({
95
89
  region: 'aws-us-east-1',
96
90
  diskName: 'myaccount/my-disk',
97
- authToken: 'my-secret-mount-token',
91
+ authToken: '<your-mount-token>',
98
92
  });
99
93
 
100
94
  // Create a filesystem adapter and a bash executor
@@ -146,7 +140,7 @@ import { ArchilClient } from '@archildata/client';
146
140
  const client = await ArchilClient.connect({
147
141
  region: 'aws-us-east-1',
148
142
  diskName: 'myaccount/my-disk',
149
- authToken: 'my-secret-mount-token',
143
+ authToken: '<your-mount-token>',
150
144
  });
151
145
  ```
152
146
 
@@ -254,17 +248,13 @@ const disks = await archil.disks.list();
254
248
  // Get a specific disk by ID
255
249
  const disk = await archil.disks.get('dsk-xxx');
256
250
 
257
- // Authorize another mount token on an existing disk
258
- const CI_TOKEN = 'ci-mount-token';
259
- await disk.addUser({
260
- type: 'token',
261
- principal: CI_TOKEN,
262
- nickname: 'ci-token',
263
- tokenSuffix: CI_TOKEN.slice(-4),
264
- });
251
+ // Add an additional mount token to an existing disk
252
+ const { token, identifier } = await disk.createToken('ci-token');
253
+ // save `token` — it's only returned once
254
+ // use `identifier` to manage or remove the token later
265
255
 
266
256
  // Remove access
267
- await disk.removeUser('token', CI_TOKEN);
257
+ await disk.removeTokenUser(identifier);
268
258
 
269
259
  // Delete a disk (this does not delete your bucket data)
270
260
  await disk.delete();
Binary file
@@ -21,8 +21,13 @@ interface paths {
21
21
  put?: never;
22
22
  /**
23
23
  * Create a new disk
24
- * @description Creates a new disk with the specified configuration. The disk will be
25
- * provisioned in the specified region with the given mounts and authentication methods.
24
+ * @description Creates a new disk with the specified configuration. A default token
25
+ * user is automatically generated and returned in the response, so the
26
+ * disk is immediately mountable. The one-time token appears in
27
+ * `authorizedUsers[].token` and cannot be retrieved again.
28
+ *
29
+ * To provide your own users instead, pass the deprecated `authMethods`
30
+ * field or call AddDiskUser after creation.
26
31
  */
27
32
  post: operations["createDisk"];
28
33
  delete?: never;
@@ -97,6 +102,28 @@ interface paths {
97
102
  patch?: never;
98
103
  trace?: never;
99
104
  };
105
+ "/api/disks/{id}/exec": {
106
+ parameters: {
107
+ query?: never;
108
+ header?: never;
109
+ path?: never;
110
+ cookie?: never;
111
+ };
112
+ get?: never;
113
+ put?: never;
114
+ /**
115
+ * Execute a command on a disk
116
+ * @description Launches a container with the specified disk mounted, runs the given
117
+ * command to completion, and shuts down the container. Returns immediately
118
+ * with the container info; poll the container endpoint for completion.
119
+ */
120
+ post: operations["execDisk"];
121
+ delete?: never;
122
+ options?: never;
123
+ head?: never;
124
+ patch?: never;
125
+ trace?: never;
126
+ };
100
127
  "/api/tokens": {
101
128
  parameters: {
102
129
  query?: never;
@@ -170,7 +197,10 @@ interface components {
170
197
  name: string;
171
198
  /** @description Storage mount to attach. Omit for archil-managed storage. */
172
199
  mounts?: components["schemas"]["MountConfig"][];
173
- /** @description Authentication methods for disk access */
200
+ /**
201
+ * @deprecated
202
+ * @description Deprecated. Use AddDiskUser after creation instead. When provided, suppresses the default auto-generated token user.
203
+ */
174
204
  authMethods?: components["schemas"]["DiskUser"][];
175
205
  };
176
206
  MountConfig: components["schemas"]["S3Mount"] | components["schemas"]["GCSMount"] | components["schemas"]["R2Mount"] | components["schemas"]["S3CompatibleMount"] | components["schemas"]["AzureBlobMount"];
@@ -344,9 +374,17 @@ interface components {
344
374
  * @enum {string}
345
375
  */
346
376
  type: "token";
347
- principal: string;
377
+ /**
378
+ * @deprecated
379
+ * @description Deprecated. Client-provided token. If omitted, the server generates a cryptographically secure token and returns it in the response.
380
+ */
381
+ principal?: string;
348
382
  nickname: string;
349
- tokenSuffix: string;
383
+ /**
384
+ * @deprecated
385
+ * @description Deprecated. Last 4 characters of the token. Required when principal is provided; ignored when the server generates the token.
386
+ */
387
+ tokenSuffix?: string;
350
388
  };
351
389
  AwsStsUser: {
352
390
  /**
@@ -461,10 +499,17 @@ interface components {
461
499
  AuthorizedUser: {
462
500
  /** @enum {string} */
463
501
  type?: "token" | "awssts";
464
- /** @description The user's principal identifier. Only populated for awssts type (the IAM ARN). Omitted for token type users to avoid exposing sensitive hashes. */
502
+ /**
503
+ * @deprecated
504
+ * @description Use identifier instead. Only populated for awssts type (the IAM ARN).
505
+ */
465
506
  principal?: string;
466
507
  nickname?: string;
467
508
  tokenSuffix?: string;
509
+ /** @description The generated mount token. Only present in the response when the server generates the token (i.e. principal was not provided). This value is shown exactly once and cannot be retrieved again. */
510
+ token?: string;
511
+ /** @description Stable identifier for this user, returned in creation and list responses. Use this value with DELETE /api/disks/{id}/users/{type}?identifier={identifier} to remove the user. For awssts users, this is the IAM ARN. */
512
+ identifier?: string;
468
513
  /** Format: date-time */
469
514
  createdAt?: string;
470
515
  };
@@ -485,6 +530,7 @@ interface components {
485
530
  data: {
486
531
  /** @example dsk-0123456789abcdef */
487
532
  diskId?: string;
533
+ authorizedUsers?: components["schemas"]["AuthorizedUser"][];
488
534
  };
489
535
  };
490
536
  ApiResponse_AuthorizedUser: {
@@ -528,6 +574,29 @@ interface components {
528
574
  token?: string;
529
575
  };
530
576
  };
577
+ ExecDiskRequest: {
578
+ /**
579
+ * @description Shell command to execute inside the container
580
+ * @example ls -la /mnt/archil
581
+ */
582
+ command: string;
583
+ };
584
+ ExecDiskResult: {
585
+ /**
586
+ * @description Exit code of the command (0 = success)
587
+ * @example 0
588
+ */
589
+ exitCode: number;
590
+ /** @description Standard output from the command */
591
+ stdout: string;
592
+ /** @description Standard error from the command */
593
+ stderr: string;
594
+ };
595
+ ApiResponse_ExecDisk: {
596
+ /** @example true */
597
+ success: boolean;
598
+ data: components["schemas"]["ExecDiskResult"];
599
+ };
531
600
  };
532
601
  responses: {
533
602
  /** @description Invalid or missing authentication credentials */
@@ -749,9 +818,14 @@ interface operations {
749
818
  };
750
819
  removeDiskUser: {
751
820
  parameters: {
752
- query: {
753
- /** @description The user's principal identifier. For token type, this is the MD5 hash (hex-encoded) of the token principal. For awssts type, this is the full IAM ARN (e.g., arn:aws:iam::123456789012:role/MyRole). */
754
- principal: string;
821
+ query?: {
822
+ /** @description Identifier of the user to remove, as returned in the creation or list response. For awssts users, this is the IAM ARN. */
823
+ identifier?: string;
824
+ /**
825
+ * @deprecated
826
+ * @description Use identifier instead.
827
+ */
828
+ principal?: string;
755
829
  };
756
830
  header?: never;
757
831
  path: {
@@ -778,6 +852,46 @@ interface operations {
778
852
  500: components["responses"]["InternalError"];
779
853
  };
780
854
  };
855
+ execDisk: {
856
+ parameters: {
857
+ query?: never;
858
+ header?: never;
859
+ path: {
860
+ /** @description Disk ID (format `dsk-{16 hex chars}`) */
861
+ id: components["parameters"]["DiskId"];
862
+ };
863
+ cookie?: never;
864
+ };
865
+ requestBody: {
866
+ content: {
867
+ "application/json": components["schemas"]["ExecDiskRequest"];
868
+ };
869
+ };
870
+ responses: {
871
+ /** @description Command completed */
872
+ 200: {
873
+ headers: {
874
+ [name: string]: unknown;
875
+ };
876
+ content: {
877
+ "application/json": components["schemas"]["ApiResponse_ExecDisk"];
878
+ };
879
+ };
880
+ 400: components["responses"]["ValidationError"];
881
+ 401: components["responses"]["Unauthorized"];
882
+ 404: components["responses"]["NotFound"];
883
+ 500: components["responses"]["InternalError"];
884
+ /** @description Command timed out */
885
+ 504: {
886
+ headers: {
887
+ [name: string]: unknown;
888
+ };
889
+ content: {
890
+ "application/json": components["schemas"]["ErrorResponse"];
891
+ };
892
+ };
893
+ };
894
+ };
781
895
  listApiTokens: {
782
896
  parameters: {
783
897
  query?: {
@@ -888,6 +1002,20 @@ interface MountOptions {
888
1002
  serverAddress?: string;
889
1003
  insecure?: boolean;
890
1004
  }
1005
+ interface ExecTiming {
1006
+ /** End-to-end wall-clock measured on the server (from request arrival to response). */
1007
+ totalMs: number;
1008
+ /** Time spent queueing, scheduling, booting/claiming a VM, and mounting the filesystem. */
1009
+ queueMs: number;
1010
+ /** Time the user's command itself ran, measured by the runtime. */
1011
+ executeMs: number;
1012
+ }
1013
+ interface ExecResult {
1014
+ exitCode: number;
1015
+ stdout: string;
1016
+ stderr: string;
1017
+ timing: ExecTiming;
1018
+ }
891
1019
  declare class Disk {
892
1020
  readonly id: string;
893
1021
  readonly name: string;
@@ -911,8 +1039,18 @@ declare class Disk {
911
1039
  /** @internal */
912
1040
  constructor(data: DiskResponse, client: ApiClient, archilRegion: string);
913
1041
  addUser(user: DiskUser): Promise<AuthorizedUser>;
914
- removeUser(userType: "token" | "awssts", principal: string): Promise<void>;
1042
+ removeUser(userType: "token" | "awssts", identifier: string): Promise<void>;
1043
+ createToken(nickname: string): Promise<AuthorizedUser & {
1044
+ token: string;
1045
+ identifier: string;
1046
+ }>;
1047
+ removeTokenUser(identifier: string): Promise<void>;
915
1048
  delete(): Promise<void>;
1049
+ /**
1050
+ * Execute a command in a container with this disk mounted.
1051
+ * Blocks until the command completes and returns stdout, stderr, and exit code.
1052
+ */
1053
+ exec(command: string): Promise<ExecResult>;
916
1054
  /**
917
1055
  * Connect to this disk's data plane via the native ArchilClient.
918
1056
  *
@@ -925,6 +1063,12 @@ interface ListDisksOptions {
925
1063
  limit?: number;
926
1064
  cursor?: string;
927
1065
  }
1066
+ interface CreateDiskResult {
1067
+ disk: Disk;
1068
+ token: string | null;
1069
+ tokenIdentifier: string | null;
1070
+ authorizedUsers: AuthorizedUser[];
1071
+ }
928
1072
  declare class Disks {
929
1073
  /** @internal */
930
1074
  private readonly _client;
@@ -935,11 +1079,12 @@ declare class Disks {
935
1079
  list(opts?: ListDisksOptions): Promise<Disk[]>;
936
1080
  get(id: string): Promise<Disk>;
937
1081
  /**
938
- * Create a new disk and return a Disk object with full details.
1082
+ * Create a new disk with an auto-generated mount token.
939
1083
  *
940
- * Internally calls POST /api/disks (returns diskId) then GET /api/disks/{id}.
1084
+ * Returns the Disk, the one-time token (save it it cannot be retrieved
1085
+ * again), and the token identifier for later management.
941
1086
  */
942
- create(req: CreateDiskRequest): Promise<Disk>;
1087
+ create(req: CreateDiskRequest): Promise<CreateDiskResult>;
943
1088
  }
944
1089
 
945
1090
  interface ListTokensOptions {
@@ -959,15 +1104,17 @@ declare class Tokens {
959
1104
  }
960
1105
 
961
1106
  interface ArchilOptions {
962
- apiKey: string;
963
- region: string;
1107
+ /** API key. Falls back to ARCHIL_API_KEY env var if not provided. */
1108
+ apiKey?: string;
1109
+ /** Region. Falls back to ARCHIL_REGION env var if not provided. */
1110
+ region?: string;
964
1111
  /** Override the control plane base URL (useful for testing). */
965
1112
  baseUrl?: string;
966
1113
  }
967
1114
  declare class Archil {
968
1115
  readonly disks: Disks;
969
1116
  readonly tokens: Tokens;
970
- constructor(opts: ArchilOptions);
1117
+ constructor(opts?: ArchilOptions);
971
1118
  }
972
1119
 
973
1120
  declare class ArchilApiError extends Error {
@@ -976,4 +1123,4 @@ declare class ArchilApiError extends Error {
976
1123
  constructor(message: string, status: number, code?: string);
977
1124
  }
978
1125
 
979
- export { type ApiTokenResponse, Archil, ArchilApiError, type ArchilOptions, type AuthorizedUser, type AwsStsUser, type AzureBlobMount, type ConnectedClient, type CreateApiTokenRequest, type CreateDiskRequest, Disk, type DiskMetrics, type DiskResponse, type DiskStatus, type DiskUser, Disks, type GCSMount, type ListDisksOptions, type ListTokensOptions, type MountConfig, type MountConfigResponse, type MountOptions, type MountResponse, type R2Mount, type S3CompatibleMount, type S3Mount, type TokenUser, Tokens };
1126
+ export { type ApiTokenResponse, Archil, ArchilApiError, type ArchilOptions, type AuthorizedUser, type AwsStsUser, type AzureBlobMount, type ConnectedClient, type CreateApiTokenRequest, type CreateDiskRequest, type CreateDiskResult, Disk, type DiskMetrics, type DiskResponse, type DiskStatus, type DiskUser, Disks, type ExecResult, type GCSMount, type ListDisksOptions, type ListTokensOptions, type MountConfig, type MountConfigResponse, type MountOptions, type MountResponse, type R2Mount, type S3CompatibleMount, type S3Mount, type TokenUser, Tokens };
@@ -21,8 +21,13 @@ interface paths {
21
21
  put?: never;
22
22
  /**
23
23
  * Create a new disk
24
- * @description Creates a new disk with the specified configuration. The disk will be
25
- * provisioned in the specified region with the given mounts and authentication methods.
24
+ * @description Creates a new disk with the specified configuration. A default token
25
+ * user is automatically generated and returned in the response, so the
26
+ * disk is immediately mountable. The one-time token appears in
27
+ * `authorizedUsers[].token` and cannot be retrieved again.
28
+ *
29
+ * To provide your own users instead, pass the deprecated `authMethods`
30
+ * field or call AddDiskUser after creation.
26
31
  */
27
32
  post: operations["createDisk"];
28
33
  delete?: never;
@@ -97,6 +102,28 @@ interface paths {
97
102
  patch?: never;
98
103
  trace?: never;
99
104
  };
105
+ "/api/disks/{id}/exec": {
106
+ parameters: {
107
+ query?: never;
108
+ header?: never;
109
+ path?: never;
110
+ cookie?: never;
111
+ };
112
+ get?: never;
113
+ put?: never;
114
+ /**
115
+ * Execute a command on a disk
116
+ * @description Launches a container with the specified disk mounted, runs the given
117
+ * command to completion, and shuts down the container. Returns immediately
118
+ * with the container info; poll the container endpoint for completion.
119
+ */
120
+ post: operations["execDisk"];
121
+ delete?: never;
122
+ options?: never;
123
+ head?: never;
124
+ patch?: never;
125
+ trace?: never;
126
+ };
100
127
  "/api/tokens": {
101
128
  parameters: {
102
129
  query?: never;
@@ -170,7 +197,10 @@ interface components {
170
197
  name: string;
171
198
  /** @description Storage mount to attach. Omit for archil-managed storage. */
172
199
  mounts?: components["schemas"]["MountConfig"][];
173
- /** @description Authentication methods for disk access */
200
+ /**
201
+ * @deprecated
202
+ * @description Deprecated. Use AddDiskUser after creation instead. When provided, suppresses the default auto-generated token user.
203
+ */
174
204
  authMethods?: components["schemas"]["DiskUser"][];
175
205
  };
176
206
  MountConfig: components["schemas"]["S3Mount"] | components["schemas"]["GCSMount"] | components["schemas"]["R2Mount"] | components["schemas"]["S3CompatibleMount"] | components["schemas"]["AzureBlobMount"];
@@ -344,9 +374,17 @@ interface components {
344
374
  * @enum {string}
345
375
  */
346
376
  type: "token";
347
- principal: string;
377
+ /**
378
+ * @deprecated
379
+ * @description Deprecated. Client-provided token. If omitted, the server generates a cryptographically secure token and returns it in the response.
380
+ */
381
+ principal?: string;
348
382
  nickname: string;
349
- tokenSuffix: string;
383
+ /**
384
+ * @deprecated
385
+ * @description Deprecated. Last 4 characters of the token. Required when principal is provided; ignored when the server generates the token.
386
+ */
387
+ tokenSuffix?: string;
350
388
  };
351
389
  AwsStsUser: {
352
390
  /**
@@ -461,10 +499,17 @@ interface components {
461
499
  AuthorizedUser: {
462
500
  /** @enum {string} */
463
501
  type?: "token" | "awssts";
464
- /** @description The user's principal identifier. Only populated for awssts type (the IAM ARN). Omitted for token type users to avoid exposing sensitive hashes. */
502
+ /**
503
+ * @deprecated
504
+ * @description Use identifier instead. Only populated for awssts type (the IAM ARN).
505
+ */
465
506
  principal?: string;
466
507
  nickname?: string;
467
508
  tokenSuffix?: string;
509
+ /** @description The generated mount token. Only present in the response when the server generates the token (i.e. principal was not provided). This value is shown exactly once and cannot be retrieved again. */
510
+ token?: string;
511
+ /** @description Stable identifier for this user, returned in creation and list responses. Use this value with DELETE /api/disks/{id}/users/{type}?identifier={identifier} to remove the user. For awssts users, this is the IAM ARN. */
512
+ identifier?: string;
468
513
  /** Format: date-time */
469
514
  createdAt?: string;
470
515
  };
@@ -485,6 +530,7 @@ interface components {
485
530
  data: {
486
531
  /** @example dsk-0123456789abcdef */
487
532
  diskId?: string;
533
+ authorizedUsers?: components["schemas"]["AuthorizedUser"][];
488
534
  };
489
535
  };
490
536
  ApiResponse_AuthorizedUser: {
@@ -528,6 +574,29 @@ interface components {
528
574
  token?: string;
529
575
  };
530
576
  };
577
+ ExecDiskRequest: {
578
+ /**
579
+ * @description Shell command to execute inside the container
580
+ * @example ls -la /mnt/archil
581
+ */
582
+ command: string;
583
+ };
584
+ ExecDiskResult: {
585
+ /**
586
+ * @description Exit code of the command (0 = success)
587
+ * @example 0
588
+ */
589
+ exitCode: number;
590
+ /** @description Standard output from the command */
591
+ stdout: string;
592
+ /** @description Standard error from the command */
593
+ stderr: string;
594
+ };
595
+ ApiResponse_ExecDisk: {
596
+ /** @example true */
597
+ success: boolean;
598
+ data: components["schemas"]["ExecDiskResult"];
599
+ };
531
600
  };
532
601
  responses: {
533
602
  /** @description Invalid or missing authentication credentials */
@@ -749,9 +818,14 @@ interface operations {
749
818
  };
750
819
  removeDiskUser: {
751
820
  parameters: {
752
- query: {
753
- /** @description The user's principal identifier. For token type, this is the MD5 hash (hex-encoded) of the token principal. For awssts type, this is the full IAM ARN (e.g., arn:aws:iam::123456789012:role/MyRole). */
754
- principal: string;
821
+ query?: {
822
+ /** @description Identifier of the user to remove, as returned in the creation or list response. For awssts users, this is the IAM ARN. */
823
+ identifier?: string;
824
+ /**
825
+ * @deprecated
826
+ * @description Use identifier instead.
827
+ */
828
+ principal?: string;
755
829
  };
756
830
  header?: never;
757
831
  path: {
@@ -778,6 +852,46 @@ interface operations {
778
852
  500: components["responses"]["InternalError"];
779
853
  };
780
854
  };
855
+ execDisk: {
856
+ parameters: {
857
+ query?: never;
858
+ header?: never;
859
+ path: {
860
+ /** @description Disk ID (format `dsk-{16 hex chars}`) */
861
+ id: components["parameters"]["DiskId"];
862
+ };
863
+ cookie?: never;
864
+ };
865
+ requestBody: {
866
+ content: {
867
+ "application/json": components["schemas"]["ExecDiskRequest"];
868
+ };
869
+ };
870
+ responses: {
871
+ /** @description Command completed */
872
+ 200: {
873
+ headers: {
874
+ [name: string]: unknown;
875
+ };
876
+ content: {
877
+ "application/json": components["schemas"]["ApiResponse_ExecDisk"];
878
+ };
879
+ };
880
+ 400: components["responses"]["ValidationError"];
881
+ 401: components["responses"]["Unauthorized"];
882
+ 404: components["responses"]["NotFound"];
883
+ 500: components["responses"]["InternalError"];
884
+ /** @description Command timed out */
885
+ 504: {
886
+ headers: {
887
+ [name: string]: unknown;
888
+ };
889
+ content: {
890
+ "application/json": components["schemas"]["ErrorResponse"];
891
+ };
892
+ };
893
+ };
894
+ };
781
895
  listApiTokens: {
782
896
  parameters: {
783
897
  query?: {
@@ -888,6 +1002,20 @@ interface MountOptions {
888
1002
  serverAddress?: string;
889
1003
  insecure?: boolean;
890
1004
  }
1005
+ interface ExecTiming {
1006
+ /** End-to-end wall-clock measured on the server (from request arrival to response). */
1007
+ totalMs: number;
1008
+ /** Time spent queueing, scheduling, booting/claiming a VM, and mounting the filesystem. */
1009
+ queueMs: number;
1010
+ /** Time the user's command itself ran, measured by the runtime. */
1011
+ executeMs: number;
1012
+ }
1013
+ interface ExecResult {
1014
+ exitCode: number;
1015
+ stdout: string;
1016
+ stderr: string;
1017
+ timing: ExecTiming;
1018
+ }
891
1019
  declare class Disk {
892
1020
  readonly id: string;
893
1021
  readonly name: string;
@@ -911,8 +1039,18 @@ declare class Disk {
911
1039
  /** @internal */
912
1040
  constructor(data: DiskResponse, client: ApiClient, archilRegion: string);
913
1041
  addUser(user: DiskUser): Promise<AuthorizedUser>;
914
- removeUser(userType: "token" | "awssts", principal: string): Promise<void>;
1042
+ removeUser(userType: "token" | "awssts", identifier: string): Promise<void>;
1043
+ createToken(nickname: string): Promise<AuthorizedUser & {
1044
+ token: string;
1045
+ identifier: string;
1046
+ }>;
1047
+ removeTokenUser(identifier: string): Promise<void>;
915
1048
  delete(): Promise<void>;
1049
+ /**
1050
+ * Execute a command in a container with this disk mounted.
1051
+ * Blocks until the command completes and returns stdout, stderr, and exit code.
1052
+ */
1053
+ exec(command: string): Promise<ExecResult>;
916
1054
  /**
917
1055
  * Connect to this disk's data plane via the native ArchilClient.
918
1056
  *
@@ -925,6 +1063,12 @@ interface ListDisksOptions {
925
1063
  limit?: number;
926
1064
  cursor?: string;
927
1065
  }
1066
+ interface CreateDiskResult {
1067
+ disk: Disk;
1068
+ token: string | null;
1069
+ tokenIdentifier: string | null;
1070
+ authorizedUsers: AuthorizedUser[];
1071
+ }
928
1072
  declare class Disks {
929
1073
  /** @internal */
930
1074
  private readonly _client;
@@ -935,11 +1079,12 @@ declare class Disks {
935
1079
  list(opts?: ListDisksOptions): Promise<Disk[]>;
936
1080
  get(id: string): Promise<Disk>;
937
1081
  /**
938
- * Create a new disk and return a Disk object with full details.
1082
+ * Create a new disk with an auto-generated mount token.
939
1083
  *
940
- * Internally calls POST /api/disks (returns diskId) then GET /api/disks/{id}.
1084
+ * Returns the Disk, the one-time token (save it it cannot be retrieved
1085
+ * again), and the token identifier for later management.
941
1086
  */
942
- create(req: CreateDiskRequest): Promise<Disk>;
1087
+ create(req: CreateDiskRequest): Promise<CreateDiskResult>;
943
1088
  }
944
1089
 
945
1090
  interface ListTokensOptions {
@@ -959,15 +1104,17 @@ declare class Tokens {
959
1104
  }
960
1105
 
961
1106
  interface ArchilOptions {
962
- apiKey: string;
963
- region: string;
1107
+ /** API key. Falls back to ARCHIL_API_KEY env var if not provided. */
1108
+ apiKey?: string;
1109
+ /** Region. Falls back to ARCHIL_REGION env var if not provided. */
1110
+ region?: string;
964
1111
  /** Override the control plane base URL (useful for testing). */
965
1112
  baseUrl?: string;
966
1113
  }
967
1114
  declare class Archil {
968
1115
  readonly disks: Disks;
969
1116
  readonly tokens: Tokens;
970
- constructor(opts: ArchilOptions);
1117
+ constructor(opts?: ArchilOptions);
971
1118
  }
972
1119
 
973
1120
  declare class ArchilApiError extends Error {
@@ -976,4 +1123,4 @@ declare class ArchilApiError extends Error {
976
1123
  constructor(message: string, status: number, code?: string);
977
1124
  }
978
1125
 
979
- export { type ApiTokenResponse, Archil, ArchilApiError, type ArchilOptions, type AuthorizedUser, type AwsStsUser, type AzureBlobMount, type ConnectedClient, type CreateApiTokenRequest, type CreateDiskRequest, Disk, type DiskMetrics, type DiskResponse, type DiskStatus, type DiskUser, Disks, type GCSMount, type ListDisksOptions, type ListTokensOptions, type MountConfig, type MountConfigResponse, type MountOptions, type MountResponse, type R2Mount, type S3CompatibleMount, type S3Mount, type TokenUser, Tokens };
1126
+ export { type ApiTokenResponse, Archil, ArchilApiError, type ArchilOptions, type AuthorizedUser, type AwsStsUser, type AzureBlobMount, type ConnectedClient, type CreateApiTokenRequest, type CreateDiskRequest, type CreateDiskResult, Disk, type DiskMetrics, type DiskResponse, type DiskStatus, type DiskUser, Disks, type ExecResult, type GCSMount, type ListDisksOptions, type ListTokensOptions, type MountConfig, type MountConfigResponse, type MountOptions, type MountResponse, type R2Mount, type S3CompatibleMount, type S3Mount, type TokenUser, Tokens };
package/dist/api/index.js CHANGED
@@ -76,7 +76,7 @@ function createApiClient(opts) {
76
76
  return (0, import_openapi_fetch.default)({
77
77
  baseUrl,
78
78
  headers: {
79
- Authorization: `key-${opts.apiKey}`
79
+ Authorization: `key-${opts.apiKey.replace(/^key-/, "")}`
80
80
  }
81
81
  });
82
82
  }
@@ -166,16 +166,31 @@ var Disk = class {
166
166
  })
167
167
  );
168
168
  }
169
- async removeUser(userType, principal) {
169
+ async removeUser(userType, identifier) {
170
170
  await unwrapEmpty(
171
171
  this._client.DELETE("/api/disks/{id}/users/{userType}", {
172
172
  params: {
173
173
  path: { id: this.id, userType },
174
- query: { principal }
174
+ query: { identifier }
175
175
  }
176
176
  })
177
177
  );
178
178
  }
179
+ async createToken(nickname) {
180
+ const user = await unwrap(
181
+ this._client.POST("/api/disks/{id}/users", {
182
+ params: { path: { id: this.id } },
183
+ body: { type: "token", nickname }
184
+ })
185
+ );
186
+ if (!user.token || !user.identifier) {
187
+ throw new Error("Server did not return a generated token");
188
+ }
189
+ return user;
190
+ }
191
+ async removeTokenUser(identifier) {
192
+ await this.removeUser("token", identifier);
193
+ }
179
194
  async delete() {
180
195
  await unwrapEmpty(
181
196
  this._client.DELETE("/api/disks/{id}", {
@@ -183,6 +198,21 @@ var Disk = class {
183
198
  })
184
199
  );
185
200
  }
201
+ /**
202
+ * Execute a command in a container with this disk mounted.
203
+ * Blocks until the command completes and returns stdout, stderr, and exit code.
204
+ */
205
+ async exec(command) {
206
+ return unwrap(
207
+ this._client.POST(
208
+ "/api/disks/{id}/exec",
209
+ {
210
+ params: { path: { id: this.id } },
211
+ body: { command }
212
+ }
213
+ )
214
+ );
215
+ }
186
216
  /**
187
217
  * Connect to this disk's data plane via the native ArchilClient.
188
218
  *
@@ -241,19 +271,28 @@ var Disks = class {
241
271
  return new Disk(data, this._client, this._region);
242
272
  }
243
273
  /**
244
- * Create a new disk and return a Disk object with full details.
274
+ * Create a new disk with an auto-generated mount token.
245
275
  *
246
- * Internally calls POST /api/disks (returns diskId) then GET /api/disks/{id}.
276
+ * Returns the Disk, the one-time token (save it it cannot be retrieved
277
+ * again), and the token identifier for later management.
247
278
  */
248
279
  async create(req) {
249
280
  const created = await unwrap(
250
281
  this._client.POST("/api/disks", { body: req })
251
282
  );
252
- const diskId = created.diskId;
253
- if (!diskId) {
283
+ const resp = created;
284
+ if (!resp.diskId) {
254
285
  throw new Error("API returned success but no diskId");
255
286
  }
256
- return this.get(diskId);
287
+ const authorizedUsers = resp.authorizedUsers ?? [];
288
+ const tokenUser = authorizedUsers.find((u) => u.token);
289
+ const disk = await this.get(resp.diskId);
290
+ return {
291
+ disk,
292
+ token: tokenUser?.token ?? null,
293
+ tokenIdentifier: tokenUser?.identifier ?? null,
294
+ authorizedUsers
295
+ };
257
296
  }
258
297
  };
259
298
 
@@ -292,13 +331,21 @@ var Tokens = class {
292
331
  var Archil = class {
293
332
  disks;
294
333
  tokens;
295
- constructor(opts) {
334
+ constructor(opts = {}) {
335
+ const apiKey = opts.apiKey ?? process.env.ARCHIL_API_KEY;
336
+ const region = opts.region ?? process.env.ARCHIL_REGION;
337
+ if (!apiKey) {
338
+ throw new Error("Missing API key: pass apiKey in options or set ARCHIL_API_KEY environment variable");
339
+ }
340
+ if (!region) {
341
+ throw new Error("Missing region: pass region in options or set ARCHIL_REGION environment variable");
342
+ }
296
343
  const client = createApiClient({
297
- apiKey: opts.apiKey,
298
- region: opts.region,
344
+ apiKey,
345
+ region,
299
346
  baseUrl: opts.baseUrl
300
347
  });
301
- this.disks = new Disks(client, opts.region);
348
+ this.disks = new Disks(client, region);
302
349
  this.tokens = new Tokens(client);
303
350
  }
304
351
  };
@@ -36,7 +36,7 @@ function createApiClient(opts) {
36
36
  return createClient({
37
37
  baseUrl,
38
38
  headers: {
39
- Authorization: `key-${opts.apiKey}`
39
+ Authorization: `key-${opts.apiKey.replace(/^key-/, "")}`
40
40
  }
41
41
  });
42
42
  }
@@ -125,16 +125,31 @@ var Disk = class {
125
125
  })
126
126
  );
127
127
  }
128
- async removeUser(userType, principal) {
128
+ async removeUser(userType, identifier) {
129
129
  await unwrapEmpty(
130
130
  this._client.DELETE("/api/disks/{id}/users/{userType}", {
131
131
  params: {
132
132
  path: { id: this.id, userType },
133
- query: { principal }
133
+ query: { identifier }
134
134
  }
135
135
  })
136
136
  );
137
137
  }
138
+ async createToken(nickname) {
139
+ const user = await unwrap(
140
+ this._client.POST("/api/disks/{id}/users", {
141
+ params: { path: { id: this.id } },
142
+ body: { type: "token", nickname }
143
+ })
144
+ );
145
+ if (!user.token || !user.identifier) {
146
+ throw new Error("Server did not return a generated token");
147
+ }
148
+ return user;
149
+ }
150
+ async removeTokenUser(identifier) {
151
+ await this.removeUser("token", identifier);
152
+ }
138
153
  async delete() {
139
154
  await unwrapEmpty(
140
155
  this._client.DELETE("/api/disks/{id}", {
@@ -142,6 +157,21 @@ var Disk = class {
142
157
  })
143
158
  );
144
159
  }
160
+ /**
161
+ * Execute a command in a container with this disk mounted.
162
+ * Blocks until the command completes and returns stdout, stderr, and exit code.
163
+ */
164
+ async exec(command) {
165
+ return unwrap(
166
+ this._client.POST(
167
+ "/api/disks/{id}/exec",
168
+ {
169
+ params: { path: { id: this.id } },
170
+ body: { command }
171
+ }
172
+ )
173
+ );
174
+ }
145
175
  /**
146
176
  * Connect to this disk's data plane via the native ArchilClient.
147
177
  *
@@ -200,19 +230,28 @@ var Disks = class {
200
230
  return new Disk(data, this._client, this._region);
201
231
  }
202
232
  /**
203
- * Create a new disk and return a Disk object with full details.
233
+ * Create a new disk with an auto-generated mount token.
204
234
  *
205
- * Internally calls POST /api/disks (returns diskId) then GET /api/disks/{id}.
235
+ * Returns the Disk, the one-time token (save it it cannot be retrieved
236
+ * again), and the token identifier for later management.
206
237
  */
207
238
  async create(req) {
208
239
  const created = await unwrap(
209
240
  this._client.POST("/api/disks", { body: req })
210
241
  );
211
- const diskId = created.diskId;
212
- if (!diskId) {
242
+ const resp = created;
243
+ if (!resp.diskId) {
213
244
  throw new Error("API returned success but no diskId");
214
245
  }
215
- return this.get(diskId);
246
+ const authorizedUsers = resp.authorizedUsers ?? [];
247
+ const tokenUser = authorizedUsers.find((u) => u.token);
248
+ const disk = await this.get(resp.diskId);
249
+ return {
250
+ disk,
251
+ token: tokenUser?.token ?? null,
252
+ tokenIdentifier: tokenUser?.identifier ?? null,
253
+ authorizedUsers
254
+ };
216
255
  }
217
256
  };
218
257
 
@@ -251,13 +290,21 @@ var Tokens = class {
251
290
  var Archil = class {
252
291
  disks;
253
292
  tokens;
254
- constructor(opts) {
293
+ constructor(opts = {}) {
294
+ const apiKey = opts.apiKey ?? process.env.ARCHIL_API_KEY;
295
+ const region = opts.region ?? process.env.ARCHIL_REGION;
296
+ if (!apiKey) {
297
+ throw new Error("Missing API key: pass apiKey in options or set ARCHIL_API_KEY environment variable");
298
+ }
299
+ if (!region) {
300
+ throw new Error("Missing region: pass region in options or set ARCHIL_REGION environment variable");
301
+ }
255
302
  const client = createApiClient({
256
- apiKey: opts.apiKey,
257
- region: opts.region,
303
+ apiKey,
304
+ region,
258
305
  baseUrl: opts.baseUrl
259
306
  });
260
- this.disks = new Disks(client, opts.region);
307
+ this.disks = new Disks(client, region);
261
308
  this.tokens = new Tokens(client);
262
309
  }
263
310
  };
package/main.mjs CHANGED
@@ -15,3 +15,4 @@ try {
15
15
  export const ArchilClient = native.ArchilClient;
16
16
  export const initLogging = native.initLogging;
17
17
  export const JsInodeType = native.JsInodeType;
18
+ export const MAXIMUM_READ_SIZE = native.MAXIMUM_READ_SIZE;
package/native.d.ts CHANGED
@@ -3,6 +3,11 @@
3
3
 
4
4
  /* auto-generated by NAPI-RS */
5
5
 
6
+ /**
7
+ * Maximum size (in bytes) of a single read request.
8
+ * Matches MAXIMUM_READ_SIZE in the Rust protocol layer (rust-common/src/network/mod.rs).
9
+ */
10
+ export declare const MAXIMUM_READ_SIZE: number
6
11
  /**
7
12
  * Initialize the Rust logger to output to stderr.
8
13
  *
package/native.js CHANGED
@@ -72,8 +72,9 @@ if (!existsSync(localPath)) {
72
72
 
73
73
  const nativeBinding = require(localPath)
74
74
 
75
- const { initLogging, ArchilClient, JsInodeType } = nativeBinding
75
+ const { initLogging, ArchilClient, JsInodeType, MAXIMUM_READ_SIZE } = nativeBinding
76
76
 
77
77
  module.exports.initLogging = initLogging
78
78
  module.exports.ArchilClient = ArchilClient
79
79
  module.exports.JsInodeType = JsInodeType
80
+ module.exports.MAXIMUM_READ_SIZE = MAXIMUM_READ_SIZE
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archildata/client",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
4
4
  "description": "High-performance Node.js client for Archil distributed filesystem",
5
5
  "main": "main.js",
6
6
  "types": "main.d.ts",