@de-otio/epimethian-mcp 6.1.1 → 6.2.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/dist/cli/index.js CHANGED
@@ -35028,6 +35028,7 @@ __export(confluence_client_exports, {
35028
35028
  ProfileNotConfiguredError: () => ProfileNotConfiguredError,
35029
35029
  _rawCreatePage: () => _rawCreatePage,
35030
35030
  _rawUpdatePage: () => _rawUpdatePage,
35031
+ _resetSiteLocaleCacheForTests: () => _resetSiteLocaleCacheForTests,
35031
35032
  addLabels: () => addLabels,
35032
35033
  createFooterComment: () => createFooterComment,
35033
35034
  createInlineComment: () => createInlineComment,
@@ -35051,6 +35052,7 @@ __export(confluence_client_exports, {
35051
35052
  getPageChildren: () => getPageChildren,
35052
35053
  getPageVersionBody: () => getPageVersionBody,
35053
35054
  getPageVersions: () => getPageVersions,
35055
+ getSiteDefaultLocale: () => getSiteDefaultLocale,
35054
35056
  getSpaces: () => getSpaces,
35055
35057
  listPages: () => listPages,
35056
35058
  looksLikeMarkdown: () => looksLikeMarkdown,
@@ -35442,7 +35444,7 @@ async function getPage(pageId, includeBody) {
35442
35444
  async function _rawCreatePage(spaceId, title, body, parentId, clientLabel) {
35443
35445
  const cfg = await getConfig();
35444
35446
  const pageBody = normalizeBodyForSubmit(body);
35445
- const epimethianTag = `Epimethian v${"6.1.1"}`;
35447
+ const epimethianTag = `Epimethian v${"6.2.1"}`;
35446
35448
  const versionMsg = cfg.attribution && clientLabel ? `Created by ${clientLabel} (via ${epimethianTag})` : `Created by ${epimethianTag}`;
35447
35449
  const payload = {
35448
35450
  title,
@@ -35463,7 +35465,7 @@ async function _rawCreatePage(spaceId, title, body, parentId, clientLabel) {
35463
35465
  async function _rawUpdatePage(pageId, opts) {
35464
35466
  const cfg = await getConfig();
35465
35467
  const newVersion = opts.version + 1;
35466
- const epimethianTag = `Epimethian v${"6.1.1"}`;
35468
+ const epimethianTag = `Epimethian v${"6.2.1"}`;
35467
35469
  const effectiveClient = cfg.attribution ? opts.clientLabel : void 0;
35468
35470
  let versionMessage;
35469
35471
  if (opts.versionMessage && effectiveClient)
@@ -35731,6 +35733,27 @@ async function removeLabel(pageId, label) {
35731
35733
  url.searchParams.set("name", label);
35732
35734
  await confluenceRequest(url.toString(), { method: "DELETE" });
35733
35735
  }
35736
+ function _resetSiteLocaleCacheForTests() {
35737
+ _siteLocaleCache.clear();
35738
+ }
35739
+ async function getSiteDefaultLocale(cfg) {
35740
+ const key = cfg.url;
35741
+ const cached2 = _siteLocaleCache.get(key);
35742
+ if (cached2 !== void 0) return cached2;
35743
+ const promise = (async () => {
35744
+ try {
35745
+ const res = await confluenceRequest(`${cfg.apiV1}/settings/systemInfo`);
35746
+ const data = await res.json();
35747
+ const raw = typeof data?.defaultLocale === "string" ? data.defaultLocale : void 0;
35748
+ if (!raw) return void 0;
35749
+ return raw.split(/[_-]/)[0].toLowerCase() || void 0;
35750
+ } catch {
35751
+ return void 0;
35752
+ }
35753
+ })();
35754
+ _siteLocaleCache.set(key, promise);
35755
+ return promise;
35756
+ }
35734
35757
  async function getContentState(pageId) {
35735
35758
  const cfg = await getConfig();
35736
35759
  const url = new URL(`${cfg.apiV1}/content/${pageId}/state`);
@@ -35907,6 +35930,9 @@ function extractHeadings(storageHtml) {
35907
35930
  }
35908
35931
  return lines.length > 0 ? lines.join("\n") : "(no headings found)";
35909
35932
  }
35933
+ function maskCdataForParse(storage) {
35934
+ return storage.replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, (m) => " ".repeat(m.length));
35935
+ }
35910
35936
  function findHeadingInTree(root, headingText) {
35911
35937
  const allHeadings = root.querySelectorAll("h1, h2, h3, h4, h5, h6");
35912
35938
  for (const heading2 of allHeadings) {
@@ -35921,69 +35947,47 @@ function findHeadingInTree(root, headingText) {
35921
35947
  }
35922
35948
  return null;
35923
35949
  }
35924
- function extractSection(storageHtml, headingText) {
35950
+ function findSectionRange(storageHtml, headingText) {
35925
35951
  const { parse: parse5 } = require_dist2();
35926
- const root = parse5(storageHtml);
35952
+ const root = parse5(maskCdataForParse(storageHtml));
35927
35953
  const found = findHeadingInTree(root, headingText);
35928
35954
  if (!found) return null;
35929
35955
  const { siblings, startIdx, headingLevel } = found;
35930
- let endIdx = siblings.length;
35956
+ const heading2 = siblings[startIdx];
35957
+ let sectionEnd;
35931
35958
  for (let i = startIdx + 1; i < siblings.length; i++) {
35932
35959
  const node = siblings[i];
35933
35960
  if (node.nodeType !== 1) continue;
35934
35961
  const el = node;
35935
35962
  const tagMatch = el.tagName?.match(/^H([1-6])$/i);
35936
35963
  if (tagMatch && parseInt(tagMatch[1], 10) <= headingLevel) {
35937
- endIdx = i;
35964
+ sectionEnd = el.range[0];
35938
35965
  break;
35939
35966
  }
35940
35967
  }
35941
- const sectionNodes = siblings.slice(startIdx, endIdx);
35942
- return sectionNodes.map((n) => n.toString()).join("");
35968
+ if (sectionEnd === void 0) {
35969
+ sectionEnd = startIdx + 1 < siblings.length ? siblings[siblings.length - 1].range[1] : heading2.range[1];
35970
+ }
35971
+ return {
35972
+ headingStart: heading2.range[0],
35973
+ headingEnd: heading2.range[1],
35974
+ sectionEnd
35975
+ };
35976
+ }
35977
+ function extractSection(storageHtml, headingText) {
35978
+ const r = findSectionRange(storageHtml, headingText);
35979
+ if (r === null) return null;
35980
+ return storageHtml.slice(r.headingStart, r.sectionEnd);
35943
35981
  }
35944
35982
  function extractSectionBody(storageHtml, headingText) {
35945
- const { parse: parse5 } = require_dist2();
35946
- const root = parse5(storageHtml);
35947
- const found = findHeadingInTree(root, headingText);
35948
- if (!found) return null;
35949
- const { siblings, startIdx, headingLevel } = found;
35950
- let endIdx = siblings.length;
35951
- for (let i = startIdx + 1; i < siblings.length; i++) {
35952
- const node = siblings[i];
35953
- if (node.nodeType !== 1) continue;
35954
- const el = node;
35955
- const tagMatch = el.tagName?.match(/^H([1-6])$/i);
35956
- if (tagMatch && parseInt(tagMatch[1], 10) <= headingLevel) {
35957
- endIdx = i;
35958
- break;
35959
- }
35960
- }
35961
- const bodyNodes = siblings.slice(startIdx + 1, endIdx);
35962
- return bodyNodes.map((n) => n.toString()).join("");
35983
+ const r = findSectionRange(storageHtml, headingText);
35984
+ if (r === null) return null;
35985
+ return storageHtml.slice(r.headingEnd, r.sectionEnd);
35963
35986
  }
35964
35987
  function replaceSection(storageHtml, headingText, newContent) {
35965
- const { parse: parse5 } = require_dist2();
35966
- const root = parse5(storageHtml);
35967
- const found = findHeadingInTree(root, headingText);
35968
- if (!found) return null;
35969
- const { siblings, startIdx, headingLevel } = found;
35970
- let endIdx = siblings.length;
35971
- for (let i = startIdx + 1; i < siblings.length; i++) {
35972
- const node = siblings[i];
35973
- if (node.nodeType !== 1) continue;
35974
- const el = node;
35975
- const tagMatch = el.tagName?.match(/^H([1-6])$/i);
35976
- if (tagMatch && parseInt(tagMatch[1], 10) <= headingLevel) {
35977
- endIdx = i;
35978
- break;
35979
- }
35980
- }
35981
- const before = siblings.slice(0, startIdx);
35982
- const heading2 = siblings[startIdx];
35983
- const after = siblings.slice(endIdx);
35984
- const parent = heading2.parentNode;
35985
- parent.innerHTML = before.map((n) => n.toString()).join("") + heading2.toString() + newContent + after.map((n) => n.toString()).join("");
35986
- return root.toString();
35988
+ const r = findSectionRange(storageHtml, headingText);
35989
+ if (r === null) return null;
35990
+ return storageHtml.slice(0, r.headingEnd) + newContent + storageHtml.slice(r.sectionEnd);
35987
35991
  }
35988
35992
  function truncateStorageFormat(storageHtml, maxLength) {
35989
35993
  if (storageHtml.length <= maxLength) return storageHtml;
@@ -36149,7 +36153,7 @@ async function formatPage(page, optionsOrIncludeBody) {
36149
36153
  }
36150
36154
  return lines.join("\n");
36151
36155
  }
36152
- var import_turndown, CLIENT_LABEL_DISALLOWED_RE, _clientLabel, _config, ProfileNotConfiguredError, PageSchema, PagesResultSchema, SpaceSchema, SpacesResultSchema, AttachmentSchema, AttachmentsResultSchema, LabelSchema, LabelsResultSchema, ContentStateSchema, CommentSchema, CommentsResultSchema, UploadResultSchema, VersionMetadataSchema, VersionsResultSchema, V1PageVersionSchema, ConfluenceApiError, ConfluenceAuthError, ConfluencePermissionError, ConfluenceNotFoundError, ConfluenceConflictError, UserSchema, UserSearchResultSchema, ATTRIBUTION_LABEL, LEGACY_ATTRIBUTION_LABEL, DANGEROUS_TAG_RE, HTML_TAG_RE, HTML_ENTITY_RE, SAFE_MACRO_PARAMS;
36156
+ var import_turndown, CLIENT_LABEL_DISALLOWED_RE, _clientLabel, _config, ProfileNotConfiguredError, PageSchema, PagesResultSchema, SpaceSchema, SpacesResultSchema, AttachmentSchema, AttachmentsResultSchema, LabelSchema, LabelsResultSchema, ContentStateSchema, CommentSchema, CommentsResultSchema, UploadResultSchema, VersionMetadataSchema, VersionsResultSchema, V1PageVersionSchema, ConfluenceApiError, ConfluenceAuthError, ConfluencePermissionError, ConfluenceNotFoundError, ConfluenceConflictError, UserSchema, UserSearchResultSchema, ATTRIBUTION_LABEL, LEGACY_ATTRIBUTION_LABEL, _siteLocaleCache, DANGEROUS_TAG_RE, HTML_TAG_RE, HTML_ENTITY_RE, SAFE_MACRO_PARAMS;
36153
36157
  var init_confluence_client = __esm({
36154
36158
  "src/server/confluence-client.ts"() {
36155
36159
  "use strict";
@@ -36307,6 +36311,7 @@ var init_confluence_client = __esm({
36307
36311
  });
36308
36312
  ATTRIBUTION_LABEL = "epimethian-edited";
36309
36313
  LEGACY_ATTRIBUTION_LABEL = "epimethian-managed";
36314
+ _siteLocaleCache = /* @__PURE__ */ new Map();
36310
36315
  DANGEROUS_TAG_RE = /<(ac:structured-macro|script|iframe|embed|object)[\s\S]*?<\/\1>|<(ac:structured-macro|script|iframe|embed|object)[^>]*\/>/gi;
36311
36316
  HTML_TAG_RE = /<\/?[a-z][a-z0-9]*(?::[a-z][a-z0-9-]*)?[\s>\/]/i;
36312
36317
  HTML_ENTITY_RE = /&(?:[a-zA-Z]+|#x?[0-9a-fA-F]+);/;
@@ -47082,11 +47087,11 @@ function parseBudget(envValue, fallback) {
47082
47087
  }
47083
47088
  return n;
47084
47089
  }
47085
- var HOUR_MS, DEFAULT_SESSION_BUDGET, DEFAULT_HOURLY_BUDGET, WriteBudget, WRITE_BUDGET_EXCEEDED, WriteBudgetExceededError, writeBudget;
47090
+ var WINDOW_MS, DEFAULT_SESSION_BUDGET, DEFAULT_HOURLY_BUDGET, WriteBudget, WRITE_BUDGET_EXCEEDED, WriteBudgetExceededError, writeBudget;
47086
47091
  var init_write_budget = __esm({
47087
47092
  "src/server/write-budget.ts"() {
47088
47093
  "use strict";
47089
- HOUR_MS = 60 * 60 * 1e3;
47094
+ WINDOW_MS = 15 * 60 * 1e3;
47090
47095
  DEFAULT_SESSION_BUDGET = 100;
47091
47096
  DEFAULT_HOURLY_BUDGET = 25;
47092
47097
  WriteBudget = class {
@@ -47113,7 +47118,7 @@ var init_write_budget = __esm({
47113
47118
  */
47114
47119
  consume() {
47115
47120
  const now = Date.now();
47116
- const cutoff = now - HOUR_MS;
47121
+ const cutoff = now - WINDOW_MS;
47117
47122
  this.hourlyTimestamps = this.hourlyTimestamps.filter((ts) => ts >= cutoff);
47118
47123
  const sessionLimit = this.sessionLimit;
47119
47124
  if (sessionLimit > 0 && this.sessionCount >= sessionLimit) {
@@ -47127,10 +47132,10 @@ var init_write_budget = __esm({
47127
47132
  const hourlyLimit = this.hourlyLimit;
47128
47133
  if (hourlyLimit > 0 && this.hourlyTimestamps.length >= hourlyLimit) {
47129
47134
  const oldest = this.hourlyTimestamps[0];
47130
- const waitMs = Math.max(0, oldest + HOUR_MS - now);
47135
+ const waitMs = Math.max(0, oldest + WINDOW_MS - now);
47131
47136
  const waitMin = Math.ceil(waitMs / 6e4);
47132
47137
  throw new WriteBudgetExceededError(
47133
- `Hourly write budget exhausted: ${this.hourlyTimestamps.length} writes in the last hour, limit ${hourlyLimit}. Window opens again in ~${waitMin} min. Raise the cap with EPIMETHIAN_WRITE_BUDGET_HOURLY=<n> (or 0 to disable).`,
47138
+ `Rolling write budget exhausted: ${this.hourlyTimestamps.length} writes in the last 15 min, limit ${hourlyLimit}. Window opens again in ~${waitMin} min. Raise the cap with EPIMETHIAN_WRITE_BUDGET_HOURLY=<n> (or 0 to disable).`,
47134
47139
  "hourly",
47135
47140
  this.hourlyTimestamps.length,
47136
47141
  hourlyLimit
@@ -47146,7 +47151,7 @@ var init_write_budget = __esm({
47146
47151
  /** Current hourly counter (for observability). */
47147
47152
  get hourly() {
47148
47153
  const now = Date.now();
47149
- const cutoff = now - HOUR_MS;
47154
+ const cutoff = now - WINDOW_MS;
47150
47155
  this.hourlyTimestamps = this.hourlyTimestamps.filter((ts) => ts >= cutoff);
47151
47156
  return this.hourlyTimestamps.length;
47152
47157
  }
@@ -48704,7 +48709,7 @@ __export(upgrade_exports, {
48704
48709
  runUpgrade: () => runUpgrade
48705
48710
  });
48706
48711
  async function runUpgrade() {
48707
- const currentVersion = "6.1.1";
48712
+ const currentVersion = "6.2.1";
48708
48713
  console.log(`epimethian-mcp upgrade: current version v${currentVersion}`);
48709
48714
  let pending = await getPendingUpdate();
48710
48715
  if (!pending) {
@@ -61787,7 +61792,7 @@ ${titleFenced}${echo2}`
61787
61792
  inputSchema: {}
61788
61793
  },
61789
61794
  async () => {
61790
- let text2 = `epimethian-mcp v${"6.1.1"}`;
61795
+ let text2 = `epimethian-mcp v${"6.2.1"}`;
61791
61796
  try {
61792
61797
  const pending = await getPendingUpdate();
61793
61798
  if (pending) {
@@ -61818,7 +61823,7 @@ ${label} update available: v${pending.current} \u2192 v${pending.latest}. Run \`
61818
61823
  const pending = await getPendingUpdate();
61819
61824
  if (!pending) {
61820
61825
  return toolResult(
61821
- `epimethian-mcp v${"6.1.1"} is already up to date.`
61826
+ `epimethian-mcp v${"6.2.1"} is already up to date.`
61822
61827
  );
61823
61828
  }
61824
61829
  const output = await performUpgrade(pending.latest);
@@ -61840,7 +61845,7 @@ async function startRecoveryServer(profile) {
61840
61845
  const server = new McpServer(
61841
61846
  {
61842
61847
  name: `confluence-${profile}-setup-needed`,
61843
- version: "6.1.1"
61848
+ version: "6.2.1"
61844
61849
  },
61845
61850
  {
61846
61851
  instructions: `The Confluence profile "${profile}" referenced by CONFLUENCE_PROFILE has no keychain entry, so no Confluence tools are available. Call the setup_profile tool for instructions to create it.`
@@ -61891,21 +61896,21 @@ async function main() {
61891
61896
  const serverName = config3.profile ? `confluence-${config3.profile}` : "confluence";
61892
61897
  const server = new McpServer({
61893
61898
  name: serverName,
61894
- version: "6.1.1"
61899
+ version: "6.2.1"
61895
61900
  });
61896
61901
  await registerTools(server, config3);
61897
61902
  const transport = new StdioServerTransport();
61898
61903
  await server.connect(transport);
61899
61904
  try {
61900
61905
  const pending = await getPendingUpdate();
61901
- if (pending && pending.current === "6.1.1") {
61906
+ if (pending && pending.current === "6.2.1") {
61902
61907
  console.error(
61903
61908
  `epimethian-mcp: update available: v${pending.current} \u2192 v${pending.latest} (${pending.type}). Run \`epimethian-mcp upgrade\` to install.`
61904
61909
  );
61905
61910
  }
61906
61911
  } catch {
61907
61912
  }
61908
- checkForUpdates("6.1.1").catch(() => {
61913
+ checkForUpdates("6.2.1").catch(() => {
61909
61914
  });
61910
61915
  }
61911
61916