@xtrable-ltd/nanoesis 0.1.20 → 0.1.22

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.
Files changed (31) hide show
  1. package/dist/adapter-azure-blob.d.ts +12 -3
  2. package/dist/adapter-azure-blob.js +16 -1
  3. package/dist/adapter-fs.d.ts +11 -2
  4. package/dist/adapter-fs.js +14 -1
  5. package/dist/{chunk-26VCV3IE.js → chunk-SHGYQTZ7.js} +317 -203
  6. package/dist/editor-api.d.ts +102 -2
  7. package/dist/editor-api.js +7 -1
  8. package/dist/index.d.ts +17 -1
  9. package/dist/mcp.js +2 -2
  10. package/editor/assets/{MigrationsPane-BW2LrxHZ.js → MigrationsPane-BuDy6yfL.js} +1 -1
  11. package/editor/assets/{TemplatesPane-Ac_wIaRF.js → TemplatesPane-B0p3MU6k.js} +7 -7
  12. package/editor/assets/{cssMode-cUeZlGLn.js → cssMode-QvwrYsqW.js} +1 -1
  13. package/editor/assets/{freemarker2-D3MVrofV.js → freemarker2-Dv-zJ_IC.js} +1 -1
  14. package/editor/assets/{handlebars-CXQ8CbWL.js → handlebars-C1cz1CE1.js} +1 -1
  15. package/editor/assets/{html-BwjQWZKo.js → html-Bzw5MVgw.js} +1 -1
  16. package/editor/assets/{htmlMode-BiXiNHEB.js → htmlMode-BVD0ozaS.js} +1 -1
  17. package/editor/assets/index-B55NpHIl.css +7 -0
  18. package/editor/assets/{index-C_48fmnL.js → index-CLQXsAlc.js} +3 -3
  19. package/editor/assets/{javascript-CtdcxuAO.js → javascript-CmaCtnu1.js} +1 -1
  20. package/editor/assets/{jsonMode-a-sHGDu1.js → jsonMode-CDzmaDvb.js} +1 -1
  21. package/editor/assets/{liquid-DOcONXN7.js → liquid-DelfLp1i.js} +1 -1
  22. package/editor/assets/{mdx-DX8hBNLk.js → mdx-BfB8QRKT.js} +1 -1
  23. package/editor/assets/{python-Ctckq5kU.js → python-ZH9O8Nnr.js} +1 -1
  24. package/editor/assets/{razor-BWm5ktlO.js → razor-CpsZTRhn.js} +1 -1
  25. package/editor/assets/{tsMode-PwP0Hlwi.js → tsMode-DnMjBpsC.js} +1 -1
  26. package/editor/assets/{typescript-BqxeJCPE.js → typescript-Cb9pXSMO.js} +1 -1
  27. package/editor/assets/{xml-P0PtTkph.js → xml-1ERPoWKr.js} +1 -1
  28. package/editor/assets/{yaml-Dq7ZSJMS.js → yaml-YVqB6TxT.js} +1 -1
  29. package/editor/index.html +2 -2
  30. package/package.json +1 -1
  31. package/editor/assets/index-BzxvxQfA.css +0 -7
