@tool-bridge/server 0.10.0 → 0.12.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.
Files changed (108) hide show
  1. package/dist/{airtable-5W6SJMSM.js → airtable-E2TJFCGB.js} +4 -4
  2. package/dist/{alt_text_generator_ai-YYVJM3RG.js → alt_text_generator_ai-UBV5H2N3.js} +4 -4
  3. package/dist/{amap-EREMQZHQ.js → amap-ASVRIYPW.js} +4 -4
  4. package/dist/{anthropic-53H7WGSE.js → anthropic-LQSJWGV3.js} +4 -4
  5. package/dist/{apify-CMRNU7VZ.js → apify-CL4YK6HT.js} +4 -4
  6. package/dist/{brave_search-AFZVZ3QK.js → brave_search-WZQF2QWE.js} +4 -4
  7. package/dist/{chunk-TPGTA43F.js → chunk-4U5OQW7F.js} +144 -115
  8. package/dist/{chunk-2DA2UI37.js → chunk-BK2ZIWLD.js} +2 -2
  9. package/dist/{chunk-Q44CYUSI.js → chunk-HIROW6TB.js} +36 -35
  10. package/dist/{chunk-SBJMMSPA.js → chunk-P4TQJHBO.js} +1 -1
  11. package/dist/{chunk-Q7YHZ2SD.js → chunk-TXOHC56J.js} +212 -183
  12. package/dist/{circleci-JG7RV2RU.js → circleci-EHGI2HKR.js} +4 -4
  13. package/dist/{clerk-U7OS5ALG.js → clerk-OUN4SX22.js} +4 -4
  14. package/dist/{cloudflare_dns-ADCTFMMI.js → cloudflare_dns-DMU3WOPG.js} +4 -4
  15. package/dist/{cohere-Z7ZEC2YV.js → cohere-3HDSCQIB.js} +4 -4
  16. package/dist/{confluence-FT22JGYQ.js → confluence-YPZXKQTQ.js} +4 -4
  17. package/dist/{convertapi-UQQCWVWP.js → convertapi-47ENNUBY.js} +4 -4
  18. package/dist/{currencyapi-Y63O7JTP.js → currencyapi-IX4ICDZA.js} +4 -4
  19. package/dist/{deepseek-7TSAMZJ6.js → deepseek-SCOPJSA6.js} +4 -4
  20. package/dist/{docker_hub-AJYTVI25.js → docker_hub-2DXJVAP4.js} +4 -4
  21. package/dist/{dropbox-CNGGNR5J.js → dropbox-LE2OM2SR.js} +4 -4
  22. package/dist/{dub-WTADKHA7.js → dub-7LKKJADI.js} +4 -4
  23. package/dist/{e2b-75EKXMN5.js → e2b-P3EOM65Q.js} +4 -4
  24. package/dist/{exa-OFLA5XND.js → exa-AETANJQ3.js} +4 -4
  25. package/dist/{fal_ai-5542HB7N.js → fal_ai-VWAMTEZE.js} +4 -4
  26. package/dist/{feishu-QQIZ7G24.js → feishu-VQMM3APW.js} +3 -3
  27. package/dist/{feishu_custom_bot-EVDKIZCR.js → feishu_custom_bot-HEK63AP3.js} +4 -4
  28. package/dist/{figma-ZEBDUMWI.js → figma-7JMV3B5O.js} +4 -4
  29. package/dist/{firehydrant-CTBNY6FL.js → firehydrant-SOU4P7SE.js} +4 -4
  30. package/dist/{fixer-ITDYMRBW.js → fixer-KELXNOIQ.js} +4 -4
  31. package/dist/{fly-IBFMSAAO.js → fly-Y26QH6AC.js} +4 -4
  32. package/dist/{front-AZQLLUJQ.js → front-2RUHP54Y.js} +4 -4
  33. package/dist/{geocodio-XHZHEWQW.js → geocodio-BTSXWU55.js} +4 -4
  34. package/dist/{ghost-RTIKXD3T.js → ghost-BA55ISIU.js} +4 -4
  35. package/dist/{github-WRJBJ5N7.js → github-VUYFDSHK.js} +4 -4
  36. package/dist/{gmail-527RVDXV.js → gmail-3Z2WCVLT.js} +4 -4
  37. package/dist/{googlecalendar-WZJSK2MR.js → googlecalendar-LZKRUXHY.js} +4 -4
  38. package/dist/{googledocs-LFBVIHIK.js → googledocs-RBK2HMPZ.js} +4 -4
  39. package/dist/{grafana-3JEKO7NT.js → grafana-E626W3N3.js} +4 -4
  40. package/dist/{graphhopper-TOJN6K2Q.js → graphhopper-YNURJCFZ.js} +4 -4
  41. package/dist/index.d.ts +11 -11
  42. package/dist/index.js +2 -2
  43. package/dist/{ip2proxy-KOA2JUNV.js → ip2proxy-4DAJUOUR.js} +4 -4
  44. package/dist/{ipgeolocation_io-KNOQD7R7.js → ipgeolocation_io-3BUSFLVD.js} +4 -4
  45. package/dist/{ipqualityscore-GF2MQKXI.js → ipqualityscore-7DKMRMEQ.js} +4 -4
  46. package/dist/{jina_ai-GB3CQPJ4.js → jina_ai-NXXZRPT6.js} +4 -4
  47. package/dist/{jira-35XYV6G4.js → jira-UG7ZIKSW.js} +4 -4
  48. package/dist/{langsmith-W5234OE7.js → langsmith-66IP4KJ6.js} +4 -4
  49. package/dist/{lemlist-QB6W2G7N.js → lemlist-MVB4EQMS.js} +4 -4
  50. package/dist/{linear-MHBD6A7X.js → linear-6UC3AFY3.js} +4 -4
  51. package/dist/{logsnag-RN57XIJS.js → logsnag-NFJM6O4U.js} +4 -4
  52. package/dist/main.js +102 -102
  53. package/dist/{mattermost-QRXSZMZA.js → mattermost-FTJER2B6.js} +4 -4
  54. package/dist/{mem0-K63NN3LA.js → mem0-RR5ALUKD.js} +4 -4
  55. package/dist/{memos-4IOUOFKH.js → memos-WOAY3T76.js} +4 -4
  56. package/dist/{metabase-IOE44EKL.js → metabase-ZYG7NBPU.js} +4 -4
  57. package/dist/{mistral_ai-X7ASGV5I.js → mistral_ai-FTCSXHJ5.js} +4 -4
  58. package/dist/{mother_duck-CNN3QWQH.js → mother_duck-ZMIGT5ZK.js} +4 -4
  59. package/dist/{ngrok-QNE4AHEH.js → ngrok-TEDZ4WUB.js} +4 -4
  60. package/dist/{notes-Q6OLBS2L.js → notes-4KZGQBRF.js} +2 -2
  61. package/dist/{notion-ADDRQTCE.js → notion-ERLC3SKM.js} +4 -4
  62. package/dist/{open_exchange_rates-WR5PJQTG.js → open_exchange_rates-GBBHZLUS.js} +4 -4
  63. package/dist/{openai-UZBNNAAH.js → openai-24N26QWI.js} +4 -4
  64. package/dist/{opengraph_io-2JNEXCPJ.js → opengraph_io-MTN4TTXP.js} +4 -4
  65. package/dist/{openrouter-EF62BDIH.js → openrouter-BK6BL7EH.js} +4 -4
  66. package/dist/{outline-O4TIRWJT.js → outline-O3LF5SNM.js} +4 -4
  67. package/dist/{paddle-SEFKNENH.js → paddle-IEDD5BMW.js} +4 -4
  68. package/dist/{perplexity-FZYOIPDS.js → perplexity-HGPLFK72.js} +4 -4
  69. package/dist/{pinecone-EXDPYDY7.js → pinecone-FTWR4JVG.js} +4 -4
  70. package/dist/{pipedrive-EDO4C3AM.js → pipedrive-DDMA36AB.js} +4 -4
  71. package/dist/{polar-QJXHVFIP.js → polar-YFGTOQYM.js} +4 -4
  72. package/dist/{posthog-U6QEXJE2.js → posthog-GFEFPIH3.js} +4 -4
  73. package/dist/{postmark-PFZIWXMP.js → postmark-N7YCE2DZ.js} +4 -4
  74. package/dist/{prerender-SK2AXNJY.js → prerender-B77XIKL7.js} +4 -4
  75. package/dist/{pubmed-CB7OJNBH.js → pubmed-XAFQSOCJ.js} +4 -4
  76. package/dist/{railway-BMMNQ6XH.js → railway-DJ4P5DAJ.js} +4 -4
  77. package/dist/{readwise-YQY7O5QR.js → readwise-UNBGXBYC.js} +4 -4
  78. package/dist/{render-7CRNEEJ3.js → render-Z43FLDJ3.js} +4 -4
  79. package/dist/{replicate-V6AX7WFC.js → replicate-BZAMQO7R.js} +4 -4
  80. package/dist/{resend-ZBH56UAQ.js → resend-MFUW2LNU.js} +4 -4
  81. package/dist/{rootly-C4FIXIF7.js → rootly-5CHCWRPM.js} +4 -4
  82. package/dist/{scrapfly-UE434KD7.js → scrapfly-36O4IJDA.js} +4 -4
  83. package/dist/{scrapingbee-PQCNKALD.js → scrapingbee-JK6HZA75.js} +4 -4
  84. package/dist/{screenshot_fyi-U6QGI6ZH.js → screenshot_fyi-BDEL4D53.js} +4 -4
  85. package/dist/{semantic_scholar-PSMUOG2W.js → semantic_scholar-QHHG7RWT.js} +4 -4
  86. package/dist/{sentry-F5CNVQA2.js → sentry-OCUW4KNC.js} +4 -4
  87. package/dist/{shodan-NOWG7NV5.js → shodan-5UBQ6RKP.js} +4 -4
  88. package/dist/{shopify-GGPLJIGC.js → shopify-OXIMRVHL.js} +4 -4
  89. package/dist/{stripe-72K2UMK3.js → stripe-W5XRW7WG.js} +4 -4
  90. package/dist/{supabase-H7NRCSAO.js → supabase-DIC7ZIG7.js} +4 -4
  91. package/dist/{tavily-ISP24QUX.js → tavily-NVWQPGF3.js} +4 -4
  92. package/dist/{telegram-POII6NKE.js → telegram-S755MPMM.js} +4 -4
  93. package/dist/{telnyx-3D7TOVNA.js → telnyx-MXRX75AP.js} +4 -4
  94. package/dist/{todoist-LKYPKRKB.js → todoist-MNVUPHIF.js} +4 -4
  95. package/dist/{trello-WW74UYIQ.js → trello-T7JLVYU4.js} +4 -4
  96. package/dist/{turso-SUFRHBHW.js → turso-JDBW2RN4.js} +4 -4
  97. package/dist/{twilio-QB7PBL2B.js → twilio-EGYNUGMZ.js} +4 -4
  98. package/dist/{umami-CAHHP3SV.js → umami-QF2V23Z5.js} +4 -4
  99. package/dist/{unsplash-ZCGEPHGQ.js → unsplash-VWTABYWL.js} +4 -4
  100. package/dist/{upstash_redis-DZ4AKZWT.js → upstash_redis-FKUGE73T.js} +4 -4
  101. package/dist/{uptimerobot-KVEQJGTZ.js → uptimerobot-55LTBUWA.js} +4 -4
  102. package/dist/{v2ex-BCYYBPH7.js → v2ex-LGG6X53N.js} +4 -4
  103. package/dist/{vercel-GH4G7EUS.js → vercel-VAZGWMOZ.js} +4 -4
  104. package/dist/{wolfram_alpha_api-XFG2MQBL.js → wolfram_alpha_api-YLVVZTPP.js} +4 -4
  105. package/dist/{wordpress-DZ2TVNAA.js → wordpress-6LDTITVV.js} +4 -4
  106. package/dist/{workos-EHUAGTGN.js → workos-ML6NIEEP.js} +4 -4
  107. package/dist/{zhihu-OJJW3ZIH.js → zhihu-CACG2ZNY.js} +4 -4
  108. package/package.json +4 -4
