thumbgate 1.26.7 → 1.27.2
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/agentic-verify.txt +1 -0
- package/.well-known/llms.txt +2 -0
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +20 -9
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/gcp/dfcx-webhook-gate.js +295 -0
- package/adapters/mcp/server-stdio.js +28 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bench/thumbgate-bench.json +2 -2
- package/bin/cli.js +147 -10
- package/bin/dashboard-cli.js +7 -0
- package/config/gate-classifier-routing.json +98 -0
- package/config/gate-templates.json +60 -0
- package/config/mcp-allowlists.json +8 -7
- package/config/model-candidates.json +71 -6
- package/package.json +26 -10
- package/public/chatgpt-app.html +330 -0
- package/public/codex-plugin.html +66 -14
- package/public/dashboard.html +203 -17
- package/public/index.html +79 -4
- package/public/learn.html +70 -0
- package/public/lessons.html +129 -6
- package/public/numbers.html +2 -2
- package/public/pricing.html +20 -2
- package/scripts/agent-operations-planner.js +621 -0
- package/scripts/agent-reward-model.js +53 -1
- package/scripts/ai-component-inventory.js +367 -0
- package/scripts/classifier-routing.js +130 -0
- package/scripts/cli-schema.js +26 -0
- package/scripts/dashboard-chat.js +64 -17
- package/scripts/feedback-sanitizer.js +105 -0
- package/scripts/gates-engine.js +258 -61
- package/scripts/hybrid-feedback-context.js +141 -7
- package/scripts/memory-scope-readiness.js +159 -0
- package/scripts/parallel-workflow-orchestrator.js +293 -0
- package/scripts/plausible-domain-config.js +86 -0
- package/scripts/plausible-server-events.js +4 -2
- package/scripts/proxy-pointer-rag-guardrails.js +42 -1
- package/scripts/qa-scenario-planner.js +136 -0
- package/scripts/repeat-metric.js +28 -12
- package/scripts/secret-fixture-tokens.js +61 -0
- package/scripts/secret-scanner.js +44 -5
- package/scripts/security-scanner.js +80 -0
- package/scripts/seo-gsd.js +53 -0
- package/scripts/thumbgate-bench.js +16 -1
- package/scripts/tool-registry.js +37 -0
- package/scripts/workflow-sentinel.js +189 -4
- package/src/api/server.js +276 -10
package/public/codex-plugin.html
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>ThumbGate for Codex -
|
|
6
|
+
<title>ThumbGate for Codex - CLI Setup and Plugin Bundle</title>
|
|
7
7
|
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
|
-
<meta name="description" content="Install ThumbGate for Codex with
|
|
8
|
+
<meta name="description" content="Install ThumbGate for Codex with CLI setup first, plus a portable Codex plugin bundle for review, marketplace, and offline workflows. Includes Pre-Action Checks and thumbs-up/down feedback memory.">
|
|
9
9
|
<meta name="keywords" content="ThumbGate Codex plugin, Codex MCP server, Codex pre-action checks, Codex guardrails, thumbgate latest, AI coding agent reliability">
|
|
10
10
|
<meta property="og:title" content="ThumbGate for Codex">
|
|
11
|
-
<meta property="og:description" content="
|
|
11
|
+
<meta property="og:description" content="CLI-first Codex setup with auto-updating MCP, hooks, and a portable plugin bundle for review or marketplace workflows.">
|
|
12
12
|
<meta property="og:type" content="website">
|
|
13
13
|
<meta property="og:url" content="https://thumbgate.ai/codex-plugin">
|
|
14
14
|
<link rel="canonical" href="https://thumbgate.ai/codex-plugin">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"name": "ThumbGate for Codex",
|
|
22
22
|
"applicationCategory": "DeveloperApplication",
|
|
23
23
|
"operatingSystem": "macOS, Linux, Windows with Node.js",
|
|
24
|
-
"description": "ThumbGate for Codex installs an MCP server and hook launcher that resolves thumbgate@latest at startup, captures thumbs-up/down feedback, and enforces Pre-Action Checks before risky agent actions run.",
|
|
24
|
+
"description": "ThumbGate for Codex installs an MCP server and hook launcher that resolves thumbgate@latest at startup, captures thumbs-up/down feedback, and enforces Pre-Action Checks before risky agent actions run. The supported fast path is npx thumbgate init --agent codex; the zip is a portable plugin bundle, not a double-click desktop installer.",
|
|
25
25
|
"url": "https://thumbgate.ai/codex-plugin",
|
|
26
26
|
"downloadUrl": "https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip",
|
|
27
27
|
"installUrl": "https://thumbgate.ai/codex-plugin",
|
|
@@ -71,7 +71,15 @@
|
|
|
71
71
|
"name": "What is the fastest Codex install path?",
|
|
72
72
|
"acceptedAnswer": {
|
|
73
73
|
"@type": "Answer",
|
|
74
|
-
"text": "Run npx thumbgate init --agent codex for the automatic local setup
|
|
74
|
+
"text": "Run npx thumbgate init --agent codex for the automatic local setup. Use the standalone Codex plugin bundle only when you want a portable plugin folder for review, marketplace wiring, offline handoff, or a Codex build that exposes local plugin import."
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"@type": "Question",
|
|
79
|
+
"name": "Does the release zip install itself in Codex Desktop?",
|
|
80
|
+
"acceptedAnswer": {
|
|
81
|
+
"@type": "Answer",
|
|
82
|
+
"text": "No. The zip is not a double-click installer. Extract it and install the folder through a Codex plugin directory or marketplace flow if your Codex build exposes one. Otherwise use npx thumbgate init --agent codex."
|
|
75
83
|
}
|
|
76
84
|
}
|
|
77
85
|
]
|
|
@@ -220,9 +228,9 @@
|
|
|
220
228
|
<p class="sub" style="font-size:13px;opacity:0.85;">Updated: <time datetime="2026-04-20">2026-04-20</time> · by <a href="https://github.com/IgorGanapolsky" style="color:inherit;">Igor Ganapolsky</a></p>
|
|
221
229
|
<p class="sub">ThumbGate wires Codex into local-first feedback memory, MCP tools, and hook enforcement. The launcher resolves <code>thumbgate@latest</code> when Codex starts, so published npm fixes reach your active MCP server after a restart.</p>
|
|
222
230
|
<div class="actions">
|
|
223
|
-
<a class="button primary" href="
|
|
231
|
+
<a class="button primary" href="/guide" onclick="if(typeof plausible==='function')plausible('codex_cli_setup')">Install with CLI setup</a>
|
|
224
232
|
<a class="button secondary" href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/plugins/codex-profile/INSTALL.md" target="_blank" rel="noopener">Read install docs</a>
|
|
225
|
-
<a class="button secondary" href="/
|
|
233
|
+
<a class="button secondary" href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip" target="_blank" rel="noopener" onclick="if(typeof plausible==='function')plausible('codex_plugin_download')">Download zip for review</a>
|
|
226
234
|
</div>
|
|
227
235
|
<nav class="proof-bar" aria-label="Codex proof and conversion links">
|
|
228
236
|
<a href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md" target="_blank" rel="noopener">Verification evidence</a>
|
|
@@ -231,8 +239,9 @@
|
|
|
231
239
|
<a href="/?utm_source=codex&utm_medium=plugin_page&utm_campaign=codex_team_follow_on&utm_content=workflow_sprint&campaign_variant=teams_follow_on&offer_code=CODEX-TEAMS_FOLLOW_ON&cta_id=codex_team_follow_on&cta_placement=plugin_page&surface=codex_plugin#workflow-sprint-intake">Team workflow sprint</a>
|
|
232
240
|
</nav>
|
|
233
241
|
<pre class="terminal">$ npx thumbgate init --agent codex
|
|
242
|
+
$ npx thumbgate feedback-self-test
|
|
234
243
|
# Writes ~/.codex/config.toml and ~/.codex/config.json
|
|
235
|
-
#
|
|
244
|
+
# Restart Codex, then confirm ThumbGate is enabled in Plugins or MCP settings</pre>
|
|
236
245
|
</div>
|
|
237
246
|
</section>
|
|
238
247
|
|
|
@@ -251,11 +260,31 @@
|
|
|
251
260
|
</div>
|
|
252
261
|
</section>
|
|
253
262
|
|
|
263
|
+
<section class="wrap">
|
|
264
|
+
<div class="eyebrow">Role plugins, Sites, and team OS</div>
|
|
265
|
+
<h2>Codex is becoming a business-work surface. ThumbGate is the action boundary.</h2>
|
|
266
|
+
<div class="steps">
|
|
267
|
+
<div class="step">
|
|
268
|
+
<h3>Role plugins</h3>
|
|
269
|
+
<p>Codex plugins bundle skills, apps, and MCP servers. ThumbGate checks role-specific writes before sales, analytics, design, finance, or support agents modify business systems.</p>
|
|
270
|
+
</div>
|
|
271
|
+
<div class="step">
|
|
272
|
+
<h3>Sites deploys</h3>
|
|
273
|
+
<p>Before a Sites workflow publishes or widens access, ThumbGate can require build proof, intended audience, secret handling, and deployment evidence.</p>
|
|
274
|
+
</div>
|
|
275
|
+
<div class="step">
|
|
276
|
+
<h3>Team Agentic OS</h3>
|
|
277
|
+
<p>Human-editable docs, agent-operating files, and git backups still need runtime checks. ThumbGate gates protected skills, MCP config, memory scope, and workflow contracts.</p>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
<p><a href="/learn/codex-role-plugins-need-governance">Read the Codex role-plugin governance guide</a> · <a href="/learn/agentic-os-team-governance">Read the Agentic OS team governance guide</a> · <a href="/learn/cost-aware-agent-gate-routing">Read cost-aware gate routing</a></p>
|
|
281
|
+
</section>
|
|
282
|
+
|
|
254
283
|
<section class="wrap split">
|
|
255
284
|
<div>
|
|
256
285
|
<div class="eyebrow">What Codex gets</div>
|
|
257
286
|
<h2>MCP memory, hook checks, and a dashboard lane in the same install path.</h2>
|
|
258
|
-
<p>Use
|
|
287
|
+
<p>Use <code>npx thumbgate init --agent codex</code> when you want the shortest path on this machine. Use the standalone zip only when you need a portable plugin folder for audit, offline handoff, marketplace wiring, or a Codex build that exposes local plugin import.</p>
|
|
259
288
|
<p class="status">The Codex launcher checks npm on startup. Restart Codex after a ThumbGate publish to let the MCP server and hook bundle pick up the latest runtime.</p>
|
|
260
289
|
</div>
|
|
261
290
|
<figure class="proof">
|
|
@@ -273,12 +302,31 @@
|
|
|
273
302
|
<p>Run <code>npx thumbgate init --agent codex</code>. ThumbGate writes the MCP server block and hook bundle into your Codex config files.</p>
|
|
274
303
|
</div>
|
|
275
304
|
<div class="step">
|
|
276
|
-
<h3>2.
|
|
277
|
-
<p>
|
|
305
|
+
<h3>2. Plugin directory</h3>
|
|
306
|
+
<p>For a true plugin install, use Codex Plugins or a marketplace source. In the Add marketplace dialog, do not keep Codex's default <code>plugins/codex</code> sparse path for this repo; use <code>.agents/plugins/marketplace.json</code> and <code>plugins/codex-profile</code>, or leave sparse paths blank for a local checkout.</p>
|
|
278
307
|
</div>
|
|
279
308
|
<div class="step">
|
|
280
|
-
<h3>3.
|
|
281
|
-
<p>
|
|
309
|
+
<h3>3. Zip for review</h3>
|
|
310
|
+
<p>The zip is a portable folder, not a double-click installer. Extract it, inspect <code>.codex-plugin/plugin.json</code>, and use CLI setup when local plugin import is unavailable.</p>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</section>
|
|
314
|
+
|
|
315
|
+
<section class="wrap">
|
|
316
|
+
<div class="eyebrow">Desktop install reality</div>
|
|
317
|
+
<h2>Do not make users guess what to do with a zip.</h2>
|
|
318
|
+
<div class="steps">
|
|
319
|
+
<div class="step">
|
|
320
|
+
<h3>Use first</h3>
|
|
321
|
+
<p><code>npx thumbgate init --agent codex</code> is the supported self-serve path. It configures MCP, hooks, and the status line without asking the user to understand plugin internals.</p>
|
|
322
|
+
</div>
|
|
323
|
+
<div class="step">
|
|
324
|
+
<h3>Use when available</h3>
|
|
325
|
+
<p>If Codex shows Plugins, open the directory, clear restrictive filters like <code>Built by OpenAI</code>, install ThumbGate from a marketplace or shared plugin entry, then start a new thread after install.</p>
|
|
326
|
+
</div>
|
|
327
|
+
<div class="step">
|
|
328
|
+
<h3>Use for operators</h3>
|
|
329
|
+
<p>Download the zip only for security review, offline delivery, or manual marketplace wiring. The user selects the extracted folder, never the compressed file.</p>
|
|
282
330
|
</div>
|
|
283
331
|
</div>
|
|
284
332
|
</section>
|
|
@@ -312,7 +360,11 @@
|
|
|
312
360
|
</div>
|
|
313
361
|
<div class="faq">
|
|
314
362
|
<h3>Where is the direct asset?</h3>
|
|
315
|
-
<p>The standalone zip remains available at <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip" target="_blank" rel="noopener">GitHub Releases</a>.
|
|
363
|
+
<p>The standalone zip remains available at <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip" target="_blank" rel="noopener">GitHub Releases</a>. It is for review, offline delivery, and manual marketplace workflows. It is not a double-click Codex Desktop installer.</p>
|
|
364
|
+
</div>
|
|
365
|
+
<div class="faq">
|
|
366
|
+
<h3>I searched Plugins for ThumbGate. Why is it missing?</h3>
|
|
367
|
+
<p>First clear the <code>Built by OpenAI</code> filter. ThumbGate is a local or third-party marketplace plugin, so an OpenAI-only filter hides it. Then confirm the marketplace includes <code>.agents/plugins/marketplace.json</code> and <code>plugins/codex-profile</code>; the default <code>plugins/codex</code> sparse path will miss this repo.</p>
|
|
316
368
|
</div>
|
|
317
369
|
</div>
|
|
318
370
|
</section>
|
package/public/dashboard.html
CHANGED
|
@@ -270,7 +270,11 @@
|
|
|
270
270
|
<input id="chatInput" class="auth-input" style="flex:1;" placeholder="e.g. What mistakes have we made, and how do we avoid them?" onkeydown="if(event.key==='Enter'){event.preventDefault();sendChat();}" />
|
|
271
271
|
<button class="btn" id="chatSend" onclick="sendChat()">Ask</button>
|
|
272
272
|
</div>
|
|
273
|
-
<div id="chatHint" style="font-size:12px;color:var(--text-muted);margin-top:8px;
|
|
273
|
+
<div id="chatHint" style="font-size:12px;color:var(--text-muted);margin-top:8px;display:flex;align-items:center;gap:8px;">
|
|
274
|
+
<span>Powered by your captured lessons + Gemini/Perplexity hybrid (local-cloud).</span>
|
|
275
|
+
<input type="password" id="geminiKeyInput" placeholder="Set GEMINI_API_KEY..." style="background:var(--bg-raised); border:1px solid var(--border); border-radius:4px; padding:4px 8px; color:var(--text); font-family:var(--mono); font-size:11px; flex:1; max-width: 250px;" onkeydown="if(event.key==='Enter'){event.preventDefault();saveGeminiKey();}" />
|
|
276
|
+
<button class="btn-outline" style="padding:4px 10px;font-size:11px;border-radius:4px;" onclick="saveGeminiKey()">Save</button>
|
|
277
|
+
</div>
|
|
274
278
|
</div>
|
|
275
279
|
|
|
276
280
|
<div class="panel" id="reviewDeltaPanel" style="margin-bottom:20px;">
|
|
@@ -304,7 +308,8 @@
|
|
|
304
308
|
<div class="tab active" onclick="switchTab('search')">🔍 Search Memories</div>
|
|
305
309
|
<div class="tab" onclick="switchTab('gates')">🛡️ Active Gates</div>
|
|
306
310
|
<div class="tab" onclick="switchTab('team')">👥 Team</div>
|
|
307
|
-
<div class="tab" onclick="switchTab('enterprise')">🏢 Enterprise Chat</div>
|
|
311
|
+
<div class="tab" onclick="switchTab('enterprise')">🏢 Enterprise Data Chat</div>
|
|
312
|
+
<div class="tab" onclick="switchTab('ai-inventory')">🧬 AI Inventory</div>
|
|
308
313
|
<div class="tab" onclick="switchTab('generated')">🧩 Generated Views</div>
|
|
309
314
|
<div class="tab" onclick="switchTab('settings')">⚙️ Policy Origins</div>
|
|
310
315
|
<div class="tab" onclick="switchTab('templates')">🧱 Gate Templates</div>
|
|
@@ -411,8 +416,8 @@
|
|
|
411
416
|
<!-- ENTERPRISE CHAT TAB -->
|
|
412
417
|
<div class="tab-content" id="tab-enterprise">
|
|
413
418
|
<div class="templates-section">
|
|
414
|
-
<h2>Enterprise
|
|
415
|
-
<p class="template-summary">Ask questions over local ThumbGate feedback, lessons, gates, team posture, and Vertex/DFCX readiness. This local panel
|
|
419
|
+
<h2>Enterprise Data Chat</h2>
|
|
420
|
+
<p class="template-summary">Ask questions over local ThumbGate feedback, lessons, gates, team posture, and Vertex/DFCX readiness. This is a governed local data-query panel with a DFCX-compatible guard before data access; it does not claim a live Google Dialogflow CX agent unless deployment evidence is configured.</p>
|
|
416
421
|
<div class="enterprise-chat-layout">
|
|
417
422
|
<div class="panel">
|
|
418
423
|
<h3>Chat With Local ThumbGate Data</h3>
|
|
@@ -429,7 +434,30 @@
|
|
|
429
434
|
<div class="panel">
|
|
430
435
|
<h3>Enterprise Readiness</h3>
|
|
431
436
|
<div class="inventory-tools" id="enterpriseStatusCards"><div class="loading">Loading enterprise status...</div></div>
|
|
432
|
-
<div class="template-summary" style="margin-top:14px;margin-bottom:0;">Live DFCX proof must come from the Conversational Agents console, deployed webhook URL, Cloud Run logs, or the Dialogflow CX REST API <code style="font-family:var(--mono);font-size:12px;">projects.locations.agents</code
|
|
437
|
+
<div class="template-summary" style="margin-top:14px;margin-bottom:0;">Live DFCX proof must come from the Conversational Agents console, deployed webhook URL, Cloud Run logs, or the Dialogflow CX REST API <code style="font-family:var(--mono);font-size:12px;">projects.locations.agents</code>. Without that proof, the panel stays local-only.</div>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
|
|
443
|
+
<!-- AI INVENTORY TAB -->
|
|
444
|
+
<div class="tab-content" id="tab-ai-inventory">
|
|
445
|
+
<div class="templates-section">
|
|
446
|
+
<h2>AI Component Inventory</h2>
|
|
447
|
+
<p class="template-summary">Enterprise evidence for AI/ML usage: provider SDKs, agent frameworks, vector databases, Vertex/Gemini/Dialogflow CX references, and local model artifacts. Export this as ML-BOM proof before claiming an AI system is production-ready.</p>
|
|
448
|
+
<div class="inventory-grid" id="aiInventorySummaryCards"><div class="loading">Scanning AI inventory...</div></div>
|
|
449
|
+
<div class="team-columns" style="margin-top:16px;">
|
|
450
|
+
<div class="panel">
|
|
451
|
+
<h3>Detected Components</h3>
|
|
452
|
+
<div class="inventory-tools" id="aiInventoryComponents"><div class="loading">Scanning components...</div></div>
|
|
453
|
+
</div>
|
|
454
|
+
<div class="panel">
|
|
455
|
+
<h3>Export Evidence</h3>
|
|
456
|
+
<div class="template-summary" style="margin-bottom:12px;">CLI export:</div>
|
|
457
|
+
<pre style="white-space:pre-wrap;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:12px;font-size:12px;color:var(--text);">npx thumbgate ai-inventory --format=cyclonedx --output=.thumbgate/ai-mlbom.json</pre>
|
|
458
|
+
<div class="template-summary" style="margin-top:12px;">API export:</div>
|
|
459
|
+
<pre style="white-space:pre-wrap;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:12px;font-size:12px;color:var(--text);">GET /v1/dashboard/ai-inventory?format=cyclonedx</pre>
|
|
460
|
+
<div id="aiInventoryExportNote" class="template-summary" style="margin-top:12px;margin-bottom:0;">Waiting for inventory evidence...</div>
|
|
433
461
|
</div>
|
|
434
462
|
</div>
|
|
435
463
|
</div>
|
|
@@ -681,8 +709,30 @@ let currentGeneratedView = 'team-review';
|
|
|
681
709
|
const BOOTSTRAP_API_KEY = __DASHBOARD_BOOTSTRAP_KEY__;
|
|
682
710
|
const LOCAL_PRO_BOOTSTRAP = __DASHBOARD_BOOTSTRAP_ENABLED__;
|
|
683
711
|
|
|
712
|
+
let ACTIVE_PROJECT_DIR = '';
|
|
713
|
+
try {
|
|
714
|
+
var urlParams = new URLSearchParams(window.location.search);
|
|
715
|
+
ACTIVE_PROJECT_DIR = urlParams.get('project') || '';
|
|
716
|
+
} catch (e) {}
|
|
717
|
+
|
|
718
|
+
function preserveProjectQueryParams() {
|
|
719
|
+
if (!ACTIVE_PROJECT_DIR) return;
|
|
720
|
+
var links = document.querySelectorAll('a[href^="/dashboard"], a[href^="/lessons"], a[href="/"]');
|
|
721
|
+
links.forEach(function(link) {
|
|
722
|
+
try {
|
|
723
|
+
var url = new URL(link.href, window.location.origin);
|
|
724
|
+
url.searchParams.set('project', ACTIVE_PROJECT_DIR);
|
|
725
|
+
link.href = url.pathname + url.search + url.hash;
|
|
726
|
+
} catch (e) {}
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
|
|
684
730
|
function getHeaders() {
|
|
685
|
-
|
|
731
|
+
var headers = { 'Authorization': 'Bearer ' + API_KEY, 'Content-Type': 'application/json' };
|
|
732
|
+
if (ACTIVE_PROJECT_DIR) {
|
|
733
|
+
headers['x-thumbgate-project-dir'] = ACTIVE_PROJECT_DIR;
|
|
734
|
+
}
|
|
735
|
+
return headers;
|
|
686
736
|
}
|
|
687
737
|
|
|
688
738
|
// --- Chat with your data ---------------------------------------------------
|
|
@@ -706,13 +756,32 @@ function chatRenderAnswer(a) {
|
|
|
706
756
|
.replace(/\[(\d+)\]/g, '<sup style="color:var(--cyan);font-weight:600;">[$1]</sup>')
|
|
707
757
|
.replace(/\n/g, '<br>');
|
|
708
758
|
}
|
|
709
|
-
function chatRenderSources(sources) {
|
|
759
|
+
function chatRenderSources(sources, answerText) {
|
|
710
760
|
if (!sources || !sources.length) return '';
|
|
761
|
+
var citedNums = new Set();
|
|
762
|
+
var regex = /\[(\d+)\]/g;
|
|
763
|
+
var match;
|
|
764
|
+
while ((match = regex.exec(answerText || '')) !== null) {
|
|
765
|
+
citedNums.add(parseInt(match[1], 10));
|
|
766
|
+
}
|
|
767
|
+
|
|
711
768
|
var items = sources.map(function (s, i) {
|
|
769
|
+
var num = i + 1;
|
|
770
|
+
var isCited = citedNums.has(num);
|
|
712
771
|
var label = chatEscape(String(s.title || s.id || '').slice(0, 64));
|
|
713
|
-
|
|
772
|
+
|
|
773
|
+
var bg = isCited ? 'var(--cyan-dim)' : 'var(--bg-raised)';
|
|
774
|
+
var color = isCited ? 'var(--cyan)' : 'var(--text-muted)';
|
|
775
|
+
var border = isCited ? '1px solid var(--cyan)' : '1px solid var(--border)';
|
|
776
|
+
var weight = isCited ? '600' : 'normal';
|
|
777
|
+
|
|
778
|
+
return '<span title="' + label + '" style="display:inline-block;font-size:11px;background:' + bg + ';color:' + color + ';border:' + border + ';font-weight:' + weight + ';padding:2px 7px;border-radius:5px;margin:4px 5px 0 0;">[' + num + '] ' + label + '</span>';
|
|
714
779
|
}).join('');
|
|
715
|
-
|
|
780
|
+
|
|
781
|
+
return '<div style="margin-top:12px;border-top:1px dashed var(--border);padding-top:10px;">' +
|
|
782
|
+
'<div style="font-size:11px;color:var(--text-muted);margin-bottom:6px;font-weight:600;">Database logs analyzed to answer your question:</div>' +
|
|
783
|
+
items +
|
|
784
|
+
'</div>';
|
|
716
785
|
}
|
|
717
786
|
async function sendChat() {
|
|
718
787
|
var input = document.getElementById('chatInput');
|
|
@@ -729,7 +798,10 @@ async function sendChat() {
|
|
|
729
798
|
var res = await fetch('/v1/chat', { method: 'POST', headers: getHeaders(), body: JSON.stringify({ question: q }) });
|
|
730
799
|
var data = await res.json();
|
|
731
800
|
if (data && data.ok) {
|
|
732
|
-
pending.innerHTML = chatRenderAnswer(data.answer) + chatRenderSources(data.sources);
|
|
801
|
+
pending.innerHTML = chatRenderAnswer(data.answer) + chatRenderSources(data.sources, data.answer);
|
|
802
|
+
} else if (data && data.error === 'gemini_error') {
|
|
803
|
+
pending.innerHTML = '<em style="color:#f87171;">Gemini rejected the key: ' + chatEscape(data.message || 'invalid') +
|
|
804
|
+
'<br>Fix: paste a valid key in the "Set GEMINI_API_KEY..." box below (get one at <a href="https://aistudio.google.com/app/apikey" target="_blank" style="color:#67e8f9;">AI Studio</a>) and click Save.</em>';
|
|
733
805
|
} else {
|
|
734
806
|
pending.innerHTML = '<em style="color:var(--text-muted);">' + chatEscape((data && data.message) || 'Chat is unavailable.') + '</em>';
|
|
735
807
|
}
|
|
@@ -741,6 +813,37 @@ async function sendChat() {
|
|
|
741
813
|
}
|
|
742
814
|
}
|
|
743
815
|
|
|
816
|
+
async function saveGeminiKey() {
|
|
817
|
+
var input = document.getElementById('geminiKeyInput');
|
|
818
|
+
var val = (input.value || '').trim();
|
|
819
|
+
if (!val) return;
|
|
820
|
+
var oldPlaceholder = input.placeholder;
|
|
821
|
+
input.disabled = true;
|
|
822
|
+
input.placeholder = 'Saving...';
|
|
823
|
+
try {
|
|
824
|
+
var res = await fetch('/v1/settings/gemini-key', {
|
|
825
|
+
method: 'POST',
|
|
826
|
+
headers: getHeaders(),
|
|
827
|
+
body: JSON.stringify({ key: val })
|
|
828
|
+
});
|
|
829
|
+
var data = await res.json();
|
|
830
|
+
if (data && data.ok) {
|
|
831
|
+
input.value = '';
|
|
832
|
+
input.placeholder = '✓ Key saved to .env';
|
|
833
|
+
setTimeout(function() {
|
|
834
|
+
document.getElementById('chatHint').innerHTML = '<span style="color:var(--green)">✓ Key validated. Hybrid (Perplexity/Gemini) supported for chat with your data.</span>';
|
|
835
|
+
}, 2000);
|
|
836
|
+
} else {
|
|
837
|
+
input.placeholder = '✗ Failed: ' + (data.message || data.error || 'Unknown error');
|
|
838
|
+
}
|
|
839
|
+
} catch (e) {
|
|
840
|
+
input.placeholder = '✗ Network error';
|
|
841
|
+
} finally {
|
|
842
|
+
input.disabled = false;
|
|
843
|
+
setTimeout(function() { if (input.placeholder.startsWith('✗')) input.placeholder = oldPlaceholder; }, 3000);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
744
847
|
function hasBootstrapKey() {
|
|
745
848
|
return LOCAL_PRO_BOOTSTRAP && Boolean(BOOTSTRAP_API_KEY);
|
|
746
849
|
}
|
|
@@ -751,8 +854,8 @@ async function connect(options) {
|
|
|
751
854
|
API_KEY = String(opts.key || input.value || '').trim();
|
|
752
855
|
if (!API_KEY) return;
|
|
753
856
|
|
|
754
|
-
|
|
755
|
-
|
|
857
|
+
var isEnterprise = API_KEY.startsWith('tg_op_') || API_KEY.startsWith('tg_creator_');
|
|
858
|
+
var tierName = isEnterprise ? 'Enterprise' : 'Pro';
|
|
756
859
|
|
|
757
860
|
const status = document.getElementById('authStatus');
|
|
758
861
|
const btn = document.getElementById('connectBtn');
|
|
@@ -763,16 +866,33 @@ async function connect(options) {
|
|
|
763
866
|
const res = await fetch('/v1/feedback/stats', { headers: getHeaders() });
|
|
764
867
|
if (!res.ok) throw new Error('Invalid API key');
|
|
765
868
|
const data = await res.json();
|
|
869
|
+
|
|
870
|
+
if (data.tier) {
|
|
871
|
+
isEnterprise = (data.tier === 'Enterprise');
|
|
872
|
+
tierName = data.tier;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
if (data.geminiKeyStatus === 'validated' || data.geminiValidatedAt) {
|
|
876
|
+
var when = data.geminiValidatedAt ? ' (validated ' + new Date(data.geminiValidatedAt).toLocaleTimeString() + ')' : '';
|
|
877
|
+
document.getElementById('chatHint').innerHTML = '<span style="color:var(--green)">✓ Gemini API key validated' + when + '. You can now chat with your data.</span>';
|
|
878
|
+
} else if (data.perplexityConfigured) {
|
|
879
|
+
document.getElementById('chatHint').innerHTML = '<span style="color:var(--green)">✓ Perplexity hybrid (local-cloud) key present. Supports hybrid inference for chat with your data.</span>';
|
|
880
|
+
} else if (data.geminiConfigured) {
|
|
881
|
+
document.getElementById('chatHint').innerHTML = '<span style="color:#f59e0b">⚠ Gemini key present in .env (click Save below to validate it). Perplexity hybrid also supported for cost/privacy.</span>';
|
|
882
|
+
}
|
|
883
|
+
|
|
766
884
|
status.className = 'auth-status ok';
|
|
767
885
|
status.textContent = opts.localPro ? `✓ Local ${tierName} connected` : '✓ Connected';
|
|
768
886
|
document.getElementById('dashboardContent').style.display = 'block';
|
|
769
887
|
if (opts.localPro) {
|
|
888
|
+
const localProActiveMessage = 'Local Pro is active on this machine. Your dashboard is using the saved license key automatically.';
|
|
889
|
+
const localEnterpriseActiveMessage = 'Local Enterprise is active on this machine. Your dashboard is using the saved license key automatically.';
|
|
770
890
|
input.value = 'local-license';
|
|
771
891
|
input.disabled = true;
|
|
772
892
|
input.placeholder = `Local ${tierName} auto-connected`;
|
|
773
893
|
btn.disabled = true;
|
|
774
894
|
document.getElementById('demoBtn').style.display = 'none';
|
|
775
|
-
document.getElementById('authHelp').textContent =
|
|
895
|
+
document.getElementById('authHelp').textContent = isEnterprise ? localEnterpriseActiveMessage : localProActiveMessage;
|
|
776
896
|
}
|
|
777
897
|
renderStats(data);
|
|
778
898
|
setSelectedCard('all');
|
|
@@ -971,11 +1091,11 @@ function switchTab(name) {
|
|
|
971
1091
|
/**
|
|
972
1092
|
* Resolve deep-link tab target from URL hash or query string.
|
|
973
1093
|
* Supports: /dashboard#insights, /dashboard?tab=gates, /dashboard#tab-export.
|
|
974
|
-
* Valid targets match tab-content ids (search, gates, team,
|
|
975
|
-
* settings, templates, insights, export).
|
|
1094
|
+
* Valid targets match tab-content ids (search, gates, team, enterprise,
|
|
1095
|
+
* ai-inventory, generated, settings, templates, insights, export).
|
|
976
1096
|
*/
|
|
977
1097
|
function getDeepLinkTab() {
|
|
978
|
-
var valid = ['search', 'gates', 'team', 'generated', 'settings', 'templates', 'insights', 'export'];
|
|
1098
|
+
var valid = ['search', 'gates', 'team', 'enterprise', 'ai-inventory', 'generated', 'settings', 'templates', 'insights', 'export'];
|
|
979
1099
|
var raw = (window.location.hash || '').replace(/^#/, '').replace(/^tab-/, '');
|
|
980
1100
|
if (!raw) {
|
|
981
1101
|
try {
|
|
@@ -1187,9 +1307,70 @@ function renderDashboardData(data) {
|
|
|
1187
1307
|
renderRegulatedProof(data.regulatedProof || {});
|
|
1188
1308
|
renderTemplates(data.templateLibrary || {});
|
|
1189
1309
|
renderInsights(data);
|
|
1310
|
+
loadAiInventory();
|
|
1190
1311
|
loadEnterpriseDialogflowStatus();
|
|
1191
1312
|
}
|
|
1192
1313
|
|
|
1314
|
+
async function loadAiInventory() {
|
|
1315
|
+
var summaryEl = document.getElementById('aiInventorySummaryCards');
|
|
1316
|
+
var componentsEl = document.getElementById('aiInventoryComponents');
|
|
1317
|
+
var exportEl = document.getElementById('aiInventoryExportNote');
|
|
1318
|
+
if (!summaryEl || !componentsEl) return;
|
|
1319
|
+
|
|
1320
|
+
try {
|
|
1321
|
+
var res = await fetch('/v1/dashboard/ai-inventory', { headers: getHeaders() });
|
|
1322
|
+
if (!res.ok) {
|
|
1323
|
+
var text = await res.text();
|
|
1324
|
+
throw new Error(text || 'Failed to load AI inventory');
|
|
1325
|
+
}
|
|
1326
|
+
var inventory = await res.json();
|
|
1327
|
+
renderAiInventory(inventory);
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
var message = e && e.message ? e.message : 'Failed to load AI inventory';
|
|
1330
|
+
setMessageState(summaryEl, 'empty', message);
|
|
1331
|
+
setMessageState(componentsEl, 'empty', message);
|
|
1332
|
+
if (exportEl) exportEl.textContent = message;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
function renderAiInventory(inventory) {
|
|
1337
|
+
var summaryEl = document.getElementById('aiInventorySummaryCards');
|
|
1338
|
+
var componentsEl = document.getElementById('aiInventoryComponents');
|
|
1339
|
+
var exportEl = document.getElementById('aiInventoryExportNote');
|
|
1340
|
+
var components = Array.isArray(inventory.components) ? inventory.components : [];
|
|
1341
|
+
var byCategory = inventory.summary && inventory.summary.byCategory ? inventory.summary.byCategory : {};
|
|
1342
|
+
var modelArtifacts = byCategory.model_artifact || 0;
|
|
1343
|
+
var vectorDbs = byCategory.vector_database || 0;
|
|
1344
|
+
|
|
1345
|
+
summaryEl.innerHTML = [
|
|
1346
|
+
'<div class="team-card"><div class="team-kicker">Files scanned</div><div class="team-value">' + escHtml(String(inventory.filesScanned || 0)) + '</div><div class="team-note">source, manifests, model artifacts</div></div>',
|
|
1347
|
+
'<div class="team-card"><div class="team-kicker">AI components</div><div class="team-value">' + escHtml(String(inventory.componentCount || components.length)) + '</div><div class="team-note">inventory evidence items</div></div>',
|
|
1348
|
+
'<div class="team-card"><div class="team-kicker">Vector stores</div><div class="team-value">' + escHtml(String(vectorDbs)) + '</div><div class="team-note">retrieval governance surface</div></div>',
|
|
1349
|
+
'<div class="team-card"><div class="team-kicker">Model artifacts</div><div class="team-value">' + escHtml(String(modelArtifacts)) + '</div><div class="team-note">local ML-BOM artifacts</div></div>'
|
|
1350
|
+
].join('');
|
|
1351
|
+
|
|
1352
|
+
if (!components.length) {
|
|
1353
|
+
componentsEl.innerHTML = '<div class="empty">No AI/ML components detected in this project root.</div>';
|
|
1354
|
+
} else {
|
|
1355
|
+
componentsEl.innerHTML = components.map(function(item) {
|
|
1356
|
+
var evidence = Array.isArray(item.evidence) ? item.evidence.slice(0, 4) : [];
|
|
1357
|
+
var evidenceHtml = evidence.map(function(e) {
|
|
1358
|
+
var loc = e.line ? (e.file + ':' + e.line) : e.file;
|
|
1359
|
+
return '<li><code>' + escHtml(loc || 'unknown') + '</code> <span style="color:var(--text-muted);">[' + escHtml(e.kind || 'evidence') + ']</span></li>';
|
|
1360
|
+
}).join('');
|
|
1361
|
+
return '<div class="tool-card">' +
|
|
1362
|
+
'<div class="tool-title">' + escHtml(item.name || item.id || 'AI component') + '</div>' +
|
|
1363
|
+
'<div class="tool-meta">' + escHtml(item.category || 'unknown') + ' · ' + escHtml(item.ecosystem || 'unknown') + '</div>' +
|
|
1364
|
+
'<ul style="margin:8px 0 0 18px;padding:0;">' + evidenceHtml + '</ul>' +
|
|
1365
|
+
'</div>';
|
|
1366
|
+
}).join('');
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
if (exportEl) {
|
|
1370
|
+
exportEl.textContent = 'Ready: export ' + String(inventory.componentCount || components.length) + ' component(s) as CycloneDX ML-BOM evidence.';
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1193
1374
|
function renderGeneratedViewToolbar(spec) {
|
|
1194
1375
|
var views = Array.isArray(spec.availableViews) ? spec.availableViews : [];
|
|
1195
1376
|
document.getElementById('generatedViewToolbar').innerHTML = views.map(function(view) {
|
|
@@ -1632,7 +1813,11 @@ function renderEnterpriseStatus(status) {
|
|
|
1632
1813
|
if (!target) return;
|
|
1633
1814
|
var vertex = status && status.vertex ? status.vertex : {};
|
|
1634
1815
|
var dfcx = status && status.dfcx ? status.dfcx : {};
|
|
1816
|
+
var connectionMode = dfcx.liveAgentConfigured
|
|
1817
|
+
? 'Dialogflow CX configured'
|
|
1818
|
+
: (vertex.configured ? 'Vertex configured' : 'Local-only');
|
|
1635
1819
|
var rows = [
|
|
1820
|
+
{ name: 'Connection mode', value: connectionMode, note: dfcx.liveAgentConfigured ? 'Live DFCX evidence is present in env.' : (vertex.configured ? 'Vertex routing is configured; DFCX still needs live-agent proof.' : 'Local data Q&A only; no cloud agent claim.') },
|
|
1636
1821
|
{ name: 'Vertex routing', value: vertex.configured ? 'Configured' : 'Not configured', note: vertex.projectId ? ('Project: ' + vertex.projectId + ' · ' + (vertex.location || '')) : 'Run setup-vertex or set GOOGLE_VERTEX_PROJECT.' },
|
|
1637
1822
|
{ name: 'DFCX live agent', value: dfcx.liveAgentConfigured ? 'Env present' : 'Not proven', note: dfcx.verification || 'Verify with REST/console evidence.' },
|
|
1638
1823
|
{ name: 'Fulfillment proxy', value: dfcx.fulfillmentProxyConfigured ? 'Configured' : 'Not configured', note: 'Set THUMBGATE_DFCX_FULFILLMENT_URL for a deployed proxy.' },
|
|
@@ -1679,7 +1864,7 @@ async function sendEnterpriseChat() {
|
|
|
1679
1864
|
}
|
|
1680
1865
|
button.disabled = true;
|
|
1681
1866
|
answer.className = 'enterprise-answer';
|
|
1682
|
-
|
|
1867
|
+
answer.textContent = 'Running the data-access guard and reading local dashboard data...';
|
|
1683
1868
|
sources.innerHTML = '';
|
|
1684
1869
|
try {
|
|
1685
1870
|
var res = await fetch('/v1/enterprise/dialogflow/chat', {
|
|
@@ -1980,6 +2165,7 @@ function loadDemo() {
|
|
|
1980
2165
|
|
|
1981
2166
|
// Auto-load demo on first visit so visitors see the product immediately
|
|
1982
2167
|
window.addEventListener('DOMContentLoaded', function() {
|
|
2168
|
+
preserveProjectQueryParams();
|
|
1983
2169
|
if (hasBootstrapKey()) {
|
|
1984
2170
|
connect({ key: BOOTSTRAP_API_KEY, localPro: true });
|
|
1985
2171
|
return;
|