@opengeni/runtime 0.2.2 → 0.3.0
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/{chunk-2PO56VAL.js → chunk-D5KU3QUC.js} +240 -23
- package/dist/chunk-D5KU3QUC.js.map +1 -0
- package/dist/index.d.ts +106 -178
- package/dist/index.js +427 -161
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +54 -6
- package/dist/sandbox/index.js +11 -1
- package/package.json +3 -3
- package/src/context-compaction.ts +217 -348
- package/src/image-history.ts +149 -0
- package/src/index.ts +195 -38
- package/src/sandbox/display-stack.ts +96 -12
- package/src/sandbox/index.ts +72 -12
- package/src/sandbox/providers/modal.ts +225 -0
- package/src/sandbox/routing/routing-session.ts +2 -2
- package/src/sandbox/selfhosted/session.ts +21 -5
- package/src/sandbox-computer.ts +88 -26
- package/dist/chunk-2PO56VAL.js.map +0 -1
|
@@ -202,6 +202,23 @@ var localProvider = {
|
|
|
202
202
|
// src/sandbox/providers/modal.ts
|
|
203
203
|
import { ModalImageSelector, ModalSandboxClient } from "@openai/agents-extensions/sandbox/modal";
|
|
204
204
|
import { effectiveModalIdleTimeoutSeconds } from "@opengeni/config";
|
|
205
|
+
var MODAL_ORPHAN_SWEEP_LIMIT = 50;
|
|
206
|
+
var MODAL_UNATTRIBUTED_ORPHAN_GRACE_MS = 30 * 6e4;
|
|
207
|
+
function modalSandboxAttributionEnvironment(input) {
|
|
208
|
+
return {
|
|
209
|
+
OPENGENI_SANDBOX_LEASE_ID: input.leaseId,
|
|
210
|
+
OPENGENI_SANDBOX_GROUP_ID: input.sandboxGroupId,
|
|
211
|
+
OPENGENI_WORKSPACE_ID: input.workspaceId
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function modalSandboxAttributionTags(input) {
|
|
215
|
+
return {
|
|
216
|
+
opengeni: "true",
|
|
217
|
+
opengeni_lease_id: input.leaseId,
|
|
218
|
+
opengeni_workspace_id: input.workspaceId,
|
|
219
|
+
opengeni_sandbox_group_id: input.sandboxGroupId
|
|
220
|
+
};
|
|
221
|
+
}
|
|
205
222
|
var modalProvider = {
|
|
206
223
|
backend: "modal",
|
|
207
224
|
descriptor: CAPABILITY_DESCRIPTORS.modal,
|
|
@@ -220,8 +237,13 @@ var modalProvider = {
|
|
|
220
237
|
const options = {
|
|
221
238
|
appName: settings.modalAppName,
|
|
222
239
|
timeoutMs: settings.modalTimeoutSeconds * 1e3,
|
|
240
|
+
sandboxCreateTimeoutS: Math.ceil(settings.sandboxWarmingTimeoutMs / 1e3),
|
|
223
241
|
exposedPorts,
|
|
224
|
-
env: environment
|
|
242
|
+
env: environment,
|
|
243
|
+
// The Modal JS SDK's sandbox default command already sleeps until timeout
|
|
244
|
+
// or explicit termination. Do not let the Agents extension stamp a separate
|
|
245
|
+
// hardcoded sleep command; OPENGENI_MODAL_TIMEOUT_SECONDS owns lifetime.
|
|
246
|
+
useSleepCmd: false
|
|
225
247
|
};
|
|
226
248
|
options.idleTimeoutMs = effectiveModalIdleTimeoutSeconds(settings) * 1e3;
|
|
227
249
|
if (settings.modalWorkspacePersistence) {
|
|
@@ -242,6 +264,137 @@ var modalProvider = {
|
|
|
242
264
|
return new ModalSandboxClient(options);
|
|
243
265
|
}
|
|
244
266
|
};
|
|
267
|
+
function modalClientOptions(settings) {
|
|
268
|
+
return {
|
|
269
|
+
...settings.modalTokenId ? { tokenId: settings.modalTokenId } : {},
|
|
270
|
+
...settings.modalTokenSecret ? { tokenSecret: settings.modalTokenSecret } : {},
|
|
271
|
+
...settings.modalEnvironment ? { environment: settings.modalEnvironment } : {},
|
|
272
|
+
...settings.modalTimeoutSeconds ? { timeoutMs: settings.modalTimeoutSeconds * 1e3 } : {}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
async function createModalClient(settings) {
|
|
276
|
+
const modal = await import("modal");
|
|
277
|
+
return new modal.ModalClient(modalClientOptions(settings));
|
|
278
|
+
}
|
|
279
|
+
async function tagModalSandbox(settings, sandboxId, attribution) {
|
|
280
|
+
if (!sandboxId) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
const modal = await createModalClient(settings);
|
|
284
|
+
try {
|
|
285
|
+
const sandbox = await modal.sandboxes.fromId(sandboxId);
|
|
286
|
+
await sandbox.setTags(modalSandboxAttributionTags(attribution));
|
|
287
|
+
return true;
|
|
288
|
+
} finally {
|
|
289
|
+
modal.close();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async function terminateModalSandboxById(settings, sandboxId) {
|
|
293
|
+
if (!sandboxId) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
const modal = await createModalClient(settings);
|
|
297
|
+
try {
|
|
298
|
+
const sandbox = await modal.sandboxes.fromId(sandboxId);
|
|
299
|
+
await sandbox.terminate();
|
|
300
|
+
return true;
|
|
301
|
+
} finally {
|
|
302
|
+
modal.close();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function tagsFromInfo(info) {
|
|
306
|
+
const tags = {};
|
|
307
|
+
for (const tag of info.tags ?? []) {
|
|
308
|
+
if (typeof tag.tagName === "string" && typeof tag.tagValue === "string") {
|
|
309
|
+
tags[tag.tagName] = tag.tagValue;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return tags;
|
|
313
|
+
}
|
|
314
|
+
function sandboxCreatedAtMs(info) {
|
|
315
|
+
if (typeof info.createdAt !== "number" || !Number.isFinite(info.createdAt) || info.createdAt <= 0) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
return info.createdAt < 1e10 ? Math.floor(info.createdAt * 1e3) : Math.floor(info.createdAt);
|
|
319
|
+
}
|
|
320
|
+
function attributionKey(input) {
|
|
321
|
+
return `${input.workspaceId}:${input.sandboxGroupId}:${input.leaseId}`;
|
|
322
|
+
}
|
|
323
|
+
async function sweepModalOrphanSandboxes(settings, liveLeases, options = {}) {
|
|
324
|
+
const nowMs = options.now?.getTime() ?? Date.now();
|
|
325
|
+
const maxTerminations = options.maxTerminations ?? MODAL_ORPHAN_SWEEP_LIMIT;
|
|
326
|
+
const unattributedGraceMs = options.unattributedGraceMs ?? MODAL_UNATTRIBUTED_ORPHAN_GRACE_MS;
|
|
327
|
+
const liveByAttribution = new Map(liveLeases.map((lease) => [attributionKey(lease), lease]));
|
|
328
|
+
const ownedClient = options.client ? null : await createModalClient(settings);
|
|
329
|
+
const modal = options.client ?? ownedClient;
|
|
330
|
+
try {
|
|
331
|
+
const app = await modal.apps.fromName(settings.modalAppName, {
|
|
332
|
+
createIfMissing: false,
|
|
333
|
+
...settings.modalEnvironment ? { environment: settings.modalEnvironment } : {}
|
|
334
|
+
});
|
|
335
|
+
const appId = app.appId;
|
|
336
|
+
if (!appId) {
|
|
337
|
+
return { examined: 0, terminated: [], skipped: 0 };
|
|
338
|
+
}
|
|
339
|
+
let examined = 0;
|
|
340
|
+
let skipped = 0;
|
|
341
|
+
const terminated = [];
|
|
342
|
+
let beforeTimestamp;
|
|
343
|
+
while (terminated.length < maxTerminations) {
|
|
344
|
+
const response = await modal.cpClient.sandboxList({
|
|
345
|
+
appId,
|
|
346
|
+
...beforeTimestamp !== void 0 ? { beforeTimestamp } : {},
|
|
347
|
+
includeFinished: false,
|
|
348
|
+
...settings.modalEnvironment ? { environmentName: settings.modalEnvironment } : {},
|
|
349
|
+
tags: []
|
|
350
|
+
});
|
|
351
|
+
const sandboxes = response.sandboxes ?? [];
|
|
352
|
+
if (sandboxes.length === 0) {
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
for (const info of sandboxes) {
|
|
356
|
+
examined += 1;
|
|
357
|
+
const tags = tagsFromInfo(info);
|
|
358
|
+
const leaseId = tags.opengeni_lease_id;
|
|
359
|
+
const workspaceId = tags.opengeni_workspace_id;
|
|
360
|
+
const sandboxGroupId = tags.opengeni_sandbox_group_id;
|
|
361
|
+
let reason = null;
|
|
362
|
+
if (leaseId && workspaceId && sandboxGroupId) {
|
|
363
|
+
const live = liveByAttribution.get(attributionKey({ leaseId, workspaceId, sandboxGroupId }));
|
|
364
|
+
if (!live || live.instanceId && live.instanceId !== info.id) {
|
|
365
|
+
reason = "stale_attribution";
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
const createdAtMs = sandboxCreatedAtMs(info);
|
|
369
|
+
if (createdAtMs !== null && nowMs - createdAtMs >= unattributedGraceMs) {
|
|
370
|
+
reason = "unattributed";
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (!reason) {
|
|
374
|
+
skipped += 1;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
const sandbox = await modal.sandboxes.fromId(info.id);
|
|
379
|
+
await sandbox.terminate();
|
|
380
|
+
terminated.push({ sandboxId: info.id, reason, tags });
|
|
381
|
+
} catch {
|
|
382
|
+
skipped += 1;
|
|
383
|
+
}
|
|
384
|
+
if (terminated.length >= maxTerminations) {
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
beforeTimestamp = sandboxes[sandboxes.length - 1]?.createdAt;
|
|
389
|
+
if (beforeTimestamp === void 0) {
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return { examined, terminated, skipped };
|
|
394
|
+
} finally {
|
|
395
|
+
ownedClient?.close();
|
|
396
|
+
}
|
|
397
|
+
}
|
|
245
398
|
|
|
246
399
|
// src/sandbox/providers/none.ts
|
|
247
400
|
var noneProvider = {
|
|
@@ -757,16 +910,24 @@ var SelfhostedSession = class {
|
|
|
757
910
|
/** Computer-use VIEW op: capture a single PNG screenshot of the machine's desktop
|
|
758
911
|
* plus its geometry (via ScreenCaptureKit / x11). NOT consent-gated (a view op —
|
|
759
912
|
* the view/control decoupling), so it works with a display but no screen-control
|
|
760
|
-
* consent. Returns the raw encoded bytes + width/height
|
|
913
|
+
* consent. Returns the raw encoded bytes + the ENCODED width/height, plus the
|
|
914
|
+
* NATIVE (pre-downscale) geometry: when the agent had to downscale the PNG to fit
|
|
915
|
+
* the transport's max payload, `nativeWidth`/`nativeHeight` carry the original
|
|
916
|
+
* capture size so the computer-use layer can scale model clicks (in encoded-pixel
|
|
917
|
+
* space) back to native pixels. An older agent leaves them 0 → read as "same as
|
|
918
|
+
* width/height" (no downscale). */
|
|
761
919
|
async screenshot() {
|
|
762
920
|
const result = await this.call({ $case: "desktopScreenshot", desktopScreenshot: {} });
|
|
763
921
|
if (result.$case !== "desktopScreenshot") {
|
|
764
922
|
throw new Error(`selfhosted screenshot: unexpected result ${result.$case}`);
|
|
765
923
|
}
|
|
924
|
+
const s = result.desktopScreenshot;
|
|
766
925
|
return {
|
|
767
|
-
png:
|
|
768
|
-
width:
|
|
769
|
-
height:
|
|
926
|
+
png: s.png,
|
|
927
|
+
width: s.width,
|
|
928
|
+
height: s.height,
|
|
929
|
+
nativeWidth: s.nativeWidth || s.width,
|
|
930
|
+
nativeHeight: s.nativeHeight || s.height
|
|
770
931
|
};
|
|
771
932
|
}
|
|
772
933
|
/** A cheap liveness probe — request a Ping on the subject; returns true iff a
|
|
@@ -1343,13 +1504,16 @@ async function verifyStreamToken(secret, token, nowSeconds = Math.floor(Date.now
|
|
|
1343
1504
|
// src/sandbox/display-stack.ts
|
|
1344
1505
|
import { DESKTOP_STREAM_PORT as DESKTOP_STREAM_PORT4 } from "@opengeni/contracts";
|
|
1345
1506
|
var STREAM_PORT = DESKTOP_STREAM_PORT4;
|
|
1346
|
-
var DISPLAY_STACK_TIMEOUT_MS =
|
|
1507
|
+
var DISPLAY_STACK_TIMEOUT_MS = 9e4;
|
|
1508
|
+
var PAINT_PROBE_ATTEMPTS = 150;
|
|
1509
|
+
var PAINT_PROBE_INTERVAL_S = 0.2;
|
|
1510
|
+
var PAINT_MIN_BYTES = 6e4;
|
|
1347
1511
|
var DEFAULT_DESKTOP_GEOMETRY = { width: 1280, height: 800, dpi: 96 };
|
|
1348
1512
|
var DisplayStackError = class extends Error {
|
|
1349
1513
|
exitCode;
|
|
1350
1514
|
stage;
|
|
1351
1515
|
constructor(exitCode, output) {
|
|
1352
|
-
const stage = exitCode === 11 ? "xvfb" : exitCode === 12 ? "x11vnc" : exitCode === 13 ? "websockify" : "unknown";
|
|
1516
|
+
const stage = exitCode === 11 ? "xvfb" : exitCode === 12 ? "x11vnc" : exitCode === 13 ? "websockify" : exitCode === 14 ? "paint" : "unknown";
|
|
1353
1517
|
super(`desktop display stack failed at stage "${stage}" (exit ${exitCode})${output ? `:
|
|
1354
1518
|
${output}` : ""}`);
|
|
1355
1519
|
this.name = "DisplayStackError";
|
|
@@ -1367,7 +1531,9 @@ function buildDisplayStackScript(options = {}) {
|
|
|
1367
1531
|
const geometry = options.geometry ?? DEFAULT_DESKTOP_GEOMETRY;
|
|
1368
1532
|
const port = options.port ?? DESKTOP_STREAM_PORT4;
|
|
1369
1533
|
const env = `DESKTOP_W=${geometry.width} DESKTOP_H=${geometry.height} DESKTOP_DPI=${geometry.dpi} STREAM_PORT=${port}`;
|
|
1370
|
-
|
|
1534
|
+
const bringUp = `if nc -z 127.0.0.1 ${port} >/dev/null 2>&1 && nc -z 127.0.0.1 5900 >/dev/null 2>&1; then echo "OPENGENI_DESKTOP_UP port=${port} geometry=${geometry.width}x${geometry.height} dpi=${geometry.dpi} (precheck)"; else mkdir -p /tmp/opengeni-desktop && flock -w 45 /tmp/opengeni-desktop/up.outer.lock env ${env} opengeni-desktop-up; fi`;
|
|
1535
|
+
const paintProbe = `p=/tmp/opengeni-desktop/paint-probe.png; for i in $(seq 1 ${PAINT_PROBE_ATTEMPTS}); do if DISPLAY=:0 scrot -o "$p" >/dev/null 2>&1; then sz=$(wc -c < "$p" 2>/dev/null || echo 0); else sz=0; fi; rm -f "$p"; if [ "$sz" -ge ${PAINT_MIN_BYTES} ]; then break; fi; if [ "$i" = "${PAINT_PROBE_ATTEMPTS}" ]; then echo "OPENGENI_DESKTOP_NOT_PAINTING scrot below ${PAINT_MIN_BYTES}B after warmup (last=$sz)"; exit 14; fi; sleep ${PAINT_PROBE_INTERVAL_S}; done`;
|
|
1536
|
+
return `mkdir -p /tmp/opengeni-desktop; { ${bringUp} ; } && { ${paintProbe} ; }`;
|
|
1371
1537
|
}
|
|
1372
1538
|
function execResultOutput(result) {
|
|
1373
1539
|
if (typeof result === "string") {
|
|
@@ -1382,6 +1548,9 @@ function execResultExitCode(result) {
|
|
|
1382
1548
|
return typeof result.exitCode === "number" ? result.exitCode : null;
|
|
1383
1549
|
}
|
|
1384
1550
|
function inferExitFromOutput(output) {
|
|
1551
|
+
if (/OPENGENI_DESKTOP_NOT_PAINTING/.test(output)) {
|
|
1552
|
+
return 14;
|
|
1553
|
+
}
|
|
1385
1554
|
if (/OPENGENI_DESKTOP_UP\b/.test(output)) {
|
|
1386
1555
|
return 0;
|
|
1387
1556
|
}
|
|
@@ -3289,6 +3458,21 @@ function readInstanceId(session) {
|
|
|
3289
3458
|
const candidate = state.sandboxId ?? state.instanceId ?? state.id ?? state.hostId ?? state.containerId;
|
|
3290
3459
|
return typeof candidate === "string" && candidate.length > 0 ? candidate : "";
|
|
3291
3460
|
}
|
|
3461
|
+
async function terminateCreatedSandbox(client, session, sessionState) {
|
|
3462
|
+
const clientWithDelete = client;
|
|
3463
|
+
if (typeof clientWithDelete.delete === "function" && sessionState !== void 0) {
|
|
3464
|
+
try {
|
|
3465
|
+
await clientWithDelete.delete(sessionState);
|
|
3466
|
+
} catch {
|
|
3467
|
+
}
|
|
3468
|
+
return;
|
|
3469
|
+
}
|
|
3470
|
+
const sess = session;
|
|
3471
|
+
try {
|
|
3472
|
+
await (sess.terminate ?? sess.kill ?? sess.close)?.();
|
|
3473
|
+
} catch {
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3292
3476
|
async function establishSandboxSessionFromEnvelope(settings, envelope, opts) {
|
|
3293
3477
|
const envelopeBackend = typeof envelope?.backendId === "string" ? envelope.backendId : void 0;
|
|
3294
3478
|
const backend = opts.backendOverride ?? envelopeBackend ?? settings.sandboxBackend;
|
|
@@ -3305,32 +3489,60 @@ async function establishSandboxSessionFromEnvelope(settings, envelope, opts) {
|
|
|
3305
3489
|
const workspaceArchive = readWorkspaceArchiveFromEnvelopeSessionState(envelopeSessionState);
|
|
3306
3490
|
const coldRestore = async (resumeFallbackState) => {
|
|
3307
3491
|
const restored = await client.create({ manifest: createManifest });
|
|
3492
|
+
let restoredState = restored.state;
|
|
3493
|
+
let established = {
|
|
3494
|
+
client,
|
|
3495
|
+
session: restored,
|
|
3496
|
+
sessionState: restoredState ?? resumeFallbackState,
|
|
3497
|
+
instanceId: readInstanceId(restored),
|
|
3498
|
+
backendId: client.backendId
|
|
3499
|
+
};
|
|
3500
|
+
if (opts.onSandboxCreated) {
|
|
3501
|
+
try {
|
|
3502
|
+
await opts.onSandboxCreated(established);
|
|
3503
|
+
} catch (createCallbackError) {
|
|
3504
|
+
await terminateCreatedSandbox(client, restored, restoredState);
|
|
3505
|
+
throw createCallbackError;
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3308
3508
|
if (workspaceArchive) {
|
|
3309
3509
|
const hydrate = restored.hydrateWorkspace;
|
|
3310
3510
|
if (typeof hydrate === "function") {
|
|
3311
3511
|
try {
|
|
3312
3512
|
await hydrate.call(restored, workspaceArchive);
|
|
3313
3513
|
} catch (hydrateError) {
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3514
|
+
await terminateCreatedSandbox(client, restored, restoredState);
|
|
3515
|
+
throw hydrateError;
|
|
3516
|
+
}
|
|
3517
|
+
const hydratedState = restored.state;
|
|
3518
|
+
const hydratedInstanceId = readInstanceId(restored);
|
|
3519
|
+
if (hydratedInstanceId && hydratedInstanceId !== established.instanceId) {
|
|
3520
|
+
established = {
|
|
3521
|
+
client,
|
|
3522
|
+
session: restored,
|
|
3523
|
+
sessionState: hydratedState ?? resumeFallbackState,
|
|
3524
|
+
instanceId: hydratedInstanceId,
|
|
3525
|
+
backendId: client.backendId
|
|
3526
|
+
};
|
|
3527
|
+
if (opts.onSandboxCreated) {
|
|
3323
3528
|
try {
|
|
3324
|
-
await
|
|
3325
|
-
} catch {
|
|
3529
|
+
await opts.onSandboxCreated(established);
|
|
3530
|
+
} catch (createCallbackError) {
|
|
3531
|
+
await terminateCreatedSandbox(client, restored, hydratedState);
|
|
3532
|
+
throw createCallbackError;
|
|
3326
3533
|
}
|
|
3327
3534
|
}
|
|
3328
|
-
throw hydrateError;
|
|
3329
3535
|
}
|
|
3330
3536
|
}
|
|
3331
3537
|
}
|
|
3332
|
-
|
|
3333
|
-
return {
|
|
3538
|
+
restoredState = restored.state;
|
|
3539
|
+
return {
|
|
3540
|
+
client,
|
|
3541
|
+
session: restored,
|
|
3542
|
+
sessionState: restoredState ?? resumeFallbackState,
|
|
3543
|
+
instanceId: readInstanceId(restored),
|
|
3544
|
+
backendId: client.backendId
|
|
3545
|
+
};
|
|
3334
3546
|
};
|
|
3335
3547
|
const envelopeProviderState = envelopeSessionState && typeof envelopeSessionState === "object" ? envelopeSessionState.providerState : void 0;
|
|
3336
3548
|
const hasResumableInstance = Boolean(
|
|
@@ -3388,6 +3600,11 @@ export {
|
|
|
3388
3600
|
assertDescriptorRegistryInvariants,
|
|
3389
3601
|
SandboxConfigError,
|
|
3390
3602
|
SandboxProviderUnavailableError,
|
|
3603
|
+
modalSandboxAttributionEnvironment,
|
|
3604
|
+
modalSandboxAttributionTags,
|
|
3605
|
+
tagModalSandbox,
|
|
3606
|
+
terminateModalSandboxById,
|
|
3607
|
+
sweepModalOrphanSandboxes,
|
|
3391
3608
|
subjectFor,
|
|
3392
3609
|
SelfhostedControlError,
|
|
3393
3610
|
agentErrorToControlError,
|
|
@@ -3475,4 +3692,4 @@ export {
|
|
|
3475
3692
|
collectSandboxEnvironment2 as collectSandboxEnvironment,
|
|
3476
3693
|
parseExposedPorts2 as parseExposedPorts
|
|
3477
3694
|
};
|
|
3478
|
-
//# sourceMappingURL=chunk-
|
|
3695
|
+
//# sourceMappingURL=chunk-D5KU3QUC.js.map
|