modal 0.7.1 → 0.7.2
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 +1 -0
- package/dist/index.cjs +56 -87
- package/dist/index.js +56 -87
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ We also provide a number of examples:
|
|
|
40
40
|
- [Create a Sandbox with GPU](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-gpu.ts)
|
|
41
41
|
- [Create a Sandbox using a private image from AWS ECR](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-private-image.ts)
|
|
42
42
|
- [Take a snapshot of the filesystem of a Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-filesystem-snapshot.ts)
|
|
43
|
+
- [Snapshot a directory, and mount it in a running Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-directory-snapshot.ts)
|
|
43
44
|
- [Execute Sandbox commands](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-exec.ts)
|
|
44
45
|
- [Running a coding agent in a Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-agent.ts)
|
|
45
46
|
- [Check the status and exit code of a Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-poll.ts)
|
package/dist/index.cjs
CHANGED
|
@@ -48940,24 +48940,33 @@ var Sandbox2 = class _Sandbox {
|
|
|
48940
48940
|
throw new ClientClosedError();
|
|
48941
48941
|
}
|
|
48942
48942
|
}
|
|
48943
|
+
static #maxGetTaskIdAttempts = 600;
|
|
48944
|
+
// 5 minutes at 500ms intervals
|
|
48943
48945
|
async #getTaskId() {
|
|
48944
|
-
if (this.#taskId
|
|
48946
|
+
if (this.#taskId !== void 0) {
|
|
48947
|
+
return this.#taskId;
|
|
48948
|
+
}
|
|
48949
|
+
for (let i = 0; i < _Sandbox.#maxGetTaskIdAttempts; i++) {
|
|
48945
48950
|
const resp = await this.#client.cpClient.sandboxGetTaskId({
|
|
48946
48951
|
sandboxId: this.sandboxId
|
|
48947
48952
|
});
|
|
48948
|
-
if (!resp.taskId) {
|
|
48949
|
-
throw new Error(
|
|
48950
|
-
`Sandbox ${this.sandboxId} does not have a task ID. It may not be running.`
|
|
48951
|
-
);
|
|
48952
|
-
}
|
|
48953
48953
|
if (resp.taskResult) {
|
|
48954
|
+
if (resp.taskResult.status === 1 /* GENERIC_STATUS_SUCCESS */ || !resp.taskResult.exception) {
|
|
48955
|
+
throw new Error(`Sandbox ${this.sandboxId} has already completed`);
|
|
48956
|
+
}
|
|
48954
48957
|
throw new Error(
|
|
48955
|
-
`Sandbox ${this.sandboxId} has already completed with result: ${resp.taskResult}`
|
|
48958
|
+
`Sandbox ${this.sandboxId} has already completed with result: exception:"${resp.taskResult.exception}"`
|
|
48956
48959
|
);
|
|
48957
48960
|
}
|
|
48958
|
-
|
|
48961
|
+
if (resp.taskId) {
|
|
48962
|
+
this.#taskId = resp.taskId;
|
|
48963
|
+
return this.#taskId;
|
|
48964
|
+
}
|
|
48965
|
+
await (0, import_promises2.setTimeout)(500);
|
|
48959
48966
|
}
|
|
48960
|
-
|
|
48967
|
+
throw new Error(
|
|
48968
|
+
`Timed out waiting for task ID for Sandbox ${this.sandboxId}`
|
|
48969
|
+
);
|
|
48961
48970
|
}
|
|
48962
48971
|
async #getOrCreateCommandRouterClient(taskId) {
|
|
48963
48972
|
if (this.#commandRouterClient !== void 0) {
|
|
@@ -49270,6 +49279,9 @@ async function* outputStreamSb(cpClient, sandboxId, fileDescriptor, signal) {
|
|
|
49270
49279
|
completed = true;
|
|
49271
49280
|
break;
|
|
49272
49281
|
}
|
|
49282
|
+
if (signal?.aborted) {
|
|
49283
|
+
return;
|
|
49284
|
+
}
|
|
49273
49285
|
}
|
|
49274
49286
|
} catch (err) {
|
|
49275
49287
|
if (signal?.aborted) {
|
|
@@ -49480,28 +49492,47 @@ var AuthTokenManager = class {
|
|
|
49480
49492
|
logger;
|
|
49481
49493
|
currentToken = "";
|
|
49482
49494
|
tokenExpiry = 0;
|
|
49483
|
-
|
|
49484
|
-
running = false;
|
|
49485
|
-
fetchPromise = null;
|
|
49495
|
+
refreshPromise = null;
|
|
49486
49496
|
constructor(client2, logger) {
|
|
49487
49497
|
this.client = client2;
|
|
49488
49498
|
this.logger = logger;
|
|
49489
49499
|
}
|
|
49490
49500
|
/**
|
|
49491
49501
|
* Returns a valid auth token.
|
|
49492
|
-
* If the current token is expired and the manager is running, triggers an on-demand refresh.
|
|
49493
49502
|
*/
|
|
49494
49503
|
async getToken() {
|
|
49495
|
-
if (this.currentToken
|
|
49496
|
-
return this.
|
|
49504
|
+
if (!this.currentToken || this.isExpired()) {
|
|
49505
|
+
return this.lockedRefreshToken();
|
|
49497
49506
|
}
|
|
49498
|
-
if (this.
|
|
49499
|
-
|
|
49500
|
-
|
|
49501
|
-
|
|
49507
|
+
if (this.needsRefresh() && !this.refreshPromise) {
|
|
49508
|
+
try {
|
|
49509
|
+
await this.lockedRefreshToken();
|
|
49510
|
+
} catch (error) {
|
|
49511
|
+
this.logger.error("refreshing auth token", "error", error);
|
|
49502
49512
|
}
|
|
49503
49513
|
}
|
|
49504
|
-
|
|
49514
|
+
return this.currentToken;
|
|
49515
|
+
}
|
|
49516
|
+
/**
|
|
49517
|
+
* Ensures only one fetch is in progress at a time. Concurrent callers
|
|
49518
|
+
* await the same promise. Includes a double-check so that if another
|
|
49519
|
+
* caller already refreshed, we skip the RPC.
|
|
49520
|
+
*/
|
|
49521
|
+
async lockedRefreshToken() {
|
|
49522
|
+
if (!this.refreshPromise) {
|
|
49523
|
+
this.refreshPromise = (async () => {
|
|
49524
|
+
try {
|
|
49525
|
+
if (this.currentToken && !this.needsRefresh()) {
|
|
49526
|
+
return;
|
|
49527
|
+
}
|
|
49528
|
+
await this.fetchToken();
|
|
49529
|
+
} finally {
|
|
49530
|
+
this.refreshPromise = null;
|
|
49531
|
+
}
|
|
49532
|
+
})();
|
|
49533
|
+
}
|
|
49534
|
+
await this.refreshPromise;
|
|
49535
|
+
return this.currentToken;
|
|
49505
49536
|
}
|
|
49506
49537
|
/**
|
|
49507
49538
|
* Fetches a new auth token from the server and stores it.
|
|
@@ -49533,56 +49564,6 @@ var AuthTokenManager = class {
|
|
|
49533
49564
|
`${refreshIn}s`
|
|
49534
49565
|
);
|
|
49535
49566
|
}
|
|
49536
|
-
/**
|
|
49537
|
-
* Background loop that refreshes tokens REFRESH_WINDOW seconds before they expire.
|
|
49538
|
-
*/
|
|
49539
|
-
async backgroundRefresh() {
|
|
49540
|
-
while (this.running) {
|
|
49541
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
49542
|
-
const refreshTime = this.tokenExpiry - REFRESH_WINDOW;
|
|
49543
|
-
const delay = Math.max(0, refreshTime - now) * 1e3;
|
|
49544
|
-
await new Promise((resolve) => {
|
|
49545
|
-
this.timeoutId = setTimeout(resolve, delay);
|
|
49546
|
-
this.timeoutId.unref();
|
|
49547
|
-
});
|
|
49548
|
-
if (!this.running) {
|
|
49549
|
-
return;
|
|
49550
|
-
}
|
|
49551
|
-
try {
|
|
49552
|
-
await this.runFetch();
|
|
49553
|
-
} catch (error) {
|
|
49554
|
-
this.logger.error("Failed to refresh auth token", "error", error);
|
|
49555
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
49556
|
-
}
|
|
49557
|
-
}
|
|
49558
|
-
}
|
|
49559
|
-
/**
|
|
49560
|
-
* Fetches the initial token and starts the refresh loop.
|
|
49561
|
-
* Throws an error if the initial token fetch fails.
|
|
49562
|
-
*/
|
|
49563
|
-
async start() {
|
|
49564
|
-
if (this.running) {
|
|
49565
|
-
return;
|
|
49566
|
-
}
|
|
49567
|
-
this.running = true;
|
|
49568
|
-
try {
|
|
49569
|
-
await this.runFetch();
|
|
49570
|
-
} catch (error) {
|
|
49571
|
-
this.running = false;
|
|
49572
|
-
throw error;
|
|
49573
|
-
}
|
|
49574
|
-
this.backgroundRefresh();
|
|
49575
|
-
}
|
|
49576
|
-
/**
|
|
49577
|
-
* Stops the background refresh.
|
|
49578
|
-
*/
|
|
49579
|
-
stop() {
|
|
49580
|
-
this.running = false;
|
|
49581
|
-
if (this.timeoutId) {
|
|
49582
|
-
clearTimeout(this.timeoutId);
|
|
49583
|
-
this.timeoutId = null;
|
|
49584
|
-
}
|
|
49585
|
-
}
|
|
49586
49567
|
/**
|
|
49587
49568
|
* Extracts the exp claim from a JWT token.
|
|
49588
49569
|
*/
|
|
@@ -49607,17 +49588,9 @@ var AuthTokenManager = class {
|
|
|
49607
49588
|
const now = Math.floor(Date.now() / 1e3);
|
|
49608
49589
|
return now >= this.tokenExpiry;
|
|
49609
49590
|
}
|
|
49610
|
-
|
|
49611
|
-
|
|
49612
|
-
|
|
49613
|
-
try {
|
|
49614
|
-
await this.fetchToken();
|
|
49615
|
-
} finally {
|
|
49616
|
-
this.fetchPromise = null;
|
|
49617
|
-
}
|
|
49618
|
-
})();
|
|
49619
|
-
}
|
|
49620
|
-
return this.fetchPromise;
|
|
49591
|
+
needsRefresh() {
|
|
49592
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
49593
|
+
return now >= this.tokenExpiry - REFRESH_WINDOW;
|
|
49621
49594
|
}
|
|
49622
49595
|
getCurrentToken() {
|
|
49623
49596
|
return this.currentToken;
|
|
@@ -49630,7 +49603,7 @@ var AuthTokenManager = class {
|
|
|
49630
49603
|
|
|
49631
49604
|
// src/version.ts
|
|
49632
49605
|
function getSDKVersion() {
|
|
49633
|
-
return true ? "0.7.
|
|
49606
|
+
return true ? "0.7.2" : "0.0.0";
|
|
49634
49607
|
}
|
|
49635
49608
|
|
|
49636
49609
|
// src/logger.ts
|
|
@@ -49813,10 +49786,7 @@ var ModalClient2 = class {
|
|
|
49813
49786
|
}
|
|
49814
49787
|
close() {
|
|
49815
49788
|
this.logger.debug("Closing Modal client");
|
|
49816
|
-
|
|
49817
|
-
this.authTokenManager.stop();
|
|
49818
|
-
this.authTokenManager = null;
|
|
49819
|
-
}
|
|
49789
|
+
this.authTokenManager = null;
|
|
49820
49790
|
this.logger.debug("Modal client closed");
|
|
49821
49791
|
}
|
|
49822
49792
|
version() {
|
|
@@ -49926,7 +49896,6 @@ var ModalClient2 = class {
|
|
|
49926
49896
|
this.cpClient,
|
|
49927
49897
|
this.logger
|
|
49928
49898
|
);
|
|
49929
|
-
this.authTokenManager.start();
|
|
49930
49899
|
}
|
|
49931
49900
|
return this.authTokenManager;
|
|
49932
49901
|
};
|
package/dist/index.js
CHANGED
|
@@ -48878,24 +48878,33 @@ var Sandbox2 = class _Sandbox {
|
|
|
48878
48878
|
throw new ClientClosedError();
|
|
48879
48879
|
}
|
|
48880
48880
|
}
|
|
48881
|
+
static #maxGetTaskIdAttempts = 600;
|
|
48882
|
+
// 5 minutes at 500ms intervals
|
|
48881
48883
|
async #getTaskId() {
|
|
48882
|
-
if (this.#taskId
|
|
48884
|
+
if (this.#taskId !== void 0) {
|
|
48885
|
+
return this.#taskId;
|
|
48886
|
+
}
|
|
48887
|
+
for (let i = 0; i < _Sandbox.#maxGetTaskIdAttempts; i++) {
|
|
48883
48888
|
const resp = await this.#client.cpClient.sandboxGetTaskId({
|
|
48884
48889
|
sandboxId: this.sandboxId
|
|
48885
48890
|
});
|
|
48886
|
-
if (!resp.taskId) {
|
|
48887
|
-
throw new Error(
|
|
48888
|
-
`Sandbox ${this.sandboxId} does not have a task ID. It may not be running.`
|
|
48889
|
-
);
|
|
48890
|
-
}
|
|
48891
48891
|
if (resp.taskResult) {
|
|
48892
|
+
if (resp.taskResult.status === 1 /* GENERIC_STATUS_SUCCESS */ || !resp.taskResult.exception) {
|
|
48893
|
+
throw new Error(`Sandbox ${this.sandboxId} has already completed`);
|
|
48894
|
+
}
|
|
48892
48895
|
throw new Error(
|
|
48893
|
-
`Sandbox ${this.sandboxId} has already completed with result: ${resp.taskResult}`
|
|
48896
|
+
`Sandbox ${this.sandboxId} has already completed with result: exception:"${resp.taskResult.exception}"`
|
|
48894
48897
|
);
|
|
48895
48898
|
}
|
|
48896
|
-
|
|
48899
|
+
if (resp.taskId) {
|
|
48900
|
+
this.#taskId = resp.taskId;
|
|
48901
|
+
return this.#taskId;
|
|
48902
|
+
}
|
|
48903
|
+
await setTimeout3(500);
|
|
48897
48904
|
}
|
|
48898
|
-
|
|
48905
|
+
throw new Error(
|
|
48906
|
+
`Timed out waiting for task ID for Sandbox ${this.sandboxId}`
|
|
48907
|
+
);
|
|
48899
48908
|
}
|
|
48900
48909
|
async #getOrCreateCommandRouterClient(taskId) {
|
|
48901
48910
|
if (this.#commandRouterClient !== void 0) {
|
|
@@ -49208,6 +49217,9 @@ async function* outputStreamSb(cpClient, sandboxId, fileDescriptor, signal) {
|
|
|
49208
49217
|
completed = true;
|
|
49209
49218
|
break;
|
|
49210
49219
|
}
|
|
49220
|
+
if (signal?.aborted) {
|
|
49221
|
+
return;
|
|
49222
|
+
}
|
|
49211
49223
|
}
|
|
49212
49224
|
} catch (err) {
|
|
49213
49225
|
if (signal?.aborted) {
|
|
@@ -49418,28 +49430,47 @@ var AuthTokenManager = class {
|
|
|
49418
49430
|
logger;
|
|
49419
49431
|
currentToken = "";
|
|
49420
49432
|
tokenExpiry = 0;
|
|
49421
|
-
|
|
49422
|
-
running = false;
|
|
49423
|
-
fetchPromise = null;
|
|
49433
|
+
refreshPromise = null;
|
|
49424
49434
|
constructor(client2, logger) {
|
|
49425
49435
|
this.client = client2;
|
|
49426
49436
|
this.logger = logger;
|
|
49427
49437
|
}
|
|
49428
49438
|
/**
|
|
49429
49439
|
* Returns a valid auth token.
|
|
49430
|
-
* If the current token is expired and the manager is running, triggers an on-demand refresh.
|
|
49431
49440
|
*/
|
|
49432
49441
|
async getToken() {
|
|
49433
|
-
if (this.currentToken
|
|
49434
|
-
return this.
|
|
49442
|
+
if (!this.currentToken || this.isExpired()) {
|
|
49443
|
+
return this.lockedRefreshToken();
|
|
49435
49444
|
}
|
|
49436
|
-
if (this.
|
|
49437
|
-
|
|
49438
|
-
|
|
49439
|
-
|
|
49445
|
+
if (this.needsRefresh() && !this.refreshPromise) {
|
|
49446
|
+
try {
|
|
49447
|
+
await this.lockedRefreshToken();
|
|
49448
|
+
} catch (error) {
|
|
49449
|
+
this.logger.error("refreshing auth token", "error", error);
|
|
49440
49450
|
}
|
|
49441
49451
|
}
|
|
49442
|
-
|
|
49452
|
+
return this.currentToken;
|
|
49453
|
+
}
|
|
49454
|
+
/**
|
|
49455
|
+
* Ensures only one fetch is in progress at a time. Concurrent callers
|
|
49456
|
+
* await the same promise. Includes a double-check so that if another
|
|
49457
|
+
* caller already refreshed, we skip the RPC.
|
|
49458
|
+
*/
|
|
49459
|
+
async lockedRefreshToken() {
|
|
49460
|
+
if (!this.refreshPromise) {
|
|
49461
|
+
this.refreshPromise = (async () => {
|
|
49462
|
+
try {
|
|
49463
|
+
if (this.currentToken && !this.needsRefresh()) {
|
|
49464
|
+
return;
|
|
49465
|
+
}
|
|
49466
|
+
await this.fetchToken();
|
|
49467
|
+
} finally {
|
|
49468
|
+
this.refreshPromise = null;
|
|
49469
|
+
}
|
|
49470
|
+
})();
|
|
49471
|
+
}
|
|
49472
|
+
await this.refreshPromise;
|
|
49473
|
+
return this.currentToken;
|
|
49443
49474
|
}
|
|
49444
49475
|
/**
|
|
49445
49476
|
* Fetches a new auth token from the server and stores it.
|
|
@@ -49471,56 +49502,6 @@ var AuthTokenManager = class {
|
|
|
49471
49502
|
`${refreshIn}s`
|
|
49472
49503
|
);
|
|
49473
49504
|
}
|
|
49474
|
-
/**
|
|
49475
|
-
* Background loop that refreshes tokens REFRESH_WINDOW seconds before they expire.
|
|
49476
|
-
*/
|
|
49477
|
-
async backgroundRefresh() {
|
|
49478
|
-
while (this.running) {
|
|
49479
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
49480
|
-
const refreshTime = this.tokenExpiry - REFRESH_WINDOW;
|
|
49481
|
-
const delay = Math.max(0, refreshTime - now) * 1e3;
|
|
49482
|
-
await new Promise((resolve) => {
|
|
49483
|
-
this.timeoutId = setTimeout(resolve, delay);
|
|
49484
|
-
this.timeoutId.unref();
|
|
49485
|
-
});
|
|
49486
|
-
if (!this.running) {
|
|
49487
|
-
return;
|
|
49488
|
-
}
|
|
49489
|
-
try {
|
|
49490
|
-
await this.runFetch();
|
|
49491
|
-
} catch (error) {
|
|
49492
|
-
this.logger.error("Failed to refresh auth token", "error", error);
|
|
49493
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
49494
|
-
}
|
|
49495
|
-
}
|
|
49496
|
-
}
|
|
49497
|
-
/**
|
|
49498
|
-
* Fetches the initial token and starts the refresh loop.
|
|
49499
|
-
* Throws an error if the initial token fetch fails.
|
|
49500
|
-
*/
|
|
49501
|
-
async start() {
|
|
49502
|
-
if (this.running) {
|
|
49503
|
-
return;
|
|
49504
|
-
}
|
|
49505
|
-
this.running = true;
|
|
49506
|
-
try {
|
|
49507
|
-
await this.runFetch();
|
|
49508
|
-
} catch (error) {
|
|
49509
|
-
this.running = false;
|
|
49510
|
-
throw error;
|
|
49511
|
-
}
|
|
49512
|
-
this.backgroundRefresh();
|
|
49513
|
-
}
|
|
49514
|
-
/**
|
|
49515
|
-
* Stops the background refresh.
|
|
49516
|
-
*/
|
|
49517
|
-
stop() {
|
|
49518
|
-
this.running = false;
|
|
49519
|
-
if (this.timeoutId) {
|
|
49520
|
-
clearTimeout(this.timeoutId);
|
|
49521
|
-
this.timeoutId = null;
|
|
49522
|
-
}
|
|
49523
|
-
}
|
|
49524
49505
|
/**
|
|
49525
49506
|
* Extracts the exp claim from a JWT token.
|
|
49526
49507
|
*/
|
|
@@ -49545,17 +49526,9 @@ var AuthTokenManager = class {
|
|
|
49545
49526
|
const now = Math.floor(Date.now() / 1e3);
|
|
49546
49527
|
return now >= this.tokenExpiry;
|
|
49547
49528
|
}
|
|
49548
|
-
|
|
49549
|
-
|
|
49550
|
-
|
|
49551
|
-
try {
|
|
49552
|
-
await this.fetchToken();
|
|
49553
|
-
} finally {
|
|
49554
|
-
this.fetchPromise = null;
|
|
49555
|
-
}
|
|
49556
|
-
})();
|
|
49557
|
-
}
|
|
49558
|
-
return this.fetchPromise;
|
|
49529
|
+
needsRefresh() {
|
|
49530
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
49531
|
+
return now >= this.tokenExpiry - REFRESH_WINDOW;
|
|
49559
49532
|
}
|
|
49560
49533
|
getCurrentToken() {
|
|
49561
49534
|
return this.currentToken;
|
|
@@ -49568,7 +49541,7 @@ var AuthTokenManager = class {
|
|
|
49568
49541
|
|
|
49569
49542
|
// src/version.ts
|
|
49570
49543
|
function getSDKVersion() {
|
|
49571
|
-
return true ? "0.7.
|
|
49544
|
+
return true ? "0.7.2" : "0.0.0";
|
|
49572
49545
|
}
|
|
49573
49546
|
|
|
49574
49547
|
// src/logger.ts
|
|
@@ -49751,10 +49724,7 @@ var ModalClient2 = class {
|
|
|
49751
49724
|
}
|
|
49752
49725
|
close() {
|
|
49753
49726
|
this.logger.debug("Closing Modal client");
|
|
49754
|
-
|
|
49755
|
-
this.authTokenManager.stop();
|
|
49756
|
-
this.authTokenManager = null;
|
|
49757
|
-
}
|
|
49727
|
+
this.authTokenManager = null;
|
|
49758
49728
|
this.logger.debug("Modal client closed");
|
|
49759
49729
|
}
|
|
49760
49730
|
version() {
|
|
@@ -49864,7 +49834,6 @@ var ModalClient2 = class {
|
|
|
49864
49834
|
this.cpClient,
|
|
49865
49835
|
this.logger
|
|
49866
49836
|
);
|
|
49867
|
-
this.authTokenManager.start();
|
|
49868
49837
|
}
|
|
49869
49838
|
return this.authTokenManager;
|
|
49870
49839
|
};
|