@molecule/api-code-sandbox-flyio 1.2.5 → 1.2.6
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/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +44 -11
- package/package.json +1 -1
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAWV,eAAe,EAGhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAwBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAIV,WAAW,EAUZ,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAWV,eAAe,EAGhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAwBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAIV,WAAW,EAUZ,MAAM,YAAY,CAAA;AA+2EnB;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,YAAY,EACrB,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,GACzB,eAAe,CAEjB"}
|
package/dist/provider.js
CHANGED
|
@@ -1713,7 +1713,12 @@ class FlyioSandboxProvider {
|
|
|
1713
1713
|
*
|
|
1714
1714
|
* A Machine that is already `started` may reject a redundant start. Rather
|
|
1715
1715
|
* than pattern-matching an error string, the state is re-read and the call
|
|
1716
|
-
* treated as successful when the Machine is in fact running.
|
|
1716
|
+
* treated as successful when the Machine is in fact running. A Machine still
|
|
1717
|
+
* `starting` (its own start lost to a concurrent one — the proxy's autostart
|
|
1718
|
+
* racing a `wake()`) is NOT that case: only `waitForStarted` may end this
|
|
1719
|
+
* call, or it resolves against a Machine that is still booting and the
|
|
1720
|
+
* caller's first `exec` fails with 412 "machine not running" — the exact
|
|
1721
|
+
* failure this method's wait exists to prevent.
|
|
1717
1722
|
* @param app - The Fly app name.
|
|
1718
1723
|
* @param machineId - The Fly Machine id.
|
|
1719
1724
|
*/
|
|
@@ -1725,7 +1730,7 @@ class FlyioSandboxProvider {
|
|
|
1725
1730
|
const machine = await this.client
|
|
1726
1731
|
.request(`/apps/${app}/machines/${machineId}`, { nullOn: [404] })
|
|
1727
1732
|
.catch(() => null);
|
|
1728
|
-
if (machine
|
|
1733
|
+
if (machine?.state === 'started') {
|
|
1729
1734
|
logger.debug('Fly Machine start rejected but the Machine is already running', {
|
|
1730
1735
|
app,
|
|
1731
1736
|
machineId,
|
|
@@ -1733,7 +1738,9 @@ class FlyioSandboxProvider {
|
|
|
1733
1738
|
});
|
|
1734
1739
|
return;
|
|
1735
1740
|
}
|
|
1736
|
-
|
|
1741
|
+
if (machine?.state !== 'starting')
|
|
1742
|
+
throw error;
|
|
1743
|
+
logger.debug('Fly Machine start rejected but the Machine is already starting — waiting for started', { app, machineId });
|
|
1737
1744
|
}
|
|
1738
1745
|
await this.waitForStarted(app, machineId);
|
|
1739
1746
|
}
|
|
@@ -1858,10 +1865,12 @@ class FlyioSandboxProvider {
|
|
|
1858
1865
|
throw new Error(t('codeSandbox.flyio.error.writeFailed', { path, error: result.stderr }, { defaultValue: `Failed to write ${path}: ${result.stderr}` }));
|
|
1859
1866
|
}
|
|
1860
1867
|
};
|
|
1861
|
-
// Chunked for the same reason as
|
|
1862
|
-
// is one
|
|
1863
|
-
//
|
|
1864
|
-
//
|
|
1868
|
+
// Chunked for the same reason as WRITE_CHUNK_BASE64 above: the whole
|
|
1869
|
+
// command is one argument to Fly's exec API, which silently rejects
|
|
1870
|
+
// anything over ~15 KB (FLY_EXEC_MAX_ARG_BYTES) — NOT the OS
|
|
1871
|
+
// MAX_ARG_STRLEN (128 KB) a Docker-based provider is bounded by. Each
|
|
1872
|
+
// chunk is a multiple of 4 base64 characters (= 3 decoded bytes) so it
|
|
1873
|
+
// decodes standalone and the bytes concatenate exactly.
|
|
1865
1874
|
failIfError(await this.exec(`mkdir -p "$(dirname ${quoted})" && printf %s ${shellQuote(base64.slice(0, WRITE_CHUNK_BASE64))} | base64 -d > ${quoted}`, { timeout: FILE_OP_TIMEOUT_MS }));
|
|
1866
1875
|
for (let i = WRITE_CHUNK_BASE64; i < base64.length; i += WRITE_CHUNK_BASE64) {
|
|
1867
1876
|
failIfError(await this.exec(`printf %s ${shellQuote(base64.slice(i, i + WRITE_CHUNK_BASE64))} | base64 -d >> ${quoted}`, { timeout: FILE_OP_TIMEOUT_MS }));
|
|
@@ -1958,6 +1967,14 @@ class FlyioSandboxProvider {
|
|
|
1958
1967
|
try {
|
|
1959
1968
|
const putUrl = await store.presignPut(key, IMPORT_PRESIGN_EXPIRY_SECONDS);
|
|
1960
1969
|
const putRes = await fetch(putUrl, { method: 'PUT', body: gz });
|
|
1970
|
+
// The PUT's body is never read (a presigned store answers an empty
|
|
1971
|
+
// 2xx or a short error document): release it on EVERY path, or the
|
|
1972
|
+
// connection stays stranded in the pool until GC reclaims it — the
|
|
1973
|
+
// same release every other discarded response body here gets.
|
|
1974
|
+
await putRes.body?.cancel().catch((_error) => {
|
|
1975
|
+
// Best-effort release only; the fallback decision below runs on
|
|
1976
|
+
// the already-known status.
|
|
1977
|
+
});
|
|
1961
1978
|
if (!putRes.ok) {
|
|
1962
1979
|
throw new Error(`presigned PUT to the object store failed: ${putRes.status}`);
|
|
1963
1980
|
}
|
|
@@ -1967,7 +1984,12 @@ class FlyioSandboxProvider {
|
|
|
1967
1984
|
`mkdir -p ${quotedPath}`,
|
|
1968
1985
|
'archive=$(mktemp /tmp/mol-import-XXXXXX.tar.gz)',
|
|
1969
1986
|
`curl --fail-with-body -sS --retry 3 --retry-connrefused -o "$archive" ${shellQuote(getUrl)}`,
|
|
1970
|
-
|
|
1987
|
+
// The archive is removed whether the extract SUCCEEDS or fails:
|
|
1988
|
+
// `&& rm` after tar (under `set -e`) left every failed extract's
|
|
1989
|
+
// download behind in the sandbox's /tmp until the Machine died.
|
|
1990
|
+
// The `|| { }` guard keeps `set -e` from aborting before the
|
|
1991
|
+
// cleanup and re-raises the real exit code.
|
|
1992
|
+
`tar -C ${quotedPath} --no-same-owner --no-same-permissions -xzf "$archive" || { rc=$?; rm -f "$archive"; exit $rc; }`,
|
|
1971
1993
|
'rm -f "$archive"',
|
|
1972
1994
|
].join('\n'), { timeout: IMPORT_EXTRACT_TIMEOUT_MS });
|
|
1973
1995
|
// Best-effort: the temp object expires anyway, but delete it promptly.
|
|
@@ -1989,15 +2011,26 @@ class FlyioSandboxProvider {
|
|
|
1989
2011
|
// configured. The temp tar lives on the WORKSPACE VOLUME, not /tmp — the
|
|
1990
2012
|
// chunked write and the extract are separate exec calls and a Machine
|
|
1991
2013
|
// with autostop can cold-start between them, wiping /tmp (rootfs) while
|
|
1992
|
-
// the ext4 volume at /workspace persists.
|
|
2014
|
+
// the ext4 volume at /workspace persists. The name is unique per call
|
|
2015
|
+
// (same shape as the store path's key): a fixed, machineId-only name
|
|
2016
|
+
// made two concurrent imports to one sandbox interleave their `>>`
|
|
2017
|
+
// appends into ONE file, so each extracted a mix of both archives.
|
|
1993
2018
|
const base64 = gz.toString('base64');
|
|
1994
|
-
const tmp = `${WORKSPACE_PATH}/.mol-import-${machineId}.tgz`;
|
|
2019
|
+
const tmp = `${WORKSPACE_PATH}/.mol-import-${machineId}-${Date.now().toString(36)}${Math.floor(Math.random() * 1e9).toString(36)}.tgz`;
|
|
1995
2020
|
const quotedTmp = shellQuote(tmp);
|
|
1996
2021
|
failIfError(await this.exec(`printf %s ${shellQuote(base64.slice(0, IMPORT_WRITE_CHUNK_BASE64))} | base64 -d > ${quotedTmp}`, { timeout: FILE_OP_TIMEOUT_MS }), 'write');
|
|
1997
2022
|
for (let i = IMPORT_WRITE_CHUNK_BASE64; i < base64.length; i += IMPORT_WRITE_CHUNK_BASE64) {
|
|
1998
2023
|
failIfError(await this.exec(`printf %s ${shellQuote(base64.slice(i, i + IMPORT_WRITE_CHUNK_BASE64))} | base64 -d >> ${quotedTmp}`, { timeout: FILE_OP_TIMEOUT_MS }), 'write');
|
|
1999
2024
|
}
|
|
2000
|
-
failIfError(await this.exec(
|
|
2025
|
+
failIfError(await this.exec(
|
|
2026
|
+
// The temp archive is removed whether the extract SUCCEEDS or fails:
|
|
2027
|
+
// `&& rm` after tar left a failed extract's multi-MB archive behind
|
|
2028
|
+
// on the WORKSPACE VOLUME — the durable path this fallback
|
|
2029
|
+
// deliberately writes to — until the next import overwrote it.
|
|
2030
|
+
// `; rc=$?; rm …; exit $rc` cleans up unconditionally and still
|
|
2031
|
+
// reports tar's real exit code (the same shape exec.ts's script
|
|
2032
|
+
// spill uses).
|
|
2033
|
+
`mkdir -p ${quotedPath} && tar -xzf ${quotedTmp} -C ${quotedPath} --no-same-owner --no-same-permissions; rc=$?; rm -f ${quotedTmp}; exit $rc`, { timeout: IMPORT_EXTRACT_TIMEOUT_MS }), 'extract');
|
|
2001
2034
|
},
|
|
2002
2035
|
getPreviewUrl(port) {
|
|
2003
2036
|
return provider.renderPreviewUrl(app, machineId, port);
|