prism-mcp-server 4.3.0 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +259 -81
- package/dist/dashboard/ui.js +333 -2
- package/dist/lifecycle.js +6 -0
- package/dist/server.js +234 -147
- package/dist/storage/sqlite.js +21 -0
- package/dist/storage/supabase.js +49 -15
- package/dist/storage/supabaseMigrations.js +42 -1
- package/dist/tools/compactionHandler.js +7 -14
- package/dist/tools/handlers.js +26 -3
- package/dist/tools/index.js +2 -2
- package/dist/tools/sessionMemoryDefinitions.js +53 -0
- package/dist/tools/sessionMemoryHandlers.js +263 -30
- package/dist/utils/briefing.js +9 -10
- package/dist/utils/factMerger.js +11 -16
- package/dist/utils/healthCheck.js +19 -22
- package/dist/utils/imageCaptioner.js +240 -0
- package/dist/utils/llm/adapters/anthropic.js +128 -0
- package/dist/utils/llm/adapters/gemini.js +152 -0
- package/dist/utils/llm/adapters/openai.js +183 -0
- package/dist/utils/llm/adapters/traced.js +190 -0
- package/dist/utils/llm/factory.js +143 -0
- package/dist/utils/llm/provider.js +25 -0
- package/dist/utils/telemetry.js +174 -0
- package/package.json +9 -2
package/dist/dashboard/ui.js
CHANGED
|
@@ -657,6 +657,8 @@ export function renderDashboardHTML(version) {
|
|
|
657
657
|
<div class="settings-tabs">
|
|
658
658
|
<button class="s-tab active" id="stab-settings" onclick="switchSettingsTab('settings')">⚙️ Settings</button>
|
|
659
659
|
<button class="s-tab" id="stab-skills" onclick="switchSettingsTab('skills')">📜 Skills</button>
|
|
660
|
+
<button class="s-tab" id="stab-providers" onclick="switchSettingsTab('providers')">🤖 AI Providers</button>
|
|
661
|
+
<button class="s-tab" id="stab-observability" onclick="switchSettingsTab('observability')">🔭 Observability</button>
|
|
660
662
|
</div>
|
|
661
663
|
|
|
662
664
|
<!-- Settings panel (existing content) -->
|
|
@@ -817,6 +819,225 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
817
819
|
</div>
|
|
818
820
|
</div><!-- /spanel-skills -->
|
|
819
821
|
|
|
822
|
+
<!-- AI Providers panel (v4.4) -->
|
|
823
|
+
<div class="s-tab-panel" id="spanel-providers">
|
|
824
|
+
|
|
825
|
+
<div class="setting-section">Text Provider <span class="boot-badge">Restart Required</span></div>
|
|
826
|
+
|
|
827
|
+
<!-- ── Text Provider ──────────────────────────────── -->
|
|
828
|
+
<div class="setting-row">
|
|
829
|
+
<div>
|
|
830
|
+
<div class="setting-label">Text Provider</div>
|
|
831
|
+
<div class="setting-desc">LLM used for compaction, briefing, security scan & fact merging</div>
|
|
832
|
+
</div>
|
|
833
|
+
<select id="select-text-provider"
|
|
834
|
+
style="padding: 0.2rem 0.4rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); cursor: pointer;"
|
|
835
|
+
onchange="onTextProviderChange(this.value)">
|
|
836
|
+
<option value="gemini">🔵 Gemini (Google)</option>
|
|
837
|
+
<option value="openai">🟢 OpenAI / Ollama</option>
|
|
838
|
+
<option value="anthropic">🟣 Anthropic (Claude)</option>
|
|
839
|
+
</select>
|
|
840
|
+
</div>
|
|
841
|
+
|
|
842
|
+
<!-- Gemini text fields -->
|
|
843
|
+
<div id="provider-fields-gemini">
|
|
844
|
+
<div class="setting-row">
|
|
845
|
+
<div>
|
|
846
|
+
<div class="setting-label">Google API Key</div>
|
|
847
|
+
<div class="setting-desc">GOOGLE_API_KEY — required for Gemini text & embeddings</div>
|
|
848
|
+
</div>
|
|
849
|
+
<input type="password" id="input-google-api-key"
|
|
850
|
+
placeholder="AIza…"
|
|
851
|
+
style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 180px;"
|
|
852
|
+
onchange="saveBootSetting('GOOGLE_API_KEY', this.value)"
|
|
853
|
+
oninput="clearTimeout(this._pt); this._pt=setTimeout(()=>saveBootSetting('GOOGLE_API_KEY',this.value),800)" />
|
|
854
|
+
</div>
|
|
855
|
+
</div>
|
|
856
|
+
|
|
857
|
+
<!-- OpenAI / Ollama text fields -->
|
|
858
|
+
<div id="provider-fields-openai" style="display:none">
|
|
859
|
+
<div class="setting-row">
|
|
860
|
+
<div>
|
|
861
|
+
<div class="setting-label">API Key</div>
|
|
862
|
+
<div class="setting-desc">Leave blank for Ollama / LM Studio (local endpoints)</div>
|
|
863
|
+
</div>
|
|
864
|
+
<input type="password" id="input-openai-api-key"
|
|
865
|
+
placeholder="sk-… (blank for Ollama)"
|
|
866
|
+
style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 180px;"
|
|
867
|
+
onchange="saveBootSetting('openai_api_key', this.value)"
|
|
868
|
+
oninput="clearTimeout(this._pt); this._pt=setTimeout(()=>saveBootSetting('openai_api_key',this.value),800)" />
|
|
869
|
+
</div>
|
|
870
|
+
<div class="setting-row">
|
|
871
|
+
<div>
|
|
872
|
+
<div class="setting-label">Base URL</div>
|
|
873
|
+
<div class="setting-desc">Ollama: http://localhost:11434/v1 · LM Studio: http://localhost:1234/v1</div>
|
|
874
|
+
</div>
|
|
875
|
+
<input type="text" id="input-openai-base-url"
|
|
876
|
+
placeholder="https://api.openai.com/v1"
|
|
877
|
+
style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 220px;"
|
|
878
|
+
onchange="saveBootSetting('openai_base_url', this.value)"
|
|
879
|
+
oninput="clearTimeout(this._pu); this._pu=setTimeout(()=>saveBootSetting('openai_base_url',this.value),800)" />
|
|
880
|
+
</div>
|
|
881
|
+
<div class="setting-row">
|
|
882
|
+
<div>
|
|
883
|
+
<div class="setting-label">Chat Model</div>
|
|
884
|
+
<div class="setting-desc">Used for compaction, briefing, security scan</div>
|
|
885
|
+
</div>
|
|
886
|
+
<input type="text" id="input-openai-model"
|
|
887
|
+
placeholder="gpt-4o-mini"
|
|
888
|
+
style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 160px;"
|
|
889
|
+
onchange="saveBootSetting('openai_model', this.value)"
|
|
890
|
+
oninput="clearTimeout(this._pm); this._pm=setTimeout(()=>saveBootSetting('openai_model',this.value),800)" />
|
|
891
|
+
</div>
|
|
892
|
+
</div>
|
|
893
|
+
|
|
894
|
+
<!-- Anthropic / Claude text fields -->
|
|
895
|
+
<div id="provider-fields-anthropic" style="display:none">
|
|
896
|
+
<div class="setting-row">
|
|
897
|
+
<div>
|
|
898
|
+
<div class="setting-label">Anthropic API Key</div>
|
|
899
|
+
<div class="setting-desc">Required. Get yours at console.anthropic.com</div>
|
|
900
|
+
</div>
|
|
901
|
+
<input type="password" id="input-anthropic-api-key"
|
|
902
|
+
placeholder="sk-ant-…"
|
|
903
|
+
style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 200px;"
|
|
904
|
+
onchange="saveBootSetting('anthropic_api_key', this.value)"
|
|
905
|
+
oninput="clearTimeout(this._pa); this._pa=setTimeout(()=>saveBootSetting('anthropic_api_key',this.value),800)" />
|
|
906
|
+
</div>
|
|
907
|
+
<div class="setting-row">
|
|
908
|
+
<div>
|
|
909
|
+
<div class="setting-label">Claude Model</div>
|
|
910
|
+
<div class="setting-desc">claude-3-5-sonnet for quality · claude-3-haiku for speed & cost</div>
|
|
911
|
+
</div>
|
|
912
|
+
<input type="text" id="input-anthropic-model"
|
|
913
|
+
placeholder="claude-3-5-sonnet-20241022"
|
|
914
|
+
style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 220px;"
|
|
915
|
+
onchange="saveBootSetting('anthropic_model', this.value)"
|
|
916
|
+
oninput="clearTimeout(this._pam); this._pam=setTimeout(()=>saveBootSetting('anthropic_model',this.value),800)" />
|
|
917
|
+
</div>
|
|
918
|
+
</div>
|
|
919
|
+
|
|
920
|
+
<!-- ── Embedding Provider (always visible) ─────────── -->
|
|
921
|
+
<div class="setting-section" style="margin-top:1.2rem">Embedding Provider <span class="boot-badge">Restart Required</span></div>
|
|
922
|
+
|
|
923
|
+
<div class="setting-row">
|
|
924
|
+
<div>
|
|
925
|
+
<div class="setting-label">Embedding Provider</div>
|
|
926
|
+
<div class="setting-desc">Source for vector embeddings used by semantic memory search</div>
|
|
927
|
+
</div>
|
|
928
|
+
<select id="select-embedding-provider"
|
|
929
|
+
style="padding: 0.2rem 0.4rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); cursor: pointer;"
|
|
930
|
+
onchange="onEmbeddingProviderChange(this.value)">
|
|
931
|
+
<option value="auto">🔄 Auto (same as Text Provider)</option>
|
|
932
|
+
<option value="gemini">🔵 Gemini</option>
|
|
933
|
+
<option value="openai">🟢 OpenAI / Ollama</option>
|
|
934
|
+
</select>
|
|
935
|
+
</div>
|
|
936
|
+
|
|
937
|
+
<!-- Anthropic + auto warning: shown when text=anthropic AND embed=auto -->
|
|
938
|
+
<div id="anthropic-embed-warning" style="display:none;margin-top:0.5rem;padding:0.5rem 0.75rem;background:rgba(251,146,60,0.1);border:1px solid rgba(251,146,60,0.3);border-radius:6px;font-size:0.78rem;color:#fb923c;line-height:1.5">
|
|
939
|
+
⚠️ <strong>Anthropic has no native embedding API.</strong>
|
|
940
|
+
Auto mode will route embeddings to <strong>Gemini</strong>.
|
|
941
|
+
Set Embedding Provider to <strong>OpenAI / Ollama</strong> to use a local model (e.g. <code>nomic-embed-text</code>).
|
|
942
|
+
</div>
|
|
943
|
+
|
|
944
|
+
<!-- OpenAI embedding model field (shown when embedding_provider = openai) -->
|
|
945
|
+
<div id="embed-fields-openai" style="display:none">
|
|
946
|
+
<div class="setting-row">
|
|
947
|
+
<div>
|
|
948
|
+
<div class="setting-label">Embedding Model</div>
|
|
949
|
+
<div class="setting-desc">Must output 768 dims. Ollama: nomic-embed-text · OpenAI: text-embedding-3-small</div>
|
|
950
|
+
</div>
|
|
951
|
+
<input type="text" id="input-openai-embedding-model"
|
|
952
|
+
placeholder="text-embedding-3-small"
|
|
953
|
+
style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 210px;"
|
|
954
|
+
onchange="saveBootSetting('openai_embedding_model', this.value)"
|
|
955
|
+
oninput="clearTimeout(this._pe); this._pe=setTimeout(()=>saveBootSetting('openai_embedding_model',this.value),800)" />
|
|
956
|
+
</div>
|
|
957
|
+
</div>
|
|
958
|
+
|
|
959
|
+
<div style="margin-top:1rem;padding:0.6rem 0.8rem;background:rgba(139,92,246,0.08);border:1px solid rgba(139,92,246,0.2);border-radius:6px;font-size:0.78rem;color:var(--text-secondary);line-height:1.5">
|
|
960
|
+
💡 <strong>Cost-optimized setup:</strong> Text Provider → <code>Anthropic</code>, Embedding Provider → <code>OpenAI / Ollama</code>.<br>
|
|
961
|
+
Use Claude 3.5 Sonnet for reasoning & <code>nomic-embed-text</code> (free, local) for embeddings.
|
|
962
|
+
</div>
|
|
963
|
+
|
|
964
|
+
<span class="setting-saved" id="savedToastProviders">Saved ✓</span>
|
|
965
|
+
</div><!-- /spanel-providers -->
|
|
966
|
+
|
|
967
|
+
<!-- ─── Observability panel (v4.6.0 — OTel) ───────────────────────── -->
|
|
968
|
+
<div class="s-tab-panel" id="spanel-observability">
|
|
969
|
+
|
|
970
|
+
<div class="setting-section">OpenTelemetry (OTel)</div>
|
|
971
|
+
|
|
972
|
+
<div class="setting-row" style="align-items: flex-start; margin-bottom: 1rem;">
|
|
973
|
+
<div class="setting-desc" style="margin: 0;">
|
|
974
|
+
Export distributed traces to
|
|
975
|
+
<a href="https://www.jaegertracing.io" target="_blank" rel="noopener" style="color: var(--accent);">Jaeger</a>,
|
|
976
|
+
<a href="https://grafana.com/oss/tempo/" target="_blank" rel="noopener" style="color: var(--accent);">Grafana Tempo</a>, or
|
|
977
|
+
<a href="https://zipkin.io" target="_blank" rel="noopener" style="color: var(--accent);">Zipkin</a>.
|
|
978
|
+
Provides a full latency waterfall for every MCP tool call, LLM provider hop, and background worker task.
|
|
979
|
+
<br><br>
|
|
980
|
+
<code style="font-size: 0.8rem; background: var(--bg-hover); padding: 2px 6px; border-radius: 4px;">
|
|
981
|
+
docker run -d -p 4318:4318 -p 16686:16686 jaegertracing/all-in-one
|
|
982
|
+
</code>
|
|
983
|
+
→ open <a href="http://localhost:16686" target="_blank" rel="noopener" style="color: var(--accent);">localhost:16686</a>
|
|
984
|
+
</div>
|
|
985
|
+
</div>
|
|
986
|
+
|
|
987
|
+
<!-- Enable toggle -->
|
|
988
|
+
<div class="setting-row">
|
|
989
|
+
<div>
|
|
990
|
+
<div class="setting-label">Enable OpenTelemetry</div>
|
|
991
|
+
<div class="setting-desc">Activates the W3C tracing pipeline. <strong>Requires server restart.</strong></div>
|
|
992
|
+
</div>
|
|
993
|
+
<label class="toggle-switch">
|
|
994
|
+
<input type="checkbox" id="input-otel-enabled"
|
|
995
|
+
onchange="saveBootSetting('otel_enabled', this.checked ? 'true' : 'false')">
|
|
996
|
+
<span class="slider"></span>
|
|
997
|
+
</label>
|
|
998
|
+
</div>
|
|
999
|
+
|
|
1000
|
+
<!-- OTLP endpoint -->
|
|
1001
|
+
<div class="setting-row">
|
|
1002
|
+
<div style="flex: 0 0 auto; min-width: 160px;">
|
|
1003
|
+
<div class="setting-label">OTLP HTTP Endpoint</div>
|
|
1004
|
+
<div class="setting-desc">Where spans are exported.</div>
|
|
1005
|
+
</div>
|
|
1006
|
+
<input type="text" id="input-otel-endpoint"
|
|
1007
|
+
class="setting-input"
|
|
1008
|
+
placeholder="http://localhost:4318/v1/traces"
|
|
1009
|
+
style="flex: 1;"
|
|
1010
|
+
onchange="saveBootSetting('otel_endpoint', this.value)"
|
|
1011
|
+
oninput="clearTimeout(this._pt); this._pt=setTimeout(()=>saveBootSetting('otel_endpoint',this.value),800)" />
|
|
1012
|
+
</div>
|
|
1013
|
+
|
|
1014
|
+
<!-- Service name -->
|
|
1015
|
+
<div class="setting-row">
|
|
1016
|
+
<div style="flex: 0 0 auto; min-width: 160px;">
|
|
1017
|
+
<div class="setting-label">Service Name</div>
|
|
1018
|
+
<div class="setting-desc">Label shown in the trace UI.</div>
|
|
1019
|
+
</div>
|
|
1020
|
+
<input type="text" id="input-otel-service"
|
|
1021
|
+
class="setting-input"
|
|
1022
|
+
placeholder="prism-mcp-server"
|
|
1023
|
+
style="flex: 1;"
|
|
1024
|
+
onchange="saveBootSetting('otel_service_name', this.value)"
|
|
1025
|
+
oninput="clearTimeout(this._ps); this._ps=setTimeout(()=>saveBootSetting('otel_service_name',this.value),800)" />
|
|
1026
|
+
</div>
|
|
1027
|
+
|
|
1028
|
+
<!-- Expected trace waterfall diagram -->
|
|
1029
|
+
<div class="setting-row" style="flex-direction: column; align-items: flex-start; margin-top: 0.5rem;">
|
|
1030
|
+
<div class="setting-label" style="margin-bottom: 0.5rem;">Expected Trace Waterfall</div>
|
|
1031
|
+
<pre style="font-size: 0.78rem; background: var(--bg-hover); padding: 0.8rem 1rem; border-radius: 6px; color: var(--text-secondary); line-height: 1.6; width: 100%; box-sizing: border-box; overflow-x: auto;">mcp.call_tool [e.g. session_save_image, ~50 ms]
|
|
1032
|
+
└─ worker.vlm_caption [~2–5 s, outlives parent ✓]
|
|
1033
|
+
└─ llm.generate_image_description [~1–4 s]
|
|
1034
|
+
└─ llm.generate_embedding [~200 ms]</pre>
|
|
1035
|
+
</div>
|
|
1036
|
+
|
|
1037
|
+
<span class="setting-saved" id="savedToastOtel">Saved ✓</span>
|
|
1038
|
+
</div><!-- /spanel-observability -->
|
|
1039
|
+
|
|
1040
|
+
|
|
820
1041
|
</div>
|
|
821
1042
|
</div>
|
|
822
1043
|
</div>
|
|
@@ -1246,15 +1467,20 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1246
1467
|
var _skillsCache = {}; // role → content cache
|
|
1247
1468
|
|
|
1248
1469
|
function switchSettingsTab(tab) {
|
|
1249
|
-
['settings','skills'].forEach(function(t) {
|
|
1470
|
+
['settings','skills','providers','observability'].forEach(function(t) {
|
|
1250
1471
|
document.getElementById('stab-' + t).classList.toggle('active', t === tab);
|
|
1251
1472
|
document.getElementById('spanel-' + t).classList.toggle('active', t === tab);
|
|
1252
1473
|
});
|
|
1253
1474
|
if (tab === 'skills') {
|
|
1254
|
-
// Load skill for whichever role is currently selected
|
|
1255
1475
|
var role = document.getElementById('skillRoleSelect').value;
|
|
1256
1476
|
loadSkillForRole(role);
|
|
1257
1477
|
}
|
|
1478
|
+
if (tab === 'providers') {
|
|
1479
|
+
loadAiProviderSettings();
|
|
1480
|
+
}
|
|
1481
|
+
if (tab === 'observability') {
|
|
1482
|
+
loadOtelSettings();
|
|
1483
|
+
}
|
|
1258
1484
|
}
|
|
1259
1485
|
|
|
1260
1486
|
async function loadSkillForRole(role) {
|
|
@@ -1310,6 +1536,84 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1310
1536
|
input.value = ''; // reset so same file can be re-uploaded
|
|
1311
1537
|
}
|
|
1312
1538
|
|
|
1539
|
+
// ─── AI Providers Settings (v4.4) ────────────────────────────────────
|
|
1540
|
+
// text_provider → governs generateText() (gemini | openai | anthropic)
|
|
1541
|
+
// embedding_provider → governs generateEmbedding() (auto | gemini | openai)
|
|
1542
|
+
|
|
1543
|
+
// Called when the TEXT provider dropdown changes.
|
|
1544
|
+
function onTextProviderChange(value) {
|
|
1545
|
+
document.getElementById('provider-fields-gemini').style.display = value === 'gemini' ? '' : 'none';
|
|
1546
|
+
document.getElementById('provider-fields-openai').style.display = value === 'openai' ? '' : 'none';
|
|
1547
|
+
document.getElementById('provider-fields-anthropic').style.display = value === 'anthropic' ? '' : 'none';
|
|
1548
|
+
// Refresh the Anthropic warning — its visibility depends on both dropdowns
|
|
1549
|
+
refreshAnthropicWarning(value, document.getElementById('select-embedding-provider').value);
|
|
1550
|
+
saveBootSetting('text_provider', value);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// Called when the EMBEDDING provider dropdown changes.
|
|
1554
|
+
function onEmbeddingProviderChange(value) {
|
|
1555
|
+
var textVal = document.getElementById('select-text-provider').value;
|
|
1556
|
+
// Show the OpenAI embedding model field only when embedding=openai
|
|
1557
|
+
document.getElementById('embed-fields-openai').style.display = value === 'openai' ? '' : 'none';
|
|
1558
|
+
refreshAnthropicWarning(textVal, value);
|
|
1559
|
+
saveBootSetting('embedding_provider', value);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// Shows/hides the Anthropic+auto warning.
|
|
1563
|
+
// Warning appears when: text=anthropic AND embedding=auto (auto-bridges to Gemini).
|
|
1564
|
+
function refreshAnthropicWarning(textVal, embedVal) {
|
|
1565
|
+
var show = textVal === 'anthropic' && embedVal === 'auto';
|
|
1566
|
+
document.getElementById('anthropic-embed-warning').style.display = show ? '' : 'none';
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// Load all AI provider settings from the API and populate fields.
|
|
1570
|
+
// Called lazily when the tab is first activated (not on every modal open).
|
|
1571
|
+
async function loadAiProviderSettings() {
|
|
1572
|
+
try {
|
|
1573
|
+
var res = await fetch('/api/settings');
|
|
1574
|
+
var data = await res.json();
|
|
1575
|
+
var s = data.settings || {};
|
|
1576
|
+
|
|
1577
|
+
// ── Text provider dropdown ────────────────────────────────────────
|
|
1578
|
+
var textProvider = s.text_provider || 'gemini';
|
|
1579
|
+
var textSel = document.getElementById('select-text-provider');
|
|
1580
|
+
if (textSel) textSel.value = textProvider;
|
|
1581
|
+
document.getElementById('provider-fields-gemini').style.display = textProvider === 'gemini' ? '' : 'none';
|
|
1582
|
+
document.getElementById('provider-fields-openai').style.display = textProvider === 'openai' ? '' : 'none';
|
|
1583
|
+
document.getElementById('provider-fields-anthropic').style.display = textProvider === 'anthropic' ? '' : 'none';
|
|
1584
|
+
|
|
1585
|
+
// ── Embedding provider dropdown ───────────────────────────────────
|
|
1586
|
+
var embedProvider = s.embedding_provider || 'auto';
|
|
1587
|
+
var embedSel = document.getElementById('select-embedding-provider');
|
|
1588
|
+
if (embedSel) embedSel.value = embedProvider;
|
|
1589
|
+
document.getElementById('embed-fields-openai').style.display = embedProvider === 'openai' ? '' : 'none';
|
|
1590
|
+
refreshAnthropicWarning(textProvider, embedProvider);
|
|
1591
|
+
|
|
1592
|
+
// ── Gemini fields ─────────────────────────────────────────────────
|
|
1593
|
+
// Never pre-fill API key values for security — use placeholder hint instead.
|
|
1594
|
+
var gKey = document.getElementById('input-google-api-key');
|
|
1595
|
+
if (gKey) gKey.placeholder = s.GOOGLE_API_KEY ? '(key saved — paste to update)' : 'AIza…';
|
|
1596
|
+
|
|
1597
|
+
// ── Anthropic fields ──────────────────────────────────────────────
|
|
1598
|
+
var aKey = document.getElementById('input-anthropic-api-key');
|
|
1599
|
+
if (aKey) aKey.placeholder = s.anthropic_api_key ? '(key saved — paste to update)' : 'sk-ant-…';
|
|
1600
|
+
var aMod = document.getElementById('input-anthropic-model');
|
|
1601
|
+
if (aMod && s.anthropic_model) aMod.value = s.anthropic_model;
|
|
1602
|
+
|
|
1603
|
+
// ── OpenAI / Ollama fields ────────────────────────────────────────
|
|
1604
|
+
var oKey = document.getElementById('input-openai-api-key');
|
|
1605
|
+
if (oKey) oKey.placeholder = s.openai_api_key ? '(key saved — paste to update)' : 'sk-… (blank for Ollama)';
|
|
1606
|
+
var oUrl = document.getElementById('input-openai-base-url');
|
|
1607
|
+
if (oUrl && s.openai_base_url) oUrl.value = s.openai_base_url;
|
|
1608
|
+
var oMod = document.getElementById('input-openai-model');
|
|
1609
|
+
if (oMod && s.openai_model) oMod.value = s.openai_model;
|
|
1610
|
+
var oEmb = document.getElementById('input-openai-embedding-model');
|
|
1611
|
+
if (oEmb && s.openai_embedding_model) oEmb.value = s.openai_embedding_model;
|
|
1612
|
+
|
|
1613
|
+
} catch(e) { console.warn('AI provider settings load failed:', e); }
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
|
|
1313
1617
|
|
|
1314
1618
|
// ─── Auto-Load Checkboxes (v4.1) ─────────────────────────────────
|
|
1315
1619
|
async function loadAutoloadCheckboxes() {
|
|
@@ -1424,9 +1728,36 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1424
1728
|
loadAutoloadCheckboxes();
|
|
1425
1729
|
// Repo path inputs are loaded dynamically
|
|
1426
1730
|
loadRepoPathInputs();
|
|
1731
|
+
// OTel settings are loaded dynamically when the tab is first opened,
|
|
1732
|
+
// but also pre-load here so values are ready if user lands on that tab.
|
|
1733
|
+
loadOtelSettings();
|
|
1427
1734
|
} catch(e) { console.warn('Settings load failed:', e); }
|
|
1428
1735
|
}
|
|
1429
1736
|
|
|
1737
|
+
// ─── OTel Settings Hydration (v4.6.0) ────────────────────────────────
|
|
1738
|
+
// Separate loader function so it can be called from both loadSettings()
|
|
1739
|
+
// (pre-warm on modal open) and switchSettingsTab('observability')
|
|
1740
|
+
// (refresh on tab focus, in case settings changed elsewhere).
|
|
1741
|
+
async function loadOtelSettings() {
|
|
1742
|
+
try {
|
|
1743
|
+
var res = await fetch('/api/settings');
|
|
1744
|
+
var data = await res.json();
|
|
1745
|
+
var s = data.settings || {};
|
|
1746
|
+
|
|
1747
|
+
// Toggle: checked when otel_enabled === 'true'
|
|
1748
|
+
var enabledEl = document.getElementById('input-otel-enabled');
|
|
1749
|
+
if (enabledEl) enabledEl.checked = s.otel_enabled === 'true';
|
|
1750
|
+
|
|
1751
|
+
// OTLP endpoint: fall back to Jaeger default so the field is never blank
|
|
1752
|
+
var endpointEl = document.getElementById('input-otel-endpoint');
|
|
1753
|
+
if (endpointEl) endpointEl.value = s.otel_endpoint || 'http://localhost:4318/v1/traces';
|
|
1754
|
+
|
|
1755
|
+
// Service name: fall back to canonical default
|
|
1756
|
+
var serviceEl = document.getElementById('input-otel-service');
|
|
1757
|
+
if (serviceEl) serviceEl.value = s.otel_service_name || 'prism-mcp-server';
|
|
1758
|
+
} catch(e) { console.warn('OTel settings load failed:', e); }
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1430
1761
|
function toggleSetting(key, el) {
|
|
1431
1762
|
var isActive = el.classList.toggle('active');
|
|
1432
1763
|
saveSetting(key, isActive ? 'true' : 'false');
|
package/dist/lifecycle.js
CHANGED
|
@@ -11,6 +11,7 @@ import * as os from "os";
|
|
|
11
11
|
import { execSync } from "child_process";
|
|
12
12
|
import { closeConfigStorage } from "./storage/configStorage.js";
|
|
13
13
|
import { getStorage } from "./storage/index.js";
|
|
14
|
+
import { shutdownTelemetry } from "./utils/telemetry.js";
|
|
14
15
|
const PRISM_DIR = path.join(os.homedir(), ".prism-mcp");
|
|
15
16
|
/**
|
|
16
17
|
* Instance-aware PID file.
|
|
@@ -126,6 +127,11 @@ export function registerShutdownHandlers() {
|
|
|
126
127
|
shuttingDown = true;
|
|
127
128
|
log(`Shutting down gracefully (${reason})...`);
|
|
128
129
|
try {
|
|
130
|
+
// 0. Flush OTel span buffer FIRST — before any DBs are closed.
|
|
131
|
+
// BatchSpanProcessor holds spans in memory (up to 5s). If we close
|
|
132
|
+
// DBs first, spans that reference DB operations lose their context.
|
|
133
|
+
// shutdownTelemetry() is a no-op when otel_enabled=false.
|
|
134
|
+
await shutdownTelemetry();
|
|
129
135
|
// 1. Close system settings DB
|
|
130
136
|
closeConfigStorage();
|
|
131
137
|
// 2. Close main ledger DB
|