@@ -115,6 +115,7 @@ import {
115
115
  pgBulkInsertSql,
116
116
  pgSearchDialect,
117
117
  pluginTokenSecretName,
118
+ prefixUpperBound,
118
119
  preprocess,
119
120
  record,
120
121
  renderHelpDsl,
@@ -140,7 +141,7 @@ import {
140
141
  url,
141
142
  validatePath,
142
143
  virtualizeTools
143
- } from "./chunk-TPGTA43F.js";
144
+ } from "./chunk-4U5OQW7F.js";
144
145
 
145
146
  // src/config.ts
146
147
  var DEFAULT_PORT = 8787;
@@ -19102,16 +19103,16 @@ function createMcpProvider(config, secrets, opts) {
19102
19103
  function createPluginToolProvider(opts) {
19103
19104
  return {
19104
19105
  list: async () => {
19105
- const value = await callPlugin(opts, "List", {});
19106
+ const value = await callPlugin(opts, "list", {});
19106
19107
  if (!Array.isArray(value)) {
19107
- throw new TBError("unavailable", `plugin '${opts.manifest.id}' \u7684 List \u672A\u8FD4\u56DE\u5DE5\u5177\u6570\u7EC4`, {
19108
+ throw new TBError("unavailable", `plugin '${opts.manifest.id}' \u7684 list \u672A\u8FD4\u56DE\u5DE5\u5177\u6570\u7EC4`, {
19108
19109
  retryable: false
19109
19110
  });
19110
19111
  }
19111
19112
  return value;
19112
19113
  },
19113
19114
  call: async (name, args) => {
19114
- const value = await callPlugin(opts, "Call", { name, args });
19115
+ const value = await callPlugin(opts, "call", { name, args });
19115
19116
  if (value !== null && typeof value === "object" && "content" in value) {
19116
19117
  return value;
19117
19118
  }
@@ -19737,23 +19738,23 @@ async function skillhubProviderFor(node, cfg, deps, requestUrl) {
19737
19738
  return createSkillhubProvider(objects, opts);
19738
19739
  }
19739
19740
  async function dispatchSkillhubCmd(provider, tool, args) {
19740
- switch (tool) {
19741
- case "List":
19742
- return await provider.List(args.opts);
19743
- case "Get":
19744
- return typeof args.file === "string" ? await provider.GetFile(args.id, args.file) : await provider.Get(args.id);
19745
- case "Search":
19746
- return await provider.Search(args.query, args.opts);
19747
- case "Publish":
19741
+ switch (tool.toLowerCase()) {
19742
+ case "list":
19743
+ return await provider.list(args.opts);
19744
+ case "get":
19745
+ return typeof args.file === "string" ? await provider.get_file(args.id, args.file) : await provider.get(args.id);
19746
+ case "search":
19747
+ return await provider.search(args.query, args.opts);
19748
+ case "publish":
19748
19749
  if (!Array.isArray(args.files)) {
19749
- throw new TBError("invalid_argument", "Publish \u9700\u8981\u6570\u7EC4 'files'");
19750
+ throw new TBError("invalid_argument", "publish \u9700\u8981\u6570\u7EC4 'files'");
19750
19751
  }
19751
- return await provider.Publish({
19752
+ return await provider.publish({
19752
19753
  ...typeof args.id === "string" ? { id: args.id } : {},
19753
19754
  files: args.files
19754
19755
  });
19755
- case "Remove":
19756
- return await provider.Remove(args.id);
19756
+ case "remove":
19757
+ return await provider.remove(args.id);
19757
19758
  default:
19758
19759
  throw new TBError("invalid_argument", `unknown cmd '${tool}'`);
19759
19760
  }
@@ -19762,31 +19763,31 @@ async function dispatchContextCmd(provider, tool, args) {
19762
19763
  const unimplemented = () => {
19763
19764
  throw new TBError("invalid_argument", `unknown cmd '${tool}'(provider \u672A\u5B9E\u73B0)`);
19764
19765
  };
19765
- switch (tool) {
19766
- case "List":
19767
- if (provider.List === void 0) return unimplemented();
19768
- return await provider.List(args.path ?? "", args.opts);
19769
- case "Get":
19770
- if (provider.Get === void 0) return unimplemented();
19771
- return await provider.Get(args.path);
19772
- case "Write":
19773
- if (provider.Write === void 0) return unimplemented();
19766
+ switch (tool.toLowerCase()) {
19767
+ case "list":
19768
+ if (provider.list === void 0) return unimplemented();
19769
+ return await provider.list(args.path ?? "", args.opts);
19770
+ case "get":
19771
+ if (provider.get === void 0) return unimplemented();
19772
+ return await provider.get(args.path);
19773
+ case "write":
19774
+ if (provider.write === void 0) return unimplemented();
19774
19775
  if (typeof args.entry !== "object" || args.entry === null) {
19775
- throw new TBError("invalid_argument", "Write \u9700\u8981\u5BF9\u8C61 'entry'");
19776
+ throw new TBError("invalid_argument", "write \u9700\u8981\u5BF9\u8C61 'entry'");
19776
19777
  }
19777
- return await provider.Write(args.path, args.entry);
19778
- case "Update":
19779
- if (provider.Update === void 0) return unimplemented();
19778
+ return await provider.write(args.path, args.entry);
19779
+ case "update":
19780
+ if (provider.update === void 0) return unimplemented();
19780
19781
  if (typeof args.patch !== "object" || args.patch === null) {
19781
- throw new TBError("invalid_argument", "Update \u9700\u8981\u5BF9\u8C61 'patch'");
19782
- }
19783
- return await provider.Update(args.path, args.patch);
19784
- case "Delete":
19785
- if (provider.Delete === void 0) return unimplemented();
19786
- return await provider.Delete(args.path);
19787
- case "Search":
19788
- if (provider.Search === void 0) return unimplemented();
19789
- return await provider.Search(args.query, args.opts);
19782
+ throw new TBError("invalid_argument", "update \u9700\u8981\u5BF9\u8C61 'patch'");
19783
+ }
19784
+ return await provider.update(args.path, args.patch);
19785
+ case "delete":
19786
+ if (provider.delete === void 0) return unimplemented();
19787
+ return await provider.delete(args.path);
19788
+ case "search":
19789
+ if (provider.search === void 0) return unimplemented();
19790
+ return await provider.search(args.query, args.opts);
19790
19791
  default:
19791
19792
  throw new TBError("invalid_argument", `unknown cmd '${tool}'`);
19792
19793
  }
@@ -35582,8 +35583,8 @@ var CfWorkerJsonSchemaValidator2 = class {
35582
35583
  function hex(bytes) {
35583
35584
  return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
35584
35585
  }
35585
- function mcpToolIdentity(invokePath, toolName, invokeWithEnvelope) {
35586
- return JSON.stringify([invokePath, toolName, invokeWithEnvelope ? "envelope" : "flat"]);
35586
+ function mcpToolIdentity(invokePath, toolName) {
35587
+ return JSON.stringify([invokePath, toolName]);
35587
35588
  }
35588
35589
  async function mcpToolName(identity) {
35589
35590
  const encoded = [...new TextEncoder().encode(identity)].map((byte) => {
@@ -35793,33 +35794,33 @@ async function handleMcpRequest(request, version, bridge, listTtlMs) {
35793
35794
  }
35794
35795
 
35795
35796
  // ../app/src/providers/pluginContext.ts
35796
- var CORE_METHODS = ["List", "Get", "Write", "Update"];
35797
+ var CORE_METHODS = ["list", "get", "write", "update"];
35797
35798
  function createPluginContextProvider(opts) {
35798
35799
  const call = (method, args) => callPlugin(opts, method, args);
35799
35800
  const declared = opts.methods !== void 0 ? new Set(opts.methods) : /* @__PURE__ */ new Set([...CORE_METHODS, ...optionalMethodsForCapabilities(opts.capabilities)]);
35800
35801
  const provider = {};
35801
- if (declared.has("List")) {
35802
- provider.List = (path, listOpts) => call("List", { path, ...listOpts !== void 0 ? { opts: listOpts } : {} });
35802
+ if (declared.has("list")) {
35803
+ provider.list = (path, listOpts) => call("list", { path, ...listOpts !== void 0 ? { opts: listOpts } : {} });
35803
35804
  }
35804
- if (declared.has("Get")) {
35805
- provider.Get = (path) => call("Get", { path });
35805
+ if (declared.has("get")) {
35806
+ provider.get = (path) => call("get", { path });
35806
35807
  }
35807
- if (declared.has("Update")) {
35808
- provider.Update = (path, patch) => call("Update", { path, patch });
35808
+ if (declared.has("update")) {
35809
+ provider.update = (path, patch) => call("update", { path, patch });
35809
35810
  }
35810
- if (declared.has("Write")) {
35811
- provider.Write = (path, entry) => call("Write", { path, entry });
35811
+ if (declared.has("write")) {
35812
+ provider.write = (path, entry) => call("write", { path, entry });
35812
35813
  }
35813
35814
  const optional2 = optionalMethodsForCapabilities(opts.capabilities);
35814
- if (declared.has("Search") && optional2.has("Search")) {
35815
- provider.Search = (query, searchOpts) => call("Search", {
35815
+ if (declared.has("search") && optional2.has("search")) {
35816
+ provider.search = (query, searchOpts) => call("search", {
35816
35817
  query,
35817
35818
  ...searchOpts !== void 0 ? { opts: searchOpts } : {}
35818
35819
  });
35819
35820
  }
35820
- if (declared.has("Delete") && optional2.has("Delete")) {
35821
- provider.Delete = async (path) => {
35822
- await call("Delete", { path });
35821
+ if (declared.has("delete") && optional2.has("delete")) {
35822
+ provider.delete = async (path) => {
35823
+ await call("delete", { path });
35823
35824
  };
35824
35825
  }
35825
35826
  return provider;
@@ -36578,10 +36579,15 @@ function registerPublicRoutes(app, env) {
36578
36579
  return tbErrorResponse(TBError.notFound("dashboard assets not deployed"));
36579
36580
  }
36580
36581
  const url2 = new URL(c.req.url);
36582
+ const fwd = new Headers();
36583
+ const ae = c.req.header("accept-encoding");
36584
+ if (ae) fwd.set("accept-encoding", ae);
36585
+ const inm = c.req.header("if-none-match");
36586
+ if (inm) fwd.set("if-none-match", inm);
36581
36587
  const sub = url2.pathname.slice("/ui".length) || "/";
36582
- const res = await assets(new Request(new URL(sub, url2.origin)));
36588
+ const res = await assets(new Request(new URL(sub, url2.origin), { headers: fwd }));
36583
36589
  if (res.status !== 404) return res;
36584
- return await assets(new Request(new URL("/", url2.origin)));
36590
+ return await assets(new Request(new URL("/", url2.origin), { headers: fwd }));
36585
36591
  };
36586
36592
  app.get("/ui", (c) => c.redirect("/ui/", 302));
36587
36593
  app.get("/ui/*", serveUi);
@@ -36860,10 +36866,10 @@ async function helpModelFor(node, registry, ctx, builtins, deps, opts) {
36860
36866
  node.config.export
36861
36867
  );
36862
36868
  const declared = exported.methods !== void 0 ? new Set(exported.methods) : /* @__PURE__ */ new Set([
36863
- "List",
36864
- "Get",
36865
- "Write",
36866
- "Update",
36869
+ "list",
36870
+ "get",
36871
+ "write",
36872
+ "update",
36867
36873
  ...optionalMethodsForCapabilities(exported.capabilities ?? [])
36868
36874
  ]);
36869
36875
  return { ...model, cmds: model.cmds.filter((c) => declared.has(c.name)) };
@@ -36876,23 +36882,40 @@ async function helpModelFor(node, registry, ctx, builtins, deps, opts) {
36876
36882
  }
36877
36883
  throw TBError.unimplemented(`~help for kind '${node.kind}' not implemented yet`);
36878
36884
  }
36885
+ async function commandHelpModelFor(registry, ctx, builtins, deps, path, opts) {
36886
+ const resolved = await registry.resolve(path).catch(() => null);
36887
+ if (resolved === null || resolved.rest === "" || resolved.rest.includes("/")) return null;
36888
+ const { node, rest: command } = resolved;
36889
+ if (!(node.kind === "builtin" || node.kind === "context" || node.kind === "skillhub")) {
36890
+ return null;
36891
+ }
36892
+ const full = await helpModelFor(node, registry, ctx, builtins, deps, {
36893
+ refresh: opts.refresh,
36894
+ schemas: true,
36895
+ now: (/* @__PURE__ */ new Date()).toISOString()
36896
+ });
36897
+ const cmd = full.cmds.find((c) => c.name === command);
36898
+ if (cmd === void 0) return null;
36899
+ return {
36900
+ node: { path: `${node.path}/${command}`, kind: node.kind, description: cmd.h ?? full.node.description },
36901
+ cmds: [cmd]
36902
+ };
36903
+ }
36879
36904
 
36880
36905
  // ../app/src/routes/mcp.ts
36881
36906
  var MCP_REMOTE_MAX_REQUESTS = 32;
36882
36907
  var mcpCommand = (nodePath, nodeDescription, command) => {
36883
36908
  const modelPath = nodePath.replace(/^\/+|\/+$/g, "");
36884
36909
  const commandPath = command.path.replace(/^\/+|\/+$/g, "");
36885
- if (commandPath !== modelPath && !commandPath.startsWith(`${modelPath}/`)) {
36910
+ if (!commandPath.startsWith(`${modelPath}/`)) {
36886
36911
  throw new TBError("internal", `command path '${command.path}' escapes node '${nodePath}'`);
36887
36912
  }
36888
36913
  const invokePath = `/${commandPath}`;
36889
- const invokeWithEnvelope = commandPath === modelPath;
36890
36914
  return {
36891
- identity: mcpToolIdentity(invokePath, command.name, invokeWithEnvelope),
36915
+ identity: mcpToolIdentity(invokePath, command.name),
36892
36916
  sourcePath: nodePath,
36893
36917
  toolName: command.name,
36894
36918
  invokePath,
36895
- invokeWithEnvelope,
36896
36919
  description: command.h ?? nodeDescription,
36897
36920
  ...command.inputSchema !== void 0 ? { inputSchema: command.inputSchema } : {},
36898
36921
  ...command.effect !== void 0 ? { effect: command.effect } : {},
@@ -36900,13 +36923,12 @@ var mcpCommand = (nodePath, nodeDescription, command) => {
36900
36923
  };
36901
36924
  };
36902
36925
  var toolSpecCommand = (node, tool, providerBacked = false) => {
36903
- const path = `/${node.path}`;
36926
+ const invokePath = `/${node.path}/${tool.name}`;
36904
36927
  return {
36905
- identity: mcpToolIdentity(path, tool.name, true),
36928
+ identity: mcpToolIdentity(invokePath, tool.name),
36906
36929
  sourcePath: node.path,
36907
36930
  toolName: tool.name,
36908
- invokePath: path,
36909
- invokeWithEnvelope: true,
36931
+ invokePath,
36910
36932
  ...providerBacked ? { providerBacked: true } : {},
36911
36933
  description: tool.description ?? node.description,
36912
36934
  ...tool.inputSchema !== void 0 ? { inputSchema: tool.inputSchema } : {},
@@ -36921,12 +36943,9 @@ var remoteCommand = (localNodePath, model, command) => {
36921
36943
  throw new TBError("unavailable", "remote ~help returned a command outside its node");
36922
36944
  }
36923
36945
  const suffix = remoteCommandPath.slice(remoteNodePath.length);
36924
- const directTool = suffix.startsWith("/");
36925
36946
  return mcpCommand(localNodePath, model.node.description, {
36926
36947
  ...command,
36927
- // Tool-layer nodes retain the envelope entrypoint. It keeps authorization on the
36928
- // node path instead of requiring callers to hold an exact scope for the tool suffix.
36929
- path: directTool ? `/${localNodePath}` : `/${localNodePath}${suffix}`
36948
+ path: `/${localNodePath}${suffix}`
36930
36949
  });
36931
36950
  };
36932
36951
  function mcpBridgeFor(c, env, app) {
@@ -36940,7 +36959,6 @@ function mcpBridgeFor(c, env, app) {
36940
36959
  sourcePath: "",
36941
36960
  toolName: "Search",
36942
36961
  invokePath: "/~search",
36943
- invokeWithEnvelope: false,
36944
36962
  mcpName: "tb_search",
36945
36963
  operation: "search",
36946
36964
  description: "Search visible tools across the Tool Bridge tree.",
@@ -36962,7 +36980,6 @@ function mcpBridgeFor(c, env, app) {
36962
36980
  sourcePath: "",
36963
36981
  toolName: "Help",
36964
36982
  invokePath: "/~help",
36965
- invokeWithEnvelope: false,
36966
36983
  mcpName: "tb_help",
36967
36984
  operation: "help",
36968
36985
  description: "Describe a visible Tool Bridge node or one of its tools.",
@@ -36982,7 +36999,6 @@ function mcpBridgeFor(c, env, app) {
36982
36999
  sourcePath: "",
36983
37000
  toolName: "List",
36984
37001
  invokePath: "/~tree",
36985
- invokeWithEnvelope: false,
36986
37002
  mcpName: "tb_list_nodes",
36987
37003
  operation: "listNodes",
36988
37004
  description: "List the visible Tool Bridge node tree from a path.",
@@ -37226,9 +37242,8 @@ function mcpBridgeFor(c, env, app) {
37226
37242
  "authorization": c.req.header("authorization") ?? "",
37227
37243
  "content-type": "application/json"
37228
37244
  });
37229
- const body = tool.invokeWithEnvelope ? { tool: tool.toolName, arguments: args } : args;
37230
37245
  const response = await app.request(
37231
- new Request(url2, { method: "POST", headers, body: JSON.stringify(body) })
37246
+ new Request(url2, { method: "POST", headers, body: JSON.stringify(args) })
37232
37247
  );
37233
37248
  return await resultFromResponse(response);
37234
37249
  }
@@ -37253,35 +37268,24 @@ async function handleInvoke(c, env) {
37253
37268
  const ctx = c.get("ctx");
37254
37269
  const store = c.get("store");
37255
37270
  const registry = new NodeRegistryStore(store);
37256
- if (!check(ctx, raw, "read").allow) throw TBError.notFound("not found");
37257
- const remote = await remotePassthroughIfMatch(c, ctx, registry, raw, null, deps);
37258
- if (remote) return remote;
37259
- let node;
37260
- let directTool = null;
37261
- try {
37262
- node = await registry.get(raw);
37263
- } catch {
37264
- const resolved = await registry.resolve(raw).catch(() => null);
37265
- if (resolved === null || resolved.node.kind !== "mcp" && resolved.node.kind !== "http" && resolved.node.kind !== "tool" || resolved.node.config === void 0 || resolved.rest === "" || resolved.rest.includes("/")) {
37266
- throw TBError.notFound("not found");
37267
- }
37268
- if (!check(ctx, resolved.node.path, "read").allow) throw TBError.notFound("not found");
37269
- node = resolved.node;
37270
- directTool = resolved.rest;
37271
+ const remoteVisible = check(ctx, raw, "read").allow;
37272
+ if (remoteVisible) {
37273
+ const remote = await remotePassthroughIfMatch(c, ctx, registry, raw, null, deps);
37274
+ if (remote) return remote;
37275
+ }
37276
+ const resolved = await registry.resolve(raw).catch(() => null);
37277
+ if (resolved === null || resolved.rest === "" || resolved.rest.includes("/")) {
37278
+ throw TBError.notFound("not found");
37271
37279
  }
37280
+ const node = resolved.node;
37281
+ const command = resolved.rest;
37282
+ if (!check(ctx, node.path, "read").allow) throw TBError.notFound("not found");
37272
37283
  const readInvokeBody = async () => {
37273
37284
  const parsed = await c.req.json().catch(() => null);
37274
- if (directTool !== null) {
37275
- if (parsed !== null && (typeof parsed !== "object" || Array.isArray(parsed))) {
37276
- throw new TBError("invalid_argument", "body must be a JSON object (tool arguments)");
37277
- }
37278
- return { tool: directTool, args: parsed ?? {} };
37285
+ if (parsed !== null && (typeof parsed !== "object" || Array.isArray(parsed))) {
37286
+ throw new TBError("invalid_argument", "body must be a JSON object (command arguments)");
37279
37287
  }
37280
- const body2 = parsed;
37281
- if (!body2 || typeof body2.tool !== "string") {
37282
- throw new TBError("invalid_argument", "body must be {tool, arguments}");
37283
- }
37284
- return { tool: body2.tool, args: body2.arguments ?? {} };
37288
+ return { tool: command, args: parsed ?? {} };
37285
37289
  };
37286
37290
  const toolMarker = deviceToolMarker(node);
37287
37291
  if (toolMarker !== null) {
@@ -37290,8 +37294,8 @@ async function handleInvoke(c, env) {
37290
37294
  }
37291
37295
  const { tool, args: args2 } = await readInvokeBody();
37292
37296
  const result2 = await invokeDevice(deps, toolMarker.deviceId, {
37293
- path: relativeDevicePath(node.path, toolMarker.mountPath),
37294
- tool,
37297
+ // path 含命令叶子段:<mount 相对路径>/<命令>。
37298
+ path: `${relativeDevicePath(node.path, toolMarker.mountPath)}/${tool}`,
37295
37299
  arguments: args2,
37296
37300
  context: deviceCallContextFrom(ctx)
37297
37301
  });
@@ -37312,17 +37316,13 @@ async function handleInvoke(c, env) {
37312
37316
  if (!check(ctx, node.path, "call").allow) {
37313
37317
  throw new TBError("permission_denied", `no scope grants 'call' on '${node.path}'`);
37314
37318
  }
37315
- const body2 = await c.req.json().catch(() => null);
37316
- if (!body2 || typeof body2.tool !== "string") {
37317
- throw new TBError("invalid_argument", "body must be {tool, arguments}");
37318
- }
37319
- if (body2.tool !== "exec") {
37320
- throw new TBError("invalid_argument", `unknown cmd '${body2.tool}' on '${node.path}'`);
37319
+ const { tool, args: args2 } = await readInvokeBody();
37320
+ if (tool !== "exec") {
37321
+ throw new TBError("invalid_argument", `unknown cmd '${tool}' on '${node.path}'`);
37321
37322
  }
37322
37323
  const result2 = await invokeDevice(deps, node.config.deviceId, {
37323
- path: "shell",
37324
- tool: body2.tool,
37325
- arguments: body2.arguments ?? {},
37324
+ path: "shell/exec",
37325
+ arguments: args2,
37326
37326
  context: deviceCallContextFrom(ctx)
37327
37327
  });
37328
37328
  return renderResult(result2, negotiate(c.req.header("accept")));
@@ -37330,25 +37330,20 @@ async function handleInvoke(c, env) {
37330
37330
  if (node.kind === "context" && node.config?.kind === "context") {
37331
37331
  const cfg = node.config;
37332
37332
  await assertContextAlive(node, cfg, registry);
37333
- const body2 = await c.req.json().catch(() => null);
37334
- if (!body2 || typeof body2.tool !== "string") {
37335
- throw new TBError("invalid_argument", "body must be {tool, arguments}");
37336
- }
37337
- const scope = contextScopeForCmd(body2.tool);
37333
+ const { tool, args: args2 } = await readInvokeBody();
37334
+ const scope = contextScopeForCmd(tool);
37338
37335
  if (scope === null) {
37339
- throw new TBError("invalid_argument", `unknown cmd '${body2.tool}' on '${node.path}'`);
37336
+ throw new TBError("invalid_argument", `unknown cmd '${tool}' on '${node.path}'`);
37340
37337
  }
37341
37338
  if (!check(ctx, node.path, scope).allow) {
37342
37339
  throw new TBError("permission_denied", `no scope grants '${scope}' on '${node.path}'`);
37343
37340
  }
37344
37341
  if (cfg.readOnly === true && scope === "write") {
37345
- throw new TBError("permission_denied", `readOnly \u6302\u8F7D\u62D2\u7EDD '${body2.tool}'`);
37342
+ throw new TBError("permission_denied", `readOnly \u6302\u8F7D\u62D2\u7EDD '${tool}'`);
37346
37343
  }
37347
- const args2 = body2.arguments ?? {};
37348
37344
  if (cfg.provider === "device-fs") {
37349
37345
  const result3 = await invokeDevice(deps, deviceIdForDeviceFs(cfg), {
37350
- path: "fs",
37351
- tool: body2.tool,
37346
+ path: `fs/${tool}`,
37352
37347
  arguments: args2,
37353
37348
  context: deviceCallContextFrom(ctx)
37354
37349
  });
@@ -37357,8 +37352,7 @@ async function handleInvoke(c, env) {
37357
37352
  const contextMarker = deviceMarkerOf(cfg.providerConfig);
37358
37353
  if (cfg.provider !== "r2" && cfg.provider !== "s3" && contextMarker !== null) {
37359
37354
  const result3 = await invokeDevice(deps, contextMarker.deviceId, {
37360
- path: relativeDevicePath(node.path, contextMarker.mountPath),
37361
- tool: body2.tool,
37355
+ path: `${relativeDevicePath(node.path, contextMarker.mountPath)}/${tool}`,
37362
37356
  arguments: args2,
37363
37357
  context: deviceCallContextFrom(ctx)
37364
37358
  });
@@ -37367,7 +37361,7 @@ async function handleInvoke(c, env) {
37367
37361
  if (cfg.provider !== "r2" && cfg.provider !== "s3") {
37368
37362
  const local = localContext(deps, node);
37369
37363
  if (local !== null) {
37370
- const result4 = await dispatchContextCmd(local, body2.tool, args2);
37364
+ const result4 = await dispatchContextCmd(local, tool, args2);
37371
37365
  return renderResult(result4, negotiate(c.req.header("accept")));
37372
37366
  }
37373
37367
  const { manifest, export: exported } = await requirePluginExport(
@@ -37387,33 +37381,29 @@ async function handleInvoke(c, env) {
37387
37381
  ...cfg.authRef !== void 0 ? { upstreamAuthRef: cfg.authRef } : {},
37388
37382
  ...deps.pluginBindings !== void 0 ? { bindings: deps.pluginBindings } : {}
37389
37383
  });
37390
- const result3 = await dispatchContextCmd(provider2, body2.tool, args2);
37384
+ const result3 = await dispatchContextCmd(provider2, tool, args2);
37391
37385
  return renderResult(result3, negotiate(c.req.header("accept")));
37392
37386
  }
37393
37387
  const provider = await contextProviderFor(node, cfg, deps, c.req.url);
37394
- const result2 = await dispatchContextCmd(provider, body2.tool, args2);
37388
+ const result2 = await dispatchContextCmd(provider, tool, args2);
37395
37389
  return renderResult(result2, negotiate(c.req.header("accept")));
37396
37390
  }
37397
37391
  if (node.kind === "skillhub" && node.config?.kind === "skillhub") {
37398
37392
  const cfg = node.config;
37399
37393
  await assertContextAlive(node, cfg, registry);
37400
- const body2 = await c.req.json().catch(() => null);
37401
- if (!body2 || typeof body2.tool !== "string") {
37402
- throw new TBError("invalid_argument", "body must be {tool, arguments}");
37403
- }
37404
- const scope = skillhubScopeForCmd(body2.tool);
37394
+ const { tool, args: args2 } = await readInvokeBody();
37395
+ const scope = skillhubScopeForCmd(tool);
37405
37396
  if (scope === null) {
37406
- throw new TBError("invalid_argument", `unknown cmd '${body2.tool}' on '${node.path}'`);
37397
+ throw new TBError("invalid_argument", `unknown cmd '${tool}' on '${node.path}'`);
37407
37398
  }
37408
37399
  if (!check(ctx, node.path, scope).allow) {
37409
37400
  throw new TBError("permission_denied", `no scope grants '${scope}' on '${node.path}'`);
37410
37401
  }
37411
37402
  if (cfg.readOnly === true && scope === "write") {
37412
- throw new TBError("permission_denied", `readOnly \u6302\u8F7D\u62D2\u7EDD '${body2.tool}'`);
37403
+ throw new TBError("permission_denied", `readOnly \u6302\u8F7D\u62D2\u7EDD '${tool}'`);
37413
37404
  }
37414
- const args2 = body2.arguments ?? {};
37415
37405
  const provider = await skillhubProviderFor(node, cfg, deps, c.req.url);
37416
- const result2 = await dispatchSkillhubCmd(provider, body2.tool, args2);
37406
+ const result2 = await dispatchSkillhubCmd(provider, tool, args2);
37417
37407
  return renderResult(result2, negotiate(c.req.header("accept")));
37418
37408
  }
37419
37409
  if (node.kind !== "builtin" || node.config?.kind !== "builtin") {
@@ -37422,12 +37412,7 @@ async function handleInvoke(c, env) {
37422
37412
  const builtins = builtinsOf(store);
37423
37413
  const mod = builtins.get(node.config.module);
37424
37414
  if (!mod) throw TBError.unimplemented(`builtin module '${node.config.module}' not available`);
37425
- const body = await c.req.json().catch(() => null);
37426
- if (!body || typeof body.tool !== "string") {
37427
- throw new TBError("invalid_argument", "body must be {tool, arguments}");
37428
- }
37429
- const cmd = body.tool;
37430
- const args = body.arguments ?? {};
37415
+ const { tool: cmd, args } = await readInvokeBody();
37431
37416
  const spec = scopeForCmd(mod, node.path, cmd);
37432
37417
  if (!spec) throw new TBError("invalid_argument", `unknown cmd '${cmd}' on '${node.path}'`);
37433
37418
  if (!check(ctx, node.path, spec.scope).allow) {
@@ -37576,17 +37561,22 @@ async function handleHelp(c, env) {
37576
37561
  if (!check(ctx, path, "read").allow) throw TBError.notFound("not found");
37577
37562
  const remote = await remotePassthroughIfMatch(c, ctx, registry, path, "~help", deps);
37578
37563
  if (remote) return remote;
37564
+ const builtins = builtinsOf(store);
37565
+ const refresh = c.req.query("refresh") === "1";
37566
+ const schemas = c.req.query("schemas") === "1";
37579
37567
  let node;
37580
37568
  try {
37581
37569
  node = await registry.get(path);
37582
37570
  } catch {
37583
37571
  const toolModel = await toolHelpModelFor(c, ctx, registry, path, deps);
37584
37572
  if (toolModel !== null) return renderHelp(await enrichHelp(toolModel, path, store), rep);
37573
+ const cmdModel = await commandHelpModelFor(registry, ctx, builtins, deps, path, {
37574
+ refresh,
37575
+ schemas
37576
+ });
37577
+ if (cmdModel !== null) return renderHelp(await enrichHelp(cmdModel, path, store), rep);
37585
37578
  throw TBError.notFound("not found");
37586
37579
  }
37587
- const builtins = builtinsOf(store);
37588
- const refresh = c.req.query("refresh") === "1";
37589
- const schemas = c.req.query("schemas") === "1";
37590
37580
  const model = await helpModelFor(node, registry, ctx, builtins, deps, {
37591
37581
  refresh,
37592
37582
  schemas,
@@ -38556,20 +38546,6 @@ var PgSearchIndex = class extends SqlSearchIndex {
38556
38546
 
38557
38547
  // src/pgStateStore.ts
38558
38548
  var DEFAULT_LIST_LIMIT = 1e3;
38559
- var MAX_CODE_POINT = 1114111;
38560
- var SURROGATE_START = 55296;
38561
- var SURROGATE_END = 57343;
38562
- function prefixUpperBound(prefix) {
38563
- const points = [...prefix];
38564
- for (let i = points.length - 1; i >= 0; i--) {
38565
- const code = points[i]?.codePointAt(0);
38566
- if (code === void 0 || code >= MAX_CODE_POINT) continue;
38567
- let next = code + 1;
38568
- if (next >= SURROGATE_START && next <= SURROGATE_END) next = SURROGATE_END + 1;
38569
- return points.slice(0, i).join("") + String.fromCodePoint(next);
38570
- }
38571
- return void 0;
38572
- }
38573
38549
  var PgStateStore = class {
38574
38550
  constructor(sql) {
38575
38551
  this.sql = sql;
@@ -38753,20 +38729,6 @@ var SqliteSearchIndex = class extends SqlSearchIndex {
38753
38729
  // src/sqliteStateStore.ts
38754
38730
  import Database2 from "better-sqlite3";
38755
38731
  var DEFAULT_LIST_LIMIT2 = 1e3;
38756
- var MAX_CODE_POINT2 = 1114111;
38757
- var SURROGATE_START2 = 55296;
38758
- var SURROGATE_END2 = 57343;
38759
- function prefixUpperBound2(prefix) {
38760
- const points = [...prefix];
38761
- for (let i = points.length - 1; i >= 0; i--) {
38762
- const code = points[i]?.codePointAt(0);
38763
- if (code === void 0 || code >= MAX_CODE_POINT2) continue;
38764
- let next = code + 1;
38765
- if (next >= SURROGATE_START2 && next <= SURROGATE_END2) next = SURROGATE_END2 + 1;
38766
- return points.slice(0, i).join("") + String.fromCodePoint(next);
38767
- }
38768
- return void 0;
38769
- }
38770
38732
  var SqliteStateStore = class {
38771
38733
  db;
38772
38734
  stmtGet;
@@ -38816,7 +38778,7 @@ var SqliteStateStore = class {
38816
38778
  async list(prefix, opts) {
38817
38779
  const limit = opts?.limit ?? DEFAULT_LIST_LIMIT2;
38818
38780
  const lowerByCursor = opts?.cursor !== void 0 && opts.cursor >= prefix;
38819
- const upper = prefix === "" ? void 0 : prefixUpperBound2(prefix);
38781
+ const upper = prefix === "" ? void 0 : prefixUpperBound(prefix);
38820
38782
  const conditions = [];
38821
38783
  const params = { n: limit + 1 };
38822
38784
  if (lowerByCursor) {
@@ -38853,7 +38815,7 @@ import { join as join4 } from "path";
38853
38815
  // package.json
38854
38816
  var package_default = {
38855
38817
  name: "@tool-bridge/server",
38856
- version: "0.10.0",
38818
+ version: "0.12.0",
38857
38819
  description: "tool-bridge self-hosted server: run the HTBP gateway on Node with SQLite or PostgreSQL persistence, filesystem object storage and WebSocket device channel \u2014 single container via docker run",
38858
38820
  type: "module",
38859
38821
  license: "MIT",
@@ -38925,10 +38887,33 @@ var package_default = {
38925
38887
  };
38926
38888
 
38927
38889
  // src/assets.ts
38890
+ import { brotliCompressSync, gzipSync, constants as zlibConstants } from "zlib";
38928
38891
  import { dirname as dirname2, join as join3, resolve as resolve2, sep as sep2 } from "path";
38929
38892
  import { readFile as readFile2 } from "fs/promises";
38930
38893
  import { createRequire } from "module";
38931
38894
  import { existsSync } from "fs";
38895
+ var COMPRESSIBLE = /^(?:text\/|application\/(?:javascript|json|manifest\+json|wasm)|image\/svg\+xml)/;
38896
+ var MIN_COMPRESS_BYTES = 1024;
38897
+ function cacheControlFor(pathname) {
38898
+ return pathname.startsWith("/assets/") ? "public, max-age=31536000, immutable" : "no-cache";
38899
+ }
38900
+ function acceptsEncoding(accept, enc) {
38901
+ if (!accept) return false;
38902
+ for (const part of accept.split(",")) {
38903
+ const [tokenRaw = "", ...params] = part.trim().split(";");
38904
+ const token = tokenRaw.trim().toLowerCase();
38905
+ if (token !== enc && token !== "*") continue;
38906
+ const q = params.map((p) => p.trim()).find((p) => p.startsWith("q="));
38907
+ if (q && Number(q.slice(2)) === 0) return false;
38908
+ return true;
38909
+ }
38910
+ return false;
38911
+ }
38912
+ function pickEncoding(accept) {
38913
+ if (acceptsEncoding(accept, "br")) return "br";
38914
+ if (acceptsEncoding(accept, "gzip")) return "gzip";
38915
+ return void 0;
38916
+ }
38932
38917
  function resolveUiDir(uiDirOverride) {
38933
38918
  if (uiDirOverride !== void 0) {
38934
38919
  return existsSync(join3(uiDirOverride, "index.html")) ? resolve2(uiDirOverride) : void 0;
@@ -38942,8 +38927,22 @@ function resolveUiDir(uiDirOverride) {
38942
38927
  return void 0;
38943
38928
  }
38944
38929
  }
38930
+ function toBytes(buf) {
38931
+ const out = new Uint8Array(new ArrayBuffer(buf.byteLength));
38932
+ out.set(buf);
38933
+ return out;
38934
+ }
38935
+ function weakEtag(data) {
38936
+ let hash = 2166136261;
38937
+ for (let i = 0; i < data.length; i++) {
38938
+ hash ^= data[i];
38939
+ hash = Math.imul(hash, 16777619);
38940
+ }
38941
+ return `W/"${(hash >>> 0).toString(16)}-${data.length.toString(16)}"`;
38942
+ }
38945
38943
  function uiAssetsFetcher(uiDir) {
38946
38944
  const root = resolve2(uiDir);
38945
+ const cache = /* @__PURE__ */ new Map();
38947
38946
  return async (request) => {
38948
38947
  const url2 = new URL(request.url);
38949
38948
  let pathname = decodeURIComponent(url2.pathname);
@@ -38952,14 +38951,44 @@ function uiAssetsFetcher(uiDir) {
38952
38951
  if (full !== root && !full.startsWith(root + sep2)) {
38953
38952
  return new Response("not found", { status: 404 });
38954
38953
  }
38955
- try {
38956
- const data = await readFile2(full);
38957
- return new Response(new Uint8Array(data), {
38958
- headers: { "content-type": fsContentTypeOf(full) }
38959
- });
38960
- } catch {
38961
- return new Response("not found", { status: 404 });
38954
+ let asset = cache.get(full);
38955
+ if (asset === void 0) {
38956
+ let data;
38957
+ try {
38958
+ data = await readFile2(full);
38959
+ } catch {
38960
+ return new Response("not found", { status: 404 });
38961
+ }
38962
+ const body = toBytes(data);
38963
+ asset = { body, contentType: fsContentTypeOf(full), etag: weakEtag(body) };
38964
+ cache.set(full, asset);
38965
+ }
38966
+ const headers = new Headers({
38967
+ "content-type": asset.contentType,
38968
+ "cache-control": cacheControlFor(pathname),
38969
+ "etag": asset.etag
38970
+ });
38971
+ if (request.headers.get("if-none-match") === asset.etag) {
38972
+ return new Response(null, { status: 304, headers });
38973
+ }
38974
+ const canCompress = COMPRESSIBLE.test(asset.contentType) && asset.body.length >= MIN_COMPRESS_BYTES;
38975
+ const encoding = canCompress ? pickEncoding(request.headers.get("accept-encoding")) : void 0;
38976
+ if (canCompress) headers.set("vary", "Accept-Encoding");
38977
+ if (encoding === "br") {
38978
+ asset.br ??= toBytes(
38979
+ brotliCompressSync(asset.body, {
38980
+ params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 11 }
38981
+ })
38982
+ );
38983
+ headers.set("content-encoding", "br");
38984
+ return new Response(asset.br, { headers });
38985
+ }
38986
+ if (encoding === "gzip") {
38987
+ asset.gzip ??= toBytes(gzipSync(asset.body, { level: 9 }));
38988
+ headers.set("content-encoding", "gzip");
38989
+ return new Response(asset.gzip, { headers });
38962
38990
  }
38991
+ return new Response(asset.body, { headers });
38963
38992
  };
38964
38993
  }
38965
38994
  function resolveUiAssets(uiDirOverride) {