@zackbart/connecta 0.8.0 → 0.9.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 (151) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +31 -55
  3. package/SECURITY.md +5 -11
  4. package/dist/activity.d.ts +0 -8
  5. package/dist/activity.d.ts.map +1 -1
  6. package/dist/activity.js +0 -1
  7. package/dist/activity.js.map +1 -1
  8. package/dist/auth/bearer.d.ts +1 -7
  9. package/dist/auth/bearer.d.ts.map +1 -1
  10. package/dist/auth/bearer.js +2 -23
  11. package/dist/auth/bearer.js.map +1 -1
  12. package/dist/auth/clerk.d.ts +2 -11
  13. package/dist/auth/clerk.d.ts.map +1 -1
  14. package/dist/auth/clerk.js +31 -46
  15. package/dist/auth/clerk.js.map +1 -1
  16. package/dist/auth/downstream-oauth.d.ts +15 -6
  17. package/dist/auth/downstream-oauth.d.ts.map +1 -1
  18. package/dist/auth/downstream-oauth.js +60 -11
  19. package/dist/auth/downstream-oauth.js.map +1 -1
  20. package/dist/call-admission.d.ts.map +1 -1
  21. package/dist/call-admission.js +0 -1
  22. package/dist/call-admission.js.map +1 -1
  23. package/dist/catalog-service.d.ts +1 -2
  24. package/dist/catalog-service.d.ts.map +1 -1
  25. package/dist/catalog-service.js +12 -18
  26. package/dist/catalog-service.js.map +1 -1
  27. package/dist/catalog.d.ts +6 -0
  28. package/dist/catalog.d.ts.map +1 -1
  29. package/dist/catalog.js +51 -0
  30. package/dist/catalog.js.map +1 -1
  31. package/dist/connectors/remote-mcp.d.ts +1 -1
  32. package/dist/connectors/remote-mcp.d.ts.map +1 -1
  33. package/dist/connectors/remote-mcp.js +73 -71
  34. package/dist/connectors/remote-mcp.js.map +1 -1
  35. package/dist/errors.d.ts +19 -1
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +1 -0
  38. package/dist/errors.js.map +1 -1
  39. package/dist/execute.d.ts +4 -9
  40. package/dist/execute.d.ts.map +1 -1
  41. package/dist/execute.js +41 -20
  42. package/dist/execute.js.map +1 -1
  43. package/dist/executor-result.js +1 -1
  44. package/dist/executor-result.js.map +1 -1
  45. package/dist/index.d.ts +3 -82
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +17 -90
  48. package/dist/index.js.map +1 -1
  49. package/dist/invocation.d.ts +5 -0
  50. package/dist/invocation.d.ts.map +1 -1
  51. package/dist/invocation.js +46 -10
  52. package/dist/invocation.js.map +1 -1
  53. package/dist/meta-tools.d.ts +4 -12
  54. package/dist/meta-tools.d.ts.map +1 -1
  55. package/dist/meta-tools.js +119 -169
  56. package/dist/meta-tools.js.map +1 -1
  57. package/dist/operator-ui/generated.d.ts +2 -2
  58. package/dist/operator-ui/generated.d.ts.map +1 -1
  59. package/dist/operator-ui/generated.js +2 -2
  60. package/dist/operator-ui/generated.js.map +1 -1
  61. package/dist/operator-ui/model.d.ts +1 -22
  62. package/dist/operator-ui/model.d.ts.map +1 -1
  63. package/dist/operator-ui/model.js.map +1 -1
  64. package/dist/registry.d.ts +11 -178
  65. package/dist/registry.d.ts.map +1 -1
  66. package/dist/registry.js +52 -330
  67. package/dist/registry.js.map +1 -1
  68. package/dist/retired-toolkits.d.ts +3 -0
  69. package/dist/retired-toolkits.d.ts.map +1 -0
  70. package/dist/retired-toolkits.js +9 -0
  71. package/dist/retired-toolkits.js.map +1 -0
  72. package/dist/routes/activity.d.ts.map +1 -1
  73. package/dist/routes/activity.js +2 -5
  74. package/dist/routes/activity.js.map +1 -1
  75. package/dist/routes/credentials.d.ts.map +1 -1
  76. package/dist/routes/credentials.js +1 -12
  77. package/dist/routes/credentials.js.map +1 -1
  78. package/dist/routes/mcp.d.ts.map +1 -1
  79. package/dist/routes/mcp.js +94 -188
  80. package/dist/routes/mcp.js.map +1 -1
  81. package/dist/routes/oauth.d.ts.map +1 -1
  82. package/dist/routes/oauth.js +3 -8
  83. package/dist/routes/oauth.js.map +1 -1
  84. package/dist/routes/shared.d.ts +9 -30
  85. package/dist/routes/shared.d.ts.map +1 -1
  86. package/dist/routes/shared.js +10 -69
  87. package/dist/routes/shared.js.map +1 -1
  88. package/dist/routes/ui.d.ts.map +1 -1
  89. package/dist/routes/ui.js +4 -10
  90. package/dist/routes/ui.js.map +1 -1
  91. package/dist/server.d.ts.map +1 -1
  92. package/dist/server.js +1 -31
  93. package/dist/server.js.map +1 -1
  94. package/dist/skills.d.ts +3 -6
  95. package/dist/skills.d.ts.map +1 -1
  96. package/dist/skills.js +9 -38
  97. package/dist/skills.js.map +1 -1
  98. package/dist/timeout.d.ts.map +1 -1
  99. package/dist/timeout.js +2 -4
  100. package/dist/timeout.js.map +1 -1
  101. package/dist/types.d.ts +8 -61
  102. package/dist/types.d.ts.map +1 -1
  103. package/dist/ui.d.ts +1 -2
  104. package/dist/ui.d.ts.map +1 -1
  105. package/dist/ui.js +5 -35
  106. package/dist/ui.js.map +1 -1
  107. package/dist/version.d.ts +1 -1
  108. package/dist/version.js +1 -1
  109. package/package.json +3 -2
  110. package/src/activity.ts +0 -9
  111. package/src/auth/bearer.ts +3 -32
  112. package/src/auth/clerk.ts +35 -48
  113. package/src/auth/downstream-oauth.ts +106 -24
  114. package/src/call-admission.ts +0 -1
  115. package/src/catalog-service.ts +23 -23
  116. package/src/catalog.ts +55 -0
  117. package/src/connectors/remote-mcp.ts +106 -86
  118. package/src/errors.ts +22 -0
  119. package/src/execute.ts +43 -24
  120. package/src/executor-result.ts +1 -1
  121. package/src/index.ts +24 -208
  122. package/src/invocation.ts +63 -10
  123. package/src/meta-tools.ts +139 -187
  124. package/src/operator-ui/browser.css +1 -54
  125. package/src/operator-ui/browser.ts +16 -150
  126. package/src/operator-ui/generated.ts +2 -2
  127. package/src/operator-ui/model.ts +1 -23
  128. package/src/registry.ts +59 -429
  129. package/src/retired-toolkits.ts +14 -0
  130. package/src/routes/activity.ts +1 -6
  131. package/src/routes/credentials.ts +0 -12
  132. package/src/routes/mcp.ts +110 -230
  133. package/src/routes/oauth.ts +2 -8
  134. package/src/routes/shared.ts +10 -100
  135. package/src/routes/ui.ts +2 -12
  136. package/src/server.ts +1 -31
  137. package/src/skills.ts +9 -38
  138. package/src/timeout.ts +2 -4
  139. package/src/types.ts +11 -62
  140. package/src/ui.ts +9 -46
  141. package/src/version.ts +1 -1
  142. package/dist/credential-health.d.ts +0 -205
  143. package/dist/credential-health.d.ts.map +0 -1
  144. package/dist/credential-health.js +0 -610
  145. package/dist/credential-health.js.map +0 -1
  146. package/dist/toolkits.d.ts +0 -124
  147. package/dist/toolkits.d.ts.map +0 -1
  148. package/dist/toolkits.js +0 -322
  149. package/dist/toolkits.js.map +0 -1
  150. package/src/credential-health.ts +0 -835
  151. package/src/toolkits.ts +0 -461
@@ -1,5 +1,5 @@
1
1
  // Generated by scripts/build-operator-ui.mjs. Do not edit.
2
2
  // Source: src/operator-ui/browser.ts and src/operator-ui/browser.css.
