@cleocode/runtime 2026.6.14 → 2026.6.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/{chunk-Q7X4XH2V.js → chunk-U2ESNIJ5.js} +231 -26
- package/dist/chunk-U2ESNIJ5.js.map +1 -0
- package/dist/gateway/http/index.d.ts +1 -1
- package/dist/gateway/http/index.js +17 -3
- package/dist/gateway/index.d.ts +1 -1
- package/dist/gateway/index.js +18 -4
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/mcp/index.d.ts +1 -1
- package/dist/gateway/rpc/index.d.ts +155 -2
- package/package.json +3 -3
- package/dist/chunk-Q7X4XH2V.js.map +0 -1
|
@@ -258,6 +258,195 @@ function resolveStreamSource(domain, operation) {
|
|
|
258
258
|
return STREAM_SOURCES.get(streamSourceKey(domain, operation));
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
// src/gateway/http/studio-static.ts
|
|
262
|
+
import { createReadStream, existsSync } from "fs";
|
|
263
|
+
import { stat } from "fs/promises";
|
|
264
|
+
import { extname, join, resolve } from "path";
|
|
265
|
+
var log = {
|
|
266
|
+
info: (obj, msg) => {
|
|
267
|
+
if (process.env["CLEO_DEBUG_STUDIO"]) console.info("[studio-static]", msg, obj);
|
|
268
|
+
},
|
|
269
|
+
warn: (obj, msg) => {
|
|
270
|
+
if (process.env["NODE_ENV"] !== "test") {
|
|
271
|
+
console.warn("[studio-static]", msg, obj);
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
debug: (_obj, _msg) => {
|
|
275
|
+
if (process.env["CLEO_DEBUG_STUDIO"]) console.debug("[studio-static]", _msg, _obj);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
var MIME_TYPES = /* @__PURE__ */ new Map([
|
|
279
|
+
[".html", "text/html; charset=utf-8"],
|
|
280
|
+
[".js", "application/javascript; charset=utf-8"],
|
|
281
|
+
[".mjs", "application/javascript; charset=utf-8"],
|
|
282
|
+
[".css", "text/css; charset=utf-8"],
|
|
283
|
+
[".json", "application/json; charset=utf-8"],
|
|
284
|
+
[".png", "image/png"],
|
|
285
|
+
[".jpg", "image/jpeg"],
|
|
286
|
+
[".jpeg", "image/jpeg"],
|
|
287
|
+
[".svg", "image/svg+xml"],
|
|
288
|
+
[".ico", "image/x-icon"],
|
|
289
|
+
[".woff", "font/woff"],
|
|
290
|
+
[".woff2", "font/woff2"],
|
|
291
|
+
[".ttf", "font/ttf"],
|
|
292
|
+
[".map", "application/json; charset=utf-8"],
|
|
293
|
+
[".txt", "text/plain; charset=utf-8"],
|
|
294
|
+
[".webmanifest", "application/manifest+json; charset=utf-8"]
|
|
295
|
+
]);
|
|
296
|
+
var MIME_FALLBACK = "application/octet-stream";
|
|
297
|
+
var STUDIO_PREFIX = "/studio";
|
|
298
|
+
function isStudioPath(pathname) {
|
|
299
|
+
return pathname === STUDIO_PREFIX || pathname.startsWith(`${STUDIO_PREFIX}/`);
|
|
300
|
+
}
|
|
301
|
+
function resolveStudioAssetPath(staticDir, pathname) {
|
|
302
|
+
const base = resolve(staticDir);
|
|
303
|
+
const stripped = pathname.slice(STUDIO_PREFIX.length) || "/";
|
|
304
|
+
const relative = stripped.replace(/^[\\/]+/, "") || ".";
|
|
305
|
+
const candidate = resolve(base, relative);
|
|
306
|
+
const safeBase = base.endsWith("/") ? base : `${base}/`;
|
|
307
|
+
if (candidate !== base && !candidate.startsWith(safeBase)) {
|
|
308
|
+
return void 0;
|
|
309
|
+
}
|
|
310
|
+
return candidate;
|
|
311
|
+
}
|
|
312
|
+
function mimeType(filePath) {
|
|
313
|
+
return MIME_TYPES.get(extname(filePath).toLowerCase()) ?? MIME_FALLBACK;
|
|
314
|
+
}
|
|
315
|
+
function writeBundleAbsentError(res, hint) {
|
|
316
|
+
const body = JSON.stringify({
|
|
317
|
+
success: false,
|
|
318
|
+
error: {
|
|
319
|
+
code: "E_STUDIO_BUNDLE_ABSENT",
|
|
320
|
+
message: "CLEO Studio assets are not available. Run `pnpm --filter @cleocode/studio run build` to build them, or install from npm (npm i -g @cleocode/cleo) for a batteries-included release.",
|
|
321
|
+
hint: hint ?? null
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
res.writeHead(503, { "Content-Type": "application/json; charset=utf-8" });
|
|
325
|
+
res.end(body);
|
|
326
|
+
}
|
|
327
|
+
async function serveStudioStatic(req, res, opts) {
|
|
328
|
+
const pathname = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
329
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
330
|
+
res.writeHead(405, {
|
|
331
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
332
|
+
Allow: "GET, HEAD"
|
|
333
|
+
});
|
|
334
|
+
res.end(
|
|
335
|
+
JSON.stringify({
|
|
336
|
+
success: false,
|
|
337
|
+
error: {
|
|
338
|
+
code: "E_METHOD_NOT_ALLOWED",
|
|
339
|
+
message: `Method ${req.method ?? "<none>"} not allowed for /studio routes`
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (pathname === STUDIO_PREFIX) {
|
|
346
|
+
res.writeHead(302, { Location: `${STUDIO_PREFIX}/` });
|
|
347
|
+
res.end();
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const staticDir = opts.staticDir;
|
|
351
|
+
if (staticDir === void 0 || !existsSync(staticDir)) {
|
|
352
|
+
log.warn({ staticDir }, "studio static dir absent");
|
|
353
|
+
writeBundleAbsentError(res, staticDir);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const assetPath = resolveStudioAssetPath(staticDir, pathname);
|
|
357
|
+
if (assetPath === void 0) {
|
|
358
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
359
|
+
res.end(
|
|
360
|
+
JSON.stringify({
|
|
361
|
+
success: false,
|
|
362
|
+
error: { code: "E_BAD_REQUEST", message: "Invalid path" }
|
|
363
|
+
})
|
|
364
|
+
);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
let targetPath = assetPath;
|
|
368
|
+
let isSpaFallback = false;
|
|
369
|
+
try {
|
|
370
|
+
const st = await stat(targetPath);
|
|
371
|
+
if (st.isDirectory()) {
|
|
372
|
+
const dirIndex = join(targetPath, "index.html");
|
|
373
|
+
if (existsSync(dirIndex)) {
|
|
374
|
+
targetPath = dirIndex;
|
|
375
|
+
} else {
|
|
376
|
+
targetPath = join(staticDir, "index.html");
|
|
377
|
+
isSpaFallback = true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch {
|
|
381
|
+
targetPath = join(staticDir, "index.html");
|
|
382
|
+
isSpaFallback = true;
|
|
383
|
+
}
|
|
384
|
+
if (!existsSync(targetPath)) {
|
|
385
|
+
res.writeHead(503, { "Content-Type": "application/json; charset=utf-8" });
|
|
386
|
+
res.end(
|
|
387
|
+
JSON.stringify({
|
|
388
|
+
success: false,
|
|
389
|
+
error: {
|
|
390
|
+
code: "E_STUDIO_BUNDLE_CORRUPT",
|
|
391
|
+
message: "Studio index.html not found in the static bundle."
|
|
392
|
+
}
|
|
393
|
+
})
|
|
394
|
+
);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const ct = mimeType(targetPath);
|
|
398
|
+
if (req.method === "HEAD") {
|
|
399
|
+
try {
|
|
400
|
+
const st = await stat(targetPath);
|
|
401
|
+
res.writeHead(200, {
|
|
402
|
+
"Content-Type": ct,
|
|
403
|
+
"Content-Length": String(st.size),
|
|
404
|
+
...isSpaFallback ? {} : { "Cache-Control": "public, max-age=31536000, immutable" }
|
|
405
|
+
});
|
|
406
|
+
res.end();
|
|
407
|
+
} catch {
|
|
408
|
+
res.writeHead(404);
|
|
409
|
+
res.end();
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const st = await stat(targetPath);
|
|
415
|
+
res.writeHead(200, {
|
|
416
|
+
"Content-Type": ct,
|
|
417
|
+
"Content-Length": String(st.size),
|
|
418
|
+
// Immutable cache for fingerprinted assets; no cache for SPA fallback.
|
|
419
|
+
...isSpaFallback ? { "Cache-Control": "no-cache" } : {
|
|
420
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
const stream = createReadStream(targetPath);
|
|
424
|
+
stream.on("error", (err) => {
|
|
425
|
+
log.warn({ err, targetPath }, "studio static read error");
|
|
426
|
+
if (!res.writableEnded) res.end();
|
|
427
|
+
});
|
|
428
|
+
stream.pipe(res);
|
|
429
|
+
log.debug({ pathname, targetPath, isSpaFallback, ct }, "studio static served");
|
|
430
|
+
} catch (err) {
|
|
431
|
+
log.warn({ err, targetPath }, "studio static stat error");
|
|
432
|
+
if (!res.headersSent) {
|
|
433
|
+
res.writeHead(500, { "Content-Type": "application/json; charset=utf-8" });
|
|
434
|
+
res.end(
|
|
435
|
+
JSON.stringify({
|
|
436
|
+
success: false,
|
|
437
|
+
error: { code: "E_INTERNAL", message: "Failed to read Studio asset" }
|
|
438
|
+
})
|
|
439
|
+
);
|
|
440
|
+
} else if (!res.writableEnded) {
|
|
441
|
+
res.end();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function resolveStudioStaticDir(cleoPackageDir) {
|
|
446
|
+
const candidate = join(cleoPackageDir, "studio-dist", "client");
|
|
447
|
+
return existsSync(candidate) ? candidate : void 0;
|
|
448
|
+
}
|
|
449
|
+
|
|
261
450
|
// src/gateway/http/ws-pty.ts
|
|
262
451
|
import { createHash } from "crypto";
|
|
263
452
|
import { delimiter } from "path";
|
|
@@ -428,7 +617,7 @@ Content-Length: ${Buffer.byteLength(reason)}\r
|
|
|
428
617
|
}
|
|
429
618
|
function swallowLateSocketError() {
|
|
430
619
|
}
|
|
431
|
-
async function bridgePty(req, socket, opts,
|
|
620
|
+
async function bridgePty(req, socket, opts, log2, onTeardown) {
|
|
432
621
|
const key = req.headers["sec-websocket-key"];
|
|
433
622
|
socket.write(
|
|
434
623
|
`HTTP/1.1 101 Switching Protocols\r
|
|
@@ -480,7 +669,7 @@ Sec-WebSocket-Accept: ${computeAcceptKey(typeof key === "string" ? key : "")}\r
|
|
|
480
669
|
onTeardown();
|
|
481
670
|
} catch {
|
|
482
671
|
}
|
|
483
|
-
|
|
672
|
+
log2.debug({ code, reason }, "ws-pty session torn down");
|
|
484
673
|
};
|
|
485
674
|
const onFrame = (frame) => {
|
|
486
675
|
switch (frame.opcode) {
|
|
@@ -538,7 +727,7 @@ Sec-WebSocket-Accept: ${computeAcceptKey(typeof key === "string" ? key : "")}\r
|
|
|
538
727
|
env: scrubPtyEnv()
|
|
539
728
|
});
|
|
540
729
|
} catch (err) {
|
|
541
|
-
|
|
730
|
+
log2.warn({ err }, "ws-pty spawn failed");
|
|
542
731
|
teardown(1011, "PTY spawn failed");
|
|
543
732
|
return { teardown };
|
|
544
733
|
}
|
|
@@ -546,7 +735,7 @@ Sec-WebSocket-Accept: ${computeAcceptKey(typeof key === "string" ? key : "")}\r
|
|
|
546
735
|
if (dataSub && typeof dataSub.dispose === "function") disposables.push(dataSub);
|
|
547
736
|
const exitSub = pty.onExit(({ exitCode }) => teardown(1e3, `pty exited (${exitCode})`));
|
|
548
737
|
if (exitSub && typeof exitSub.dispose === "function") disposables.push(exitSub);
|
|
549
|
-
|
|
738
|
+
log2.debug({ shell }, "ws-pty session established");
|
|
550
739
|
return { teardown };
|
|
551
740
|
}
|
|
552
741
|
function tryParseControl(text) {
|
|
@@ -562,13 +751,13 @@ function tryParseControl(text) {
|
|
|
562
751
|
return void 0;
|
|
563
752
|
}
|
|
564
753
|
function attachWsPtyEndpoint(server, opts = {}) {
|
|
565
|
-
const
|
|
754
|
+
const log2 = getLogger("gateway-ws-pty");
|
|
566
755
|
const sessions = /* @__PURE__ */ new Set();
|
|
567
756
|
const onUpgrade = (req, socket, _head) => {
|
|
568
757
|
if (!isWsPtyPath(req.url)) return;
|
|
569
758
|
const decision = authorizeUpgrade(req, req.socket.remoteAddress, opts);
|
|
570
759
|
if (!decision.ok) {
|
|
571
|
-
|
|
760
|
+
log2.debug({ status: decision.status, reason: decision.reason }, "ws-pty upgrade rejected");
|
|
572
761
|
rejectUpgrade(socket, decision.status, decision.reason);
|
|
573
762
|
return;
|
|
574
763
|
}
|
|
@@ -579,11 +768,11 @@ function attachWsPtyEndpoint(server, opts = {}) {
|
|
|
579
768
|
deregistered = true;
|
|
580
769
|
if (session !== void 0) sessions.delete(session);
|
|
581
770
|
};
|
|
582
|
-
bridgePty(req, socket, opts,
|
|
771
|
+
bridgePty(req, socket, opts, log2, deregister).then((s) => {
|
|
583
772
|
session = s;
|
|
584
773
|
if (!deregistered) sessions.add(s);
|
|
585
774
|
}).catch((err) => {
|
|
586
|
-
|
|
775
|
+
log2.warn({ err }, "ws-pty bridge failed");
|
|
587
776
|
deregister();
|
|
588
777
|
if (!socket.destroyed) socket.destroy();
|
|
589
778
|
});
|
|
@@ -670,7 +859,7 @@ function parseHttpRoute(method, url) {
|
|
|
670
859
|
return { ok: true, kind: "unary", gateway, domain, operation };
|
|
671
860
|
}
|
|
672
861
|
function readBody(req) {
|
|
673
|
-
return new Promise((
|
|
862
|
+
return new Promise((resolve2, reject) => {
|
|
674
863
|
const chunks = [];
|
|
675
864
|
let size = 0;
|
|
676
865
|
req.on("data", (chunk) => {
|
|
@@ -682,7 +871,7 @@ function readBody(req) {
|
|
|
682
871
|
}
|
|
683
872
|
chunks.push(chunk);
|
|
684
873
|
});
|
|
685
|
-
req.on("end", () =>
|
|
874
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
|
|
686
875
|
req.on("error", reject);
|
|
687
876
|
});
|
|
688
877
|
}
|
|
@@ -742,12 +931,12 @@ function writeStreamError(res, domain, operation, requestId) {
|
|
|
742
931
|
res.writeHead(200, { ...SSE_HEADERS });
|
|
743
932
|
res.end(encodeStreamEvent(frame));
|
|
744
933
|
}
|
|
745
|
-
async function handleStreamRequest(req, res, domain, operation,
|
|
934
|
+
async function handleStreamRequest(req, res, domain, operation, log2) {
|
|
746
935
|
const requestId = randomUUID2();
|
|
747
936
|
const source = resolveStreamSource(domain, operation);
|
|
748
937
|
if (source === void 0) {
|
|
749
938
|
writeStreamError(res, domain, operation, requestId);
|
|
750
|
-
|
|
939
|
+
log2.warn({ domain, operation }, "sse stream has no registered source");
|
|
751
940
|
return;
|
|
752
941
|
}
|
|
753
942
|
const controller = new AbortController();
|
|
@@ -768,20 +957,25 @@ async function handleStreamRequest(req, res, domain, operation, log) {
|
|
|
768
957
|
if (done) break;
|
|
769
958
|
if (res.writableEnded) break;
|
|
770
959
|
if (!res.write(value)) {
|
|
771
|
-
await new Promise((
|
|
960
|
+
await new Promise((resolve2) => res.once("drain", resolve2));
|
|
772
961
|
}
|
|
773
962
|
}
|
|
774
963
|
} catch (err) {
|
|
775
|
-
|
|
964
|
+
log2.warn({ err, domain, operation }, "sse stream pipe failed");
|
|
776
965
|
} finally {
|
|
777
966
|
reader.releaseLock();
|
|
778
967
|
req.removeListener("close", onClose);
|
|
779
968
|
if (!controller.signal.aborted) controller.abort();
|
|
780
969
|
if (!res.writableEnded) res.end();
|
|
781
|
-
|
|
970
|
+
log2.debug({ domain, operation, requestId }, "sse stream closed");
|
|
782
971
|
}
|
|
783
972
|
}
|
|
784
|
-
async function handleHttpRequest(handler, req, res,
|
|
973
|
+
async function handleHttpRequest(handler, req, res, log2, studioOpts) {
|
|
974
|
+
const pathname = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
975
|
+
if (studioOpts !== void 0 && isStudioPath(pathname)) {
|
|
976
|
+
await serveStudioStatic(req, res, studioOpts);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
785
979
|
const route = parseHttpRoute(req.method, req.url);
|
|
786
980
|
if (!route.ok) {
|
|
787
981
|
writeEdgeError(res, route.status, route.code, route.message);
|
|
@@ -789,11 +983,11 @@ async function handleHttpRequest(handler, req, res, log) {
|
|
|
789
983
|
}
|
|
790
984
|
if (route.kind === "health") {
|
|
791
985
|
writeHealth(res);
|
|
792
|
-
|
|
986
|
+
log2.debug({ route: "health" }, "http health probe served");
|
|
793
987
|
return;
|
|
794
988
|
}
|
|
795
989
|
if (route.kind === "stream") {
|
|
796
|
-
await handleStreamRequest(req, res, route.domain, route.operation,
|
|
990
|
+
await handleStreamRequest(req, res, route.domain, route.operation, log2);
|
|
797
991
|
return;
|
|
798
992
|
}
|
|
799
993
|
let params;
|
|
@@ -821,7 +1015,7 @@ async function handleHttpRequest(handler, req, res, log) {
|
|
|
821
1015
|
const result = await routeUnary(handler, unary);
|
|
822
1016
|
res.writeHead(result.status, { "Content-Type": "application/json" });
|
|
823
1017
|
res.end(JSON.stringify(result.body));
|
|
824
|
-
|
|
1018
|
+
log2.debug(
|
|
825
1019
|
{
|
|
826
1020
|
gateway: route.gateway,
|
|
827
1021
|
domain: route.domain,
|
|
@@ -832,12 +1026,16 @@ async function handleHttpRequest(handler, req, res, log) {
|
|
|
832
1026
|
);
|
|
833
1027
|
}
|
|
834
1028
|
function startHttpServer(handler, opts) {
|
|
835
|
-
const
|
|
1029
|
+
const log2 = getLogger2("gateway-http");
|
|
836
1030
|
const host = opts.host ?? "127.0.0.1";
|
|
1031
|
+
const studioOpts = opts.studioStaticDir !== void 0 ? { staticDir: opts.studioStaticDir } : void 0;
|
|
1032
|
+
if (studioOpts !== void 0) {
|
|
1033
|
+
log2.info({ studioStaticDir: opts.studioStaticDir }, "studio static serving enabled at /studio");
|
|
1034
|
+
}
|
|
837
1035
|
const server = createServer((req, res) => {
|
|
838
|
-
handleHttpRequest(handler, req, res,
|
|
1036
|
+
handleHttpRequest(handler, req, res, log2, studioOpts).catch((err) => {
|
|
839
1037
|
const message = err instanceof Error ? err.message : String(err);
|
|
840
|
-
|
|
1038
|
+
log2.warn({ err }, "http request handling failed");
|
|
841
1039
|
if (!res.headersSent) {
|
|
842
1040
|
writeEdgeError(res, 500, "E_HTTP_INTERNAL", message);
|
|
843
1041
|
} else if (!res.writableEnded) {
|
|
@@ -846,13 +1044,13 @@ function startHttpServer(handler, opts) {
|
|
|
846
1044
|
});
|
|
847
1045
|
});
|
|
848
1046
|
const wsPty = opts.wsPty !== void 0 ? attachWsPtyEndpoint(server, opts.wsPty) : void 0;
|
|
849
|
-
return new Promise((
|
|
1047
|
+
return new Promise((resolve2, reject) => {
|
|
850
1048
|
server.once("error", reject);
|
|
851
1049
|
server.listen(opts.port, host, () => {
|
|
852
1050
|
server.removeListener("error", reject);
|
|
853
1051
|
const address = server.address();
|
|
854
1052
|
const boundPort = typeof address === "object" && address !== null ? address.port : opts.port;
|
|
855
|
-
|
|
1053
|
+
log2.info(
|
|
856
1054
|
{ host, port: boundPort, wsPty: wsPty !== void 0 },
|
|
857
1055
|
"http gateway server listening"
|
|
858
1056
|
);
|
|
@@ -871,7 +1069,7 @@ function startHttpServer(handler, opts) {
|
|
|
871
1069
|
});
|
|
872
1070
|
}
|
|
873
1071
|
};
|
|
874
|
-
|
|
1072
|
+
resolve2(handle);
|
|
875
1073
|
});
|
|
876
1074
|
});
|
|
877
1075
|
}
|
|
@@ -886,6 +1084,13 @@ export {
|
|
|
886
1084
|
tickStreamSource,
|
|
887
1085
|
registerStreamSource,
|
|
888
1086
|
resolveStreamSource,
|
|
1087
|
+
STUDIO_PREFIX,
|
|
1088
|
+
isStudioPath,
|
|
1089
|
+
resolveStudioAssetPath,
|
|
1090
|
+
mimeType,
|
|
1091
|
+
writeBundleAbsentError,
|
|
1092
|
+
serveStudioStatic,
|
|
1093
|
+
resolveStudioStaticDir,
|
|
889
1094
|
WS_PTY_PATH,
|
|
890
1095
|
NODE_PTY_INSTALL_HINT,
|
|
891
1096
|
loadNodePty,
|
|
@@ -899,4 +1104,4 @@ export {
|
|
|
899
1104
|
parseHttpRoute,
|
|
900
1105
|
startHttpServer
|
|
901
1106
|
};
|
|
902
|
-
//# sourceMappingURL=chunk-
|
|
1107
|
+
//# sourceMappingURL=chunk-U2ESNIJ5.js.map
|