@@ -1,4 +1,5 @@
1
1
  import {
2
+ IndexedStore,
2
3
  analyzeTemplate,
3
4
  applyMigration,
4
5
  baseTemplateName,
@@ -12,6 +13,7 @@ import {
12
13
  loadComponents,
13
14
  loadTemplate,
14
15
  pendingMigrations,
16
+ publishSite,
15
17
  renderReferenceMarkdown,
16
18
  validateSite,
17
19
  workingStoreRoundTripDiagnostic
@@ -633,202 +635,6 @@ function authorDirectory(users) {
633
635
  };
634
636
  }
635
637
 
636
- // ../editor-api/src/mcp.ts
637
- function str(args, key) {
638
- const value = args[key];
639
- return typeof value === "string" ? value : "";
640
- }
641
- function request(method, path, query, bodyText, token) {
642
- const body = bodyText === void 0 ? new Uint8Array() : new TextEncoder().encode(bodyText);
643
- return {
644
- method,
645
- path,
646
- query: new URLSearchParams(query),
647
- getHeader: (name) => name.toLowerCase() === "authorization" && token !== void 0 ? `Bearer ${token}` : void 0,
648
- body: async () => body
649
- };
650
- }
651
- var pathArg = {
652
- type: "string",
653
- description: 'Site-relative path with forward slashes, e.g. "content/blog/post.json".'
654
- };
655
- var TOOL_SPECS = [
656
- {
657
- name: "list_dir",
658
- description: "List the immediate children (files and folders) of a site directory. Use '' or omit for the site root; the top-level folders are content/ (pages), templates/, components/, and public/.",
659
- inputSchema: {
660
- type: "object",
661
- properties: {
662
- path: { type: "string", description: "Directory path, '' for the site root." }
663
- },
664
- additionalProperties: false
665
- },
666
- toRequest: (args, token) => request("GET", "/api/list", { dir: str(args, "path") }, void 0, token)
667
- },
668
- {
669
- name: "read_file",
670
- description: "Read a file's text contents (content JSON, a template/component's HTML, CSS, etc.). Reports an error if the file does not exist.",
671
- inputSchema: {
672
- type: "object",
673
- properties: { path: pathArg },
674
- required: ["path"],
675
- additionalProperties: false
676
- },
677
- toRequest: (args, token) => request("GET", "/api/read", { path: str(args, "path") }, void 0, token)
678
- },
679
- {
680
- name: "describe_template",
681
- description: "List the fields a template defines (name, type, label, whether required, length limits), so you know what a page using it needs. Pass the template name without its path or extension, e.g. 'article' for templates/article.html.",
682
- inputSchema: {
683
- type: "object",
684
- properties: {
685
- name: {
686
- type: "string",
687
- description: 'Template name without path or extension, e.g. "article".'
688
- }
689
- },
690
- required: ["name"],
691
- additionalProperties: false
692
- },
693
- toRequest: (args, token) => request("GET", "/api/template-fields", { name: str(args, "name") }, void 0, token)
694
- },
695
- {
696
- name: "write_file",
697
- description: "Create or overwrite a text file. content/ requires the author role; templates/, components/, and public/ require the developer role. Read the nanoesis://reference resource first for the authoring syntax. For template/component paths the response includes `schemaDelta` (added/removed/typeChanged fields) \u2014 if `typeChanged` contains `destructive: true` or `removed` is non-empty, an auto-stamp has been written and authored content may need migration, so surface to the user before further edits. For content/*.json writes the response may include `parseDiagnostics.unknownTopLevelKeys` \u2014 top-level keys the parser dropped because they are not system keys; content values belong under `fields` (system keys are case-sensitive: `isPublished`, not `IsPublished`). See the reference's 'Authoring guardrails for LLMs' section for the common pitfalls (notably: `data-*` annotations bind to the element, not the token inside it \u2014 moving a token out of its annotated container silently strips them).",
698
- inputSchema: {
699
- type: "object",
700
- properties: {
701
- path: pathArg,
702
- contents: { type: "string", description: "The full new text contents of the file." }
703
- },
704
- required: ["path", "contents"],
705
- additionalProperties: false
706
- },
707
- toRequest: (args, token) => request("POST", "/api/write", { path: str(args, "path") }, str(args, "contents"), token)
708
- },
709
- {
710
- name: "delete_path",
711
- description: "Delete a file or a whole folder subtree. Same role rules as write_file. Deleting a path that does not exist is a no-op.",
712
- inputSchema: {
713
- type: "object",
714
- properties: { path: pathArg },
715
- required: ["path"],
716
- additionalProperties: false
717
- },
718
- toRequest: (args, token) => request("POST", "/api/delete", { path: str(args, "path") }, void 0, token)
719
- },
720
- {
721
- name: "rename_path",
722
- description: "Move or rename a file or folder subtree. Refuses to overwrite an existing destination. Requires write rights at both the source and the destination. Renaming a template also requires updating every content item bound to the old name (the validation gate will catch unbound items on publish, but `list_pending_migrations` is the cheaper check).",
723
- inputSchema: {
724
- type: "object",
725
- properties: {
726
- from: { type: "string", description: "The current path." },
727
- to: { type: "string", description: "The new path." }
728
- },
729
- required: ["from", "to"],
730
- additionalProperties: false
731
- },
732
- toRequest: (args, token) => request(
733
- "POST",
734
- "/api/rename",
735
- { from: str(args, "from"), to: str(args, "to") },
736
- void 0,
737
- token
738
- )
739
- },
740
- {
741
- name: "validate",
742
- description: "Run the validation gate over the whole site and return its diagnostics, without publishing. Use it to check your work: errors would block a publish, warnings (e.g. length constraints) only inform.",
743
- inputSchema: { type: "object", properties: {}, additionalProperties: false },
744
- toRequest: (_args, token) => request("GET", "/api/validate", {}, void 0, token)
745
- },
746
- {
747
- name: "publish",
748
- description: "Compile and publish the whole site. Runs the validation gate first; if anything would break, it reports the problems and writes nothing.",
749
- inputSchema: { type: "object", properties: {}, additionalProperties: false },
750
- toRequest: (_args, token) => request("POST", "/api/publish", {}, void 0, token)
751
- },
752
- {
753
- name: "list_pending_migrations",
754
- description: "List content items whose JSON fields don't match their bound template's schema (orphan fields or missing required fields). Use this after a destructive template edit (you'll see a `stamped` record on write_file) to see what needs migrating, or any time to check site-wide drift.",
755
- inputSchema: { type: "object", properties: {}, additionalProperties: false },
756
- toRequest: (_args, token) => request("GET", "/api/migrations", {}, void 0, token)
757
- },
758
- {
759
- name: "preview_migration",
760
- description: 'For one content item, return the current template HTML, the best-fit snapshot HTML (the "before"), the list of orphan fields with their values, and the names of fields the current template defines. Use this to plan how to migrate (drop, rename, or keep each orphan).',
761
- inputSchema: {
762
- type: "object",
763
- properties: { path: pathArg },
764
- required: ["path"],
765
- additionalProperties: false
766
- },
767
- toRequest: (args, token) => request("GET", "/api/migrations/preview", { path: str(args, "path") }, void 0, token)
768
- },
769
- {
770
- name: "apply_migration",
771
- description: "Resolve a content item's schema drift in one go. `resolution` is a JSON object: `drop` (array of orphan field names to delete), `rename` (object mapping orphan field name \u2192 current template field name to copy the value into), `keep` (array of orphan field names to leave untouched), and `fill` (object mapping current template field name \u2192 value, for missing required fields). Returns the refreshed pending list.",
772
- inputSchema: {
773
- type: "object",
774
- properties: {
775
- path: pathArg,
776
- resolution: {
777
- type: "string",
778
- description: 'A JSON object encoded as a string: { "drop": string[], "rename": { from: to }, "keep": string[], "fill": { name: value } }.'
779
- }
780
- },
781
- required: ["path", "resolution"],
782
- additionalProperties: false
783
- },
784
- toRequest: (args, token) => request(
785
- "POST",
786
- "/api/migrations/apply",
787
- {},
788
- JSON.stringify({
789
- path: str(args, "path"),
790
- resolution: parseResolutionArg(str(args, "resolution"))
791
- }),
792
- token
793
- )
794
- }
795
- ];
796
- function parseResolutionArg(raw) {
797
- if (raw === "") return {};
798
- try {
799
- return JSON.parse(raw);
800
- } catch {
801
- return {};
802
- }
803
- }
804
- var MCP_TOOLS = TOOL_SPECS.map(
805
- ({ name, description, inputSchema }) => ({ name, description, inputSchema })
806
- );
807
- function toResult(response) {
808
- const text = typeof response.body === "string" ? response.body : response.body === void 0 ? "" : new TextDecoder().decode(response.body);
809
- return { text, isError: response.status >= 400 };
810
- }
811
- async function callMcpTool(deps, name, args = {}, options = {}) {
812
- const spec = TOOL_SPECS.find((tool) => tool.name === name);
813
- if (spec === void 0) return { text: `Unknown tool: ${name}`, isError: true };
814
- return toResult(await handleApi(deps, spec.toRequest(args, options.token)));
815
- }
816
- var REFERENCE_URI = "nanoesis://reference";
817
- var MCP_RESOURCES = [
818
- {
819
- uri: REFERENCE_URI,
820
- name: "nanoesis authoring reference",
821
- description: "The generated reference for nanoesis templates and content: tokens, field types, annotations, loops, components, and the document shell. Read this before writing templates or content.",
822
- mimeType: "text/markdown"
823
- }
824
- ];
825
- function readMcpResource(uri) {
826
- if (uri === REFERENCE_URI) {
827
- return { text: renderReferenceMarkdown(buildAuthoringReference()), mimeType: "text/markdown" };
828
- }
829
- return void 0;
830
- }
831
-
832
638
  // ../editor-api/src/diagnose.ts
833
639
  function buildDefaultDiagnostics() {
834
640
  const registry = createDiagnosticRegistry();
@@ -1101,8 +907,313 @@ async function anyPublishedContentItem(store, dir = "content") {
1101
907
  return false;
1102
908
  }
1103
909
 
910
+ // ../editor-api/src/create-editor.ts
911
+ function asSink(storage) {
912
+ const encoder = new TextEncoder();
913
+ return {
914
+ write: (path, contents) => storage.put(path, typeof contents === "string" ? encoder.encode(contents) : contents)
915
+ };
916
+ }
917
+ function createEditor(config) {
918
+ const working = new IndexedStore(config.editorFiles);
919
+ const sink = asSink(config.website);
920
+ const wipeBeforePublish = config.wipeBeforePublish ?? true;
921
+ const reconcile = config.enumerate === void 0 ? void 0 : async () => working.reconcile([...await config.enumerate()]);
922
+ const publish = async () => {
923
+ const validation = await validateSite(working);
924
+ if (validation.ok && wipeBeforePublish && config.website.wipe !== void 0) {
925
+ await config.website.wipe();
926
+ }
927
+ const dir = config.users === void 0 ? void 0 : authorDirectory(await config.users());
928
+ const prebuild = typeof config.prebuild === "function" ? await config.prebuild() : config.prebuild;
929
+ return publishSite(working, sink, {
930
+ ...config.images !== void 0 && { imageEncoder: config.images },
931
+ ...config.purge !== void 0 && { purge: config.purge },
932
+ ...config.baseUrl !== void 0 && { baseUrl: config.baseUrl },
933
+ ...dir !== void 0 && { authorDirectory: dir },
934
+ ...prebuild !== void 0 && { prebuild }
935
+ });
936
+ };
937
+ const users = config.users;
938
+ const deps = {
939
+ store: working,
940
+ identity: config.login,
941
+ publish,
942
+ diagnostics: config.diagnostics ?? buildDefaultDiagnostics(),
943
+ ...reconcile !== void 0 && { reconcile },
944
+ ...config.authEndpoints !== void 0 && { authEndpoints: config.authEndpoints },
945
+ ...config.userAdmin !== void 0 && { userAdmin: config.userAdmin },
946
+ ...config.branding !== void 0 && { branding: config.branding },
947
+ ...users !== void 0 && { authors: async () => authorOptions(await users()) }
948
+ };
949
+ return {
950
+ handleApi: (req) => handleApi(deps, req),
951
+ publish,
952
+ store: working,
953
+ deps
954
+ };
955
+ }
956
+ function devNoAuth() {
957
+ console.warn(
958
+ "[nanoesis] devNoAuth(): every request is treated as an admin. Do not use in production."
959
+ );
960
+ return {
961
+ authenticate: async () => ({
962
+ userId: "dev",
963
+ username: "dev",
964
+ roles: ["author", "developer", "admin"]
965
+ })
966
+ };
967
+ }
968
+
969
+ // ../editor-api/src/serve-asset.ts
970
+ import { readFile } from "fs/promises";
971
+ import { resolve, sep } from "path";
972
+ var MIME = {
973
+ ".html": "text/html; charset=utf-8",
974
+ ".js": "application/javascript; charset=utf-8",
975
+ ".mjs": "application/javascript; charset=utf-8",
976
+ ".css": "text/css; charset=utf-8",
977
+ ".json": "application/json; charset=utf-8",
978
+ ".svg": "image/svg+xml",
979
+ ".png": "image/png",
980
+ ".jpg": "image/jpeg",
981
+ ".jpeg": "image/jpeg",
982
+ ".ico": "image/x-icon",
983
+ ".woff": "font/woff",
984
+ ".woff2": "font/woff2",
985
+ ".map": "application/json; charset=utf-8"
986
+ };
987
+ function mimeFor(path) {
988
+ const dot = path.lastIndexOf(".");
989
+ return dot < 0 ? "application/octet-stream" : MIME[path.slice(dot).toLowerCase()] ?? "application/octet-stream";
990
+ }
991
+ async function serveEditorAsset(distDir, pathname, options = {}) {
992
+ if (pathname === "/config.json" && options.config !== void 0) {
993
+ return {
994
+ status: 200,
995
+ headers: { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" },
996
+ body: JSON.stringify(options.config)
997
+ };
998
+ }
999
+ const base = resolve(distDir);
1000
+ const requested = pathname === "/" || pathname === "" ? "/index.html" : pathname;
1001
+ const filePath = resolve(base, requested.replace(/^\/+/, ""));
1002
+ if (filePath !== base && !filePath.startsWith(base + sep)) {
1003
+ return { status: 403, headers: { "content-type": "text/plain" }, body: "Forbidden" };
1004
+ }
1005
+ try {
1006
+ const bytes = new Uint8Array(await readFile(filePath));
1007
+ return { status: 200, headers: { "content-type": mimeFor(filePath) }, body: bytes };
1008
+ } catch (error) {
1009
+ if (error.code !== "ENOENT") throw error;
1010
+ const indexBytes = new Uint8Array(await readFile(resolve(base, "index.html")));
1011
+ return {
1012
+ status: 200,
1013
+ headers: { "content-type": "text/html; charset=utf-8" },
1014
+ body: indexBytes
1015
+ };
1016
+ }
1017
+ }
1018
+
1019
+ // ../editor-api/src/mcp.ts
1020
+ function str(args, key) {
1021
+ const value = args[key];
1022
+ return typeof value === "string" ? value : "";
1023
+ }
1024
+ function request(method, path, query, bodyText, token) {
1025
+ const body = bodyText === void 0 ? new Uint8Array() : new TextEncoder().encode(bodyText);
1026
+ return {
1027
+ method,
1028
+ path,
1029
+ query: new URLSearchParams(query),
1030
+ getHeader: (name) => name.toLowerCase() === "authorization" && token !== void 0 ? `Bearer ${token}` : void 0,
1031
+ body: async () => body
1032
+ };
1033
+ }
1034
+ var pathArg = {
1035
+ type: "string",
1036
+ description: 'Site-relative path with forward slashes, e.g. "content/blog/post.json".'
1037
+ };
1038
+ var TOOL_SPECS = [
1039
+ {
1040
+ name: "list_dir",
1041
+ description: "List the immediate children (files and folders) of a site directory. Use '' or omit for the site root; the top-level folders are content/ (pages), templates/, components/, and public/.",
1042
+ inputSchema: {
1043
+ type: "object",
1044
+ properties: {
1045
+ path: { type: "string", description: "Directory path, '' for the site root." }
1046
+ },
1047
+ additionalProperties: false
1048
+ },
1049
+ toRequest: (args, token) => request("GET", "/api/list", { dir: str(args, "path") }, void 0, token)
1050
+ },
1051
+ {
1052
+ name: "read_file",
1053
+ description: "Read a file's text contents (content JSON, a template/component's HTML, CSS, etc.). Reports an error if the file does not exist.",
1054
+ inputSchema: {
1055
+ type: "object",
1056
+ properties: { path: pathArg },
1057
+ required: ["path"],
1058
+ additionalProperties: false
1059
+ },
1060
+ toRequest: (args, token) => request("GET", "/api/read", { path: str(args, "path") }, void 0, token)
1061
+ },
1062
+ {
1063
+ name: "describe_template",
1064
+ description: "List the fields a template defines (name, type, label, whether required, length limits), so you know what a page using it needs. Pass the template name without its path or extension, e.g. 'article' for templates/article.html.",
1065
+ inputSchema: {
1066
+ type: "object",
1067
+ properties: {
1068
+ name: {
1069
+ type: "string",
1070
+ description: 'Template name without path or extension, e.g. "article".'
1071
+ }
1072
+ },
1073
+ required: ["name"],
1074
+ additionalProperties: false
1075
+ },
1076
+ toRequest: (args, token) => request("GET", "/api/template-fields", { name: str(args, "name") }, void 0, token)
1077
+ },
1078
+ {
1079
+ name: "write_file",
1080
+ description: "Create or overwrite a text file. content/ requires the author role; templates/, components/, and public/ require the developer role. Read the nanoesis://reference resource first for the authoring syntax. For template/component paths the response includes `schemaDelta` (added/removed/typeChanged fields) \u2014 if `typeChanged` contains `destructive: true` or `removed` is non-empty, an auto-stamp has been written and authored content may need migration, so surface to the user before further edits. For content/*.json writes the response may include `parseDiagnostics.unknownTopLevelKeys` \u2014 top-level keys the parser dropped because they are not system keys; content values belong under `fields` (system keys are case-sensitive: `isPublished`, not `IsPublished`). See the reference's 'Authoring guardrails for LLMs' section for the common pitfalls (notably: `data-*` annotations bind to the element, not the token inside it \u2014 moving a token out of its annotated container silently strips them).",
1081
+ inputSchema: {
1082
+ type: "object",
1083
+ properties: {
1084
+ path: pathArg,
1085
+ contents: { type: "string", description: "The full new text contents of the file." }
1086
+ },
1087
+ required: ["path", "contents"],
1088
+ additionalProperties: false
1089
+ },
1090
+ toRequest: (args, token) => request("POST", "/api/write", { path: str(args, "path") }, str(args, "contents"), token)
1091
+ },
1092
+ {
1093
+ name: "delete_path",
1094
+ description: "Delete a file or a whole folder subtree. Same role rules as write_file. Deleting a path that does not exist is a no-op.",
1095
+ inputSchema: {
1096
+ type: "object",
1097
+ properties: { path: pathArg },
1098
+ required: ["path"],
1099
+ additionalProperties: false
1100
+ },
1101
+ toRequest: (args, token) => request("POST", "/api/delete", { path: str(args, "path") }, void 0, token)
1102
+ },
1103
+ {
1104
+ name: "rename_path",
1105
+ description: "Move or rename a file or folder subtree. Refuses to overwrite an existing destination. Requires write rights at both the source and the destination. Renaming a template also requires updating every content item bound to the old name (the validation gate will catch unbound items on publish, but `list_pending_migrations` is the cheaper check).",
1106
+ inputSchema: {
1107
+ type: "object",
1108
+ properties: {
1109
+ from: { type: "string", description: "The current path." },
1110
+ to: { type: "string", description: "The new path." }
1111
+ },
1112
+ required: ["from", "to"],
1113
+ additionalProperties: false
1114
+ },
1115
+ toRequest: (args, token) => request(
1116
+ "POST",
1117
+ "/api/rename",
1118
+ { from: str(args, "from"), to: str(args, "to") },
1119
+ void 0,
1120
+ token
1121
+ )
1122
+ },
1123
+ {
1124
+ name: "validate",
1125
+ description: "Run the validation gate over the whole site and return its diagnostics, without publishing. Use it to check your work: errors would block a publish, warnings (e.g. length constraints) only inform.",
1126
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
1127
+ toRequest: (_args, token) => request("GET", "/api/validate", {}, void 0, token)
1128
+ },
1129
+ {
1130
+ name: "publish",
1131
+ description: "Compile and publish the whole site. Runs the validation gate first; if anything would break, it reports the problems and writes nothing.",
1132
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
1133
+ toRequest: (_args, token) => request("POST", "/api/publish", {}, void 0, token)
1134
+ },
1135
+ {
1136
+ name: "list_pending_migrations",
1137
+ description: "List content items whose JSON fields don't match their bound template's schema (orphan fields or missing required fields). Use this after a destructive template edit (you'll see a `stamped` record on write_file) to see what needs migrating, or any time to check site-wide drift.",
1138
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
1139
+ toRequest: (_args, token) => request("GET", "/api/migrations", {}, void 0, token)
1140
+ },
1141
+ {
1142
+ name: "preview_migration",
1143
+ description: 'For one content item, return the current template HTML, the best-fit snapshot HTML (the "before"), the list of orphan fields with their values, and the names of fields the current template defines. Use this to plan how to migrate (drop, rename, or keep each orphan).',
1144
+ inputSchema: {
1145
+ type: "object",
1146
+ properties: { path: pathArg },
1147
+ required: ["path"],
1148
+ additionalProperties: false
1149
+ },
1150
+ toRequest: (args, token) => request("GET", "/api/migrations/preview", { path: str(args, "path") }, void 0, token)
1151
+ },
1152
+ {
1153
+ name: "apply_migration",
1154
+ description: "Resolve a content item's schema drift in one go. `resolution` is a JSON object: `drop` (array of orphan field names to delete), `rename` (object mapping orphan field name \u2192 current template field name to copy the value into), `keep` (array of orphan field names to leave untouched), and `fill` (object mapping current template field name \u2192 value, for missing required fields). Returns the refreshed pending list.",
1155
+ inputSchema: {
1156
+ type: "object",
1157
+ properties: {
1158
+ path: pathArg,
1159
+ resolution: {
1160
+ type: "string",
1161
+ description: 'A JSON object encoded as a string: { "drop": string[], "rename": { from: to }, "keep": string[], "fill": { name: value } }.'
1162
+ }
1163
+ },
1164
+ required: ["path", "resolution"],
1165
+ additionalProperties: false
1166
+ },
1167
+ toRequest: (args, token) => request(
1168
+ "POST",
1169
+ "/api/migrations/apply",
1170
+ {},
1171
+ JSON.stringify({
1172
+ path: str(args, "path"),
1173
+ resolution: parseResolutionArg(str(args, "resolution"))
1174
+ }),
1175
+ token
1176
+ )
1177
+ }
1178
+ ];
1179
+ function parseResolutionArg(raw) {
1180
+ if (raw === "") return {};
1181
+ try {
1182
+ return JSON.parse(raw);
1183
+ } catch {
1184
+ return {};
1185
+ }
1186
+ }
1187
+ var MCP_TOOLS = TOOL_SPECS.map(
1188
+ ({ name, description, inputSchema }) => ({ name, description, inputSchema })
1189
+ );
1190
+ function toResult(response) {
1191
+ const text = typeof response.body === "string" ? response.body : response.body === void 0 ? "" : new TextDecoder().decode(response.body);
1192
+ return { text, isError: response.status >= 400 };
1193
+ }
1194
+ async function callMcpTool(deps, name, args = {}, options = {}) {
1195
+ const spec = TOOL_SPECS.find((tool) => tool.name === name);
1196
+ if (spec === void 0) return { text: `Unknown tool: ${name}`, isError: true };
1197
+ return toResult(await handleApi(deps, spec.toRequest(args, options.token)));
1198
+ }
1199
+ var REFERENCE_URI = "nanoesis://reference";
1200
+ var MCP_RESOURCES = [
1201
+ {
1202
+ uri: REFERENCE_URI,
1203
+ name: "nanoesis authoring reference",
1204
+ description: "The generated reference for nanoesis templates and content: tokens, field types, annotations, loops, components, and the document shell. Read this before writing templates or content.",
1205
+ mimeType: "text/markdown"
1206
+ }
1207
+ ];
1208
+ function readMcpResource(uri) {
1209
+ if (uri === REFERENCE_URI) {
1210
+ return { text: renderReferenceMarkdown(buildAuthoringReference()), mimeType: "text/markdown" };
1211
+ }
1212
+ return void 0;
1213
+ }
1214
+
1104
1215
  // ../editor-api/src/branding.ts
1105
- import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
1216
+ import { mkdir, readFile as readFile2, rename, rm, writeFile } from "fs/promises";
1106
1217
  import { dirname, join } from "path";
1107
1218
  var EMPTY = { name: null, logo: null };
1108
1219
  var InMemoryBrandingStore = class {
@@ -1144,7 +1255,7 @@ var FileBrandingStore = class {
1144
1255
  logoPath;
1145
1256
  async loadMeta() {
1146
1257
  try {
1147
- const raw = await readFile(this.metaPath, "utf8");
1258
+ const raw = await readFile2(this.metaPath, "utf8");
1148
1259
  const text = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
1149
1260
  const parsed = JSON.parse(text);
1150
1261
  return {
@@ -1172,7 +1283,7 @@ var FileBrandingStore = class {
1172
1283
  const meta = await this.loadMeta();
1173
1284
  if (meta.logo === null) return null;
1174
1285
  try {
1175
- const bytes = await readFile(this.logoPath);
1286
+ const bytes = await readFile2(this.logoPath);
1176
1287
  return { bytes: new Uint8Array(bytes), contentType: meta.logo.contentType };
1177
1288
  } catch (error) {
1178
1289
  if (error.code === "ENOENT") return null;
@@ -1203,10 +1314,6 @@ export {
1203
1314
  handleApi,
1204
1315
  authorOptions,
1205
1316
  authorDirectory,
1206
- MCP_TOOLS,
1207
- callMcpTool,
1208
- MCP_RESOURCES,
1209
- readMcpResource,
1210
1317
  buildDefaultDiagnostics,
1211
1318
  homeTemplateMissingDiagnostic,
1212
1319
  recreateHomeTemplateRepair,
@@ -1216,6 +1323,13 @@ export {
1216
1323
  templateSuffixConflictDiagnostic,
1217
1324
  copySnapshotToCurrentRepair,
1218
1325
  pendingMigrationsDiagnostic,
1326
+ createEditor,
1327
+ devNoAuth,
1328
+ serveEditorAsset,
1329
+ MCP_TOOLS,
1330
+ callMcpTool,
1331
+ MCP_RESOURCES,
1332
+ readMcpResource,
1219
1333
  InMemoryBrandingStore,
1220
1334
  FileBrandingStore
1221
1335
  };