setsuna-microvm 0.5.3 → 0.5.4
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/LICENSE +32 -32
- package/dist/cli.js +128 -279
- package/package.json +1 -1
package/LICENSE
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
SemSwitch Setsuna Research Preview Client License
|
|
2
|
-
|
|
3
|
-
Copyright © 2026 SemSwitch, Inc. All rights reserved.
|
|
4
|
-
|
|
5
|
-
The Setsuna client distributed in this package is proprietary software owned by SemSwitch, Inc.
|
|
6
|
-
("SemSwitch"). It is provided for authorized use with the Setsuna Research Preview.
|
|
7
|
-
|
|
8
|
-
1. Grant. Subject to the SemSwitch Terms of Service (https://semswitch.com/terms) and any
|
|
9
|
-
applicable Order or evaluation agreement, an authorized user receives a limited, non-exclusive
|
|
10
|
-
right to install and use the Setsuna client solely to access or evaluate the Setsuna service
|
|
11
|
-
that the user is authorized to use.
|
|
12
|
-
|
|
13
|
-
2. Ownership. The Setsuna client is licensed, not sold. SemSwitch retains all ownership,
|
|
14
|
-
copyright, and other intellectual property rights in the Setsuna client. This license transfers
|
|
15
|
-
no ownership or other intellectual property rights.
|
|
16
|
-
|
|
17
|
-
3. Restrictions. Except where expressly permitted by an applicable agreement with SemSwitch or by
|
|
18
|
-
non-waivable law, no right is granted to redistribute, sublicense, sell, rent, publish, modify,
|
|
19
|
-
create derivative works from, reverse engineer, decompile, or disassemble the Setsuna client.
|
|
20
|
-
|
|
21
|
-
4. Third-party software. The Setsuna client uses third-party software and components that are not
|
|
22
|
-
owned by SemSwitch. They remain governed by their respective licenses and notices, which this
|
|
23
|
-
license does not modify or replace.
|
|
24
|
-
|
|
25
|
-
5. Term. The right to use the Setsuna client ends when the applicable Setsuna authorization ends.
|
|
26
|
-
|
|
27
|
-
6. Agreements. If an applicable signed Order or separately negotiated agreement with SemSwitch
|
|
28
|
-
expressly conflicts with this license, that Order or agreement controls.
|
|
29
|
-
|
|
30
|
-
7. Warranties, liability, and service terms. Warranties, disclaimers, limitations of liability,
|
|
31
|
-
and the terms of the Setsuna service are governed by the applicable agreement and the SemSwitch
|
|
32
|
-
Terms of Service rather than by this license.
|
|
1
|
+
SemSwitch Setsuna Research Preview Client License
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 SemSwitch, Inc. All rights reserved.
|
|
4
|
+
|
|
5
|
+
The Setsuna client distributed in this package is proprietary software owned by SemSwitch, Inc.
|
|
6
|
+
("SemSwitch"). It is provided for authorized use with the Setsuna Research Preview.
|
|
7
|
+
|
|
8
|
+
1. Grant. Subject to the SemSwitch Terms of Service (https://semswitch.com/terms) and any
|
|
9
|
+
applicable Order or evaluation agreement, an authorized user receives a limited, non-exclusive
|
|
10
|
+
right to install and use the Setsuna client solely to access or evaluate the Setsuna service
|
|
11
|
+
that the user is authorized to use.
|
|
12
|
+
|
|
13
|
+
2. Ownership. The Setsuna client is licensed, not sold. SemSwitch retains all ownership,
|
|
14
|
+
copyright, and other intellectual property rights in the Setsuna client. This license transfers
|
|
15
|
+
no ownership or other intellectual property rights.
|
|
16
|
+
|
|
17
|
+
3. Restrictions. Except where expressly permitted by an applicable agreement with SemSwitch or by
|
|
18
|
+
non-waivable law, no right is granted to redistribute, sublicense, sell, rent, publish, modify,
|
|
19
|
+
create derivative works from, reverse engineer, decompile, or disassemble the Setsuna client.
|
|
20
|
+
|
|
21
|
+
4. Third-party software. The Setsuna client uses third-party software and components that are not
|
|
22
|
+
owned by SemSwitch. They remain governed by their respective licenses and notices, which this
|
|
23
|
+
license does not modify or replace.
|
|
24
|
+
|
|
25
|
+
5. Term. The right to use the Setsuna client ends when the applicable Setsuna authorization ends.
|
|
26
|
+
|
|
27
|
+
6. Agreements. If an applicable signed Order or separately negotiated agreement with SemSwitch
|
|
28
|
+
expressly conflicts with this license, that Order or agreement controls.
|
|
29
|
+
|
|
30
|
+
7. Warranties, liability, and service terms. Warranties, disclaimers, limitations of liability,
|
|
31
|
+
and the terms of the Setsuna service are governed by the applicable agreement and the SemSwitch
|
|
32
|
+
Terms of Service rather than by this license.
|
package/dist/cli.js
CHANGED
|
@@ -1113,6 +1113,7 @@ function parsePreviewCliArgs(args) {
|
|
|
1113
1113
|
"timeout-ms": { type: "string" },
|
|
1114
1114
|
"request-timeout-ms": { type: "string" },
|
|
1115
1115
|
"configuration-id": { type: "string" },
|
|
1116
|
+
duration: { type: "string" },
|
|
1116
1117
|
attach: { type: "string" },
|
|
1117
1118
|
json: { type: "boolean", default: false },
|
|
1118
1119
|
help: { type: "boolean", short: "h", default: false },
|
|
@@ -1224,14 +1225,14 @@ function hasOnlyKeys(value, allowed) {
|
|
|
1224
1225
|
}
|
|
1225
1226
|
function parseLease(value, expectedConfigurationId) {
|
|
1226
1227
|
const sandboxId = parseLeaseSandboxId(value);
|
|
1227
|
-
if (!isRecord(value) || typeof value.expiresAt !== "string") {
|
|
1228
|
+
if (!isRecord(value) || typeof value.expiresAt !== "string" || !Number.isFinite(Date.parse(value.expiresAt)) || value.duration !== "20m" && value.duration !== "1h" && value.duration !== "2h") {
|
|
1228
1229
|
throw invalidResponse("sandbox lease");
|
|
1229
1230
|
}
|
|
1230
1231
|
const microvmConfiguration = parseMicrovmConfiguration(value.microvmConfiguration);
|
|
1231
1232
|
if (expectedConfigurationId !== void 0 && microvmConfiguration.id !== expectedConfigurationId) {
|
|
1232
1233
|
throw invalidResponse("sandbox lease");
|
|
1233
1234
|
}
|
|
1234
|
-
return { sandboxId, expiresAt: value.expiresAt, microvmConfiguration };
|
|
1235
|
+
return { sandboxId, expiresAt: value.expiresAt, duration: value.duration, microvmConfiguration };
|
|
1235
1236
|
}
|
|
1236
1237
|
function parseLeaseSandboxId(value) {
|
|
1237
1238
|
if (!isRecord(value) || !nonEmptyResponseString(value.sandboxId))
|
|
@@ -1339,12 +1340,16 @@ function normalizeCreateOptions(options) {
|
|
|
1339
1340
|
throw new TypeError("create options must be an object.");
|
|
1340
1341
|
}
|
|
1341
1342
|
const unsupported = Object.keys(options).find(
|
|
1342
|
-
(key) => key !== "microvmConfigurationId" && key !== "network"
|
|
1343
|
+
(key) => key !== "microvmConfigurationId" && key !== "network" && key !== "duration"
|
|
1343
1344
|
);
|
|
1344
1345
|
if (unsupported !== void 0) throw new TypeError(`Unsupported create option: ${unsupported}.`);
|
|
1346
|
+
const duration = options.duration === void 0 ? "20m" : options.duration;
|
|
1347
|
+
if (duration !== "20m" && duration !== "1h" && duration !== "2h")
|
|
1348
|
+
throw new TypeError("duration must be 20m, 1h, or 2h.");
|
|
1345
1349
|
const microvmConfigurationId = options.microvmConfigurationId === void 0 ? void 0 : nonEmptyString(options.microvmConfigurationId, "microvmConfigurationId");
|
|
1346
1350
|
const network = options.network === void 0 ? void 0 : validateRoutedNetwork(options.network);
|
|
1347
1351
|
return {
|
|
1352
|
+
duration,
|
|
1348
1353
|
...microvmConfigurationId === void 0 ? {} : { microvmConfigurationId },
|
|
1349
1354
|
...network === void 0 ? {} : { network }
|
|
1350
1355
|
};
|
|
@@ -1829,10 +1834,11 @@ function reportRunPhase(callback, phase) {
|
|
|
1829
1834
|
|
|
1830
1835
|
// ../setsuna/src/client.ts
|
|
1831
1836
|
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
1832
|
-
var
|
|
1837
|
+
var DEFAULT_RUN_REQUEST_TIMEOUT_MS = 6e4 + 12e4 + 6e4;
|
|
1833
1838
|
var SetsunaClient = class {
|
|
1834
1839
|
#serviceUrl;
|
|
1835
1840
|
#requestTimeoutMs;
|
|
1841
|
+
#explicitRequestTimeoutMs;
|
|
1836
1842
|
#getAccessToken;
|
|
1837
1843
|
#affinity;
|
|
1838
1844
|
constructor(options = {}) {
|
|
@@ -1850,6 +1856,7 @@ var SetsunaClient = class {
|
|
|
1850
1856
|
this.#getAccessToken = options.remote.getAccessToken;
|
|
1851
1857
|
this.#affinity = new FrontDoorAffinity(this.#serviceUrl);
|
|
1852
1858
|
}
|
|
1859
|
+
this.#explicitRequestTimeoutMs = options.requestTimeoutMs;
|
|
1853
1860
|
this.#requestTimeoutMs = positiveMilliseconds(
|
|
1854
1861
|
options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
|
|
1855
1862
|
"requestTimeoutMs"
|
|
@@ -1869,20 +1876,7 @@ var SetsunaClient = class {
|
|
|
1869
1876
|
method: "POST",
|
|
1870
1877
|
body: { ...createOptions }
|
|
1871
1878
|
});
|
|
1872
|
-
|
|
1873
|
-
try {
|
|
1874
|
-
return parseLease(response, createOptions.microvmConfigurationId);
|
|
1875
|
-
} catch (error) {
|
|
1876
|
-
try {
|
|
1877
|
-
await this.#destroy(sandboxId, DEFAULT_CLEANUP_TIMEOUT_MS);
|
|
1878
|
-
} catch (cleanupFailure) {
|
|
1879
|
-
throw new AggregateError(
|
|
1880
|
-
[error, cleanupFailure],
|
|
1881
|
-
"Sandbox lease validation and destruction both failed."
|
|
1882
|
-
);
|
|
1883
|
-
}
|
|
1884
|
-
throw error;
|
|
1885
|
-
}
|
|
1879
|
+
return parseLease(response, createOptions.microvmConfigurationId);
|
|
1886
1880
|
}
|
|
1887
1881
|
async list() {
|
|
1888
1882
|
return parseSandboxList(await this.#request("v1/sandboxes", { method: "GET" }));
|
|
@@ -1895,14 +1889,6 @@ var SetsunaClient = class {
|
|
|
1895
1889
|
})
|
|
1896
1890
|
);
|
|
1897
1891
|
}
|
|
1898
|
-
async renew(sandboxId) {
|
|
1899
|
-
const validatedSandboxId = nonEmptyString(sandboxId, "sandboxId");
|
|
1900
|
-
return parseSandboxStatus(
|
|
1901
|
-
await this.#request(`v1/sandboxes/${encodeURIComponent(validatedSandboxId)}/renew`, {
|
|
1902
|
-
method: "POST"
|
|
1903
|
-
})
|
|
1904
|
-
);
|
|
1905
|
-
}
|
|
1906
1892
|
async execute(sandboxId, command, options = {}) {
|
|
1907
1893
|
const validatedSandboxId = nonEmptyString(sandboxId, "sandboxId");
|
|
1908
1894
|
const validatedCommand = nonEmptyString(command, "command");
|
|
@@ -1977,44 +1963,30 @@ var SetsunaClient = class {
|
|
|
1977
1963
|
});
|
|
1978
1964
|
}
|
|
1979
1965
|
async run(command, options = {}) {
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1966
|
+
const unsupported = Object.keys(options).find(
|
|
1967
|
+
(key) => !["microvmConfigurationId", "timeoutMs", "requestTimeoutMs", "signal", "onPhase"].includes(
|
|
1968
|
+
key
|
|
1969
|
+
)
|
|
1984
1970
|
);
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
} catch (cleanupFailure) {
|
|
2005
|
-
if (!executionOutcome.ok) {
|
|
2006
|
-
throw new AggregateError(
|
|
2007
|
-
[executionOutcome.error, cleanupFailure],
|
|
2008
|
-
"Sandbox execution and destruction both failed."
|
|
2009
|
-
);
|
|
2010
|
-
}
|
|
2011
|
-
throw cleanupFailure;
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
if (!executionOutcome.ok) {
|
|
2015
|
-
throw executionOutcome.error;
|
|
2016
|
-
}
|
|
2017
|
-
return executionOutcome.result;
|
|
1971
|
+
if (unsupported !== void 0) throw new TypeError(`Unsupported run option: ${unsupported}.`);
|
|
1972
|
+
const validatedCommand = nonEmptyString(command, "command");
|
|
1973
|
+
const timeoutMs = options.timeoutMs === void 0 ? void 0 : positiveMilliseconds(options.timeoutMs, "timeoutMs");
|
|
1974
|
+
const microvmConfigurationId = options.microvmConfigurationId === void 0 ? void 0 : nonEmptyString(options.microvmConfigurationId, "microvmConfigurationId");
|
|
1975
|
+
reportRunPhase(options.onPhase, "running");
|
|
1976
|
+
const result = parseExecutionResult(
|
|
1977
|
+
await this.#request("v1/run", {
|
|
1978
|
+
method: "POST",
|
|
1979
|
+
body: {
|
|
1980
|
+
command: validatedCommand,
|
|
1981
|
+
...microvmConfigurationId === void 0 ? {} : { microvmConfigurationId },
|
|
1982
|
+
...timeoutMs === void 0 ? {} : { timeoutMs }
|
|
1983
|
+
},
|
|
1984
|
+
signal: options.signal,
|
|
1985
|
+
requestTimeoutMs: options.requestTimeoutMs ?? this.#explicitRequestTimeoutMs ?? Math.max(DEFAULT_RUN_REQUEST_TIMEOUT_MS, (timeoutMs ?? 0) + 12e4)
|
|
1986
|
+
})
|
|
1987
|
+
);
|
|
1988
|
+
reportRunPhase(options.onPhase, "complete");
|
|
1989
|
+
return result;
|
|
2018
1990
|
}
|
|
2019
1991
|
async #request(path3, options) {
|
|
2020
1992
|
return requestJson(
|
|
@@ -2044,7 +2016,7 @@ async function runWithSignalCleanup(client, command, options, io, signalRuntime)
|
|
|
2044
2016
|
const exitCode = signal === "SIGINT" ? 130 : 143;
|
|
2045
2017
|
if (interruptedExitCode === void 0) {
|
|
2046
2018
|
interruptedExitCode = exitCode;
|
|
2047
|
-
io.errorOutput.write("Interrupt received;
|
|
2019
|
+
io.errorOutput.write("Interrupt received; Setsuna owns run cleanup.\n");
|
|
2048
2020
|
controller.abort();
|
|
2049
2021
|
return;
|
|
2050
2022
|
}
|
|
@@ -2160,90 +2132,6 @@ async function restoreInput(input, wasRaw, wasPaused) {
|
|
|
2160
2132
|
input.setRawMode?.(wasRaw);
|
|
2161
2133
|
}
|
|
2162
2134
|
|
|
2163
|
-
// ../setsuna/src/lease-keeper.ts
|
|
2164
|
-
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
2165
|
-
var SYSTEM_RUNTIME = {
|
|
2166
|
-
now: Date.now,
|
|
2167
|
-
setTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
2168
|
-
clearTimeout: (handle) => clearTimeout(handle)
|
|
2169
|
-
};
|
|
2170
|
-
var ActiveLeaseKeeper = class {
|
|
2171
|
-
#client;
|
|
2172
|
-
#sandboxId;
|
|
2173
|
-
#runtime;
|
|
2174
|
-
#expiresAtMs;
|
|
2175
|
-
#failureHandler;
|
|
2176
|
-
#running = false;
|
|
2177
|
-
#timer;
|
|
2178
|
-
constructor(client, sandboxId, runtime = SYSTEM_RUNTIME) {
|
|
2179
|
-
this.#client = client;
|
|
2180
|
-
this.#sandboxId = sandboxId;
|
|
2181
|
-
this.#runtime = runtime;
|
|
2182
|
-
}
|
|
2183
|
-
async renewNow() {
|
|
2184
|
-
if (this.#running) throw new TypeError("Cannot prime a running lease keeper.");
|
|
2185
|
-
this.#expiresAtMs = await this.#renewExpiry();
|
|
2186
|
-
}
|
|
2187
|
-
start(onFailure) {
|
|
2188
|
-
if (this.#running) throw new TypeError("The lease keeper is already running.");
|
|
2189
|
-
if (this.#expiresAtMs === void 0) {
|
|
2190
|
-
throw new TypeError("The lease keeper must renew the sandbox before it starts.");
|
|
2191
|
-
}
|
|
2192
|
-
this.#running = true;
|
|
2193
|
-
this.#failureHandler = onFailure;
|
|
2194
|
-
try {
|
|
2195
|
-
this.#schedule(this.#expiresAtMs);
|
|
2196
|
-
} catch (error) {
|
|
2197
|
-
this.#running = false;
|
|
2198
|
-
this.#failureHandler = void 0;
|
|
2199
|
-
throw error;
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
stop() {
|
|
2203
|
-
this.#running = false;
|
|
2204
|
-
this.#failureHandler = void 0;
|
|
2205
|
-
if (this.#timer !== void 0) this.#runtime.clearTimeout(this.#timer);
|
|
2206
|
-
this.#timer = void 0;
|
|
2207
|
-
}
|
|
2208
|
-
async #renewExpiry() {
|
|
2209
|
-
const { expiresAt } = await this.#client.renew(this.#sandboxId);
|
|
2210
|
-
const expiresAtMs = Date.parse(expiresAt);
|
|
2211
|
-
if (!Number.isFinite(expiresAtMs) || expiresAtMs <= this.#runtime.now()) {
|
|
2212
|
-
throw new TypeError("Sandbox renewal returned an invalid or expired expiresAt value.");
|
|
2213
|
-
}
|
|
2214
|
-
return expiresAtMs;
|
|
2215
|
-
}
|
|
2216
|
-
#schedule(expiresAtMs) {
|
|
2217
|
-
const remainingMs = expiresAtMs - this.#runtime.now();
|
|
2218
|
-
if (!Number.isFinite(remainingMs) || remainingMs <= 0) {
|
|
2219
|
-
throw new TypeError(
|
|
2220
|
-
"The renewed sandbox lease expired before its next renewal was scheduled."
|
|
2221
|
-
);
|
|
2222
|
-
}
|
|
2223
|
-
const delayMs = Math.min(MAX_TIMER_DELAY_MS, Math.max(1, Math.floor(remainingMs / 2)));
|
|
2224
|
-
this.#timer = this.#runtime.setTimeout(() => {
|
|
2225
|
-
this.#timer = void 0;
|
|
2226
|
-
void this.#renewAndSchedule().catch((error) => this.#fail(error));
|
|
2227
|
-
}, delayMs);
|
|
2228
|
-
}
|
|
2229
|
-
async #renewAndSchedule() {
|
|
2230
|
-
const expiresAtMs = await this.#renewExpiry();
|
|
2231
|
-
if (!this.#running) return;
|
|
2232
|
-
this.#expiresAtMs = expiresAtMs;
|
|
2233
|
-
this.#schedule(expiresAtMs);
|
|
2234
|
-
}
|
|
2235
|
-
#fail(error) {
|
|
2236
|
-
if (!this.#running) return;
|
|
2237
|
-
this.#running = false;
|
|
2238
|
-
const handler = this.#failureHandler;
|
|
2239
|
-
this.#failureHandler = void 0;
|
|
2240
|
-
try {
|
|
2241
|
-
handler?.(error);
|
|
2242
|
-
} catch {
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
};
|
|
2246
|
-
|
|
2247
2135
|
// ../setsuna/src/cli-shell.ts
|
|
2248
2136
|
var DETACH_BYTE = 29;
|
|
2249
2137
|
var DEFAULT_TERM = "xterm-256color";
|
|
@@ -2264,7 +2152,7 @@ async function runShellCliCommand(commandName, commandArguments, attachProcessId
|
|
|
2264
2152
|
);
|
|
2265
2153
|
return true;
|
|
2266
2154
|
}
|
|
2267
|
-
async function runShellCommand(commandArguments, attachProcessId, client, io, terminal
|
|
2155
|
+
async function runShellCommand(commandArguments, attachProcessId, client, io, terminal) {
|
|
2268
2156
|
if (commandArguments.length !== 1 || commandArguments[0] === "") {
|
|
2269
2157
|
throw new TypeError("setsuna shell requires exactly one sandbox ID.");
|
|
2270
2158
|
}
|
|
@@ -2273,21 +2161,9 @@ async function runShellCommand(commandArguments, attachProcessId, client, io, te
|
|
|
2273
2161
|
}
|
|
2274
2162
|
if (attachProcessId === "") throw new TypeError("--attach requires a process ID.");
|
|
2275
2163
|
const sandboxId = commandArguments[0];
|
|
2276
|
-
const
|
|
2277
|
-
await
|
|
2278
|
-
|
|
2279
|
-
const size = terminal.size();
|
|
2280
|
-
const processId = await resolveProcess(
|
|
2281
|
-
client,
|
|
2282
|
-
sandboxId,
|
|
2283
|
-
attachProcessId,
|
|
2284
|
-
size,
|
|
2285
|
-
terminal.term()
|
|
2286
|
-
);
|
|
2287
|
-
await attachTerminal(client, sandboxId, processId, io, terminal, leaseKeeper);
|
|
2288
|
-
} finally {
|
|
2289
|
-
leaseKeeper.stop();
|
|
2290
|
-
}
|
|
2164
|
+
const size = terminal.size();
|
|
2165
|
+
const processId = await resolveProcess(client, sandboxId, attachProcessId, size, terminal.term());
|
|
2166
|
+
await attachTerminal(client, sandboxId, processId, io, terminal);
|
|
2291
2167
|
}
|
|
2292
2168
|
async function resolveProcess(client, sandboxId, attachProcessId, size, localTerm) {
|
|
2293
2169
|
if (attachProcessId === void 0) {
|
|
@@ -2310,21 +2186,12 @@ async function resolveProcess(client, sandboxId, attachProcessId, size, localTer
|
|
|
2310
2186
|
if (process2.state === "running") await client.resizeProcess(sandboxId, attachProcessId, size);
|
|
2311
2187
|
return attachProcessId;
|
|
2312
2188
|
}
|
|
2313
|
-
async function attachTerminal(client, sandboxId, processId, io, terminal
|
|
2189
|
+
async function attachTerminal(client, sandboxId, processId, io, terminal) {
|
|
2314
2190
|
const controller = new AbortController();
|
|
2315
|
-
const session = createAttachedSession(
|
|
2316
|
-
client,
|
|
2317
|
-
sandboxId,
|
|
2318
|
-
processId,
|
|
2319
|
-
io,
|
|
2320
|
-
terminal,
|
|
2321
|
-
controller,
|
|
2322
|
-
() => leaseKeeper.stop()
|
|
2323
|
-
);
|
|
2191
|
+
const session = createAttachedSession(client, sandboxId, processId, io, terminal, controller);
|
|
2324
2192
|
let exitFrame;
|
|
2325
2193
|
let attachError;
|
|
2326
2194
|
try {
|
|
2327
|
-
leaseKeeper.start(session.fail);
|
|
2328
2195
|
for await (const frame of client.attachProcess(sandboxId, processId, {
|
|
2329
2196
|
maxStreamBytes: Number.MAX_SAFE_INTEGER,
|
|
2330
2197
|
signal: controller.signal
|
|
@@ -2351,7 +2218,7 @@ async function attachTerminal(client, sandboxId, processId, io, terminal, leaseK
|
|
|
2351
2218
|
}
|
|
2352
2219
|
if (exitFrame.exitCode !== null) terminal.setExitCode(exitFrame.exitCode);
|
|
2353
2220
|
}
|
|
2354
|
-
function createAttachedSession(client, sandboxId, processId, io, terminal, controller
|
|
2221
|
+
function createAttachedSession(client, sandboxId, processId, io, terminal, controller) {
|
|
2355
2222
|
let detached = false;
|
|
2356
2223
|
let failure;
|
|
2357
2224
|
let cleaned = false;
|
|
@@ -2364,7 +2231,6 @@ function createAttachedSession(client, sandboxId, processId, io, terminal, contr
|
|
|
2364
2231
|
cleaned = true;
|
|
2365
2232
|
io.input.off("data", onInput);
|
|
2366
2233
|
terminal.offResize(onResize);
|
|
2367
|
-
stopLeaseKeeper();
|
|
2368
2234
|
restored = restoreRawMode();
|
|
2369
2235
|
};
|
|
2370
2236
|
const fail = (error) => {
|
|
@@ -2455,11 +2321,12 @@ function isDetachAbort(error) {
|
|
|
2455
2321
|
// ../setsuna/src/cli-interactive-persistent-actions.ts
|
|
2456
2322
|
function createPersistentInteractiveActions(client, io, shellTerminal) {
|
|
2457
2323
|
return {
|
|
2458
|
-
async startMicrovm(microvmConfigurationId) {
|
|
2459
|
-
const lease = await client.create({ microvmConfigurationId });
|
|
2324
|
+
async startMicrovm(microvmConfigurationId, duration) {
|
|
2325
|
+
const lease = await client.create({ microvmConfigurationId, duration });
|
|
2460
2326
|
return {
|
|
2461
2327
|
sandboxId: lease.sandboxId,
|
|
2462
2328
|
expiresAt: lease.expiresAt,
|
|
2329
|
+
duration: lease.duration,
|
|
2463
2330
|
vcpuCount: lease.microvmConfiguration.vcpuCount,
|
|
2464
2331
|
memoryMiB: lease.microvmConfiguration.memoryMiB
|
|
2465
2332
|
};
|
|
@@ -2476,13 +2343,11 @@ function createPersistentInteractiveActions(client, io, shellTerminal) {
|
|
|
2476
2343
|
return (await client.listProcesses(sandboxId)).filter((process2) => process2.state === "running" && process2.terminal !== void 0).map(({ processId, pid }) => ({ processId, pid }));
|
|
2477
2344
|
},
|
|
2478
2345
|
async runPersistentCommand(sandboxId, command, timeoutMs) {
|
|
2479
|
-
await client.renew(sandboxId);
|
|
2480
2346
|
const result = await client.execute(sandboxId, command, {
|
|
2481
2347
|
...timeoutMs === void 0 ? {} : { timeoutMs }
|
|
2482
2348
|
});
|
|
2483
2349
|
return interactiveRunResult(result);
|
|
2484
2350
|
},
|
|
2485
|
-
renewMicrovm: (sandboxId) => client.renew(sandboxId),
|
|
2486
2351
|
destroyMicrovm: (sandboxId) => client.destroy(sandboxId)
|
|
2487
2352
|
};
|
|
2488
2353
|
}
|
|
@@ -2543,18 +2408,7 @@ var DEFAULT_SIGNAL_RUNTIME = {
|
|
|
2543
2408
|
var STANDALONE_BOX = { withGuide: false, width: "auto" };
|
|
2544
2409
|
var EXIT_MESSAGE = "Sayonara!";
|
|
2545
2410
|
function formatRunPhase(phase) {
|
|
2546
|
-
|
|
2547
|
-
case "starting-microvm":
|
|
2548
|
-
return "Starting microVM";
|
|
2549
|
-
case "microvm-ready":
|
|
2550
|
-
return "microVM ready";
|
|
2551
|
-
case "running-command":
|
|
2552
|
-
return "Running command";
|
|
2553
|
-
case "destroying-microvm":
|
|
2554
|
-
return "Cleaning up";
|
|
2555
|
-
case "microvm-destroyed":
|
|
2556
|
-
return "microVM destroyed";
|
|
2557
|
-
}
|
|
2411
|
+
return phase === "running" ? "Running on Setsuna" : "Complete";
|
|
2558
2412
|
}
|
|
2559
2413
|
function formatRunResult(result) {
|
|
2560
2414
|
const facts = [`Exit status ${result.exitStatus}`, `${result.durationMs} ms`];
|
|
@@ -2593,6 +2447,10 @@ function formatMicrovmState(microvm, now) {
|
|
|
2593
2447
|
if (microvm.vcpuCount !== void 0 && microvm.memoryMiB !== void 0) {
|
|
2594
2448
|
lines.push(`Compute ${formatMicrovmShape(microvm.vcpuCount, microvm.memoryMiB)}`);
|
|
2595
2449
|
}
|
|
2450
|
+
if (microvm.duration !== void 0) {
|
|
2451
|
+
lines.push(`Duration ${microvm.duration}`);
|
|
2452
|
+
return lines.join("\n");
|
|
2453
|
+
}
|
|
2596
2454
|
const remaining = remainingTime(microvm.expiresAt, now);
|
|
2597
2455
|
lines.push(remaining === void 0 ? "Expired" : `Expires in ${remaining}`);
|
|
2598
2456
|
return lines.join("\n");
|
|
@@ -2908,12 +2766,25 @@ async function startMicrovmMenu(io, actions, promptUi) {
|
|
|
2908
2766
|
const configurations = (await actions.microvmConfigurations()).configurations;
|
|
2909
2767
|
const configuration = await promptForMicrovmConfiguration(io, promptUi, configurations);
|
|
2910
2768
|
if (configuration === null) return;
|
|
2911
|
-
const
|
|
2769
|
+
const duration = await promptUi.select(
|
|
2770
|
+
{
|
|
2771
|
+
message: "Duration",
|
|
2772
|
+
options: [
|
|
2773
|
+
{ value: "20m", label: "20 minutes" },
|
|
2774
|
+
{ value: "1h", label: "1 hour" },
|
|
2775
|
+
{ value: "2h", label: "2 hours" }
|
|
2776
|
+
],
|
|
2777
|
+
initialValue: "20m"
|
|
2778
|
+
},
|
|
2779
|
+
io
|
|
2780
|
+
);
|
|
2781
|
+
if (duration === TUI_CANCEL) return;
|
|
2782
|
+
const sandbox = await startSandbox(io, actions, promptUi, configuration.id, duration);
|
|
2912
2783
|
if (sandbox === void 0) return;
|
|
2913
2784
|
promptUi.box(formatMicrovmState(sandbox, Date.now()), "microVM ready", io, STANDALONE_BOX);
|
|
2914
2785
|
await manageSandbox(io, actions, promptUi, sandbox);
|
|
2915
2786
|
}
|
|
2916
|
-
async function startSandbox(io, actions, promptUi, microvmConfigurationId) {
|
|
2787
|
+
async function startSandbox(io, actions, promptUi, microvmConfigurationId, duration) {
|
|
2917
2788
|
let interrupted = false;
|
|
2918
2789
|
const spinner = promptUi.spinner(io, {
|
|
2919
2790
|
indicator: "timer",
|
|
@@ -2924,7 +2795,7 @@ async function startSandbox(io, actions, promptUi, microvmConfigurationId) {
|
|
|
2924
2795
|
spinner.start("Starting microVM");
|
|
2925
2796
|
let sandbox;
|
|
2926
2797
|
try {
|
|
2927
|
-
sandbox = await actions.startMicrovm(microvmConfigurationId);
|
|
2798
|
+
sandbox = await actions.startMicrovm(microvmConfigurationId, duration);
|
|
2928
2799
|
} catch (error) {
|
|
2929
2800
|
spinner.error(formatTuiError(error));
|
|
2930
2801
|
return void 0;
|
|
@@ -2978,7 +2849,6 @@ async function manageSandbox(io, actions, promptUi, initial) {
|
|
|
2978
2849
|
{ value: "open-terminal", label: "Open terminal" },
|
|
2979
2850
|
{ value: "reattach-terminal", label: "Reattach terminal" },
|
|
2980
2851
|
{ value: "run-command", label: "Run command" },
|
|
2981
|
-
{ value: "renew", label: "Renew" },
|
|
2982
2852
|
{ value: "destroy", label: "Destroy microVM" },
|
|
2983
2853
|
{ value: "back", label: "Back" }
|
|
2984
2854
|
],
|
|
@@ -2999,33 +2869,17 @@ async function manageSandbox(io, actions, promptUi, initial) {
|
|
|
2999
2869
|
async function dispatchSandboxChoice(choice, microvm, io, actions, promptUi) {
|
|
3000
2870
|
if (choice === "open-terminal") {
|
|
3001
2871
|
await actions.openTerminal(microvm.sandboxId);
|
|
3002
|
-
return
|
|
2872
|
+
return microvm;
|
|
3003
2873
|
}
|
|
3004
2874
|
if (choice === "reattach-terminal") {
|
|
3005
|
-
|
|
3006
|
-
return
|
|
2875
|
+
await reattachTerminal(io, actions, promptUi, microvm.sandboxId);
|
|
2876
|
+
return microvm;
|
|
3007
2877
|
}
|
|
3008
2878
|
if (choice === "run-command") {
|
|
3009
|
-
|
|
3010
|
-
return ran ? refreshExpiry(actions, microvm) : microvm;
|
|
3011
|
-
}
|
|
3012
|
-
if (choice === "renew") return renewSandbox(io, actions, promptUi, microvm);
|
|
3013
|
-
return await destroySandbox(io, actions, promptUi, microvm.sandboxId) ? void 0 : microvm;
|
|
3014
|
-
}
|
|
3015
|
-
async function renewSandbox(io, actions, promptUi, microvm) {
|
|
3016
|
-
const status = await actions.renewMicrovm(microvm.sandboxId);
|
|
3017
|
-
promptUi.success(`Renewed \xB7 ${formatExpiry(status.expiresAt, Date.now())}`, io);
|
|
3018
|
-
return { ...microvm, expiresAt: status.expiresAt };
|
|
3019
|
-
}
|
|
3020
|
-
async function refreshExpiry(actions, microvm) {
|
|
3021
|
-
try {
|
|
3022
|
-
const current = (await actions.activeMicrovms()).find(
|
|
3023
|
-
({ sandboxId }) => sandboxId === microvm.sandboxId
|
|
3024
|
-
);
|
|
3025
|
-
return current === void 0 ? microvm : { ...microvm, expiresAt: current.expiresAt };
|
|
3026
|
-
} catch {
|
|
2879
|
+
await runPersistentCommand(io, actions, promptUi, microvm.sandboxId);
|
|
3027
2880
|
return microvm;
|
|
3028
2881
|
}
|
|
2882
|
+
return await destroySandbox(io, actions, promptUi, microvm.sandboxId) ? void 0 : microvm;
|
|
3029
2883
|
}
|
|
3030
2884
|
async function reattachTerminal(io, actions, promptUi, sandboxId) {
|
|
3031
2885
|
const processes = await actions.terminalProcesses(sandboxId);
|
|
@@ -3164,68 +3018,18 @@ async function dispatchMainChoice(choice, io, actions, promptUi, session) {
|
|
|
3164
3018
|
return await accountMenu(io, actions.auth, promptUi, session) === "signed-out" ? "signed-out" : "continue";
|
|
3165
3019
|
}
|
|
3166
3020
|
|
|
3167
|
-
// ../setsuna/src/cli-network-options.ts
|
|
3168
|
-
var NETWORK_OPTION_NAMES = [
|
|
3169
|
-
"network-mode",
|
|
3170
|
-
"host-ip",
|
|
3171
|
-
"guest-ip",
|
|
3172
|
-
"prefix-length",
|
|
3173
|
-
"uplink-interface",
|
|
3174
|
-
"dns-resolvers",
|
|
3175
|
-
"mtu"
|
|
3176
|
-
];
|
|
3177
|
-
function routedNetworkFromCli(parsed) {
|
|
3178
|
-
if (!NETWORK_OPTION_NAMES.some((name) => parsed.values[name] !== void 0)) return void 0;
|
|
3179
|
-
if (parsed.values["network-mode"] !== "routed") {
|
|
3180
|
-
throw new TypeError("--network-mode must be routed.");
|
|
3181
|
-
}
|
|
3182
|
-
const hostIp = required(parsed.values["host-ip"], "--host-ip");
|
|
3183
|
-
const guestIp = required(parsed.values["guest-ip"], "--guest-ip");
|
|
3184
|
-
const prefixLength = integer(parsed.values["prefix-length"], "--prefix-length");
|
|
3185
|
-
if (prefixLength !== 30) throw new TypeError("--prefix-length must be 30.");
|
|
3186
|
-
const uplinkInterface = required(parsed.values["uplink-interface"], "--uplink-interface");
|
|
3187
|
-
const dnsResolvers = parsed.values["dns-resolvers"]?.split(",");
|
|
3188
|
-
const mtu = optionalInteger2(parsed.values.mtu, "--mtu");
|
|
3189
|
-
return {
|
|
3190
|
-
mode: "routed",
|
|
3191
|
-
hostIp,
|
|
3192
|
-
guestIp,
|
|
3193
|
-
prefixLength: 30,
|
|
3194
|
-
uplinkInterface,
|
|
3195
|
-
...dnsResolvers === void 0 ? {} : { dnsResolvers },
|
|
3196
|
-
...mtu === void 0 ? {} : { mtu }
|
|
3197
|
-
};
|
|
3198
|
-
}
|
|
3199
|
-
function required(value, name) {
|
|
3200
|
-
if (value === void 0 || value === "") throw new TypeError(`${name} is required.`);
|
|
3201
|
-
return value;
|
|
3202
|
-
}
|
|
3203
|
-
function integer(value, name) {
|
|
3204
|
-
if (value === void 0 || !/^[0-9]+$/u.test(value)) {
|
|
3205
|
-
throw new TypeError(`${name} must be an integer.`);
|
|
3206
|
-
}
|
|
3207
|
-
const parsed = Number(value);
|
|
3208
|
-
if (!Number.isSafeInteger(parsed)) throw new TypeError(`${name} is too large.`);
|
|
3209
|
-
return parsed;
|
|
3210
|
-
}
|
|
3211
|
-
function optionalInteger2(value, name) {
|
|
3212
|
-
return value === void 0 ? void 0 : integer(value, name);
|
|
3213
|
-
}
|
|
3214
|
-
|
|
3215
3021
|
// ../setsuna/src/cli-commands.ts
|
|
3216
3022
|
async function runRunCommand(commandName, commandArguments, parsed, client, requestTimeoutMs, io, options) {
|
|
3217
3023
|
if (commandName !== "run") throw new TypeError(`Unknown command: ${commandName ?? ""}`);
|
|
3218
3024
|
if (commandArguments.length === 0)
|
|
3219
3025
|
throw new TypeError("setsuna run requires a command after --.");
|
|
3220
|
-
const network = routedNetworkFromCli(parsed);
|
|
3221
3026
|
const result = await runWithSignalCleanup(
|
|
3222
3027
|
client,
|
|
3223
3028
|
commandArguments.map(shellQuote).join(" "),
|
|
3224
3029
|
{
|
|
3225
3030
|
timeoutMs: optionalInteger(parsed.values["timeout-ms"], "--timeout-ms"),
|
|
3226
3031
|
requestTimeoutMs,
|
|
3227
|
-
...parsed.values["configuration-id"] === void 0 ? {} : { microvmConfigurationId: parsed.values["configuration-id"] }
|
|
3228
|
-
...network === void 0 ? {} : { network }
|
|
3032
|
+
...parsed.values["configuration-id"] === void 0 ? {} : { microvmConfigurationId: parsed.values["configuration-id"] }
|
|
3229
3033
|
},
|
|
3230
3034
|
io,
|
|
3231
3035
|
options.signalRuntime ?? DEFAULT_SIGNAL_RUNTIME
|
|
@@ -3368,8 +3172,56 @@ function exactly(arguments_, count, message) {
|
|
|
3368
3172
|
return arguments_;
|
|
3369
3173
|
}
|
|
3370
3174
|
|
|
3175
|
+
// ../setsuna/src/cli-network-options.ts
|
|
3176
|
+
var NETWORK_OPTION_NAMES = [
|
|
3177
|
+
"network-mode",
|
|
3178
|
+
"host-ip",
|
|
3179
|
+
"guest-ip",
|
|
3180
|
+
"prefix-length",
|
|
3181
|
+
"uplink-interface",
|
|
3182
|
+
"dns-resolvers",
|
|
3183
|
+
"mtu"
|
|
3184
|
+
];
|
|
3185
|
+
function routedNetworkFromCli(parsed) {
|
|
3186
|
+
if (!NETWORK_OPTION_NAMES.some((name) => parsed.values[name] !== void 0)) return void 0;
|
|
3187
|
+
if (parsed.values["network-mode"] !== "routed") {
|
|
3188
|
+
throw new TypeError("--network-mode must be routed.");
|
|
3189
|
+
}
|
|
3190
|
+
const hostIp = required(parsed.values["host-ip"], "--host-ip");
|
|
3191
|
+
const guestIp = required(parsed.values["guest-ip"], "--guest-ip");
|
|
3192
|
+
const prefixLength = integer(parsed.values["prefix-length"], "--prefix-length");
|
|
3193
|
+
if (prefixLength !== 30) throw new TypeError("--prefix-length must be 30.");
|
|
3194
|
+
const uplinkInterface = required(parsed.values["uplink-interface"], "--uplink-interface");
|
|
3195
|
+
const dnsResolvers = parsed.values["dns-resolvers"]?.split(",");
|
|
3196
|
+
const mtu = optionalInteger2(parsed.values.mtu, "--mtu");
|
|
3197
|
+
return {
|
|
3198
|
+
mode: "routed",
|
|
3199
|
+
hostIp,
|
|
3200
|
+
guestIp,
|
|
3201
|
+
prefixLength: 30,
|
|
3202
|
+
uplinkInterface,
|
|
3203
|
+
...dnsResolvers === void 0 ? {} : { dnsResolvers },
|
|
3204
|
+
...mtu === void 0 ? {} : { mtu }
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
function required(value, name) {
|
|
3208
|
+
if (value === void 0 || value === "") throw new TypeError(`${name} is required.`);
|
|
3209
|
+
return value;
|
|
3210
|
+
}
|
|
3211
|
+
function integer(value, name) {
|
|
3212
|
+
if (value === void 0 || !/^[0-9]+$/u.test(value)) {
|
|
3213
|
+
throw new TypeError(`${name} must be an integer.`);
|
|
3214
|
+
}
|
|
3215
|
+
const parsed = Number(value);
|
|
3216
|
+
if (!Number.isSafeInteger(parsed)) throw new TypeError(`${name} is too large.`);
|
|
3217
|
+
return parsed;
|
|
3218
|
+
}
|
|
3219
|
+
function optionalInteger2(value, name) {
|
|
3220
|
+
return value === void 0 ? void 0 : integer(value, name);
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3371
3223
|
// ../setsuna/src/cli-sandbox-commands.ts
|
|
3372
|
-
var SANDBOX_COMMANDS = /* @__PURE__ */ new Set(["create", "list", "status", "
|
|
3224
|
+
var SANDBOX_COMMANDS = /* @__PURE__ */ new Set(["create", "list", "status", "exec", "destroy"]);
|
|
3373
3225
|
async function runSandboxCommand(commandName, commandArguments, parsed, client, requestTimeoutMs, io, options) {
|
|
3374
3226
|
if (commandName === void 0 || !SANDBOX_COMMANDS.has(commandName)) return false;
|
|
3375
3227
|
if (commandName === "create") {
|
|
@@ -3377,6 +3229,7 @@ async function runSandboxCommand(commandName, commandArguments, parsed, client,
|
|
|
3377
3229
|
const network = routedNetworkFromCli(parsed);
|
|
3378
3230
|
writeLease(
|
|
3379
3231
|
await client.create({
|
|
3232
|
+
duration: parsed.values.duration ?? "20m",
|
|
3380
3233
|
...parsed.values["configuration-id"] === void 0 ? {} : { microvmConfigurationId: parsed.values["configuration-id"] },
|
|
3381
3234
|
...network === void 0 ? {} : { network }
|
|
3382
3235
|
}),
|
|
@@ -3390,9 +3243,6 @@ async function runSandboxCommand(commandName, commandArguments, parsed, client,
|
|
|
3390
3243
|
} else if (commandName === "status") {
|
|
3391
3244
|
const sandboxId = requireSandboxId(commandName, commandArguments);
|
|
3392
3245
|
writeStatus(await client.status(sandboxId), parsed.values.json, io.output, "Sandbox");
|
|
3393
|
-
} else if (commandName === "renew") {
|
|
3394
|
-
const sandboxId = requireSandboxId(commandName, commandArguments);
|
|
3395
|
-
writeStatus(await client.renew(sandboxId), parsed.values.json, io.output, "Renewed sandbox");
|
|
3396
3246
|
} else if (commandName === "destroy") {
|
|
3397
3247
|
const sandboxId = requireSandboxId(commandName, commandArguments);
|
|
3398
3248
|
await client.destroy(sandboxId);
|
|
@@ -3474,7 +3324,7 @@ function writeLease(lease, json, output, verb) {
|
|
|
3474
3324
|
`);
|
|
3475
3325
|
else {
|
|
3476
3326
|
output.write(
|
|
3477
|
-
`${verb} sandbox ${lease.sandboxId} with configuration ${lease.microvmConfiguration.id}; lease expires at ${lease.expiresAt}.
|
|
3327
|
+
`${verb} sandbox ${lease.sandboxId} with configuration ${lease.microvmConfiguration.id}; duration ${lease.duration}, expires at ${lease.expiresAt}.
|
|
3478
3328
|
`
|
|
3479
3329
|
);
|
|
3480
3330
|
}
|
|
@@ -3507,6 +3357,8 @@ function writeDestroyed(sandboxId, json, output) {
|
|
|
3507
3357
|
|
|
3508
3358
|
// ../setsuna/src/cli-client.ts
|
|
3509
3359
|
async function runClientCli(commandName, commandArguments, parsed, authenticated, io, options) {
|
|
3360
|
+
if (parsed.values.duration !== void 0 && commandName !== "create")
|
|
3361
|
+
throw new TypeError("--duration is supported only by setsuna create.");
|
|
3510
3362
|
const requestTimeoutMs = optionalInteger(
|
|
3511
3363
|
parsed.values["request-timeout-ms"],
|
|
3512
3364
|
"--request-timeout-ms"
|
|
@@ -3721,7 +3573,6 @@ function customerSafeSandboxActions(actions) {
|
|
|
3721
3573
|
openTerminal: customerSafe(actions.openTerminal),
|
|
3722
3574
|
terminalProcesses: customerSafe(actions.terminalProcesses),
|
|
3723
3575
|
runPersistentCommand: customerSafe(actions.runPersistentCommand),
|
|
3724
|
-
renewMicrovm: customerSafe(actions.renewMicrovm),
|
|
3725
3576
|
destroyMicrovm: customerSafe(actions.destroyMicrovm)
|
|
3726
3577
|
};
|
|
3727
3578
|
}
|
|
@@ -3750,10 +3601,9 @@ var PREVIEW_HELP = `Usage:
|
|
|
3750
3601
|
setsuna auth logout [--json]
|
|
3751
3602
|
setsuna health [--json]
|
|
3752
3603
|
setsuna configurations [--json]
|
|
3753
|
-
setsuna create [--configuration-id ID] [--request-timeout-ms MS] [--json]
|
|
3604
|
+
setsuna create [--duration 20m|1h|2h] [--configuration-id ID] [--request-timeout-ms MS] [--json]
|
|
3754
3605
|
setsuna list [--request-timeout-ms MS] [--json]
|
|
3755
3606
|
setsuna status SANDBOX_ID [--request-timeout-ms MS] [--json]
|
|
3756
|
-
setsuna renew SANDBOX_ID [--request-timeout-ms MS] [--json]
|
|
3757
3607
|
setsuna exec SANDBOX_ID [--timeout-ms MS] [--request-timeout-ms MS] [--json] -- COMMAND [ARG...]
|
|
3758
3608
|
setsuna destroy SANDBOX_ID [--request-timeout-ms MS] [--json]
|
|
3759
3609
|
setsuna shell SANDBOX_ID [--attach PROCESS_ID] [--request-timeout-ms MS]
|
|
@@ -3770,8 +3620,7 @@ Commands:
|
|
|
3770
3620
|
configurations List available microVM configurations.
|
|
3771
3621
|
create Create a persistent sandbox with a selected or default configuration.
|
|
3772
3622
|
list List active persistent sandboxes.
|
|
3773
|
-
status Inspect an active persistent sandbox
|
|
3774
|
-
renew Extend an active persistent sandbox lease.
|
|
3623
|
+
status Inspect an active persistent sandbox.
|
|
3775
3624
|
exec Execute a command without destroying the persistent sandbox.
|
|
3776
3625
|
destroy Explicitly destroy a persistent sandbox.
|
|
3777
3626
|
shell Open an interactive shell in a persistent sandbox, or reattach to one.
|