3
- export const OPERATOR_UI_CSS = "/* src/operator-ui/browser.css */\n:root {\n color-scheme: light;\n --ink: #000;\n --paper: #fff;\n --rule: #ccc;\n --muted: #666;\n --trace: #f5f5f5;\n --shell: 70rem;\n --pad: 1rem;\n --gap: 1.5rem;\n --sans:\n \"Helvetica Neue\",\n Helvetica,\n Arial,\n sans-serif;\n --mono:\n ui-monospace,\n \"SF Mono\",\n Menlo,\n Monaco,\n \"Cascadia Code\",\n Consolas,\n monospace;\n}\n* {\n border-radius: 0;\n box-sizing: border-box;\n}\nhtml {\n background: var(--paper);\n color: var(--ink);\n font-family: var(--sans);\n font-size: 16px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizeLegibility;\n}\nbody {\n margin: 0;\n min-height: 100vh;\n}\n::selection {\n background: var(--ink);\n color: var(--paper);\n}\n:is(h1, h2, h3, p, ul, ol) {\n margin: 0;\n padding: 0;\n}\n:is(h1, h2, h3) {\n font-size: inherit;\n font-weight: 400;\n}\n:is(ul, ol) {\n list-style: none;\n}\na {\n color: inherit;\n}\nbutton,\ninput {\n font: inherit;\n}\nbutton {\n background: none;\n border: 0;\n color: inherit;\n cursor: pointer;\n margin: 0;\n padding: 0;\n text-align: left;\n}\nbutton:disabled {\n cursor: wait;\n opacity: .5;\n}\ninput {\n background: var(--paper);\n border: 1px solid var(--rule);\n color: var(--ink);\n min-height: 2rem;\n padding: .2rem .5rem;\n}\ninput:focus-visible {\n outline-offset: -1px;\n}\n:is(a, button, input, summary):focus-visible {\n outline: 1px solid var(--ink);\n}\n:is(a, button, summary):focus-visible {\n outline-offset: 2px;\n}\n.skip-link {\n background: var(--paper);\n left: var(--pad);\n padding: .5rem;\n position: fixed;\n top: -4rem;\n z-index: 10;\n}\n.skip-link:focus {\n top: var(--pad);\n}\n.shell {\n margin: 0 auto;\n max-width: var(--shell);\n padding-left: var(--pad);\n padding-right: var(--pad);\n}\n.pgrid {\n column-gap: var(--gap);\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n row-gap: 1rem;\n}\n.pcap {\n grid-column: 1;\n}\n.pbody {\n grid-column: 2 / -1;\n min-width: 0;\n}\n.cap,\n.meta {\n color: var(--muted);\n font-size: .9em;\n}\n.mono {\n font-family: var(--mono);\n font-size: .78rem;\n}\n.hidden {\n display: none !important;\n}\n.visually-hidden {\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n height: 1px;\n overflow: hidden;\n position: absolute;\n white-space: nowrap;\n width: 1px;\n}\n.masthead {\n align-items: start;\n padding-bottom: var(--pad);\n padding-top: var(--pad);\n}\n.brand {\n font-weight: 500;\n grid-column: 1;\n text-decoration: none;\n}\n.mast-nav {\n display: flex;\n gap: var(--gap);\n grid-column: 2 / -1;\n justify-content: space-between;\n min-width: 0;\n}\n.mast-actions {\n display: flex;\n gap: var(--gap);\n justify-content: flex-end;\n min-width: 0;\n}\n.page-nav,\n.session-actions {\n display: flex;\n flex-wrap: wrap;\n gap: .5rem var(--gap);\n}\n.mast-actions :is(a, button) {\n align-items: center;\n display: inline-flex;\n min-height: 2rem;\n}\n.navlink,\n.linklike {\n text-decoration: underline;\n text-decoration-thickness: 1.5px;\n text-underline-offset: .22em;\n}\n.navlink {\n text-decoration-color: transparent;\n}\n.navlink:hover,\n.navlink:focus-visible,\n.navlink[aria-current=page] {\n text-decoration-color: currentColor;\n}\n.linklike {\n text-decoration-color: currentColor;\n}\n.linklike:hover,\n.linklike:focus-visible {\n text-decoration-color: transparent;\n}\n.page {\n padding-bottom: 5rem;\n}\n.lead {\n margin-top: 6rem;\n}\n.section,\n.section + .section {\n margin-top: 3rem;\n}\n.lead-copy,\n.body-copy {\n max-width: 34em;\n}\n.lead-copy > * + *,\n.body-copy > * + * {\n margin-top: 1.5rem;\n}\n.row,\n.actions {\n align-items: center;\n display: flex;\n flex-wrap: wrap;\n gap: var(--gap);\n}\n.row input {\n flex: 1;\n min-width: 12rem;\n}\n.gate-actions {\n margin-top: 1.5rem;\n}\n#err {\n margin-top: 1.5rem;\n text-decoration: underline;\n}\n.endpoint {\n border-bottom: 1px solid var(--rule);\n border-top: 1px solid var(--rule);\n}\n.endpoint-row {\n align-items: baseline;\n display: flex;\n gap: var(--gap);\n min-width: 0;\n padding: .75rem 0;\n}\n.endpoint-row code {\n flex: 1;\n min-width: 0;\n overflow-x: auto;\n white-space: nowrap;\n}\n.endpoint-row button {\n flex: none;\n}\n.connector-tools {\n border-bottom: 1px solid var(--rule);\n}\n.toolbar {\n margin-bottom: 1.5rem;\n}\n.toolbar input {\n flex-basis: 18rem;\n}\n#notice {\n margin-bottom: .75rem;\n}\n#notice:empty {\n display: none;\n}\n#notice:not(:empty) {\n text-decoration: underline;\n}\n#oauthNotice {\n margin-bottom: .75rem;\n}\n#oauthNotice:empty {\n display: none;\n}\n.error-notice,\n.msg {\n text-decoration: underline;\n}\n.card,\n.credential-card,\n.activity-item {\n padding-left: 1.25rem;\n position: relative;\n}\n.card::before,\n.credential-card::before,\n.activity-item::before {\n background: var(--rule);\n bottom: 0;\n content: \"\";\n left: .25rem;\n position: absolute;\n top: 0;\n width: 1px;\n}\n.card {\n border-top: 1px solid var(--rule);\n padding-bottom: .75rem;\n padding-top: .75rem;\n}\n.connector-head {\n display: grid;\n gap: var(--gap);\n grid-template-columns: minmax(0, 2fr) minmax(10rem, 1fr);\n}\n.connector-title {\n align-items: baseline;\n display: flex;\n gap: .5rem;\n}\n.connector-title .dot,\n.activity-stamp .dot {\n margin-left: -1.25rem;\n}\n.activity-stamp {\n align-items: baseline;\n display: flex;\n gap: .75rem;\n}\n.card h2 {\n overflow-wrap: anywhere;\n}\n.connector-state {\n text-align: right;\n}\n.dot {\n background: var(--paper);\n border: 1px solid var(--ink);\n display: inline-block;\n flex: none;\n height: .5rem;\n width: .5rem;\n z-index: 1;\n}\n.dot.ok {\n background: var(--ink);\n}\n.dot.auth_required {\n background:\n linear-gradient(\n 90deg,\n var(--ink) 50%,\n var(--paper) 50%);\n}\n.connector-description {\n margin-top: .25rem;\n max-width: 40rem;\n}\n.connector-message,\n.connector-auth {\n margin-top: .75rem;\n}\n.connector-toolkits {\n align-items: baseline;\n display: flex;\n flex-wrap: wrap;\n gap: .25rem .5rem;\n margin-top: .5rem;\n}\n.scope-label {\n border: 1px solid var(--rule);\n display: inline-block;\n font-family: var(--mono);\n font-size: .72rem;\n padding: .05rem .35rem;\n}\n.toolkit-copy {\n margin-bottom: 1rem;\n max-width: 42rem;\n}\n.toolkit-ledger {\n border-bottom: 1px solid var(--rule);\n}\n.toolkit-card {\n border-top: 1px solid var(--rule);\n padding: .9rem 0 1rem;\n}\n.toolkit-head {\n display: grid;\n gap: var(--gap);\n grid-template-columns: minmax(0, 2fr) minmax(10rem, 1fr);\n}\n.toolkit-name {\n align-items: baseline;\n display: flex;\n flex-wrap: wrap;\n gap: .5rem;\n}\n.toolkit-name h3 {\n overflow-wrap: anywhere;\n}\n.toolkit-state {\n text-align: right;\n}\n.toolkit-connectors {\n display: flex;\n flex-wrap: wrap;\n gap: .5rem;\n margin-top: .75rem;\n}\n.toolkit-connector {\n border-left: 1px solid var(--ink);\n display: inline-flex;\n flex-wrap: wrap;\n gap: .4rem;\n padding-left: .5rem;\n}\n.toolkit-rules {\n margin-top: .75rem;\n}\n.toolkit-rules > * + * {\n margin-top: .25rem;\n}\n.toolkit-endpoint {\n margin-top: .75rem;\n}\n.credential-ledger {\n border-bottom: 1px solid var(--rule);\n}\n.credential-card {\n border-top: 1px solid var(--rule);\n padding-bottom: .75rem;\n padding-top: .75rem;\n}\n.credential-head {\n align-items: baseline;\n display: flex;\n flex-wrap: wrap;\n gap: .25rem var(--gap);\n justify-content: space-between;\n}\n.credential-copy {\n margin-top: .25rem;\n max-width: 40rem;\n}\n.credential-field-summary {\n border-top: 1px solid var(--rule);\n margin-top: .75rem;\n}\n.credential-field-summary > div {\n border-bottom: 1px solid var(--rule);\n display: flex;\n flex-wrap: wrap;\n gap: .25rem var(--gap);\n justify-content: space-between;\n padding: .5rem 0;\n}\n.credential-actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--gap);\n margin-top: .75rem;\n}\n.credential-actions button,\n.credential-form button,\n.activity-controls button,\n.activity-more {\n align-items: center;\n display: inline-flex;\n min-height: 2.75rem;\n}\n.credential-form {\n align-items: center;\n display: flex;\n flex-wrap: wrap;\n gap: .75rem var(--gap);\n margin-top: .75rem;\n}\n.credential-form > input {\n flex: 1 1 18rem;\n}\n.credential-fields {\n display: grid;\n flex: 1 1 100%;\n gap: .75rem;\n}\n.credential-field {\n align-items: center;\n display: grid;\n gap: var(--gap);\n grid-template-columns: minmax(9rem, 12rem) 1fr;\n}\n.credential-field input {\n min-width: 0;\n width: 100%;\n}\n.danger {\n text-decoration-style: double;\n}\ndetails {\n margin-top: .75rem;\n}\nsummary {\n cursor: pointer;\n list-style: none;\n width: max-content;\n}\nsummary::-webkit-details-marker {\n display: none;\n}\n.tool-list {\n border-bottom: 1px solid var(--rule);\n margin-top: .5rem;\n}\n.tool {\n border-top: 1px solid var(--rule);\n display: grid;\n gap: .25rem var(--gap);\n grid-template-columns: minmax(12rem, 1fr) minmax(0, 2fr);\n padding: .5rem 0;\n}\n.tool code {\n font-family: var(--mono);\n font-size: .78rem;\n overflow-wrap: anywhere;\n}\n.tool .td {\n color: var(--muted);\n font-size: .9em;\n}\n.empty {\n border-top: 1px solid var(--rule);\n padding: .75rem 0;\n}\n.activity-copy {\n margin-bottom: 1.5rem;\n}\n.activity-controls {\n margin-bottom: 1.5rem;\n}\n.activity-controls input {\n flex: 1 1 18rem;\n}\n#activityNotice {\n margin-bottom: .75rem;\n}\n.activity-ledger {\n border-bottom: 1px solid var(--rule);\n}\n.activity-item {\n border-top: 1px solid var(--rule);\n display: grid;\n gap: .25rem var(--gap);\n grid-template-columns: minmax(9rem, .85fr) minmax(12rem, 1.4fr) minmax(8rem, .9fr);\n padding-bottom: .75rem;\n padding-top: .75rem;\n}\n.activity-time,\n.activity-actor,\n.activity-detail {\n color: var(--muted);\n font-size: .82rem;\n}\n.activity-actor-id {\n color: var(--muted);\n margin-top: .1rem;\n}\n.activity-address {\n font-family: var(--mono);\n font-size: .78rem;\n overflow-wrap: anywhere;\n}\n.activity-outcome {\n font-size: .9em;\n}\n.activity-item.error .activity-outcome,\n.activity-item.timeout .activity-outcome,\n.activity-item.cancelled .activity-outcome {\n text-decoration: underline;\n}\n.activity-empty {\n border-top: 1px solid var(--rule);\n padding: .75rem 0;\n}\n.activity-more {\n margin-top: .75rem;\n}\n.unavailable {\n background: var(--trace);\n border-bottom: 1px solid var(--rule);\n border-top: 1px solid var(--rule);\n padding: .75rem;\n}\n@media (prefers-reduced-motion: reduce) {\n html:focus-within {\n scroll-behavior: auto;\n }\n}\n@media (max-width: 36.99rem) {\n .pgrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n .pcap,\n .pbody {\n grid-column: 1 / -1;\n }\n .masthead .brand {\n grid-column: 1;\n }\n .mast-nav {\n grid-column: 1 / -1;\n grid-row: 2;\n justify-content: flex-start;\n }\n .product {\n display: none;\n }\n .mast-actions {\n align-items: flex-start;\n flex-direction: column;\n font-size: .875rem;\n gap: .25rem;\n }\n .lead {\n margin-top: 4rem;\n }\n .section,\n .section + .section {\n margin-top: 2.5rem;\n }\n .connector-head,\n .toolkit-head,\n .tool,\n .activity-item {\n grid-template-columns: 1fr;\n }\n .connector-state,\n .toolkit-state {\n text-align: left;\n }\n .credential-field {\n align-items: start;\n grid-template-columns: 1fr;\n gap: .25rem;\n }\n input {\n min-height: 2.75rem;\n }\n}\n";
4
- export const OPERATOR_UI_SCRIPT = "\"use strict\";\n(() => {\n // src/operator-ui/model.ts\n function filterUiConnectors(connectors, query) {\n const q = query.trim().toLowerCase();\n const filtered = [];\n for (const connector of connectors) {\n const connectorText = [\n connector.id,\n connector.title,\n connector.description,\n connector.status\n ].join(\" \").toLowerCase();\n const connectorMatches = Boolean(q && connectorText.includes(q));\n const tools = connector.tools.filter(\n (tool) => !q || connectorMatches || `${tool.name} ${tool.description ?? \"\"}`.toLowerCase().includes(q)\n );\n if (q && tools.length === 0 && !connectorMatches) continue;\n filtered.push({ connector, tools });\n }\n return filtered;\n }\n\n // src/operator-ui/browser.ts\n var KEY = \"connecta:token\";\n var $ = (id) => {\n const element = document.getElementById(id);\n if (!element) throw new Error(`Missing operator UI element #${id}`);\n return element;\n };\n var PAGE_META = {\n connections: { path: \"/\", label: \"Connections\" },\n credentials: { path: \"/credentials\", label: \"Credentials\" },\n activity: { path: \"/activity\", label: \"Activity\" }\n };\n var DATA = null;\n var ACTIVITY = [];\n var ACTIVITY_CURSOR = null;\n var ACTIVITY_LOADED = false;\n var CURRENT_PAGE = INITIAL_PAGE;\n var SESSION_GENERATION = 0;\n var ACTIVITY_GENERATION = 0;\n var CLERK_SESSION_ID = null;\n $(\"mcpUrl\").textContent = MCP_URL;\n function errorMessage(error, fallback) {\n return error instanceof Error && error.message ? error.message : fallback;\n }\n function closestElement(target, selector) {\n const candidate = target;\n return typeof candidate?.closest === \"function\" ? candidate.closest(selector) : null;\n }\n function esc(s) {\n return String(s == null ? \"\" : s).replace(/[&<>\"]/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" })[c] ?? c);\n }\n function safeHttp(u) {\n try {\n const p = new URL(u).protocol;\n return p === \"http:\" || p === \"https:\" ? u : null;\n } catch {\n return null;\n }\n }\n function formatDate(value) {\n if (!value) return \"\";\n const date = new Date(value);\n return Number.isNaN(date.valueOf()) ? \"\" : date.toLocaleString();\n }\n function setNotice(message, isError = false) {\n $(\"credentialNotice\").textContent = message || \"\";\n $(\"credentialNotice\").classList.toggle(\"error-notice\", Boolean(isError));\n $(\"credentialNotice\").setAttribute(\"role\", isError ? \"alert\" : \"status\");\n }\n function setOauthNotice(message, isError = false) {\n $(\"oauthNotice\").textContent = message || \"\";\n $(\"oauthNotice\").classList.toggle(\"error-notice\", Boolean(isError));\n $(\"oauthNotice\").setAttribute(\"role\", isError ? \"alert\" : \"status\");\n }\n async function sessionToken() {\n return AUTH.kind === \"clerk\" ? await Clerk.session?.getToken() : localStorage.getItem(KEY);\n }\n function clearActivityState() {\n ACTIVITY_GENERATION += 1;\n ACTIVITY = [];\n ACTIVITY_CURSOR = null;\n ACTIVITY_LOADED = false;\n $(\"activityList\").innerHTML = \"\";\n $(\"activityList\").setAttribute(\"aria-busy\", \"false\");\n $(\"activityNotice\").textContent = \"\";\n $(\"activityNotice\").setAttribute(\"role\", \"status\");\n $(\"activitySummary\").textContent = \"Arguments and results are never stored.\";\n $(\"activitySearch\").value = \"\";\n $(\"refreshActivity\").disabled = false;\n $(\"moreActivity\").disabled = false;\n $(\"moreActivity\").classList.add(\"hidden\");\n }\n function clearIdentityState() {\n SESSION_GENERATION += 1;\n DATA = null;\n clearActivityState();\n $(\"list\").innerHTML = \"\";\n setOauthNotice(\"\");\n $(\"toolkitList\").innerHTML = \"\";\n $(\"filter\").value = \"\";\n $(\"credentialList\").innerHTML = \"\";\n $(\"credentialList\").setAttribute(\"aria-busy\", \"false\");\n $(\"credentialNotice\").textContent = \"\";\n $(\"credentialNotice\").setAttribute(\"role\", \"status\");\n $(\"credentialNotice\").classList.remove(\"error-notice\");\n $(\"credentialUnavailable\").textContent = \"\";\n $(\"credentialUnavailable\").classList.add(\"hidden\");\n $(\"credentialList\").classList.add(\"hidden\");\n $(\"activityUnavailable\").classList.add(\"hidden\");\n $(\"activityAvailable\").classList.add(\"hidden\");\n $(\"credentialsNav\").classList.add(\"hidden\");\n $(\"activityNav\").classList.add(\"hidden\");\n $(\"serverInfo\").textContent = PRODUCT_OPERATOR_LABEL;\n }\n function showGate(msg) {\n clearIdentityState();\n $(\"app\").classList.add(\"hidden\");\n $(\"appNav\").classList.add(\"hidden\");\n $(\"gate\").classList.remove(\"hidden\");\n $(\"err\").textContent = msg || \"\";\n if (AUTH.kind === \"clerk\") {\n const signedIn = Boolean(window.Clerk && Clerk.user);\n $(\"gateCopy\").textContent = signedIn ? \"Signed in with Clerk, but this account cannot open deployment-wide operator pages.\" : \"Sign in with Clerk to open this operator page.\";\n $(\"signin\").classList.toggle(\"hidden\", signedIn);\n $(\"gateSignout\").classList.toggle(\"hidden\", !signedIn);\n } else {\n $(\"gateCopy\").textContent = \"\";\n }\n }\n function pageForPath(path) {\n if (path === \"/credentials\") return \"credentials\";\n if (path === \"/activity\") return \"activity\";\n return \"connections\";\n }\n function credentialUnavailableCopy(capability) {\n if (capability === \"no_slots\") {\n return \"No connectors declare operator-managed credential slots. Connector credentials remain configuration-as-code until a slot is declared.\";\n }\n if (capability === \"vault_not_configured\") {\n return \"Credential storage is not configured. Set credentials.encryptionKey before managing connector credentials here.\";\n }\n return \"Credential management requires an eligible Clerk operator. Bearer-authenticated sessions can inspect connections but cannot manage stored credentials.\";\n }\n function updateCapabilities() {\n const credentialsAvailable = DATA?.credentialManagement === \"available\";\n $(\"credentialsNav\").classList.toggle(\"hidden\", !credentialsAvailable);\n $(\"activityNav\").classList.toggle(\"hidden\", !DATA?.activityEnabled);\n $(\"credentialUnavailable\").classList.toggle(\"hidden\", credentialsAvailable);\n $(\"credentialList\").classList.toggle(\"hidden\", !credentialsAvailable);\n $(\"credentialUnavailable\").textContent = credentialsAvailable ? \"\" : credentialUnavailableCopy(DATA?.credentialManagement);\n $(\"activityUnavailable\").classList.toggle(\"hidden\", Boolean(DATA?.activityEnabled));\n $(\"activityAvailable\").classList.toggle(\"hidden\", !DATA?.activityEnabled);\n }\n function activatePage(page, options) {\n const next = PAGE_META[page] ? page : \"connections\";\n CURRENT_PAGE = next;\n for (const name of Object.keys(PAGE_META)) {\n $(name + \"View\").classList.toggle(\"hidden\", name !== next);\n const link = $(name + \"Nav\");\n if (name === next) link.setAttribute(\"aria-current\", \"page\");\n else link.removeAttribute(\"aria-current\");\n }\n $(\"gateHeading\").textContent = PAGE_META[next].label;\n document.title = PAGE_META[next].label + \" — \" + TITLE_SUFFIX;\n if (DATA) {\n updateCapabilities();\n if (next === \"connections\") renderConnections();\n if (next === \"credentials\") renderCredentials();\n if (next === \"activity\" && DATA.activityEnabled && !ACTIVITY_LOADED) {\n loadActivity(true);\n }\n }\n if (next !== \"credentials\") {\n $(\"credentialList\").innerHTML = \"\";\n setNotice(\"\");\n }\n if (options?.focus) $(DATA ? next + \"Heading\" : \"gateHeading\").focus();\n }\n function navigateTo(page, href) {\n history.pushState({ operatorPage: page }, \"\", href);\n activatePage(page, { focus: true });\n }\n async function load() {\n const generation = SESSION_GENERATION;\n let token;\n try {\n token = await sessionToken();\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n return showGate(\n \"Could not read the Clerk session: \" + errorMessage(error, \"unknown error\")\n );\n }\n if (generation !== SESSION_GENERATION) return;\n if (!token) return showGate(\"\");\n let res;\n try {\n res = await fetch(\"/ui/data\", { headers: { Authorization: \"Bearer \" + token } });\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n return showGate(\"Network error: \" + errorMessage(error, \"unknown error\"));\n }\n if (generation !== SESSION_GENERATION) return;\n if (res.status === 401 || res.status === 403) {\n if (AUTH.kind === \"clerk\") {\n return showGate(\n res.status === 403 ? \"This Clerk account is not allowed to access connecta.\" : \"Your Clerk session was not accepted. Sign out and try again.\"\n );\n }\n localStorage.removeItem(KEY);\n return showGate(\"Token rejected — enter a valid bearer token.\");\n }\n if (!res.ok) return showGate(\"Error \" + res.status);\n let data;\n try {\n data = await res.json();\n } catch {\n if (generation !== SESSION_GENERATION) return;\n return showGate(\"Operator data could not be read.\");\n }\n if (generation !== SESSION_GENERATION) return;\n DATA = data;\n $(\"gate\").classList.add(\"hidden\");\n $(\"app\").classList.remove(\"hidden\");\n $(\"appNav\").classList.remove(\"hidden\");\n const si = data.serverInfo || {};\n $(\"serverInfo\").textContent = (si.name || PRODUCT_NAME) + \" v\" + (data.connectaVersion || \"?\");\n updateCapabilities();\n activatePage(CURRENT_PAGE);\n }\n function actorLabel(actor) {\n if (!actor || !actor.kind) return \"unknown\";\n return actor.label ? actor.kind + \" · \" + actor.label : actor.id ? actor.kind + \" · \" + actor.id : actor.kind;\n }\n function renderActivity() {\n const list = $(\"activityList\");\n list.innerHTML = \"\";\n const query = $(\"activitySearch\").value.trim().toLowerCase();\n const visible = ACTIVITY.filter((event) => {\n if (!query) return true;\n const actor = event.actor || {};\n return [\n event.address,\n event.connectorId,\n event.toolName,\n event.source,\n event.outcome,\n event.errorCode,\n actor.kind,\n actor.id,\n actor.namespace,\n actor.label\n ].some((value) => String(value || \"\").toLowerCase().includes(query));\n });\n const uniqueTools = new Set(ACTIVITY.map((event) => event.address)).size;\n $(\"activitySummary\").textContent = ACTIVITY.length ? ACTIVITY.length + \" loaded call\" + (ACTIVITY.length === 1 ? \"\" : \"s\") + \" · \" + uniqueTools + \" tool\" + (uniqueTools === 1 ? \"\" : \"s\") + \" · no arguments or results stored\" : \"Arguments and results are never stored.\";\n if (visible.length === 0) {\n list.innerHTML = '<div class=\"activity-empty\">' + (query ? \"No loaded activity matches this search.\" : \"No connector tool calls recorded yet.\") + \"</div>\";\n }\n for (const event of visible) {\n const item = document.createElement(\"article\");\n const outcomeClass = [\n \"success\",\n \"error\",\n \"timeout\",\n \"cancelled\"\n ].includes(event.outcome) ? event.outcome : \"error\";\n item.className = \"activity-item \" + outcomeClass;\n const retryCopy = event.attempts > 1 ? \" · \" + esc(event.attempts) + \" attempts\" : \"\";\n const errorCopy = event.errorCode ? \" · \" + esc(event.errorCode) : \"\";\n const actorId = event.actor?.id ? event.actor.namespace ? event.actor.namespace + \" · \" + event.actor.id : event.actor.id : \"\";\n const stableActorId = actorId && (event.actor?.label || event.actor?.namespace) ? '<div class=\"activity-actor-id mono\">' + esc(actorId) + \"</div>\" : \"\";\n item.innerHTML = '<div class=\"activity-stamp\"><span class=\"dot ' + (outcomeClass === \"success\" ? \"ok\" : \"\") + '\" aria-hidden=\"true\"></span><div><time class=\"activity-time\" datetime=\"' + esc(event.occurredAt) + '\">' + esc(formatDate(event.occurredAt)) + '</time><div class=\"activity-actor\">' + esc(actorLabel(event.actor)) + \"</div>\" + stableActorId + '</div></div><div><div class=\"activity-address\">' + esc(event.address) + '</div><div class=\"activity-detail\">' + esc(event.source) + retryCopy + errorCopy + '</div></div><div><div class=\"activity-outcome\">' + esc(event.outcome) + '</div><div class=\"activity-detail\">' + esc(event.durationMs) + \" ms</div></div>\";\n list.appendChild(item);\n }\n $(\"moreActivity\").classList.toggle(\"hidden\", !ACTIVITY_CURSOR);\n }\n async function loadActivity(reset) {\n if (!DATA?.activityEnabled) return;\n const sessionGeneration = SESSION_GENERATION;\n if (reset) ACTIVITY_GENERATION += 1;\n const activityGeneration = ACTIVITY_GENERATION;\n const isCurrent = () => sessionGeneration === SESSION_GENERATION && activityGeneration === ACTIVITY_GENERATION;\n $(\"activityNotice\").textContent = \"Loading activity…\";\n $(\"activityNotice\").setAttribute(\"role\", \"status\");\n $(\"activityList\").setAttribute(\"aria-busy\", \"true\");\n $(\"refreshActivity\").disabled = true;\n $(\"moreActivity\").disabled = true;\n try {\n const token = await sessionToken();\n if (!isCurrent()) return;\n if (!token) return showGate(\"Your session has expired.\");\n const cursor = reset ? null : ACTIVITY_CURSOR;\n const params = new URLSearchParams({ limit: \"50\" });\n if (cursor) params.set(\"cursor\", cursor);\n const res = await fetch(\"/ui/activity?\" + params, {\n headers: { Authorization: \"Bearer \" + token }\n });\n if (!isCurrent()) return;\n let payload = {};\n try {\n payload = await res.json();\n } catch {\n }\n if (!isCurrent()) return;\n if (res.status === 401) {\n return showGate(\"Your session was not accepted. Sign in again.\");\n }\n if (res.status === 403) {\n clearActivityState();\n $(\"activityNotice\").setAttribute(\"role\", \"alert\");\n $(\"activityNotice\").textContent = \"This identity may not read activity history.\";\n return;\n }\n if (!res.ok) {\n throw new Error(\n payload.error || \"Activity could not be loaded (\" + res.status + \").\"\n );\n }\n ACTIVITY = reset ? payload.events || [] : ACTIVITY.concat(payload.events || []);\n ACTIVITY_CURSOR = payload.nextCursor || null;\n ACTIVITY_LOADED = true;\n $(\"activityNotice\").textContent = \"\";\n renderActivity();\n } catch (error) {\n if (!isCurrent()) return;\n $(\"activityNotice\").setAttribute(\"role\", \"alert\");\n $(\"activityNotice\").textContent = errorMessage(\n error,\n \"Activity could not be loaded.\"\n );\n } finally {\n if (isCurrent()) {\n $(\"activityList\").setAttribute(\"aria-busy\", \"false\");\n $(\"refreshActivity\").disabled = false;\n $(\"moreActivity\").disabled = false;\n }\n }\n }\n function toolkitMcpUrl(name) {\n const url = new URL(MCP_URL, window.location.href);\n url.searchParams.set(\"toolkit\", name);\n return url.toString();\n }\n function renderToolkits() {\n const data = DATA;\n if (!data) return;\n const list = $(\"toolkitList\");\n const toolkits = data.toolkits || [];\n list.innerHTML = \"\";\n if (!toolkits.length) {\n list.innerHTML = '<p class=\"empty\">No toolkits are configured. Unscoped clients see the full connector registry.</p>';\n return;\n }\n const connectors = new Map(\n data.connectors.map((connector) => [connector.id, connector])\n );\n for (const toolkit of toolkits) {\n const el = document.createElement(\"article\");\n el.className = \"toolkit-card\";\n const connectorCount = toolkit.connectors.length;\n const toolCount = toolkit.toolCount || 0;\n let body = '<div class=\"toolkit-head\"><div><div class=\"toolkit-name\"><h3 class=\"mono\">' + esc(toolkit.name) + '</h3><span class=\"cap\">configured view</span></div>';\n body += '</div><div class=\"toolkit-state cap\">' + connectorCount + (connectorCount === 1 ? \" connector\" : \" connectors\") + \" · \" + toolCount + (toolCount === 1 ? \" loaded tool\" : \" loaded tools\") + \"</div></div>\";\n body += '<ul class=\"toolkit-connectors\" aria-label=\"Included connectors\">';\n for (const id of toolkit.connectors) {\n const connector = connectors.get(id);\n body += '<li class=\"toolkit-connector\"><span>' + esc(connector?.title || id) + '</span><code class=\"mono\">' + esc(id) + \"</code></li>\";\n }\n body += \"</ul>\";\n body += '<div class=\"toolkit-rules meta\">';\n if (toolkit.includeTools.length) {\n body += '<p>Per-connector allowlist: <span class=\"mono\">' + toolkit.includeTools.map(esc).join('</span>, <span class=\"mono\">') + \"</span>. Connectors without an allowlist keep all tools.</p>\";\n }\n if (toolkit.excludeTools.length) {\n body += '<p>Hidden: <span class=\"mono\">' + toolkit.excludeTools.map(esc).join('</span>, <span class=\"mono\">') + \"</span></p>\";\n }\n if (!toolkit.includeTools.length && !toolkit.excludeTools.length) {\n body += \"<p>All tools on the included connectors are visible.</p>\";\n }\n body += \"</div>\";\n const endpoint = toolkitMcpUrl(toolkit.name);\n body += '<div class=\"endpoint toolkit-endpoint\"><div class=\"endpoint-row\"><code class=\"mono\">' + esc(endpoint) + '</code><button class=\"linklike\" type=\"button\" data-toolkit-copy=\"' + esc(toolkit.name) + '\" aria-label=\"Copy endpoint for ' + esc(toolkit.name) + '\">Copy URL</button></div></div>';\n el.innerHTML = body;\n list.appendChild(el);\n }\n }\n function renderConnections() {\n const data = DATA;\n if (!data) return;\n const q = $(\"filter\").value.trim().toLowerCase();\n const list = $(\"list\");\n list.innerHTML = \"\";\n for (const filtered of filterUiConnectors(data.connectors, q)) {\n const c = filtered.connector;\n const tools = filtered.tools;\n const el = document.createElement(\"div\");\n el.className = \"card\";\n const status = c.status === \"ok\" ? \"Connected\" : c.status === \"auth_required\" ? \"Authorization needed\" : \"Unavailable\";\n let head = '<div class=\"connector-head\"><div><div class=\"connector-title\"><span class=\"dot ' + esc(c.status) + '\" aria-hidden=\"true\"></span><h2>' + esc(c.title || c.id) + \"</h2></div>\";\n if (c.description) {\n head += '<p class=\"connector-description meta\">' + esc(c.description) + \"</p>\";\n }\n const memberships = (data.toolkits || []).filter((toolkit) => toolkit.connectors.includes(c.id)).map((toolkit) => toolkit.name);\n if (memberships.length) {\n head += '<div class=\"connector-toolkits meta\"><span>Toolkits</span>' + memberships.map(\n (name) => '<span class=\"scope-label\">' + esc(name) + \"</span>\"\n ).join(\"\") + \"</div>\";\n }\n head += '</div><div class=\"connector-state cap\">' + esc(status) + \" · \" + c.toolCount + (c.toolCount === 1 ? \" tool\" : \" tools\") + '<br><span class=\"mono\">' + esc(c.id) + \"</span></div></div>\";\n if (c.message) {\n head += '<p class=\"connector-message msg\">' + esc(c.message) + \"</p>\";\n }\n if (c.credentialCheck) {\n const check = c.credentialCheck;\n const verdict = check.state === \"ok\" ? \"credential verified\" : check.state === \"auth_required\" ? \"credential needs authorization\" : \"credential check failed\";\n head += '<p class=\"connector-check meta\">Credential check: ' + esc(verdict) + \" · \" + esc(formatDate(check.checkedAt)) + (check.message && check.message !== c.message ? \" — \" + esc(check.message) : \"\") + \"</p>\";\n }\n if (c.authorizationUrl) {\n const safe = safeHttp(c.authorizationUrl);\n head += safe ? '<p class=\"connector-auth\"><a class=\"linklike\" href=\"' + esc(safe) + '\" target=\"_blank\" rel=\"noopener\">Authorize connector →</a></p>' : '<p class=\"connector-auth meta\">Authorization URL: ' + esc(c.authorizationUrl) + \"</p>\";\n }\n if (c.oauth && data.oauthManagement) {\n const oauthName = c.title || c.id;\n head += '<div class=\"credential-actions\">';\n head += '<button type=\"button\" class=\"linklike danger\" aria-label=\"Disconnect OAuth for ' + esc(oauthName) + '\" data-oauth-action=\"disconnect\" data-connector=\"' + esc(c.id) + '\">Disconnect OAuth</button>';\n head += '<button type=\"button\" class=\"linklike\" aria-label=\"' + (c.status === \"ok\" ? \"Reconnect OAuth for \" : \"Restart authorization for \") + esc(oauthName) + '\" data-oauth-action=\"reconnect\" data-connector=\"' + esc(c.id) + '\">' + (c.status === \"ok\" ? \"Reconnect OAuth\" : \"Restart authorization\") + \"</button></div>\";\n }\n if (c.credential) {\n head += '<p class=\"connector-auth\"><a class=\"linklike\" href=\"/credentials\" data-operator-page=\"credentials\">Manage credential →</a></p>';\n }\n let body = \"\";\n if (tools.length) {\n body = \"<details\" + (q ? \" open\" : \"\") + '><summary class=\"linklike\">Show tools (' + tools.length + ')</summary><div class=\"tool-list\">';\n for (const t of tools) {\n body += '<div class=\"tool\"><code>' + esc(t.address) + \"</code>\";\n if (t.description) body += '<span class=\"td\">' + esc(t.description) + \"</span>\";\n body += \"</div>\";\n }\n body += \"</div></details>\";\n }\n el.innerHTML = head + body;\n list.appendChild(el);\n }\n if (!list.children.length) {\n list.innerHTML = '<p class=\"empty\">' + (q ? \"No connectors or tools match this filter.\" : \"No connectors are declared in this deployment.\") + \"</p>\";\n }\n renderToolkits();\n }\n async function oauthRequest(connector, method, generation) {\n const token = await sessionToken();\n if (generation !== SESSION_GENERATION) {\n throw new Error(\"The operator session changed.\");\n }\n if (!token) throw new Error(\"Your Clerk session has expired.\");\n const res = await fetch(\"/ui/oauth/\" + encodeURIComponent(connector), {\n method,\n headers: { Authorization: \"Bearer \" + token }\n });\n if (res.status === 204) return null;\n let payload = {};\n try {\n payload = await res.json();\n } catch {\n }\n if (!res.ok) throw new Error(payload.error || \"Request failed (\" + res.status + \").\");\n return payload;\n }\n $(\"list\").onclick = async (event) => {\n const button = closestElement(\n event.target,\n \"[data-oauth-action]\"\n );\n if (!button) return;\n const connector = button.dataset.connector;\n const action = button.dataset.oauthAction;\n if (!connector || action !== \"disconnect\" && action !== \"reconnect\") return;\n const generation = SESSION_GENERATION;\n const question = action === \"disconnect\" ? \"Disconnect OAuth for \" + connector + \"? Stored credentials and any pending authorization will be removed.\" : \"Restart OAuth for \" + connector + \"? Stored credentials and any pending authorization will be replaced.\";\n if (!window.confirm(question)) return;\n setOauthNotice(\"\");\n const buttons = [...document.querySelectorAll(\n '[data-connector=\"' + CSS.escape(connector) + '\"]'\n )];\n buttons.forEach((item) => {\n item.disabled = true;\n });\n try {\n const result = await oauthRequest(\n connector,\n action === \"disconnect\" ? \"DELETE\" : \"POST\",\n generation\n );\n if (generation !== SESSION_GENERATION) return;\n await load();\n if (generation !== SESSION_GENERATION) return;\n setOauthNotice(\n action === \"disconnect\" ? \"OAuth disconnected. Restart authorization when you are ready to reconnect.\" : result?.message || \"Authorization restarted. Open the authorization link to reconnect.\"\n );\n $(\"oauthNotice\").focus();\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n try {\n await load();\n } catch {\n }\n if (generation !== SESSION_GENERATION) return;\n setOauthNotice(errorMessage(error, \"OAuth action failed.\"), true);\n $(\"oauthNotice\").focus();\n } finally {\n if (generation === SESSION_GENERATION) {\n buttons.forEach((item) => {\n item.disabled = false;\n });\n }\n }\n };\n function renderCredentials() {\n const data = DATA;\n if (!data) return;\n const list = $(\"credentialList\");\n list.innerHTML = \"\";\n if (data.credentialManagement !== \"available\") return;\n for (const c of data.connectors) {\n const cred = c.credential;\n if (!cred) continue;\n const configured = Boolean(cred.configured);\n const removable = configured || Boolean(cred.removable);\n const state = configured ? cred.fields?.length ? \"configured\" : \"configured · ••••\" + esc(cred.lastFour || \"\") : \"not configured\";\n const updated = configured && cred.updatedAt ? \" · updated \" + esc(formatDate(cred.updatedAt)) : \"\";\n const el = document.createElement(\"section\");\n el.className = \"credential-card\";\n el.id = \"credential-\" + c.id;\n el.setAttribute(\"aria-labelledby\", \"credential-title-\" + c.id);\n let body = '<div class=\"credential-head\"><div class=\"connector-title\"><span class=\"dot ' + (configured ? \"ok\" : \"auth_required\") + '\" aria-hidden=\"true\"></span><h2 id=\"credential-title-' + esc(c.id) + '\">' + esc(c.title || c.id) + '</h2></div><span class=\"credential-state\">' + state + updated + \"</span></div>\";\n body += '<p class=\"mono\">' + esc(c.id) + \" · \" + esc(cred.label) + \"</p>\";\n if (cred.description) {\n body += '<p class=\"credential-copy meta\">' + esc(cred.description) + \"</p>\";\n }\n if (cred.fields?.length) {\n body += '<div class=\"credential-field-summary\">';\n for (const field of cred.fields) {\n const fieldState = field.configured ? \"configured · ••••\" + esc(field.lastFour || \"\") + (field.updatedAt ? \" · updated \" + esc(formatDate(field.updatedAt)) : \"\") : \"not configured\";\n body += \"<div><span>\" + esc(field.label) + '</span><span class=\"meta\">' + fieldState + \"</span></div>\";\n }\n body += \"</div>\";\n }\n if (c.credentialCheck) {\n const check = c.credentialCheck;\n const verdict = check.state === \"ok\" ? \"healthy\" : check.state === \"auth_required\" ? \"needs authorization\" : \"check failed\";\n body += '<p class=\"connector-check meta\">Liveness: ' + esc(verdict) + \" · \" + esc(formatDate(check.checkedAt)) + (check.message ? \" — \" + esc(check.message) : \"\") + \"</p>\";\n }\n if (cred.error) body += '<div class=\"msg\">' + esc(cred.error) + \"</div>\";\n if (cred.notice) {\n body += '<p class=\"credential-copy meta\">' + esc(cred.notice) + \"</p>\";\n }\n body += '<div class=\"credential-actions\">';\n body += '<button class=\"linklike\" type=\"button\" data-credential-action=\"edit\" data-connector=\"' + esc(c.id) + '\">' + (removable ? \"Replace\" : \"Add credential\") + \"</button>\";\n if (configured && cred.testable) {\n body += '<button class=\"linklike\" type=\"button\" data-credential-action=\"test\" data-connector=\"' + esc(c.id) + '\">Test</button>';\n }\n if (removable) {\n body += '<button type=\"button\" class=\"linklike danger\" data-credential-action=\"remove\" data-connector=\"' + esc(c.id) + '\">Remove</button>';\n }\n body += \"</div>\";\n body += '<div class=\"credential-form hidden\" data-credential-form=\"' + esc(c.id) + '\">';\n if (cred.fields && cred.fields.length) {\n body += '<div class=\"credential-fields\">';\n for (const [index, field] of cred.fields.entries()) {\n const inputId = \"credential-input-\" + c.id + \"-\" + index;\n body += '<div class=\"credential-field\"><label for=\"' + esc(inputId) + '\">' + esc(field.label) + '</label><input id=\"' + esc(inputId) + '\" type=\"' + esc(field.inputType || \"password\") + '\" data-credential-field=\"' + esc(field.name) + '\" placeholder=\"' + esc(field.placeholder || field.label) + '\" autocomplete=\"' + (field.inputType === \"password\" ? \"new-password\" : \"off\") + '\" autocapitalize=\"none\" spellcheck=\"false\"></div>';\n }\n body += \"</div>\";\n } else {\n const inputId = \"credential-input-\" + c.id;\n body += '<label class=\"visually-hidden\" for=\"' + esc(inputId) + '\">' + esc(cred.label) + '</label><input id=\"' + esc(inputId) + '\" type=\"password\" data-credential-input=\"' + esc(c.id) + '\" aria-label=\"' + esc(cred.label) + '\" placeholder=\"' + esc(cred.placeholder || \"Paste credential\") + '\" autocomplete=\"new-password\" autocapitalize=\"none\" spellcheck=\"false\">';\n }\n body += '<button class=\"linklike\" type=\"button\" data-credential-action=\"save\" data-connector=\"' + esc(c.id) + '\">Save</button><button class=\"linklike\" type=\"button\" data-credential-action=\"cancel\" data-connector=\"' + esc(c.id) + '\">Cancel</button></div>';\n el.innerHTML = body;\n list.appendChild(el);\n }\n }\n async function credentialRequest(connector, method, action, body, generation) {\n const token = await sessionToken();\n if (generation !== SESSION_GENERATION) {\n throw new Error(\"The operator session changed.\");\n }\n if (!token) throw new Error(\"Your Clerk session has expired.\");\n const suffix = action ? \"/\" + action : \"\";\n const res = await fetch(\n \"/ui/credentials/\" + encodeURIComponent(connector) + suffix,\n {\n method,\n headers: {\n Authorization: \"Bearer \" + token,\n ...body ? { \"Content-Type\": \"application/json\" } : {}\n },\n ...body ? { body: JSON.stringify(body) } : {}\n }\n );\n if (res.status === 204) return null;\n let payload = {};\n try {\n payload = await res.json();\n } catch {\n }\n if (!res.ok) throw new Error(payload.error || \"Request failed (\" + res.status + \").\");\n return payload;\n }\n function credentialForm(connector) {\n const form = document.querySelector(\n '[data-credential-form=\"' + CSS.escape(connector) + '\"]'\n );\n if (!form) throw new Error(\"Credential form is unavailable.\");\n return form;\n }\n $(\"credentialList\").onclick = async (event) => {\n const button = closestElement(\n event.target,\n \"[data-credential-action]\"\n );\n if (!button) return;\n const connector = button.dataset.connector;\n const action = button.dataset.credentialAction;\n if (!connector || !action) return;\n const form = credentialForm(connector);\n const generation = SESSION_GENERATION;\n if (action === \"edit\") {\n form.classList.remove(\"hidden\");\n form.querySelector(\"input\")?.focus();\n return;\n }\n if (action === \"cancel\") {\n form.querySelectorAll(\"input\").forEach((input) => {\n input.value = \"\";\n });\n form.classList.add(\"hidden\");\n document.querySelector(\n '[data-credential-action=\"edit\"][data-connector=\"' + CSS.escape(connector) + '\"]'\n )?.focus();\n return;\n }\n if (action === \"remove\" && !window.confirm(\n \"Remove this credential? The connector will stop authenticating until a replacement is added.\"\n )) return;\n setNotice(\"\");\n $(\"credentialList\").setAttribute(\"aria-busy\", \"true\");\n const buttons = [...document.querySelectorAll(\n '[data-connector=\"' + CSS.escape(connector) + '\"]'\n )];\n buttons.forEach((item) => {\n item.disabled = true;\n });\n try {\n if (action === \"save\") {\n const fieldInputs = [\n ...form.querySelectorAll(\"[data-credential-field]\")\n ];\n if (fieldInputs.length) {\n const values = {};\n for (const input of fieldInputs) {\n const value = input.value.trim();\n if (!value) throw new Error(\"Complete every credential field before saving.\");\n const field = input.dataset.credentialField;\n if (!field) throw new Error(\"Credential field is unnamed.\");\n values[field] = value;\n }\n await credentialRequest(connector, \"PUT\", \"\", { values }, generation);\n } else {\n const input = form.querySelector(\n \"[data-credential-input]\"\n );\n if (!input) throw new Error(\"Credential input is unavailable.\");\n const value = input.value.trim();\n if (!value) throw new Error(\"Paste a credential before saving.\");\n await credentialRequest(connector, \"PUT\", \"\", { value }, generation);\n }\n if (generation !== SESSION_GENERATION) return;\n form.querySelectorAll(\"input\").forEach((input) => {\n input.value = \"\";\n });\n setNotice(\"Credential saved.\");\n await load();\n if (generation !== SESSION_GENERATION) return;\n $(\"credentialNotice\").focus();\n } else if (action === \"remove\") {\n await credentialRequest(connector, \"DELETE\", \"\", null, generation);\n if (generation !== SESSION_GENERATION) return;\n setNotice(\"Credential removed.\");\n await load();\n if (generation !== SESSION_GENERATION) return;\n $(\"credentialNotice\").focus();\n } else if (action === \"test\") {\n const result = await credentialRequest(\n connector,\n \"POST\",\n \"test\",\n null,\n generation\n );\n if (generation !== SESSION_GENERATION) return;\n setNotice(\n result?.message || (result?.ok ? \"Credential is valid.\" : \"Credential test failed.\"),\n !result?.ok\n );\n }\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n setNotice(errorMessage(error, \"Credential action failed.\"), true);\n } finally {\n if (generation === SESSION_GENERATION) {\n $(\"credentialList\").setAttribute(\"aria-busy\", \"false\");\n buttons.forEach((item) => {\n item.disabled = false;\n });\n }\n }\n };\n $(\"save\").onclick = async () => {\n const v = $(\"token\").value.trim();\n if (!v) return;\n clearIdentityState();\n localStorage.setItem(KEY, v);\n $(\"token\").value = \"\";\n await load();\n if (DATA) $(CURRENT_PAGE + \"Heading\").focus();\n };\n $(\"change\").onclick = () => {\n localStorage.removeItem(KEY);\n showGate(\"\");\n $(\"token\").focus();\n };\n $(\"copyMcpUrl\").onclick = async () => {\n const button = $(\"copyMcpUrl\");\n try {\n await navigator.clipboard.writeText(MCP_URL);\n button.textContent = \"Copied\";\n } catch {\n button.textContent = \"Copy failed\";\n }\n window.setTimeout(() => {\n button.textContent = \"Copy URL\";\n }, 1600);\n };\n $(\"toolkitList\").onclick = async (event) => {\n const button = closestElement(\n event.target,\n \"[data-toolkit-copy]\"\n );\n if (!button) return;\n const toolkit = button.dataset.toolkitCopy;\n if (!toolkit) return;\n try {\n await navigator.clipboard.writeText(\n toolkitMcpUrl(toolkit)\n );\n button.textContent = \"Copied\";\n } catch {\n button.textContent = \"Copy failed\";\n }\n window.setTimeout(() => {\n button.textContent = \"Copy URL\";\n }, 1600);\n };\n $(\"signin\").onclick = () => Clerk.redirectToSignIn({\n signInFallbackRedirectUrl: window.location.href,\n signUpFallbackRedirectUrl: window.location.href\n });\n function signOut() {\n clearIdentityState();\n return Clerk.signOut({ redirectUrl: window.location.href });\n }\n $(\"gateSignout\").onclick = signOut;\n $(\"signout\").onclick = signOut;\n $(\"filter\").oninput = () => {\n if (DATA) renderConnections();\n };\n $(\"refreshActivity\").onclick = () => loadActivity(true);\n $(\"moreActivity\").onclick = () => loadActivity(false);\n $(\"activitySearch\").oninput = () => renderActivity();\n document.addEventListener(\"click\", (event) => {\n const link = closestElement(\n event.target,\n \"a[data-operator-page]\"\n );\n if (!link || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n const target = new URL(link.href, window.location.href);\n if (target.origin !== window.location.origin) return;\n const page = link.dataset.operatorPage;\n if (page !== \"connections\" && page !== \"credentials\" && page !== \"activity\") return;\n event.preventDefault();\n navigateTo(page, target.pathname + target.search + target.hash);\n });\n window.addEventListener(\"popstate\", () => {\n activatePage(pageForPath(window.location.pathname), { focus: true });\n });\n async function init() {\n if (AUTH.kind === \"clerk\") {\n $(\"clerkGate\").classList.remove(\"hidden\");\n $(\"signout\").classList.remove(\"hidden\");\n try {\n if (!window.Clerk) {\n return showGate(\"Clerk could not load. Check your network and try again.\");\n }\n await Clerk.load({\n ...AUTH.signInUrl ? { signInUrl: AUTH.signInUrl } : {},\n ...AUTH.signUpUrl ? { signUpUrl: AUTH.signUpUrl } : {},\n signInFallbackRedirectUrl: window.location.href,\n signUpFallbackRedirectUrl: window.location.href,\n afterSignOutUrl: window.location.href\n });\n CLERK_SESSION_ID = Clerk.session?.id ?? null;\n Clerk.addListener((resources) => {\n const nextSessionId = resources.session?.id ?? null;\n if (nextSessionId === CLERK_SESSION_ID) return;\n CLERK_SESSION_ID = nextSessionId;\n showGate(\"\");\n void load();\n });\n } catch (error) {\n return showGate(\n \"Clerk could not initialize: \" + errorMessage(error, \"unknown error\")\n );\n }\n } else {\n $(\"tokenGate\").classList.remove(\"hidden\");\n $(\"change\").classList.remove(\"hidden\");\n }\n activatePage(pageForPath(window.location.pathname));\n await load();\n }\n if (AUTH.kind === \"clerk\") {\n window.addEventListener(\"load\", init);\n } else {\n init();\n }\n})();\n";
3
+ export const OPERATOR_UI_CSS = "/* src/operator-ui/browser.css */\n:root {\n color-scheme: light;\n --ink: #000;\n --paper: #fff;\n --rule: #ccc;\n --muted: #666;\n --trace: #f5f5f5;\n --shell: 70rem;\n --pad: 1rem;\n --gap: 1.5rem;\n --sans:\n \"Helvetica Neue\",\n Helvetica,\n Arial,\n sans-serif;\n --mono:\n ui-monospace,\n \"SF Mono\",\n Menlo,\n Monaco,\n \"Cascadia Code\",\n Consolas,\n monospace;\n}\n* {\n border-radius: 0;\n box-sizing: border-box;\n}\nhtml {\n background: var(--paper);\n color: var(--ink);\n font-family: var(--sans);\n font-size: 16px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizeLegibility;\n}\nbody {\n margin: 0;\n min-height: 100vh;\n}\n::selection {\n background: var(--ink);\n color: var(--paper);\n}\n:is(h1, h2, h3, p, ul, ol) {\n margin: 0;\n padding: 0;\n}\n:is(h1, h2, h3) {\n font-size: inherit;\n font-weight: 400;\n}\n:is(ul, ol) {\n list-style: none;\n}\na {\n color: inherit;\n}\nbutton,\ninput {\n font: inherit;\n}\nbutton {\n background: none;\n border: 0;\n color: inherit;\n cursor: pointer;\n margin: 0;\n padding: 0;\n text-align: left;\n}\nbutton:disabled {\n cursor: wait;\n opacity: .5;\n}\ninput {\n background: var(--paper);\n border: 1px solid var(--rule);\n color: var(--ink);\n min-height: 2rem;\n padding: .2rem .5rem;\n}\ninput:focus-visible {\n outline-offset: -1px;\n}\n:is(a, button, input, summary):focus-visible {\n outline: 1px solid var(--ink);\n}\n:is(a, button, summary):focus-visible {\n outline-offset: 2px;\n}\n.skip-link {\n background: var(--paper);\n left: var(--pad);\n padding: .5rem;\n position: fixed;\n top: -4rem;\n z-index: 10;\n}\n.skip-link:focus {\n top: var(--pad);\n}\n.shell {\n margin: 0 auto;\n max-width: var(--shell);\n padding-left: var(--pad);\n padding-right: var(--pad);\n}\n.pgrid {\n column-gap: var(--gap);\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n row-gap: 1rem;\n}\n.pcap {\n grid-column: 1;\n}\n.pbody {\n grid-column: 2 / -1;\n min-width: 0;\n}\n.cap,\n.meta {\n color: var(--muted);\n font-size: .9em;\n}\n.mono {\n font-family: var(--mono);\n font-size: .78rem;\n}\n.hidden {\n display: none !important;\n}\n.visually-hidden {\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n height: 1px;\n overflow: hidden;\n position: absolute;\n white-space: nowrap;\n width: 1px;\n}\n.masthead {\n align-items: start;\n padding-bottom: var(--pad);\n padding-top: var(--pad);\n}\n.brand {\n font-weight: 500;\n grid-column: 1;\n text-decoration: none;\n}\n.mast-nav {\n display: flex;\n gap: var(--gap);\n grid-column: 2 / -1;\n justify-content: space-between;\n min-width: 0;\n}\n.mast-actions {\n display: flex;\n gap: var(--gap);\n justify-content: flex-end;\n min-width: 0;\n}\n.page-nav,\n.session-actions {\n display: flex;\n flex-wrap: wrap;\n gap: .5rem var(--gap);\n}\n.mast-actions :is(a, button) {\n align-items: center;\n display: inline-flex;\n min-height: 2rem;\n}\n.navlink,\n.linklike {\n text-decoration: underline;\n text-decoration-thickness: 1.5px;\n text-underline-offset: .22em;\n}\n.navlink {\n text-decoration-color: transparent;\n}\n.navlink:hover,\n.navlink:focus-visible,\n.navlink[aria-current=page] {\n text-decoration-color: currentColor;\n}\n.linklike {\n text-decoration-color: currentColor;\n}\n.linklike:hover,\n.linklike:focus-visible {\n text-decoration-color: transparent;\n}\n.page {\n padding-bottom: 5rem;\n}\n.lead {\n margin-top: 6rem;\n}\n.section,\n.section + .section {\n margin-top: 3rem;\n}\n.lead-copy,\n.body-copy {\n max-width: 34em;\n}\n.lead-copy > * + *,\n.body-copy > * + * {\n margin-top: 1.5rem;\n}\n.row,\n.actions {\n align-items: center;\n display: flex;\n flex-wrap: wrap;\n gap: var(--gap);\n}\n.row input {\n flex: 1;\n min-width: 12rem;\n}\n.gate-actions {\n margin-top: 1.5rem;\n}\n#err {\n margin-top: 1.5rem;\n text-decoration: underline;\n}\n.endpoint {\n border-bottom: 1px solid var(--rule);\n border-top: 1px solid var(--rule);\n}\n.endpoint-row {\n align-items: baseline;\n display: flex;\n gap: var(--gap);\n min-width: 0;\n padding: .75rem 0;\n}\n.endpoint-row code {\n flex: 1;\n min-width: 0;\n overflow-x: auto;\n white-space: nowrap;\n}\n.endpoint-row button {\n flex: none;\n}\n.connector-tools {\n border-bottom: 1px solid var(--rule);\n}\n.toolbar {\n margin-bottom: 1.5rem;\n}\n.toolbar input {\n flex-basis: 18rem;\n}\n#notice {\n margin-bottom: .75rem;\n}\n#notice:empty {\n display: none;\n}\n#notice:not(:empty) {\n text-decoration: underline;\n}\n#oauthNotice {\n margin-bottom: .75rem;\n}\n#oauthNotice:empty {\n display: none;\n}\n.error-notice,\n.msg {\n text-decoration: underline;\n}\n.card,\n.credential-card,\n.activity-item {\n padding-left: 1.25rem;\n position: relative;\n}\n.card::before,\n.credential-card::before,\n.activity-item::before {\n background: var(--rule);\n bottom: 0;\n content: \"\";\n left: .25rem;\n position: absolute;\n top: 0;\n width: 1px;\n}\n.card {\n border-top: 1px solid var(--rule);\n padding-bottom: .75rem;\n padding-top: .75rem;\n}\n.connector-head {\n display: grid;\n gap: var(--gap);\n grid-template-columns: minmax(0, 2fr) minmax(10rem, 1fr);\n}\n.connector-title {\n align-items: baseline;\n display: flex;\n gap: .5rem;\n}\n.connector-title .dot,\n.activity-stamp .dot {\n margin-left: -1.25rem;\n}\n.activity-stamp {\n align-items: baseline;\n display: flex;\n gap: .75rem;\n}\n.card h2 {\n overflow-wrap: anywhere;\n}\n.connector-state {\n text-align: right;\n}\n.dot {\n background: var(--paper);\n border: 1px solid var(--ink);\n display: inline-block;\n flex: none;\n height: .5rem;\n width: .5rem;\n z-index: 1;\n}\n.dot.ok {\n background: var(--ink);\n}\n.dot.auth_required {\n background:\n linear-gradient(\n 90deg,\n var(--ink) 50%,\n var(--paper) 50%);\n}\n.connector-description {\n margin-top: .25rem;\n max-width: 40rem;\n}\n.connector-message,\n.connector-auth {\n margin-top: .75rem;\n}\n.credential-ledger {\n border-bottom: 1px solid var(--rule);\n}\n.credential-card {\n border-top: 1px solid var(--rule);\n padding-bottom: .75rem;\n padding-top: .75rem;\n}\n.credential-head {\n align-items: baseline;\n display: flex;\n flex-wrap: wrap;\n gap: .25rem var(--gap);\n justify-content: space-between;\n}\n.credential-copy {\n margin-top: .25rem;\n max-width: 40rem;\n}\n.credential-field-summary {\n border-top: 1px solid var(--rule);\n margin-top: .75rem;\n}\n.credential-field-summary > div {\n border-bottom: 1px solid var(--rule);\n display: flex;\n flex-wrap: wrap;\n gap: .25rem var(--gap);\n justify-content: space-between;\n padding: .5rem 0;\n}\n.credential-actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--gap);\n margin-top: .75rem;\n}\n.credential-actions button,\n.credential-form button,\n.activity-controls button,\n.activity-more {\n align-items: center;\n display: inline-flex;\n min-height: 2.75rem;\n}\n.credential-form {\n align-items: center;\n display: flex;\n flex-wrap: wrap;\n gap: .75rem var(--gap);\n margin-top: .75rem;\n}\n.credential-form > input {\n flex: 1 1 18rem;\n}\n.credential-fields {\n display: grid;\n flex: 1 1 100%;\n gap: .75rem;\n}\n.credential-field {\n align-items: center;\n display: grid;\n gap: var(--gap);\n grid-template-columns: minmax(9rem, 12rem) 1fr;\n}\n.credential-field input {\n min-width: 0;\n width: 100%;\n}\n.danger {\n text-decoration-style: double;\n}\ndetails {\n margin-top: .75rem;\n}\nsummary {\n cursor: pointer;\n list-style: none;\n width: max-content;\n}\nsummary::-webkit-details-marker {\n display: none;\n}\n.tool-list {\n border-bottom: 1px solid var(--rule);\n margin-top: .5rem;\n}\n.tool {\n border-top: 1px solid var(--rule);\n display: grid;\n gap: .25rem var(--gap);\n grid-template-columns: minmax(12rem, 1fr) minmax(0, 2fr);\n padding: .5rem 0;\n}\n.tool code {\n font-family: var(--mono);\n font-size: .78rem;\n overflow-wrap: anywhere;\n}\n.tool .td {\n color: var(--muted);\n font-size: .9em;\n}\n.empty {\n border-top: 1px solid var(--rule);\n padding: .75rem 0;\n}\n.activity-copy {\n margin-bottom: 1.5rem;\n}\n.activity-controls {\n margin-bottom: 1.5rem;\n}\n.activity-controls input {\n flex: 1 1 18rem;\n}\n#activityNotice {\n margin-bottom: .75rem;\n}\n.activity-ledger {\n border-bottom: 1px solid var(--rule);\n}\n.activity-item {\n border-top: 1px solid var(--rule);\n display: grid;\n gap: .25rem var(--gap);\n grid-template-columns: minmax(9rem, .85fr) minmax(12rem, 1.4fr) minmax(8rem, .9fr);\n padding-bottom: .75rem;\n padding-top: .75rem;\n}\n.activity-time,\n.activity-actor,\n.activity-detail {\n color: var(--muted);\n font-size: .82rem;\n}\n.activity-actor-id {\n color: var(--muted);\n margin-top: .1rem;\n}\n.activity-address {\n font-family: var(--mono);\n font-size: .78rem;\n overflow-wrap: anywhere;\n}\n.activity-outcome {\n font-size: .9em;\n}\n.activity-item.error .activity-outcome,\n.activity-item.timeout .activity-outcome,\n.activity-item.cancelled .activity-outcome {\n text-decoration: underline;\n}\n.activity-empty {\n border-top: 1px solid var(--rule);\n padding: .75rem 0;\n}\n.activity-more {\n margin-top: .75rem;\n}\n.unavailable {\n background: var(--trace);\n border-bottom: 1px solid var(--rule);\n border-top: 1px solid var(--rule);\n padding: .75rem;\n}\n@media (prefers-reduced-motion: reduce) {\n html:focus-within {\n scroll-behavior: auto;\n }\n}\n@media (max-width: 36.99rem) {\n .pgrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n .pcap,\n .pbody {\n grid-column: 1 / -1;\n }\n .masthead .brand {\n grid-column: 1;\n }\n .mast-nav {\n grid-column: 1 / -1;\n grid-row: 2;\n justify-content: flex-start;\n }\n .product {\n display: none;\n }\n .mast-actions {\n align-items: flex-start;\n flex-direction: column;\n font-size: .875rem;\n gap: .25rem;\n }\n .lead {\n margin-top: 4rem;\n }\n .section,\n .section + .section {\n margin-top: 2.5rem;\n }\n .connector-head,\n .tool,\n .activity-item {\n grid-template-columns: 1fr;\n }\n .connector-state {\n text-align: left;\n }\n .credential-field {\n align-items: start;\n grid-template-columns: 1fr;\n gap: .25rem;\n }\n input {\n min-height: 2.75rem;\n }\n}\n";
4
+ export const OPERATOR_UI_SCRIPT = "\"use strict\";\n(() => {\n // src/operator-ui/model.ts\n function filterUiConnectors(connectors, query) {\n const q = query.trim().toLowerCase();\n const filtered = [];\n for (const connector of connectors) {\n const connectorText = [\n connector.id,\n connector.title,\n connector.description,\n connector.status\n ].join(\" \").toLowerCase();\n const connectorMatches = Boolean(q && connectorText.includes(q));\n const tools = connector.tools.filter(\n (tool) => !q || connectorMatches || `${tool.name} ${tool.description ?? \"\"}`.toLowerCase().includes(q)\n );\n if (q && tools.length === 0 && !connectorMatches) continue;\n filtered.push({ connector, tools });\n }\n return filtered;\n }\n\n // src/operator-ui/browser.ts\n var KEY = \"connecta:token\";\n var $ = (id) => {\n const element = document.getElementById(id);\n if (!element) throw new Error(`Missing operator UI element #${id}`);\n return element;\n };\n var PAGE_META = {\n connections: { path: \"/\", label: \"Connections\" },\n credentials: { path: \"/credentials\", label: \"Credentials\" },\n activity: { path: \"/activity\", label: \"Activity\" }\n };\n var DATA = null;\n var ACTIVITY = [];\n var ACTIVITY_CURSOR = null;\n var ACTIVITY_LOADED = false;\n var CURRENT_PAGE = INITIAL_PAGE;\n var SESSION_GENERATION = 0;\n var ACTIVITY_GENERATION = 0;\n var CLERK_SESSION_ID = null;\n $(\"mcpUrl\").textContent = MCP_URL;\n function errorMessage(error, fallback) {\n return error instanceof Error && error.message ? error.message : fallback;\n }\n function closestElement(target, selector) {\n const candidate = target;\n return typeof candidate?.closest === \"function\" ? candidate.closest(selector) : null;\n }\n function esc(s) {\n return String(s == null ? \"\" : s).replace(/[&<>\"]/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" })[c] ?? c);\n }\n function safeHttp(u) {\n try {\n const p = new URL(u).protocol;\n return p === \"http:\" || p === \"https:\" ? u : null;\n } catch {\n return null;\n }\n }\n function formatDate(value) {\n if (!value) return \"\";\n const date = new Date(value);\n return Number.isNaN(date.valueOf()) ? \"\" : date.toLocaleString();\n }\n function setNotice(message, isError = false) {\n $(\"credentialNotice\").textContent = message || \"\";\n $(\"credentialNotice\").classList.toggle(\"error-notice\", Boolean(isError));\n $(\"credentialNotice\").setAttribute(\"role\", isError ? \"alert\" : \"status\");\n }\n function setOauthNotice(message, isError = false) {\n $(\"oauthNotice\").textContent = message || \"\";\n $(\"oauthNotice\").classList.toggle(\"error-notice\", Boolean(isError));\n $(\"oauthNotice\").setAttribute(\"role\", isError ? \"alert\" : \"status\");\n }\n async function sessionToken() {\n return AUTH.kind === \"clerk\" ? await Clerk.session?.getToken() : localStorage.getItem(KEY);\n }\n function clearActivityState() {\n ACTIVITY_GENERATION += 1;\n ACTIVITY = [];\n ACTIVITY_CURSOR = null;\n ACTIVITY_LOADED = false;\n $(\"activityList\").innerHTML = \"\";\n $(\"activityList\").setAttribute(\"aria-busy\", \"false\");\n $(\"activityNotice\").textContent = \"\";\n $(\"activityNotice\").setAttribute(\"role\", \"status\");\n $(\"activitySummary\").textContent = \"Arguments and results are never stored.\";\n $(\"activitySearch\").value = \"\";\n $(\"refreshActivity\").disabled = false;\n $(\"moreActivity\").disabled = false;\n $(\"moreActivity\").classList.add(\"hidden\");\n }\n function clearIdentityState() {\n SESSION_GENERATION += 1;\n DATA = null;\n clearActivityState();\n $(\"list\").innerHTML = \"\";\n setOauthNotice(\"\");\n $(\"filter\").value = \"\";\n $(\"credentialList\").innerHTML = \"\";\n $(\"credentialList\").setAttribute(\"aria-busy\", \"false\");\n $(\"credentialNotice\").textContent = \"\";\n $(\"credentialNotice\").setAttribute(\"role\", \"status\");\n $(\"credentialNotice\").classList.remove(\"error-notice\");\n $(\"credentialUnavailable\").textContent = \"\";\n $(\"credentialUnavailable\").classList.add(\"hidden\");\n $(\"credentialList\").classList.add(\"hidden\");\n $(\"activityUnavailable\").classList.add(\"hidden\");\n $(\"activityAvailable\").classList.add(\"hidden\");\n $(\"credentialsNav\").classList.add(\"hidden\");\n $(\"activityNav\").classList.add(\"hidden\");\n $(\"serverInfo\").textContent = PRODUCT_OPERATOR_LABEL;\n }\n function showGate(msg) {\n clearIdentityState();\n $(\"app\").classList.add(\"hidden\");\n $(\"appNav\").classList.add(\"hidden\");\n $(\"gate\").classList.remove(\"hidden\");\n $(\"err\").textContent = msg || \"\";\n if (AUTH.kind === \"clerk\") {\n const signedIn = Boolean(window.Clerk && Clerk.user);\n $(\"gateCopy\").textContent = signedIn ? \"Signed in with Clerk, but this account cannot open deployment-wide operator pages.\" : \"Sign in with Clerk to open this operator page.\";\n $(\"signin\").classList.toggle(\"hidden\", signedIn);\n $(\"gateSignout\").classList.toggle(\"hidden\", !signedIn);\n } else {\n $(\"gateCopy\").textContent = \"\";\n }\n }\n function pageForPath(path) {\n if (path === \"/credentials\") return \"credentials\";\n if (path === \"/activity\") return \"activity\";\n return \"connections\";\n }\n function credentialUnavailableCopy(capability) {\n if (capability === \"no_slots\") {\n return \"No connectors declare operator-managed credential slots. Connector credentials remain configuration-as-code until a slot is declared.\";\n }\n if (capability === \"vault_not_configured\") {\n return \"Credential storage is not configured. Set credentials.encryptionKey before managing connector credentials here.\";\n }\n return \"Credential management requires an eligible Clerk operator. Bearer-authenticated sessions can inspect connections but cannot manage stored credentials.\";\n }\n function updateCapabilities() {\n const credentialsAvailable = DATA?.credentialManagement === \"available\";\n $(\"credentialsNav\").classList.toggle(\"hidden\", !credentialsAvailable);\n $(\"activityNav\").classList.toggle(\"hidden\", !DATA?.activityEnabled);\n $(\"credentialUnavailable\").classList.toggle(\"hidden\", credentialsAvailable);\n $(\"credentialList\").classList.toggle(\"hidden\", !credentialsAvailable);\n $(\"credentialUnavailable\").textContent = credentialsAvailable ? \"\" : credentialUnavailableCopy(DATA?.credentialManagement);\n $(\"activityUnavailable\").classList.toggle(\"hidden\", Boolean(DATA?.activityEnabled));\n $(\"activityAvailable\").classList.toggle(\"hidden\", !DATA?.activityEnabled);\n }\n function activatePage(page, options) {\n const next = PAGE_META[page] ? page : \"connections\";\n CURRENT_PAGE = next;\n for (const name of Object.keys(PAGE_META)) {\n $(name + \"View\").classList.toggle(\"hidden\", name !== next);\n const link = $(name + \"Nav\");\n if (name === next) link.setAttribute(\"aria-current\", \"page\");\n else link.removeAttribute(\"aria-current\");\n }\n $(\"gateHeading\").textContent = PAGE_META[next].label;\n document.title = PAGE_META[next].label + \" — \" + TITLE_SUFFIX;\n if (DATA) {\n updateCapabilities();\n if (next === \"connections\") renderConnections();\n if (next === \"credentials\") renderCredentials();\n if (next === \"activity\" && DATA.activityEnabled && !ACTIVITY_LOADED) {\n loadActivity(true);\n }\n }\n if (next !== \"credentials\") {\n $(\"credentialList\").innerHTML = \"\";\n setNotice(\"\");\n }\n if (options?.focus) $(DATA ? next + \"Heading\" : \"gateHeading\").focus();\n }\n function navigateTo(page, href) {\n history.pushState({ operatorPage: page }, \"\", href);\n activatePage(page, { focus: true });\n }\n async function load() {\n const generation = SESSION_GENERATION;\n let token;\n try {\n token = await sessionToken();\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n return showGate(\n \"Could not read the Clerk session: \" + errorMessage(error, \"unknown error\")\n );\n }\n if (generation !== SESSION_GENERATION) return;\n if (!token) return showGate(\"\");\n let res;\n try {\n res = await fetch(\"/ui/data\", { headers: { Authorization: \"Bearer \" + token } });\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n return showGate(\"Network error: \" + errorMessage(error, \"unknown error\"));\n }\n if (generation !== SESSION_GENERATION) return;\n if (res.status === 401 || res.status === 403) {\n if (AUTH.kind === \"clerk\") {\n return showGate(\n res.status === 403 ? \"This Clerk account is not allowed to access connecta.\" : \"Your Clerk session was not accepted. Sign out and try again.\"\n );\n }\n localStorage.removeItem(KEY);\n return showGate(\"Token rejected — enter a valid bearer token.\");\n }\n if (!res.ok) return showGate(\"Error \" + res.status);\n let data;\n try {\n data = await res.json();\n } catch {\n if (generation !== SESSION_GENERATION) return;\n return showGate(\"Operator data could not be read.\");\n }\n if (generation !== SESSION_GENERATION) return;\n DATA = data;\n $(\"gate\").classList.add(\"hidden\");\n $(\"app\").classList.remove(\"hidden\");\n $(\"appNav\").classList.remove(\"hidden\");\n const si = data.serverInfo || {};\n $(\"serverInfo\").textContent = (si.name || PRODUCT_NAME) + \" v\" + (data.connectaVersion || \"?\");\n updateCapabilities();\n activatePage(CURRENT_PAGE);\n }\n function actorLabel(actor) {\n if (!actor || !actor.kind) return \"unknown\";\n return actor.label ? actor.kind + \" · \" + actor.label : actor.id ? actor.kind + \" · \" + actor.id : actor.kind;\n }\n function renderActivity() {\n const list = $(\"activityList\");\n list.innerHTML = \"\";\n const query = $(\"activitySearch\").value.trim().toLowerCase();\n const visible = ACTIVITY.filter((event) => {\n if (!query) return true;\n const actor = event.actor || {};\n return [\n event.address,\n event.connectorId,\n event.toolName,\n event.source,\n event.outcome,\n event.errorCode,\n actor.kind,\n actor.id,\n actor.namespace,\n actor.label\n ].some((value) => String(value || \"\").toLowerCase().includes(query));\n });\n const uniqueTools = new Set(ACTIVITY.map((event) => event.address)).size;\n $(\"activitySummary\").textContent = ACTIVITY.length ? ACTIVITY.length + \" loaded call\" + (ACTIVITY.length === 1 ? \"\" : \"s\") + \" · \" + uniqueTools + \" tool\" + (uniqueTools === 1 ? \"\" : \"s\") + \" · no arguments or results stored\" : \"Arguments and results are never stored.\";\n if (visible.length === 0) {\n list.innerHTML = '<div class=\"activity-empty\">' + (query ? \"No loaded activity matches this search.\" : \"No connector tool calls recorded yet.\") + \"</div>\";\n }\n for (const event of visible) {\n const item = document.createElement(\"article\");\n const outcomeClass = [\n \"success\",\n \"error\",\n \"timeout\",\n \"cancelled\"\n ].includes(event.outcome) ? event.outcome : \"error\";\n item.className = \"activity-item \" + outcomeClass;\n const retryCopy = event.attempts > 1 ? \" · \" + esc(event.attempts) + \" attempts\" : \"\";\n const errorCopy = event.errorCode ? \" · \" + esc(event.errorCode) : \"\";\n const actorId = event.actor?.id ? event.actor.namespace ? event.actor.namespace + \" · \" + event.actor.id : event.actor.id : \"\";\n const stableActorId = actorId && (event.actor?.label || event.actor?.namespace) ? '<div class=\"activity-actor-id mono\">' + esc(actorId) + \"</div>\" : \"\";\n item.innerHTML = '<div class=\"activity-stamp\"><span class=\"dot ' + (outcomeClass === \"success\" ? \"ok\" : \"\") + '\" aria-hidden=\"true\"></span><div><time class=\"activity-time\" datetime=\"' + esc(event.occurredAt) + '\">' + esc(formatDate(event.occurredAt)) + '</time><div class=\"activity-actor\">' + esc(actorLabel(event.actor)) + \"</div>\" + stableActorId + '</div></div><div><div class=\"activity-address\">' + esc(event.address) + '</div><div class=\"activity-detail\">' + esc(event.source) + retryCopy + errorCopy + '</div></div><div><div class=\"activity-outcome\">' + esc(event.outcome) + '</div><div class=\"activity-detail\">' + esc(event.durationMs) + \" ms</div></div>\";\n list.appendChild(item);\n }\n $(\"moreActivity\").classList.toggle(\"hidden\", !ACTIVITY_CURSOR);\n }\n async function loadActivity(reset) {\n if (!DATA?.activityEnabled) return;\n const sessionGeneration = SESSION_GENERATION;\n if (reset) ACTIVITY_GENERATION += 1;\n const activityGeneration = ACTIVITY_GENERATION;\n const isCurrent = () => sessionGeneration === SESSION_GENERATION && activityGeneration === ACTIVITY_GENERATION;\n $(\"activityNotice\").textContent = \"Loading activity…\";\n $(\"activityNotice\").setAttribute(\"role\", \"status\");\n $(\"activityList\").setAttribute(\"aria-busy\", \"true\");\n $(\"refreshActivity\").disabled = true;\n $(\"moreActivity\").disabled = true;\n try {\n const token = await sessionToken();\n if (!isCurrent()) return;\n if (!token) return showGate(\"Your session has expired.\");\n const cursor = reset ? null : ACTIVITY_CURSOR;\n const params = new URLSearchParams({ limit: \"50\" });\n if (cursor) params.set(\"cursor\", cursor);\n const res = await fetch(\"/ui/activity?\" + params, {\n headers: { Authorization: \"Bearer \" + token }\n });\n if (!isCurrent()) return;\n let payload = {};\n try {\n payload = await res.json();\n } catch {\n }\n if (!isCurrent()) return;\n if (res.status === 401) {\n return showGate(\"Your session was not accepted. Sign in again.\");\n }\n if (res.status === 403) {\n clearActivityState();\n $(\"activityNotice\").setAttribute(\"role\", \"alert\");\n $(\"activityNotice\").textContent = \"This identity may not read activity history.\";\n return;\n }\n if (!res.ok) {\n throw new Error(\n payload.error || \"Activity could not be loaded (\" + res.status + \").\"\n );\n }\n ACTIVITY = reset ? payload.events || [] : ACTIVITY.concat(payload.events || []);\n ACTIVITY_CURSOR = payload.nextCursor || null;\n ACTIVITY_LOADED = true;\n $(\"activityNotice\").textContent = \"\";\n renderActivity();\n } catch (error) {\n if (!isCurrent()) return;\n $(\"activityNotice\").setAttribute(\"role\", \"alert\");\n $(\"activityNotice\").textContent = errorMessage(\n error,\n \"Activity could not be loaded.\"\n );\n } finally {\n if (isCurrent()) {\n $(\"activityList\").setAttribute(\"aria-busy\", \"false\");\n $(\"refreshActivity\").disabled = false;\n $(\"moreActivity\").disabled = false;\n }\n }\n }\n function renderConnections() {\n const data = DATA;\n if (!data) return;\n const q = $(\"filter\").value.trim().toLowerCase();\n const list = $(\"list\");\n list.innerHTML = \"\";\n for (const filtered of filterUiConnectors(data.connectors, q)) {\n const c = filtered.connector;\n const tools = filtered.tools;\n const el = document.createElement(\"div\");\n el.className = \"card\";\n const status = c.status === \"ok\" ? \"Connected\" : c.status === \"auth_required\" ? \"Authorization needed\" : \"Unavailable\";\n let head = '<div class=\"connector-head\"><div><div class=\"connector-title\"><span class=\"dot ' + esc(c.status) + '\" aria-hidden=\"true\"></span><h2>' + esc(c.title || c.id) + \"</h2></div>\";\n if (c.description) {\n head += '<p class=\"connector-description meta\">' + esc(c.description) + \"</p>\";\n }\n head += '</div><div class=\"connector-state cap\">' + esc(status) + \" · \" + c.toolCount + (c.toolCount === 1 ? \" tool\" : \" tools\") + '<br><span class=\"mono\">' + esc(c.id) + \"</span></div></div>\";\n if (c.message) {\n head += '<p class=\"connector-message msg\">' + esc(c.message) + \"</p>\";\n }\n if (c.authorizationUrl) {\n const safe = safeHttp(c.authorizationUrl);\n head += safe ? '<p class=\"connector-auth\"><a class=\"linklike\" href=\"' + esc(safe) + '\" target=\"_blank\" rel=\"noopener\">Authorize connector →</a></p>' : '<p class=\"connector-auth meta\">Authorization URL: ' + esc(c.authorizationUrl) + \"</p>\";\n }\n if (c.oauth && data.oauthManagement) {\n const oauthName = c.title || c.id;\n head += '<div class=\"credential-actions\">';\n head += '<button type=\"button\" class=\"linklike danger\" aria-label=\"Disconnect OAuth for ' + esc(oauthName) + '\" data-oauth-action=\"disconnect\" data-connector=\"' + esc(c.id) + '\">Disconnect OAuth</button>';\n head += '<button type=\"button\" class=\"linklike\" aria-label=\"' + (c.status === \"ok\" ? \"Reconnect OAuth for \" : \"Restart authorization for \") + esc(oauthName) + '\" data-oauth-action=\"reconnect\" data-connector=\"' + esc(c.id) + '\">' + (c.status === \"ok\" ? \"Reconnect OAuth\" : \"Restart authorization\") + \"</button></div>\";\n }\n if (c.credential) {\n head += '<p class=\"connector-auth\"><a class=\"linklike\" href=\"/credentials\" data-operator-page=\"credentials\">Manage credential →</a></p>';\n }\n let body = \"\";\n if (tools.length) {\n body = \"<details\" + (q ? \" open\" : \"\") + '><summary class=\"linklike\">Show tools (' + tools.length + ')</summary><div class=\"tool-list\">';\n for (const t of tools) {\n body += '<div class=\"tool\"><code>' + esc(t.address) + \"</code>\";\n if (t.description) body += '<span class=\"td\">' + esc(t.description) + \"</span>\";\n body += \"</div>\";\n }\n body += \"</div></details>\";\n }\n el.innerHTML = head + body;\n list.appendChild(el);\n }\n if (!list.children.length) {\n list.innerHTML = '<p class=\"empty\">' + (q ? \"No connectors or tools match this filter.\" : \"No connectors are declared in this deployment.\") + \"</p>\";\n }\n }\n async function operatorRequest(path, method, generation, body) {\n const token = await sessionToken();\n if (generation !== SESSION_GENERATION) {\n throw new Error(\"The operator session changed.\");\n }\n if (!token) throw new Error(\"Your Clerk session has expired.\");\n const res = await fetch(path, {\n method,\n headers: {\n Authorization: \"Bearer \" + token,\n ...body ? { \"Content-Type\": \"application/json\" } : {}\n },\n ...body ? { body: JSON.stringify(body) } : {}\n });\n if (res.status === 204) return null;\n let payload = {};\n try {\n payload = await res.json();\n } catch {\n }\n if (!res.ok) throw new Error(payload.error || \"Request failed (\" + res.status + \").\");\n return payload;\n }\n $(\"list\").onclick = async (event) => {\n const button = closestElement(\n event.target,\n \"[data-oauth-action]\"\n );\n if (!button) return;\n const connector = button.dataset.connector;\n const action = button.dataset.oauthAction;\n if (!connector || action !== \"disconnect\" && action !== \"reconnect\") return;\n const generation = SESSION_GENERATION;\n const question = action === \"disconnect\" ? \"Disconnect OAuth for \" + connector + \"? Stored credentials and any pending authorization will be removed.\" : \"Restart OAuth for \" + connector + \"? Stored credentials and any pending authorization will be replaced.\";\n if (!window.confirm(question)) return;\n setOauthNotice(\"\");\n const buttons = [...document.querySelectorAll(\n '[data-connector=\"' + CSS.escape(connector) + '\"]'\n )];\n buttons.forEach((item) => {\n item.disabled = true;\n });\n try {\n const result = await operatorRequest(\n \"/ui/oauth/\" + encodeURIComponent(connector),\n action === \"disconnect\" ? \"DELETE\" : \"POST\",\n generation\n );\n if (generation !== SESSION_GENERATION) return;\n await load();\n if (generation !== SESSION_GENERATION) return;\n setOauthNotice(\n action === \"disconnect\" ? \"OAuth disconnected. Restart authorization when you are ready to reconnect.\" : result?.message || \"Authorization restarted. Open the authorization link to reconnect.\"\n );\n $(\"oauthNotice\").focus();\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n try {\n await load();\n } catch {\n }\n if (generation !== SESSION_GENERATION) return;\n setOauthNotice(errorMessage(error, \"OAuth action failed.\"), true);\n $(\"oauthNotice\").focus();\n } finally {\n if (generation === SESSION_GENERATION) {\n buttons.forEach((item) => {\n item.disabled = false;\n });\n }\n }\n };\n function renderCredentials() {\n const data = DATA;\n if (!data) return;\n const list = $(\"credentialList\");\n list.innerHTML = \"\";\n if (data.credentialManagement !== \"available\") return;\n for (const c of data.connectors) {\n const cred = c.credential;\n if (!cred) continue;\n const configured = Boolean(cred.configured);\n const removable = configured || Boolean(cred.removable);\n const state = configured ? cred.fields?.length ? \"configured\" : \"configured · ••••\" + esc(cred.lastFour || \"\") : \"not configured\";\n const updated = configured && cred.updatedAt ? \" · updated \" + esc(formatDate(cred.updatedAt)) : \"\";\n const el = document.createElement(\"section\");\n el.className = \"credential-card\";\n el.id = \"credential-\" + c.id;\n el.setAttribute(\"aria-labelledby\", \"credential-title-\" + c.id);\n let body = '<div class=\"credential-head\"><div class=\"connector-title\"><span class=\"dot ' + (configured ? \"ok\" : \"auth_required\") + '\" aria-hidden=\"true\"></span><h2 id=\"credential-title-' + esc(c.id) + '\">' + esc(c.title || c.id) + '</h2></div><span class=\"credential-state\">' + state + updated + \"</span></div>\";\n body += '<p class=\"mono\">' + esc(c.id) + \" · \" + esc(cred.label) + \"</p>\";\n if (cred.description) {\n body += '<p class=\"credential-copy meta\">' + esc(cred.description) + \"</p>\";\n }\n if (cred.fields?.length) {\n body += '<div class=\"credential-field-summary\">';\n for (const field of cred.fields) {\n const fieldState = field.configured ? \"configured · ••••\" + esc(field.lastFour || \"\") + (field.updatedAt ? \" · updated \" + esc(formatDate(field.updatedAt)) : \"\") : \"not configured\";\n body += \"<div><span>\" + esc(field.label) + '</span><span class=\"meta\">' + fieldState + \"</span></div>\";\n }\n body += \"</div>\";\n }\n if (cred.error) body += '<div class=\"msg\">' + esc(cred.error) + \"</div>\";\n if (cred.notice) {\n body += '<p class=\"credential-copy meta\">' + esc(cred.notice) + \"</p>\";\n }\n body += '<div class=\"credential-actions\">';\n body += '<button class=\"linklike\" type=\"button\" data-credential-action=\"edit\" data-connector=\"' + esc(c.id) + '\">' + (removable ? \"Replace\" : \"Add credential\") + \"</button>\";\n if (configured && cred.testable) {\n body += '<button class=\"linklike\" type=\"button\" data-credential-action=\"test\" data-connector=\"' + esc(c.id) + '\">Test</button>';\n }\n if (removable) {\n body += '<button type=\"button\" class=\"linklike danger\" data-credential-action=\"remove\" data-connector=\"' + esc(c.id) + '\">Remove</button>';\n }\n body += \"</div>\";\n body += '<div class=\"credential-form hidden\" data-credential-form=\"' + esc(c.id) + '\">';\n if (cred.fields && cred.fields.length) {\n body += '<div class=\"credential-fields\">';\n for (const [index, field] of cred.fields.entries()) {\n const inputId = \"credential-input-\" + c.id + \"-\" + index;\n body += '<div class=\"credential-field\"><label for=\"' + esc(inputId) + '\">' + esc(field.label) + '</label><input id=\"' + esc(inputId) + '\" type=\"' + esc(field.inputType || \"password\") + '\" data-credential-field=\"' + esc(field.name) + '\" placeholder=\"' + esc(field.placeholder || field.label) + '\" autocomplete=\"' + (field.inputType === \"password\" ? \"new-password\" : \"off\") + '\" autocapitalize=\"none\" spellcheck=\"false\"></div>';\n }\n body += \"</div>\";\n } else {\n const inputId = \"credential-input-\" + c.id;\n body += '<label class=\"visually-hidden\" for=\"' + esc(inputId) + '\">' + esc(cred.label) + '</label><input id=\"' + esc(inputId) + '\" type=\"password\" data-credential-input=\"' + esc(c.id) + '\" aria-label=\"' + esc(cred.label) + '\" placeholder=\"' + esc(cred.placeholder || \"Paste credential\") + '\" autocomplete=\"new-password\" autocapitalize=\"none\" spellcheck=\"false\">';\n }\n body += '<button class=\"linklike\" type=\"button\" data-credential-action=\"save\" data-connector=\"' + esc(c.id) + '\">Save</button><button class=\"linklike\" type=\"button\" data-credential-action=\"cancel\" data-connector=\"' + esc(c.id) + '\">Cancel</button></div>';\n el.innerHTML = body;\n list.appendChild(el);\n }\n }\n async function credentialRequest(connector, method, action, body, generation) {\n const suffix = action ? \"/\" + action : \"\";\n return operatorRequest(\n \"/ui/credentials/\" + encodeURIComponent(connector) + suffix,\n method,\n generation,\n body ?? void 0\n );\n }\n function credentialForm(connector) {\n const form = document.querySelector(\n '[data-credential-form=\"' + CSS.escape(connector) + '\"]'\n );\n if (!form) throw new Error(\"Credential form is unavailable.\");\n return form;\n }\n $(\"credentialList\").onclick = async (event) => {\n const button = closestElement(\n event.target,\n \"[data-credential-action]\"\n );\n if (!button) return;\n const connector = button.dataset.connector;\n const action = button.dataset.credentialAction;\n if (!connector || !action) return;\n const form = credentialForm(connector);\n const generation = SESSION_GENERATION;\n if (action === \"edit\") {\n form.classList.remove(\"hidden\");\n form.querySelector(\"input\")?.focus();\n return;\n }\n if (action === \"cancel\") {\n form.querySelectorAll(\"input\").forEach((input) => {\n input.value = \"\";\n });\n form.classList.add(\"hidden\");\n document.querySelector(\n '[data-credential-action=\"edit\"][data-connector=\"' + CSS.escape(connector) + '\"]'\n )?.focus();\n return;\n }\n if (action === \"remove\" && !window.confirm(\n \"Remove this credential? The connector will stop authenticating until a replacement is added.\"\n )) return;\n setNotice(\"\");\n $(\"credentialList\").setAttribute(\"aria-busy\", \"true\");\n const buttons = [...document.querySelectorAll(\n '[data-connector=\"' + CSS.escape(connector) + '\"]'\n )];\n buttons.forEach((item) => {\n item.disabled = true;\n });\n try {\n if (action === \"save\") {\n const fieldInputs = [\n ...form.querySelectorAll(\"[data-credential-field]\")\n ];\n if (fieldInputs.length) {\n const values = {};\n for (const input of fieldInputs) {\n const value = input.value.trim();\n if (!value) throw new Error(\"Complete every credential field before saving.\");\n const field = input.dataset.credentialField;\n if (!field) throw new Error(\"Credential field is unnamed.\");\n values[field] = value;\n }\n await credentialRequest(connector, \"PUT\", \"\", { values }, generation);\n } else {\n const input = form.querySelector(\n \"[data-credential-input]\"\n );\n if (!input) throw new Error(\"Credential input is unavailable.\");\n const value = input.value.trim();\n if (!value) throw new Error(\"Paste a credential before saving.\");\n await credentialRequest(connector, \"PUT\", \"\", { value }, generation);\n }\n if (generation !== SESSION_GENERATION) return;\n form.querySelectorAll(\"input\").forEach((input) => {\n input.value = \"\";\n });\n setNotice(\"Credential saved.\");\n await load();\n if (generation !== SESSION_GENERATION) return;\n $(\"credentialNotice\").focus();\n } else if (action === \"remove\") {\n await credentialRequest(connector, \"DELETE\", \"\", null, generation);\n if (generation !== SESSION_GENERATION) return;\n setNotice(\"Credential removed.\");\n await load();\n if (generation !== SESSION_GENERATION) return;\n $(\"credentialNotice\").focus();\n } else if (action === \"test\") {\n const result = await credentialRequest(\n connector,\n \"POST\",\n \"test\",\n null,\n generation\n );\n if (generation !== SESSION_GENERATION) return;\n setNotice(\n result?.message || (result?.ok ? \"Credential is valid.\" : \"Credential test failed.\"),\n !result?.ok\n );\n }\n } catch (error) {\n if (generation !== SESSION_GENERATION) return;\n setNotice(errorMessage(error, \"Credential action failed.\"), true);\n } finally {\n if (generation === SESSION_GENERATION) {\n $(\"credentialList\").setAttribute(\"aria-busy\", \"false\");\n buttons.forEach((item) => {\n item.disabled = false;\n });\n }\n }\n };\n $(\"save\").onclick = async () => {\n const v = $(\"token\").value.trim();\n if (!v) return;\n clearIdentityState();\n localStorage.setItem(KEY, v);\n $(\"token\").value = \"\";\n await load();\n if (DATA) $(CURRENT_PAGE + \"Heading\").focus();\n };\n $(\"change\").onclick = () => {\n localStorage.removeItem(KEY);\n showGate(\"\");\n $(\"token\").focus();\n };\n $(\"copyMcpUrl\").onclick = async () => {\n const button = $(\"copyMcpUrl\");\n try {\n await navigator.clipboard.writeText(MCP_URL);\n button.textContent = \"Copied\";\n } catch {\n button.textContent = \"Copy failed\";\n }\n window.setTimeout(() => {\n button.textContent = \"Copy URL\";\n }, 1600);\n };\n $(\"signin\").onclick = () => Clerk.redirectToSignIn({\n signInFallbackRedirectUrl: window.location.href,\n signUpFallbackRedirectUrl: window.location.href\n });\n function signOut() {\n clearIdentityState();\n return Clerk.signOut({ redirectUrl: window.location.href });\n }\n $(\"gateSignout\").onclick = signOut;\n $(\"signout\").onclick = signOut;\n $(\"filter\").oninput = () => {\n if (DATA) renderConnections();\n };\n $(\"refreshActivity\").onclick = () => loadActivity(true);\n $(\"moreActivity\").onclick = () => loadActivity(false);\n $(\"activitySearch\").oninput = () => renderActivity();\n document.addEventListener(\"click\", (event) => {\n const link = closestElement(\n event.target,\n \"a[data-operator-page]\"\n );\n if (!link || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n const target = new URL(link.href, window.location.href);\n if (target.origin !== window.location.origin) return;\n const page = link.dataset.operatorPage;\n if (page !== \"connections\" && page !== \"credentials\" && page !== \"activity\") return;\n event.preventDefault();\n navigateTo(page, target.pathname + target.search + target.hash);\n });\n window.addEventListener(\"popstate\", () => {\n activatePage(pageForPath(window.location.pathname), { focus: true });\n });\n async function init() {\n if (AUTH.kind === \"clerk\") {\n $(\"clerkGate\").classList.remove(\"hidden\");\n $(\"signout\").classList.remove(\"hidden\");\n try {\n if (!window.Clerk) {\n return showGate(\"Clerk could not load. Check your network and try again.\");\n }\n await Clerk.load({\n ...AUTH.signInUrl ? { signInUrl: AUTH.signInUrl } : {},\n ...AUTH.signUpUrl ? { signUpUrl: AUTH.signUpUrl } : {},\n signInFallbackRedirectUrl: window.location.href,\n signUpFallbackRedirectUrl: window.location.href,\n afterSignOutUrl: window.location.href\n });\n CLERK_SESSION_ID = Clerk.session?.id ?? null;\n Clerk.addListener((resources) => {\n const nextSessionId = resources.session?.id ?? null;\n if (nextSessionId === CLERK_SESSION_ID) return;\n CLERK_SESSION_ID = nextSessionId;\n showGate(\"\");\n void load();\n });\n } catch (error) {\n return showGate(\n \"Clerk could not initialize: \" + errorMessage(error, \"unknown error\")\n );\n }\n } else {\n $(\"tokenGate\").classList.remove(\"hidden\");\n $(\"change\").classList.remove(\"hidden\");\n }\n activatePage(pageForPath(window.location.pathname));\n await load();\n }\n if (AUTH.kind === \"clerk\") {\n window.addEventListener(\"load\", init);\n } else {\n init();\n }\n})();\n";
5
5
  //# sourceMappingURL=generated.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generated.js","sourceRoot":"","sources":["../../src/operator-ui/generated.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,i8WAAi8W,CAAC;AACj+W,MAAM,CAAC,MAAM,kBAAkB,GAAG,0itCAA0itC,CAAC"}
1
+ {"version":3,"file":"generated.js","sourceRoot":"","sources":["../../src/operator-ui/generated.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,quUAAquU,CAAC;AACrwU,MAAM,CAAC,MAAM,kBAAkB,GAAG,07iCAA07iC,CAAC"}
@@ -43,27 +43,8 @@ export interface UiConnector {
43
43
  tools: UiTool[];
44
44
  /** This connector exposes operator-managed downstream OAuth lifecycle hooks. */
45
45
  oauth?: boolean;
46
- /**
47
- * Verdict of the last proactive credential liveness check (issue #24), for the
48
- * connectors that hold a credential connecta stores. Shown beside the live
49
- * status so an operator can tell "checked just now" from "last verified an
50
- * hour ago", and see a dead credential the page's own probe may not reach.
51
- */
52
- credentialCheck?: {
53
- state: "ok" | "auth_required" | "error";
54
- checkedAt: string;
55
- message?: string;
56
- };
57
46
  credential?: UiCredential;
58
47
  }
59
- export interface UiToolkit {
60
- name: string;
61
- connectors: string[];
62
- includeTools: string[];
63
- excludeTools: string[];
64
- /** Tools currently loaded through healthy connectors and visible in this view. */
65
- toolCount: number;
66
- }
67
48
  export type CredentialManagementCapability = "available" | "requires_clerk" | "vault_not_configured" | "no_slots";
68
49
  export interface UiData {
69
50
  serverInfo: {
@@ -73,11 +54,9 @@ export interface UiData {
73
54
  /** Version of the installed @zackbart/connecta package. */
74
55
  connectaVersion: string;
75
56
  connectors: UiConnector[];
76
- /** Read-only projection of the validated deployment config. */
77
- toolkits: UiToolkit[];
78
57
  activityEnabled: boolean;
79
58
  credentialManagement: CredentialManagementCapability;
80
- /** True only for an eligible, unrestricted Clerk operator. */
59
+ /** True only for an eligible Clerk operator. */
81
60
  oauthManagement: boolean;
82
61
  }
83
62
  export interface FilteredUiConnector {
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/operator-ui/model.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACzC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,IAAI,GAAG,eAAe,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,eAAe,CAAC,EAAE;QAChB,KAAK,EAAE,IAAI,GAAG,eAAe,GAAG,OAAO,CAAC;QACxC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,8BAA8B,GACtC,WAAW,GACX,gBAAgB,GAChB,sBAAsB,GACtB,UAAU,CAAC;AAEf,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,oBAAoB,EAAE,8BAA8B,CAAC;IACrD,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,WAAW,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,WAAW,EAAE,EACzB,KAAK,EAAE,MAAM,GACZ,mBAAmB,EAAE,CAuBvB"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/operator-ui/model.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACzC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,IAAI,GAAG,eAAe,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B;AAED,MAAM,MAAM,8BAA8B,GACtC,WAAW,GACX,gBAAgB,GAChB,sBAAsB,GACtB,UAAU,CAAC;AAEf,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,oBAAoB,EAAE,8BAA8B,CAAC;IACrD,gDAAgD;IAChD,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,WAAW,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,WAAW,EAAE,EACzB,KAAK,EAAE,MAAM,GACZ,mBAAmB,EAAE,CAuBvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/operator-ui/model.ts"],"names":[],"mappings":"AA+FA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAyB,EACzB,KAAa;IAEb,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG;YACpB,SAAS,CAAC,EAAE;YACZ,SAAS,CAAC,KAAK;YACf,SAAS,CAAC,WAAW;YACrB,SAAS,CAAC,MAAM;SACjB;aACE,IAAI,CAAC,GAAG,CAAC;aACT,WAAW,EAAE,CAAC;QACjB,MAAM,gBAAgB,GAAG,OAAO,CAAC,CAAC,IAAI,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAClC,CAAC,IAAI,EAAE,EAAE,CACP,CAAC,CAAC;YACF,gBAAgB;YAChB,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CACrE,CAAC;QACF,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,gBAAgB;YAAE,SAAS;QAC3D,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/operator-ui/model.ts"],"names":[],"mappings":"AAyEA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAyB,EACzB,KAAa;IAEb,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG;YACpB,SAAS,CAAC,EAAE;YACZ,SAAS,CAAC,KAAK;YACf,SAAS,CAAC,WAAW;YACrB,SAAS,CAAC,MAAM;SACjB;aACE,IAAI,CAAC,GAAG,CAAC;aACT,WAAW,EAAE,CAAC;QACjB,MAAM,gBAAgB,GAAG,OAAO,CAAC,CAAC,IAAI,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAClC,CAAC,IAAI,EAAE,EAAE,CACP,CAAC,CAAC;YACF,gBAAgB;YAChB,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CACrE,CAAC;QACF,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,gBAAgB;YAAE,SAAS;QAC3D,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -1,9 +1,6 @@
1
1
  import type { Connector, ConnectorContext, ConnectorStatus, KVStorage, Logger, ToolDef } from "./types.js";
2
2
  import { type CredentialVault } from "./credentials.js";
3
- import { type CredentialCheckOptions, type CredentialCheckResult, type CredentialHealthConfig, type CredentialHealthRecord } from "./credential-health.js";
4
3
  import { type CallAdmissionPermit, type ConnectorCallAdmissionSnapshot } from "./call-admission.js";
5
- import type { DeferredWork } from "./connector-scope.js";
6
- import { type Toolkit } from "./toolkits.js";
7
4
  /**
8
5
  * Smallest accepted inline-result cap. One byte is pathological but harmless:
9
6
  * `alignEndToCharBoundary` widens a window narrower than the codepoint at the
@@ -40,23 +37,6 @@ export interface HealthObservation {
40
37
  consecutiveFailures: number;
41
38
  lastError?: string;
42
39
  }
43
- /**
44
- * Recent real-call outcomes per connector, as observed by ONE view.
45
- *
46
- * The deployment keeps one log, and each toolkit keeps its own. That split is a
47
- * scope boundary, not bookkeeping: `lastError` is a downstream error string
48
- * that routinely names the tool that failed, and `list_connectors` returns the
49
- * observation verbatim. Sharing one log would let a toolkit read back the tools
50
- * and failures of a sibling toolkit through a connector they happen to share —
51
- * and let a sibling's failures flip this view's reported status. A scoped view
52
- * reads back only the calls it made itself.
53
- */
54
- export declare class HealthLog {
55
- private readonly observations;
56
- recordSuccess(id: string, latencyMs: number): void;
57
- recordFailure(id: string, latencyMs: number, error: unknown): void;
58
- get(id: string): HealthObservation | undefined;
59
- }
60
40
  export interface RegistryOptions {
61
41
  storage: KVStorage;
62
42
  logger: Logger;
@@ -75,8 +55,6 @@ export interface RegistryOptions {
75
55
  * of bytes >= 1; anything else warns and falls back to 100_000.
76
56
  */
77
57
  maxBatchResultBytes?: number;
78
- /** Tuning for the credential liveness checks (issue #24). */
79
- credentialHealth?: CredentialHealthConfig;
80
58
  }
81
59
  export type ConnectorOperationOptions = Pick<ConnectorContext, "signal" | "timeoutMs">;
82
60
  /**
@@ -84,12 +62,9 @@ export type ConnectorOperationOptions = Pick<ConnectorContext, "signal" | "timeo
84
62
  * (`src/meta-tools.ts`) and the `execute_code` sandbox bridge (`src/execute.ts`)
85
63
  * is typed against THIS, never against the concrete `Registry`.
86
64
  *
87
- * That indirection is the toolkit boundary (issue #22): a scoped connection is
88
- * handed a `ScopedRegistry` instead of the registry, and every meta-tool
89
- * inherits the scope from one place. Reaching for a registry method that isn't
90
- * here is a compile error, and adding one here is a compile error until
91
- * `ScopedRegistry` filters it — so a new meta-tool cannot quietly step around
92
- * the boundary.
65
+ * The read-only seam remains useful even without scoped views: meta-tools can
66
+ * consume registry behavior without depending on the concrete implementation
67
+ * or its construction-only methods.
93
68
  */
94
69
  export interface RegistryView {
95
70
  /** Deployment-wide result-size cap threaded to the meta-tools. */
@@ -106,10 +81,7 @@ export interface RegistryView {
106
81
  refreshTools(id: string, baseUrl: string, requestScope?: object, callOptions?: ConnectorOperationOptions): Promise<ToolDef[]>;
107
82
  peekTools(id: string): ToolDef[] | undefined;
108
83
  contextFor(id: string, baseUrl: string, requestScope?: object, callOptions?: ConnectorOperationOptions): ConnectorContext;
109
- /**
110
- * Acquire the connector's shared downstream-call permit. Scoped views
111
- * delegate to the base registry so every toolkit contends on the same pool.
112
- */
84
+ /** Acquire the connector's shared downstream-call permit. */
113
85
  admitCall(id: string, input: {
114
86
  toolName: string;
115
87
  args: unknown;
@@ -120,12 +92,9 @@ export interface RegistryView {
120
92
  recordFailure(id: string, latencyMs: number, error: unknown): void;
121
93
  healthFor(id: string): HealthObservation | undefined;
122
94
  hasObservedSuccess(id: string): boolean;
123
- /** When ANY view last saw a successful call to `id`, deployment-wide. */
124
95
  observedSuccessAt(id: string): string | undefined;
125
- /** Last credential-liveness verdict for `id`. Cached; no downstream I/O. */
126
- credentialHealthFor(id: string): Promise<CredentialHealthRecord | undefined>;
127
- /** Store a liveness verdict a live status check just produced. */
128
- recordCredentialHealth(id: string, record: CredentialHealthRecord): Promise<void>;
96
+ /** Local declared-vs-stored credential mismatch, with no downstream I/O. */
97
+ credentialDriftFor(id: string): Promise<string | undefined>;
129
98
  statusFor(id: string, baseUrl: string, requestScope?: object, callOptions?: ConnectorOperationOptions): Promise<ConnectorStatus>;
130
99
  invalidateStored(id: string): Promise<void>;
131
100
  }
@@ -155,8 +124,6 @@ export declare class Registry implements RegistryView {
155
124
  readonly maxResultBytes: number;
156
125
  /** Final batch envelope cap threaded to the meta-tools. */
157
126
  readonly maxBatchResultBytes: number;
158
- /** Proactive liveness checks over stored downstream credentials (issue #24). */
159
- private readonly credentialHealth;
160
127
  constructor(connectors: Connector[], opts: RegistryOptions);
161
128
  /**
162
129
  * Warn once per unusable result cap, at construction time — the same
@@ -233,152 +200,18 @@ export declare class Registry implements RegistryView {
233
200
  recordSuccess(id: string, latencyMs: number): void;
234
201
  recordFailure(id: string, latencyMs: number, error: unknown): void;
235
202
  healthFor(id: string): HealthObservation | undefined;
236
- /**
237
- * Whether ANY view of this deployment has seen a successful call to `id` —
238
- * a bare boolean, never the observation. Connector liveness (reachable,
239
- * credentials still valid) is a deployment-level fact, not a per-view one, so
240
- * `list_connectors` may classify a connector as ok/unknown from it. The
241
- * observation itself — `lastError` above all, which names the tool that
242
- * failed — stays strictly per view.
243
- */
203
+ /** Whether this deployment has seen a successful call to `id`. */
244
204
  hasObservedSuccess(id: string): boolean;
245
- /**
246
- * The timestamp behind `hasObservedSuccess`, on the same deployment-wide
247
- * terms and for the same reason: it says only *when* the connector last
248
- * answered, never what was called or what failed. Credential health reads it
249
- * to decide whether a failed verdict has been overtaken by real traffic.
250
- */
205
+ /** The timestamp behind `hasObservedSuccess`. */
251
206
  observedSuccessAt(id: string): string | undefined;
252
- /**
253
- * The last credential-liveness verdict for `id` — the layer that lets a cached
254
- * status read report `auth_required` before a real call discovers it. Read
255
- * from storage (mirrored in memory for a few seconds) rather than held in
256
- * memory alone, because on Workers the isolate that ran the check is usually
257
- * not the isolate answering this read.
258
- */
259
- credentialHealthFor(id: string): Promise<CredentialHealthRecord | undefined>;
260
- recordCredentialHealth(id: string, record: CredentialHealthRecord): Promise<void>;
261
- /**
262
- * Check stored downstream credentials now and return one outcome per
263
- * connector considered. THE operator-facing entry point behind
264
- * `Connecta.checkCredentials()`: wire it to a Worker cron trigger or a Node
265
- * interval. Never rejects; connectors checked recently are reported as
266
- * `fresh` unless `force` is set.
267
- */
268
- checkCredentialHealth(baseUrl: string, opts?: CredentialCheckOptions, defer?: DeferredWork): Promise<CredentialCheckResult[]>;
269
- /**
270
- * The traffic-triggered sweep: a promise for the caller to defer (Workers:
271
- * `ctx.waitUntil`), or `undefined` when nothing is due — which is the common
272
- * case and costs no I/O. Called by the server after an authenticated request;
273
- * the checker owns the rate limiting.
274
- */
275
- sweepCredentialHealthIfDue(baseUrl: string, defer?: DeferredWork): Promise<CredentialCheckResult[]> | undefined;
276
- /**
277
- * Drop a connector's liveness verdict, because its credential just changed
278
- * under us (OAuth callback completed, credential stored or removed on
279
- * /credentials). A
280
- * stale `auth_required` must not outlive the re-authorization that fixed it —
281
- * that is the difference between recovery working and needing a restart.
282
- */
283
- clearCredentialHealth(id: string): Promise<void>;
207
+ credentialDriftFor(id: string): Promise<string | undefined>;
284
208
  /** Best-effort connector status for list_connectors. */
285
209
  statusFor(id: string, baseUrl: string, requestScope?: object, callOptions?: ConnectorOperationOptions): Promise<ConnectorStatus>;
210
+ private markCatalogInvalid;
211
+ private deleteStoredCatalog;
286
212
  /** Drop a connector's cached tool list (e.g. after auth completes). */
287
213
  invalidate(id: string): void;
288
214
  /** Drop both in-memory and persisted tool catalogs. */
289
215
  invalidateStored(id: string): Promise<void>;
290
216
  }
291
- /**
292
- * THE toolkit enforcement point (issue #22).
293
- *
294
- * A filtered VIEW of one long-lived `Registry`: same connectors, same tool
295
- * caches, same health — narrowed to the connectors and tool addresses one
296
- * toolkit selects. `serveMcp` builds it once per scoped connection and hands it
297
- * to `registerMetaTools`/`registerExecuteTool`, so `list_connectors`,
298
- * `search_tools`, `describe_tools`, `call_tool`, `call_destructive_tool`,
299
- * `batch_call`, `authorize_connector`, `get_result`, `skills`, and the
300
- * `execute_code` host bridge all inherit the boundary from here instead of
301
- * re-implementing nine checks.
302
- *
303
- * Two invariants make it reviewable:
304
- *
305
- * 1. **Indistinguishability.** Out-of-scope input fails through the SAME code
306
- * path, with the same error class and message, as input naming something
307
- * that does not exist at all. An out-of-scope CONNECTOR disappears from
308
- * `resolveAddress`/`getConnector` (→ `Unknown address` / `Unknown
309
- * connector`); an out-of-scope TOOL disappears from the catalog this view
310
- * returns (→ `Unknown tool "<t>" on connector "<c>"`), which is exactly the
311
- * error a misspelled tool name already produced. Scoping deliberately does
312
- * NOT reject at `resolveAddress` for tools: that would answer with the
313
- * connector-level message and make "hidden here" distinguishable from
314
- * "never existed".
315
- * 2. **It filters views, never state.** Every read delegates to the shared
316
- * registry and filters the returned array, so the tool cache, the persisted
317
- * catalog, and the health map stay whole and shared across scopes.
318
- */
319
- export declare class ScopedRegistry implements RegistryView {
320
- private readonly base;
321
- private readonly toolkit;
322
- /** This toolkit's own health observations — see HealthLog. */
323
- private readonly health;
324
- constructor(base: Registry, toolkit: Toolkit);
325
- get maxResultBytes(): number;
326
- get maxBatchResultBytes(): number;
327
- /** In scope AND actually registered. */
328
- private visible;
329
- /** Byte-identical to what the unscoped registry throws for an unknown id. */
330
- private unknownConnector;
331
- private inScopeTools;
332
- listConnectors(): Connector[];
333
- getConnector(id: string): Connector | undefined;
334
- resolveAddress(address: string): {
335
- connector: Connector;
336
- toolName: string;
337
- } | null;
338
- getTools(id: string, baseUrl: string, requestScope?: object, callOptions?: ConnectorOperationOptions): Promise<ToolDef[]>;
339
- refreshTools(id: string, baseUrl: string, requestScope?: object, callOptions?: ConnectorOperationOptions): Promise<ToolDef[]>;
340
- peekTools(id: string): ToolDef[] | undefined;
341
- contextFor(id: string, baseUrl: string, requestScope?: object, callOptions?: ConnectorOperationOptions): ConnectorContext;
342
- admitCall(id: string, input: {
343
- toolName: string;
344
- args: unknown;
345
- signal?: AbortSignal;
346
- }): Promise<CallAdmissionPermit>;
347
- /**
348
- * Stashed oversized results are bound to the scope that produced them: a
349
- * scoped session cannot page a result it could not have produced, and an id
350
- * from another scope reads back as the ordinary "Unknown or expired result
351
- * id". Unscoped sessions keep the historical `results:` prefix untouched.
352
- */
353
- resultsStorage(): KVStorage;
354
- recordSuccess(id: string, latencyMs: number): void;
355
- recordFailure(id: string, latencyMs: number, error: unknown): void;
356
- healthFor(id: string): HealthObservation | undefined;
357
- /**
358
- * Deliberately NOT per view: whether the connector has ever answered is a
359
- * fact about the connector, not about a team's traffic, and withholding it
360
- * would report every remote connector as "unknown" to a scoped session that
361
- * has not called it yet. It carries no tool name, error text, or count — the
362
- * per-view isolation of those, above, is unchanged.
363
- */
364
- hasObservedSuccess(id: string): boolean;
365
- /** Deployment-wide for the same reason as `hasObservedSuccess` above. */
366
- observedSuccessAt(id: string): string | undefined;
367
- /**
368
- * Also deliberately NOT per view, for the same reason as
369
- * `hasObservedSuccess`: whether the credential connecta stores for a connector
370
- * still works is a fact about the deployment's credential, not about a team's
371
- * traffic. Withholding it would leave a scoped session unable to see that the
372
- * connector it shares needs re-authorization. The verdict carries the
373
- * connector's own connector-level reason — never a tool name — so the per-view
374
- * isolation of `lastError` above is unchanged.
375
- */
376
- credentialHealthFor(id: string): Promise<CredentialHealthRecord | undefined>;
377
- recordCredentialHealth(id: string, record: CredentialHealthRecord): Promise<void>;
378
- statusFor(id: string, baseUrl: string, requestScope?: object, callOptions?: {
379
- signal?: AbortSignal;
380
- timeoutMs?: number;
381
- }): Promise<ConnectorStatus>;
382
- invalidateStored(id: string): Promise<void>;
383
- }
384
217
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}