@toon-protocol/client-mcp 0.12.2 → 0.13.1
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/README.md +1 -1
- package/dist/{chunk-XVGREVEL.js → chunk-SL7UGVOC.js} +290 -3
- package/dist/chunk-SL7UGVOC.js.map +1 -0
- package/dist/{chunk-AYIP46A2.js → chunk-UXCFHAUC.js} +44 -5
- package/dist/chunk-UXCFHAUC.js.map +1 -0
- package/dist/{chunk-EAPVMGLZ.js → chunk-YVLSWU46.js} +1508 -10
- package/dist/chunk-YVLSWU46.js.map +1 -0
- package/dist/daemon.js +3 -3
- package/dist/daemon.js.map +1 -1
- package/dist/index.d.ts +303 -10
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +4 -4
- package/dist/mcp.js.map +1 -1
- package/package.json +5 -4
- package/dist/chunk-AYIP46A2.js.map +0 -1
- package/dist/chunk-EAPVMGLZ.js.map +0 -1
- package/dist/chunk-XVGREVEL.js.map +0 -1
package/README.md
CHANGED
|
@@ -274,7 +274,7 @@ receive a target-chain claim.
|
|
|
274
274
|
results**, never private keys.
|
|
275
275
|
- A single-instance PID lock prevents two daemons from racing the channel nonce
|
|
276
276
|
watermark (which would corrupt the payment proof).
|
|
277
|
-
- The control
|
|
277
|
+
- The control API binds `127.0.0.1` only (no auth layer — it never leaves
|
|
278
278
|
loopback).
|
|
279
279
|
|
|
280
280
|
## Tests
|
|
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
|
|
|
2
2
|
import {
|
|
3
3
|
ControlApiError,
|
|
4
4
|
DaemonUnreachableError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UXCFHAUC.js";
|
|
6
6
|
|
|
7
7
|
// ../views/dist/tool-names.js
|
|
8
8
|
var PUBLISH_TOOL = "toon_publish_unsigned";
|
|
@@ -727,6 +727,7 @@ var EXAMPLE_VIEWSPECS = [
|
|
|
727
727
|
];
|
|
728
728
|
|
|
729
729
|
// src/mcp-tools.ts
|
|
730
|
+
import { basename } from "path";
|
|
730
731
|
var TOOL_DEFINITIONS = [
|
|
731
732
|
{
|
|
732
733
|
name: "toon_status",
|
|
@@ -824,6 +825,167 @@ var TOOL_DEFINITIONS = [
|
|
|
824
825
|
additionalProperties: false
|
|
825
826
|
}
|
|
826
827
|
},
|
|
828
|
+
{
|
|
829
|
+
name: "toon_git_push",
|
|
830
|
+
description: "Pay-to-write: push a local git repository to TOON (NIP-34) \u2014 uploads the object delta to Arweave via the paid store and publishes the cumulative kind:30618 refs event (+ kind:30617 announcement on first push). The daemon identity is the repo owner. TWO-STEP, PAID + IRREVERSIBLE: ALWAYS call with dry_run:true first \u2014 it plans the push without paying and returns the itemized fee table (per-object upload fees + event fees). Quote estimate.totalFee to the user and get their explicit confirmation, THEN call again with confirm:true to execute; a push without confirm:true is rejected. Uploads and events are permanent and non-refundable (they cannot be unpublished).",
|
|
831
|
+
inputSchema: {
|
|
832
|
+
type: "object",
|
|
833
|
+
properties: {
|
|
834
|
+
repoPath: {
|
|
835
|
+
type: "string",
|
|
836
|
+
description: "Absolute path to the local git repository (worktree or .git dir) the daemon reads with git plumbing."
|
|
837
|
+
},
|
|
838
|
+
repoId: {
|
|
839
|
+
type: "string",
|
|
840
|
+
description: 'Repository identifier (NIP-34 `d` tag). Default: the basename of repoPath with any trailing `.git` stripped (so /repos/demo, /repos/demo/.git and /repos/demo.git all derive "demo").'
|
|
841
|
+
},
|
|
842
|
+
refspecs: {
|
|
843
|
+
type: "array",
|
|
844
|
+
items: { type: "string" },
|
|
845
|
+
description: 'Full refnames to push (e.g. ["refs/heads/main"]). Default: every local branch and tag.'
|
|
846
|
+
},
|
|
847
|
+
force: {
|
|
848
|
+
type: "boolean",
|
|
849
|
+
description: "Allow non-fast-forward updates (default false). A forced push abandons remote commits \u2014 get explicit user confirmation first."
|
|
850
|
+
},
|
|
851
|
+
relayUrls: {
|
|
852
|
+
type: "array",
|
|
853
|
+
items: { type: "string" },
|
|
854
|
+
description: "Relay URLs to read remote state from and publish to (default: the daemon's config-seeded relay)."
|
|
855
|
+
},
|
|
856
|
+
dry_run: {
|
|
857
|
+
type: "boolean",
|
|
858
|
+
description: "true \u2192 plan + price only (free, nothing is paid or published). Required first step before any real push."
|
|
859
|
+
},
|
|
860
|
+
confirm: {
|
|
861
|
+
type: "boolean",
|
|
862
|
+
description: "Must be literally true to execute the paid push \u2014 only after the user confirmed the dry_run fee quote."
|
|
863
|
+
}
|
|
864
|
+
},
|
|
865
|
+
required: ["repoPath"],
|
|
866
|
+
additionalProperties: false
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
name: "toon_git_issue",
|
|
871
|
+
description: "Pay-to-write: file a NIP-34 issue (kind:1621) against a TOON-hosted repo. One paid event publish. PAID + IRREVERSIBLE: quote the per-event fee (feePerEvent via toon_status / fee config) and confirm with the user before calling \u2014 events cannot be unpublished.",
|
|
872
|
+
inputSchema: {
|
|
873
|
+
type: "object",
|
|
874
|
+
properties: {
|
|
875
|
+
repoOwnerPubkey: {
|
|
876
|
+
type: "string",
|
|
877
|
+
description: "Repository owner's Nostr pubkey (64-char hex \u2014 the author of the kind:30617 announcement)."
|
|
878
|
+
},
|
|
879
|
+
repoId: {
|
|
880
|
+
type: "string",
|
|
881
|
+
description: "Repository identifier (NIP-34 `d` tag)."
|
|
882
|
+
},
|
|
883
|
+
title: { type: "string", description: "Issue title (`subject` tag)." },
|
|
884
|
+
body: { type: "string", description: "Issue body (Markdown)." },
|
|
885
|
+
labels: {
|
|
886
|
+
type: "array",
|
|
887
|
+
items: { type: "string" },
|
|
888
|
+
description: "Labels (`t` tags)."
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
required: ["repoOwnerPubkey", "repoId", "title", "body"],
|
|
892
|
+
additionalProperties: false
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
name: "toon_git_comment",
|
|
897
|
+
description: "Pay-to-write: comment (kind:1622) on a TOON-hosted issue or patch. One paid event publish. PAID + IRREVERSIBLE: quote the per-event fee (feePerEvent via toon_status / fee config) and confirm with the user before calling \u2014 events cannot be unpublished.",
|
|
898
|
+
inputSchema: {
|
|
899
|
+
type: "object",
|
|
900
|
+
properties: {
|
|
901
|
+
repoOwnerPubkey: {
|
|
902
|
+
type: "string",
|
|
903
|
+
description: "Repository owner's Nostr pubkey (64-char hex)."
|
|
904
|
+
},
|
|
905
|
+
repoId: {
|
|
906
|
+
type: "string",
|
|
907
|
+
description: "Repository identifier (NIP-34 `d` tag)."
|
|
908
|
+
},
|
|
909
|
+
rootEventId: {
|
|
910
|
+
type: "string",
|
|
911
|
+
description: "Event id of the issue or patch being commented on."
|
|
912
|
+
},
|
|
913
|
+
body: { type: "string", description: "Comment body (Markdown)." },
|
|
914
|
+
parentAuthorPubkey: {
|
|
915
|
+
type: "string",
|
|
916
|
+
description: "Pubkey of the TARGET event's author (NIP-34 `p` threading tag). Default: the repo owner."
|
|
917
|
+
},
|
|
918
|
+
marker: {
|
|
919
|
+
type: "string",
|
|
920
|
+
enum: ["root", "reply"],
|
|
921
|
+
description: "`e`-tag marker (default root: commenting directly on the issue/patch)."
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
required: ["repoOwnerPubkey", "repoId", "rootEventId", "body"],
|
|
925
|
+
additionalProperties: false
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
name: "toon_git_patch",
|
|
930
|
+
description: "Pay-to-write: submit a patch/PR (kind:1617, real `git format-patch` content) to a TOON-hosted repo. Supply EXACTLY ONE of patchText (literal format-patch output) or repoPath+range (the daemon runs format-patch locally). One paid event publish. PAID + IRREVERSIBLE: quote the per-event fee (feePerEvent via toon_status / fee config) and confirm with the user before calling \u2014 events cannot be unpublished.",
|
|
931
|
+
inputSchema: {
|
|
932
|
+
type: "object",
|
|
933
|
+
properties: {
|
|
934
|
+
repoOwnerPubkey: {
|
|
935
|
+
type: "string",
|
|
936
|
+
description: "Repository owner's Nostr pubkey (64-char hex)."
|
|
937
|
+
},
|
|
938
|
+
repoId: {
|
|
939
|
+
type: "string",
|
|
940
|
+
description: "Repository identifier (NIP-34 `d` tag)."
|
|
941
|
+
},
|
|
942
|
+
title: { type: "string", description: "Patch/PR title (`subject` tag)." },
|
|
943
|
+
patchText: {
|
|
944
|
+
type: "string",
|
|
945
|
+
description: "Literal `git format-patch` output. Mutually exclusive with repoPath+range."
|
|
946
|
+
},
|
|
947
|
+
repoPath: {
|
|
948
|
+
type: "string",
|
|
949
|
+
description: "Local repository to run format-patch in. Requires range."
|
|
950
|
+
},
|
|
951
|
+
range: {
|
|
952
|
+
type: "string",
|
|
953
|
+
description: "Revision range for format-patch (`<rev>`, `<rev>..<rev>`, `<rev>...<rev>`)."
|
|
954
|
+
},
|
|
955
|
+
branch: { type: "string", description: "Branch name for the `t` tag." }
|
|
956
|
+
},
|
|
957
|
+
required: ["repoOwnerPubkey", "repoId", "title"],
|
|
958
|
+
additionalProperties: false
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
name: "toon_git_status",
|
|
963
|
+
description: "Pay-to-write: set the status of a TOON-hosted issue or patch (kind:1630-1633: open/applied/closed/draft). One paid event publish. PAID + IRREVERSIBLE: quote the per-event fee (feePerEvent via toon_status / fee config) and confirm with the user before calling \u2014 events cannot be unpublished.",
|
|
964
|
+
inputSchema: {
|
|
965
|
+
type: "object",
|
|
966
|
+
properties: {
|
|
967
|
+
repoOwnerPubkey: {
|
|
968
|
+
type: "string",
|
|
969
|
+
description: "Repository owner's Nostr pubkey (64-char hex)."
|
|
970
|
+
},
|
|
971
|
+
repoId: {
|
|
972
|
+
type: "string",
|
|
973
|
+
description: "Repository identifier (NIP-34 `d` tag)."
|
|
974
|
+
},
|
|
975
|
+
targetEventId: {
|
|
976
|
+
type: "string",
|
|
977
|
+
description: "Event id of the issue/patch whose status is being set."
|
|
978
|
+
},
|
|
979
|
+
status: {
|
|
980
|
+
type: "string",
|
|
981
|
+
enum: ["open", "applied", "closed", "draft"],
|
|
982
|
+
description: "open \u2192 kind:1630, applied \u2192 1631, closed \u2192 1632, draft \u2192 1633."
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
required: ["repoOwnerPubkey", "repoId", "targetEventId", "status"],
|
|
986
|
+
additionalProperties: false
|
|
987
|
+
}
|
|
988
|
+
},
|
|
827
989
|
{
|
|
828
990
|
name: "toon_atoms",
|
|
829
991
|
description: "REQUIRED first call before any toon_render \u2014 returns the atom allowlist (ids + the kinds each renders, props, write actions) plus example ViewSpecs, for composing a view to pass to toon_render. Never guess atom ids or kinds; always fetch them here first. This is how you build the user a generative UI for their journey.",
|
|
@@ -1197,6 +1359,14 @@ var TOOL_ANNOTATIONS = {
|
|
|
1197
1359
|
toon_publish_unsigned: PAID_WRITE,
|
|
1198
1360
|
toon_upload: PAID_WRITE,
|
|
1199
1361
|
toon_swap: PAID_WRITE,
|
|
1362
|
+
// paid git writes (NIP-34): push uploads objects + publishes events; the
|
|
1363
|
+
// single-event tools each spend one channel claim. toon_git_push with
|
|
1364
|
+
// dry_run:true is a free estimate, but the tool as a whole is a paid write.
|
|
1365
|
+
toon_git_push: PAID_WRITE,
|
|
1366
|
+
toon_git_issue: PAID_WRITE,
|
|
1367
|
+
toon_git_comment: PAID_WRITE,
|
|
1368
|
+
toon_git_patch: PAID_WRITE,
|
|
1369
|
+
toon_git_status: PAID_WRITE,
|
|
1200
1370
|
// on-chain channel ops
|
|
1201
1371
|
toon_open_channel: { ...ONCHAIN_WRITE, idempotentHint: true },
|
|
1202
1372
|
// returns the existing channel if open
|
|
@@ -1250,6 +1420,71 @@ async function dispatchTool(client, name, args) {
|
|
|
1250
1420
|
return ok(
|
|
1251
1421
|
await client.uploadMedia(args)
|
|
1252
1422
|
);
|
|
1423
|
+
case "toon_git_push": {
|
|
1424
|
+
const req = {
|
|
1425
|
+
repoPath: String(args["repoPath"] ?? ""),
|
|
1426
|
+
repoId: typeof args["repoId"] === "string" && args["repoId"] !== "" ? args["repoId"] : defaultRepoId(String(args["repoPath"] ?? "")),
|
|
1427
|
+
...Array.isArray(args["refspecs"]) ? { refspecs: args["refspecs"].map(String) } : {},
|
|
1428
|
+
...typeof args["force"] === "boolean" ? { force: args["force"] } : {},
|
|
1429
|
+
...Array.isArray(args["relayUrls"]) ? { relayUrls: args["relayUrls"].map(String) } : {}
|
|
1430
|
+
};
|
|
1431
|
+
if (args["dry_run"] === true) {
|
|
1432
|
+
const plan = await client.gitEstimate(req);
|
|
1433
|
+
return okStructured(
|
|
1434
|
+
JSON.stringify(compactPushPlan(plan)) + `
|
|
1435
|
+
Quote estimate.totalFee (base units) to the user and get explicit confirmation before pushing with confirm:true \u2014 the push is permanent and non-refundable.`,
|
|
1436
|
+
plan
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
if (args["confirm"] !== true) {
|
|
1440
|
+
return err(
|
|
1441
|
+
`Refusing to push without confirm:true. Run toon_git_push with dry_run:true first (free), quote estimate.totalFee to the user, and only after their explicit confirmation call again with confirm:true. Pushes are PAID, permanent, and non-refundable.`
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
const pushed = await client.gitPush({ ...req, confirm: true });
|
|
1445
|
+
return okStructured(
|
|
1446
|
+
JSON.stringify(compactPushResult(pushed)),
|
|
1447
|
+
pushed
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
case "toon_git_issue":
|
|
1451
|
+
return ok(
|
|
1452
|
+
await client.gitIssue({
|
|
1453
|
+
repoAddr: gitRepoAddr(args),
|
|
1454
|
+
title: String(args["title"] ?? ""),
|
|
1455
|
+
body: String(args["body"] ?? ""),
|
|
1456
|
+
...Array.isArray(args["labels"]) ? { labels: args["labels"].map(String) } : {}
|
|
1457
|
+
})
|
|
1458
|
+
);
|
|
1459
|
+
case "toon_git_comment":
|
|
1460
|
+
return ok(
|
|
1461
|
+
await client.gitComment({
|
|
1462
|
+
repoAddr: gitRepoAddr(args),
|
|
1463
|
+
rootEventId: String(args["rootEventId"] ?? ""),
|
|
1464
|
+
body: String(args["body"] ?? ""),
|
|
1465
|
+
...typeof args["parentAuthorPubkey"] === "string" ? { parentAuthorPubkey: args["parentAuthorPubkey"] } : {},
|
|
1466
|
+
...args["marker"] === "root" || args["marker"] === "reply" ? { marker: args["marker"] } : {}
|
|
1467
|
+
})
|
|
1468
|
+
);
|
|
1469
|
+
case "toon_git_patch": {
|
|
1470
|
+
const req = {
|
|
1471
|
+
repoAddr: gitRepoAddr(args),
|
|
1472
|
+
title: String(args["title"] ?? ""),
|
|
1473
|
+
...typeof args["patchText"] === "string" ? { patchText: args["patchText"] } : {},
|
|
1474
|
+
...typeof args["repoPath"] === "string" ? { repoPath: args["repoPath"] } : {},
|
|
1475
|
+
...typeof args["range"] === "string" ? { range: args["range"] } : {},
|
|
1476
|
+
...typeof args["branch"] === "string" ? { branch: args["branch"] } : {}
|
|
1477
|
+
};
|
|
1478
|
+
return ok(await client.gitPatch(req));
|
|
1479
|
+
}
|
|
1480
|
+
case "toon_git_status":
|
|
1481
|
+
return ok(
|
|
1482
|
+
await client.gitStatus({
|
|
1483
|
+
repoAddr: gitRepoAddr(args),
|
|
1484
|
+
targetEventId: String(args["targetEventId"] ?? ""),
|
|
1485
|
+
status: String(args["status"] ?? "")
|
|
1486
|
+
})
|
|
1487
|
+
);
|
|
1253
1488
|
case "toon_atoms": {
|
|
1254
1489
|
const atomsPayload = { atoms: ATOM_CATALOG, examples: EXAMPLE_VIEWSPECS };
|
|
1255
1490
|
return okStructured(JSON.stringify(atomsPayload, null, 2), atomsPayload);
|
|
@@ -1386,7 +1621,7 @@ async function dispatchTool(client, name, args) {
|
|
|
1386
1621
|
} catch (e) {
|
|
1387
1622
|
if (e instanceof ControlApiError && e.status === 504 && name === "toon_balances") {
|
|
1388
1623
|
return err(
|
|
1389
|
-
`${e.detail ?? e.message} \u2014 the balances control
|
|
1624
|
+
`${e.detail ?? e.message} \u2014 the balances control API (:8787 GET /balances) stalled reading on-chain balances; the relay and apex are unaffected. Retry shortly.`
|
|
1390
1625
|
);
|
|
1391
1626
|
}
|
|
1392
1627
|
if (e instanceof ControlApiError && e.status === 504 && name === "toon_fund_wallet") {
|
|
@@ -1402,6 +1637,26 @@ async function dispatchTool(client, name, args) {
|
|
|
1402
1637
|
if (e instanceof ControlApiError && e.status === 402) {
|
|
1403
1638
|
return err(e.detail ?? e.message);
|
|
1404
1639
|
}
|
|
1640
|
+
if (e instanceof ControlApiError && e.message === "non_fast_forward") {
|
|
1641
|
+
return err(
|
|
1642
|
+
JSON.stringify({
|
|
1643
|
+
error: "non_fast_forward",
|
|
1644
|
+
detail: e.detail,
|
|
1645
|
+
refs: e.data?.["refs"] ?? [],
|
|
1646
|
+
hint: "The remote moved since the last known state. Re-run toon_git_push with dry_run:true and force:true to preview the forced update, get explicit user confirmation (a forced push abandons the remote commits), then push with force:true + confirm:true."
|
|
1647
|
+
})
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
if (e instanceof ControlApiError && e.message === "oversize_objects") {
|
|
1651
|
+
return err(
|
|
1652
|
+
JSON.stringify({
|
|
1653
|
+
error: "oversize_objects",
|
|
1654
|
+
detail: e.detail,
|
|
1655
|
+
objects: e.data?.["objects"] ?? [],
|
|
1656
|
+
hint: "These objects exceed the 95KB single-packet upload limit \u2014 a v1 hard error (paid blob storage for larger objects is the epic #222 follow-up, toon-client#235). Remove or shrink the listed paths (e.g. git filter-repo) and re-run the estimate."
|
|
1657
|
+
})
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1405
1660
|
if (e instanceof ControlApiError && e.retryable) {
|
|
1406
1661
|
return err(
|
|
1407
1662
|
`TOON client is still bootstrapping (the BTP session can take a few seconds) \u2014 retry shortly. (${e.message})`
|
|
@@ -1426,6 +1681,38 @@ function wrapList(key, res) {
|
|
|
1426
1681
|
}
|
|
1427
1682
|
return { [key]: [] };
|
|
1428
1683
|
}
|
|
1684
|
+
function defaultRepoId(repoPath) {
|
|
1685
|
+
const trimmed = repoPath.replace(/[/\\]+$/, "");
|
|
1686
|
+
const base = basename(trimmed);
|
|
1687
|
+
if (base === ".git") {
|
|
1688
|
+
return basename(trimmed.slice(0, -".git".length).replace(/[/\\]+$/, ""));
|
|
1689
|
+
}
|
|
1690
|
+
return base.length > ".git".length && base.endsWith(".git") ? base.slice(0, -".git".length) : base;
|
|
1691
|
+
}
|
|
1692
|
+
function gitRepoAddr(args) {
|
|
1693
|
+
return {
|
|
1694
|
+
ownerPubkey: String(args["repoOwnerPubkey"] ?? ""),
|
|
1695
|
+
repoId: String(args["repoId"] ?? "")
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
function compactPushPlan(plan) {
|
|
1699
|
+
const { objects, knownShaToTxId, newRefs, ...rest } = plan;
|
|
1700
|
+
return {
|
|
1701
|
+
...rest,
|
|
1702
|
+
newRefCount: Object.keys(newRefs).length,
|
|
1703
|
+
plannedObjectCount: objects.length,
|
|
1704
|
+
knownOnArweaveCount: Object.keys(knownShaToTxId).length
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
function compactPushResult(res) {
|
|
1708
|
+
const { uploads, arweaveMap, ...rest } = res;
|
|
1709
|
+
return {
|
|
1710
|
+
...rest,
|
|
1711
|
+
uploadCount: uploads.length,
|
|
1712
|
+
skippedUploadCount: uploads.filter((u) => u.skipped).length,
|
|
1713
|
+
arweaveMapSize: Object.keys(arweaveMap).length
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1429
1716
|
function ok(data) {
|
|
1430
1717
|
const result = {
|
|
1431
1718
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
@@ -1524,4 +1811,4 @@ export {
|
|
|
1524
1811
|
TOOL_DEFINITIONS,
|
|
1525
1812
|
dispatchTool
|
|
1526
1813
|
};
|
|
1527
|
-
//# sourceMappingURL=chunk-
|
|
1814
|
+
//# sourceMappingURL=chunk-SL7UGVOC.js.map
|