@molecule/api-code-sandbox-flyio 1.0.13 → 1.0.15
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/api.d.ts +2 -7
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +39 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +6 -1
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -113,13 +113,8 @@ export interface FlyApiClientOptions {
|
|
|
113
113
|
/** Injectable sleep, for tests. Defaults to `setTimeout`. */
|
|
114
114
|
sleep?: (ms: number) => Promise<void>;
|
|
115
115
|
}
|
|
116
|
-
/**
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
* Deliberately not a generated SDK: this provider touches ~12 endpoints, and a
|
|
120
|
-
* generated client would drag in the whole Machines + Postgres + tokens surface
|
|
121
|
-
* for no benefit.
|
|
122
|
-
*/
|
|
116
|
+
/** Test-only: reset the shared pacer so timing tests start from a clean slate. */
|
|
117
|
+
export declare function resetFlyPacerForTests(): void;
|
|
123
118
|
export declare class FlyApiClient {
|
|
124
119
|
private readonly token;
|
|
125
120
|
private readonly baseUrl;
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgBH;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;;;;;OAOG;gBAED,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAgBhC;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0DAA0D;IAC1D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAQhF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAoBjE;AAED,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,KAAK,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IAC/B,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mEAAmE;IACnE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgBH;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;;;;;OAOG;gBAED,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAgBhC;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0DAA0D;IAC1D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAQhF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAoBjE;AAED,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,KAAK,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IAC/B,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mEAAmE;IACnE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC;AAyCD,kFAAkF;AAClF,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IAErD;;;OAGG;gBACS,OAAO,EAAE,mBAAmB;IASxC;;;;;;;;;;OAUG;IACG,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAiFnF"}
|
package/dist/api.js
CHANGED
|
@@ -124,6 +124,42 @@ export function normalizeApiUrl(value) {
|
|
|
124
124
|
* generated client would drag in the whole Machines + Postgres + tokens surface
|
|
125
125
|
* for no benefit.
|
|
126
126
|
*/
|
|
127
|
+
/**
|
|
128
|
+
* Minimum gap between Fly Machines API requests, PROCESS-WIDE. Fly rate-limits
|
|
129
|
+
* per account (most tightly the exec endpoint, ~a few req/s), and one molecule
|
|
130
|
+
* process makes every call with one token — so without pacing, a sandbox boot's
|
|
131
|
+
* burst of exec calls trips `429`, and retrying under the cap sustains a storm
|
|
132
|
+
* that LIVELOCKS the boot (observed live 2026-08-09: a workspace stuck
|
|
133
|
+
* "creating" with continuous per-second 429s that never drained). ~220 ms ≈
|
|
134
|
+
* 4.5 req/s keeps us under the cap. Tunable via `FLY_API_MIN_REQUEST_GAP_MS`.
|
|
135
|
+
*/
|
|
136
|
+
function minRequestGapMs() {
|
|
137
|
+
const v = Number(process.env.FLY_API_MIN_REQUEST_GAP_MS);
|
|
138
|
+
return Number.isFinite(v) && v >= 0 ? v : 220;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Serializes every request through a shared chain so no two fire closer than
|
|
142
|
+
* {@link MIN_REQUEST_GAP_MS}. Module-level (not per-client) so concurrent
|
|
143
|
+
* sandbox boots share ONE pacer and cannot collectively exceed the account cap.
|
|
144
|
+
* Errors in the chain are swallowed so one failed wait never wedges the queue.
|
|
145
|
+
*/
|
|
146
|
+
let flyPacerTail = Promise.resolve();
|
|
147
|
+
let flyLastRequestAt = 0;
|
|
148
|
+
function paceFlyRequest(sleep) {
|
|
149
|
+
const mine = flyPacerTail.then(async () => {
|
|
150
|
+
const wait = Math.max(0, flyLastRequestAt + minRequestGapMs() - Date.now());
|
|
151
|
+
if (wait > 0)
|
|
152
|
+
await sleep(wait);
|
|
153
|
+
flyLastRequestAt = Date.now();
|
|
154
|
+
});
|
|
155
|
+
flyPacerTail = mine.catch(() => { });
|
|
156
|
+
return mine;
|
|
157
|
+
}
|
|
158
|
+
/** Test-only: reset the shared pacer so timing tests start from a clean slate. */
|
|
159
|
+
export function resetFlyPacerForTests() {
|
|
160
|
+
flyPacerTail = Promise.resolve();
|
|
161
|
+
flyLastRequestAt = 0;
|
|
162
|
+
}
|
|
127
163
|
export class FlyApiClient {
|
|
128
164
|
token;
|
|
129
165
|
baseUrl;
|
|
@@ -164,6 +200,9 @@ export class FlyApiClient {
|
|
|
164
200
|
}
|
|
165
201
|
let lastError;
|
|
166
202
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
203
|
+
// Pace EVERY network call (including retries) through the process-wide
|
|
204
|
+
// limiter so a boot's burst of exec calls never trips Fly's account 429.
|
|
205
|
+
await paceFlyRequest(this.sleep);
|
|
167
206
|
const controller = new AbortController();
|
|
168
207
|
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? this.timeoutMs);
|
|
169
208
|
// Initialized to 0 ("no answer received"), which is the value that
|
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,EAUV,eAAe,EAEhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAsBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAEV,WAAW,EAUZ,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAUV,eAAe,EAEhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAsBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAEV,WAAW,EAUZ,MAAM,YAAY,CAAA;AAm4DnB;;;;;;;;;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
|
@@ -635,11 +635,16 @@ class FlyioSandboxProvider {
|
|
|
635
635
|
*/
|
|
636
636
|
async assignSharedIpv4(app) {
|
|
637
637
|
try {
|
|
638
|
+
// NO org_slug: the ip_assignments endpoint derives the org from the app
|
|
639
|
+
// (like the private-IP allocation) and 400s "unknown field org_slug" when
|
|
640
|
+
// it is sent — which silently left every sandbox app WITHOUT a public IP,
|
|
641
|
+
// so its `<app>.fly.dev` preview URL never routed and the IDE hung on
|
|
642
|
+
// "Loading preview…" forever. Same fix that was already applied to the
|
|
643
|
+
// private-address allocation.
|
|
638
644
|
await this.client.request(`/apps/${app}/ip_assignments`, {
|
|
639
645
|
method: 'POST',
|
|
640
646
|
body: {
|
|
641
647
|
type: this.config.ipAssignmentType ?? 'shared_v4',
|
|
642
|
-
org_slug: this.orgSlug(),
|
|
643
648
|
region: this.region(),
|
|
644
649
|
},
|
|
645
650
|
});
|