clay-server 5.2.0-beta.2 → 5.2.0-beta.4
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/lib/builtin-mates.js +2 -2
- package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
- package/lib/dm-digest-cursor.js +120 -0
- package/lib/knowledge-db-import-validation.js +46 -0
- package/lib/knowledge-db-import.js +98 -0
- package/lib/knowledge-db-service.js +18 -30
- package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
- package/lib/knowledge-db-storage-nedb.js +66 -0
- package/lib/knowledge-db-storage.js +72 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-file-store.js +7 -1
- package/lib/mate-avatar-catalog.js +12 -0
- package/lib/mate-avatar-migration.js +41 -0
- package/lib/mate-knowledge-mate-bound.js +53 -0
- package/lib/mate-knowledge-mcp-server.js +55 -8
- package/lib/mate-knowledge-service.js +9 -30
- package/lib/mate-knowledge-text-tools.js +106 -0
- package/lib/mates.js +14 -3
- package/lib/mcp-agent-tools.js +117 -0
- package/lib/mcp-connection-store.js +119 -15
- package/lib/mcp-import-credentials.js +31 -0
- package/lib/mcp-local-transport.js +13 -0
- package/lib/mcp-oauth.js +8 -6
- package/lib/mcp-query-bridge.js +30 -0
- package/lib/mcp-remote-connections.js +74 -20
- package/lib/mcp-source-adapters.js +91 -0
- package/lib/mcp-source-context.js +42 -0
- package/lib/mcp-source-discovery.js +169 -0
- package/lib/mcp-source-plugins.js +68 -0
- package/lib/mcp-source-restrictions.js +47 -0
- package/lib/mcp-tool-policy.js +34 -0
- package/lib/project-ask-user.js +6 -4
- package/lib/project-debate.js +1 -0
- package/lib/project-mate-interaction.js +40 -62
- package/lib/project-mcp.js +54 -16
- package/lib/project-session-document.js +8 -3
- package/lib/project-session-github.js +16 -7
- package/lib/project-session-linear.js +14 -6
- package/lib/project-session-notes.js +7 -3
- package/lib/project-sessions.js +6 -2
- package/lib/project-shared-browser.js +56 -12
- package/lib/project.js +49 -34
- package/lib/public/app.js +5 -0
- package/lib/public/appearance-bootstrap.js +14 -0
- package/lib/public/css/clay-workspace.css +29 -0
- package/lib/public/css/home-debate-planning.css +2 -0
- package/lib/public/css/mate-avatar-gallery.css +12 -0
- package/lib/public/css/mate-conversation.css +10 -5
- package/lib/public/css/session-github.css +43 -1
- package/lib/public/css/shared-browser.css +35 -1
- package/lib/public/index.html +10 -4
- package/lib/public/mate-avatars/B01.png +0 -0
- package/lib/public/mate-avatars/B02.png +0 -0
- package/lib/public/mate-avatars/B03.png +0 -0
- package/lib/public/mate-avatars/B04.png +0 -0
- package/lib/public/mate-avatars/B05.png +0 -0
- package/lib/public/mate-avatars/B06.png +0 -0
- package/lib/public/mate-avatars/B07.png +0 -0
- package/lib/public/mate-avatars/B08.png +0 -0
- package/lib/public/mate-avatars/B09.png +0 -0
- package/lib/public/mate-avatars/B10.png +0 -0
- package/lib/public/mate-avatars/B11.png +0 -0
- package/lib/public/mate-avatars/B12.png +0 -0
- package/lib/public/mate-avatars/B13.png +0 -0
- package/lib/public/mate-avatars/B14.png +0 -0
- package/lib/public/mate-avatars/B15.png +0 -0
- package/lib/public/mate-avatars/B16.png +0 -0
- package/lib/public/mate-avatars/B17.png +0 -0
- package/lib/public/mate-avatars/B18.png +0 -0
- package/lib/public/mate-avatars/B19.png +0 -0
- package/lib/public/mate-avatars/B20.png +0 -0
- package/lib/public/mate-avatars/B21.png +0 -0
- package/lib/public/mate-avatars/B22.png +0 -0
- package/lib/public/mate-avatars/B23.png +0 -0
- package/lib/public/mate-avatars/B24.png +0 -0
- package/lib/public/mate-avatars/B25.png +0 -0
- package/lib/public/mate-avatars/B26.png +0 -0
- package/lib/public/mate-avatars/B27.png +0 -0
- package/lib/public/mate-avatars/B28.png +0 -0
- package/lib/public/mate-avatars/B29.png +0 -0
- package/lib/public/mate-avatars/B30.png +0 -0
- package/lib/public/mate-avatars/B31.png +0 -0
- package/lib/public/mate-avatars/B32.png +0 -0
- package/lib/public/mate-avatars/B33.png +0 -0
- package/lib/public/mate-avatars/B34.png +0 -0
- package/lib/public/mate-avatars/B35.png +0 -0
- package/lib/public/mate-avatars/B36.png +0 -0
- package/lib/public/mate-avatars/B37.png +0 -0
- package/lib/public/mate-avatars/B38.png +0 -0
- package/lib/public/mate-avatars/B39.png +0 -0
- package/lib/public/mate-avatars/B40.png +0 -0
- package/lib/public/mate-avatars/B41.png +0 -0
- package/lib/public/mate-avatars/B42.png +0 -0
- package/lib/public/mate-avatars/B43.png +0 -0
- package/lib/public/mate-avatars/B44.png +0 -0
- package/lib/public/mate-avatars/B45.png +0 -0
- package/lib/public/mate-avatars/B46.png +0 -0
- package/lib/public/mate-avatars/B47.png +0 -0
- package/lib/public/mate-avatars/B48.png +0 -0
- package/lib/public/mate-avatars/C01.png +0 -0
- package/lib/public/mate-avatars/C02.png +0 -0
- package/lib/public/mate-avatars/C03.png +0 -0
- package/lib/public/mate-avatars/C04.png +0 -0
- package/lib/public/mate-avatars/C05.png +0 -0
- package/lib/public/mate-avatars/C06.png +0 -0
- package/lib/public/mate-avatars/C07.png +0 -0
- package/lib/public/mate-avatars/C08.png +0 -0
- package/lib/public/mate-avatars/C09.png +0 -0
- package/lib/public/mate-avatars/C10.png +0 -0
- package/lib/public/mate-avatars/C11.png +0 -0
- package/lib/public/mate-avatars/C12.png +0 -0
- package/lib/public/mate-avatars/C13.png +0 -0
- package/lib/public/mate-avatars/C14.png +0 -0
- package/lib/public/mate-avatars/C15.png +0 -0
- package/lib/public/mate-avatars/C16.png +0 -0
- package/lib/public/mate-avatars/C17.png +0 -0
- package/lib/public/mate-avatars/C18.png +0 -0
- package/lib/public/mate-avatars/C19.png +0 -0
- package/lib/public/mate-avatars/C20.png +0 -0
- package/lib/public/mate-avatars/C21.png +0 -0
- package/lib/public/mate-avatars/C22.png +0 -0
- package/lib/public/mate-avatars/C23.png +0 -0
- package/lib/public/mate-avatars/C24.png +0 -0
- package/lib/public/mate-avatars/C25.png +0 -0
- package/lib/public/mate-avatars/C26.png +0 -0
- package/lib/public/mate-avatars/C27.png +0 -0
- package/lib/public/mate-avatars/C28.png +0 -0
- package/lib/public/mate-avatars/C29.png +0 -0
- package/lib/public/mate-avatars/C30.png +0 -0
- package/lib/public/mate-avatars/C31.png +0 -0
- package/lib/public/mate-avatars/C32.png +0 -0
- package/lib/public/mate-avatars/C33.png +0 -0
- package/lib/public/mate-avatars/C34.png +0 -0
- package/lib/public/mate-avatars/C35.png +0 -0
- package/lib/public/mate-avatars/C36.png +0 -0
- package/lib/public/mate-avatars/C37.png +0 -0
- package/lib/public/mate-avatars/C38.png +0 -0
- package/lib/public/mate-avatars/C39.png +0 -0
- package/lib/public/mate-avatars/C40.png +0 -0
- package/lib/public/mate-avatars/C41.png +0 -0
- package/lib/public/mate-avatars/C42.png +0 -0
- package/lib/public/mate-avatars/C43.png +0 -0
- package/lib/public/mate-avatars/C44.png +0 -0
- package/lib/public/mate-avatars/C45.png +0 -0
- package/lib/public/mate-avatars/C46.png +0 -0
- package/lib/public/mate-avatars/C47.png +0 -0
- package/lib/public/mate-avatars/C48.png +0 -0
- package/lib/public/mate-avatars/catalog.json +482 -0
- package/lib/public/mate-avatars/lead-clay.png +0 -0
- package/lib/public/modules/app-message-router.js +4 -0
- package/lib/public/modules/app-messages.js +13 -2
- package/lib/public/modules/app-rendering.js +1 -1
- package/lib/public/modules/avatar.js +15 -41
- package/lib/public/modules/chat-render-runtime.js +10 -1
- package/lib/public/modules/clay-browser-links.js +47 -0
- package/lib/public/modules/clay-log-links.js +1 -1
- package/lib/public/modules/command-palette.js +1 -1
- package/lib/public/modules/debate.js +1 -0
- package/lib/public/modules/history-replay-batch.js +5 -1
- package/lib/public/modules/home-debate-live.js +2 -0
- package/lib/public/modules/home-debate-planning.js +7 -7
- package/lib/public/modules/home-mate-avatar-editor.js +127 -208
- package/lib/public/modules/home-mate-chat.js +6 -6
- package/lib/public/modules/home-mate-settings.js +19 -12
- package/lib/public/modules/markdown.js +4 -0
- package/lib/public/modules/mate-avatar-catalog.js +97 -0
- package/lib/public/modules/mate-management.js +40 -7
- package/lib/public/modules/mcp-connection-view.js +31 -13
- package/lib/public/modules/mcp-discovery-view.js +31 -0
- package/lib/public/modules/mcp-ui.js +23 -4
- package/lib/public/modules/mention.js +3 -0
- package/lib/public/modules/pane-browser-bridge.js +20 -0
- package/lib/public/modules/profile.js +1 -238
- package/lib/public/modules/project-mate-navigation.js +99 -14
- package/lib/public/modules/search-clay-chat.js +16 -2
- package/lib/public/modules/session-github.js +23 -9
- package/lib/public/modules/session-work-primary.js +51 -0
- package/lib/public/modules/shared-browser-settings.js +112 -0
- package/lib/public/modules/shared-browser-tabs.js +13 -3
- package/lib/public/modules/shared-browser.js +27 -6
- package/lib/public/modules/split-view.js +2 -0
- package/lib/public/modules/theme.js +8 -1
- package/lib/public/modules/tools.js +2 -1
- package/lib/public/style.css +1 -0
- package/lib/public/sw.js +3 -1
- package/lib/sdk-bridge.js +9 -3
- package/lib/server-appearance-settings.js +13 -1
- package/lib/server-dm.js +1 -0
- package/lib/server-home-chat-events.js +14 -4
- package/lib/server-home-chat.js +10 -2
- package/lib/server-home-mcp-consent.js +14 -0
- package/lib/server-mates.js +6 -1
- package/lib/server-settings.js +4 -1
- package/lib/server.js +45 -12
- package/lib/session-work-primary.js +50 -0
- package/lib/sessions.js +3 -0
- package/lib/shared-browser-cookie-sync.js +32 -0
- package/lib/shared-browser-cookies.js +79 -0
- package/lib/shared-browser-runtime.js +25 -9
- package/lib/shared-browser-storage-sync.js +43 -0
- package/lib/shared-browser-storage.js +101 -0
- package/lib/shared-browser-tools.js +2 -1
- package/lib/shared-browser-worker.js +46 -10
- package/lib/yoke/acp-agent-profiles.js +2 -2
- package/package.json +5 -1
|
@@ -8,7 +8,7 @@ import { store } from './store.js';
|
|
|
8
8
|
import { getWs } from './ws-ref.js';
|
|
9
9
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
10
10
|
import { confirmMateRemoval } from './mate-management.js';
|
|
11
|
-
import { renderHomeMateAvatarEditor, confirmHomeMateAvatar, failHomeMateAvatar, clearHomeMateAvatarEditor } from './home-mate-avatar-editor.js';
|
|
11
|
+
import { hasHomeMateAvatarDraft, homeMateAvatarSaving, renderHomeMateAvatarEditor, confirmHomeMateAvatar, failHomeMateAvatar, clearHomeMateAvatarEditor } from './home-mate-avatar-editor.js';
|
|
12
12
|
import { resetHomeMateModelPicker, clearHomeMateModelPicker, hasHomeMateModelDraft, requestHomeMateModels, renderHomeMateModelPicker, applyHomeMateModelsState, applyHomeMateModelResult } from './home-mate-model-picker.js';
|
|
13
13
|
|
|
14
14
|
var dialog = null;
|
|
@@ -73,16 +73,19 @@ function renderGeneral(body, mate) {
|
|
|
73
73
|
description.textContent = "Manage " + getMateName(mate) + "’s identity and appearance.";
|
|
74
74
|
body.appendChild(description);
|
|
75
75
|
renderMateProfile(body, mate, renderDialogContent, function () {
|
|
76
|
+
if (homeMateAvatarSaving()) return;
|
|
77
|
+
if (store.get('mateSettingsAvatarOpen') && hasHomeMateAvatarDraft()) {
|
|
78
|
+
showConfirm('Discard this avatar choice?', function () { clearHomeMateAvatarEditor(); store.set({mateSettingsAvatarOpen:false}); renderDialogContent(); document.getElementById('mate-profile-avatar').focus(); }, 'Discard choice');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
clearHomeMateAvatarEditor();
|
|
76
82
|
store.set({mateSettingsAvatarOpen: !store.get('mateSettingsAvatarOpen')});
|
|
77
83
|
renderDialogContent();
|
|
78
84
|
});
|
|
79
85
|
if (store.get('mateSettingsAvatarOpen')) {
|
|
80
86
|
var appearance = document.createElement("section");
|
|
81
87
|
appearance.className = "mate-settings-appearance";
|
|
82
|
-
renderHomeMateAvatarEditor(appearance, mate, renderDialogContent);
|
|
83
|
-
var done = document.createElement('button'); done.type = 'button'; done.className = 'mate-settings-button'; done.textContent = 'Done';
|
|
84
|
-
done.addEventListener('click',function () { store.set({mateSettingsAvatarOpen:false}); renderDialogContent(); document.getElementById('mate-profile-avatar').focus(); });
|
|
85
|
-
appearance.appendChild(done);
|
|
88
|
+
renderHomeMateAvatarEditor(appearance, mate, renderDialogContent, store.get('mateSettingsAvatarDirect') ? function () { clearHomeMateAvatarEditor(); renderDialogContent(); closeHomeMateSettings(); } : null);
|
|
86
89
|
body.appendChild(appearance);
|
|
87
90
|
}
|
|
88
91
|
renderMateColorPicker(body, mate, renderDialogContent);
|
|
@@ -118,7 +121,7 @@ function requestSection(section) {
|
|
|
118
121
|
function savePending() {
|
|
119
122
|
var prompt = store.get('mateInstructionsDraft');
|
|
120
123
|
var model = store.get('mateModelDraft');
|
|
121
|
-
return mateColorSaving() || mateProfileSaving() || !!((prompt && prompt.status === 'saving') || (model && model.saveId));
|
|
124
|
+
return homeMateAvatarSaving() || mateColorSaving() || mateProfileSaving() || !!((prompt && prompt.status === 'saving') || (model && model.saveId));
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
function renderDialogContent() {
|
|
@@ -199,7 +202,7 @@ function handleDialogKeydown(event) {
|
|
|
199
202
|
|
|
200
203
|
export function closeHomeMateSettings(force) {
|
|
201
204
|
if (force !== true && savePending()) return false;
|
|
202
|
-
if (force !== true && (hasMateProfileDraft() || hasMateInstructionDraft() || hasHomeMateModelDraft())) {
|
|
205
|
+
if (force !== true && (hasHomeMateAvatarDraft() || hasMateProfileDraft() || hasMateInstructionDraft() || hasHomeMateModelDraft())) {
|
|
203
206
|
var previous = document.activeElement;
|
|
204
207
|
showConfirm("Discard unsaved Mate settings?", function () { closeHomeMateSettings(true); }, "Discard changes", false, function () { if (previous && previous.isConnected) previous.focus(); });
|
|
205
208
|
document.getElementById("confirm-cancel").focus();
|
|
@@ -215,7 +218,7 @@ export function closeHomeMateSettings(force) {
|
|
|
215
218
|
memoryState = null;
|
|
216
219
|
knowledgeState = null;
|
|
217
220
|
modelRequested = false;
|
|
218
|
-
store.set({mateSettingsAvatarOpen:false});
|
|
221
|
+
store.set({mateSettingsAvatarOpen:false, mateSettingsAvatarDirect:false});
|
|
219
222
|
resetMateProfile();
|
|
220
223
|
resetMateColor();
|
|
221
224
|
clearHomeMateModelPicker();
|
|
@@ -230,12 +233,13 @@ export function closeHomeMateSettings(force) {
|
|
|
230
233
|
export function openHomeMateSettings(mateId, opener, options) {
|
|
231
234
|
var mate = getMate(mateId);
|
|
232
235
|
if (!mate || (dialog && savePending())) return false;
|
|
233
|
-
if (dialog && (hasMateProfileDraft() || hasMateInstructionDraft() || hasHomeMateModelDraft())) {
|
|
236
|
+
if (dialog && (hasHomeMateAvatarDraft() || hasMateProfileDraft() || hasMateInstructionDraft() || hasHomeMateModelDraft())) {
|
|
234
237
|
showConfirm("Discard unsaved Mate settings?", function () { closeHomeMateSettings(true); openHomeMateSettings(mateId, opener, options); }, "Discard changes", false);
|
|
235
238
|
return false;
|
|
236
239
|
}
|
|
237
240
|
closeHomeMateSettings(true);
|
|
238
241
|
resetMateInstructions(mateId);
|
|
242
|
+
store.set({ mateSettingsAvatarOpen: !!(options && options.avatar && !(mate.profile || {}).avatarLocked), mateSettingsAvatarDirect: !!(options && options.avatar) });
|
|
239
243
|
dialogMateId = mateId;
|
|
240
244
|
dialogOpener = opener || document.activeElement;
|
|
241
245
|
dialogSection = options && ["model", "prompt"].indexOf(options.section) !== -1 ? options.section : "general";
|
|
@@ -308,7 +312,10 @@ export function openHomeMateSettings(mateId, opener, options) {
|
|
|
308
312
|
requestSection(dialogSection);
|
|
309
313
|
loadMateInstructions(renderDialogContent);
|
|
310
314
|
refreshIcons();
|
|
311
|
-
requestAnimationFrame(function () {
|
|
315
|
+
requestAnimationFrame(function () {
|
|
316
|
+
var first = options && options.avatar && dialog ? (dialog.querySelector('.home-mate-avatar-choice[aria-pressed="true"]:not(:disabled)') || dialog.querySelector('.home-mate-avatar-choice:not(:disabled)')) : null;
|
|
317
|
+
(first || close).focus();
|
|
318
|
+
});
|
|
312
319
|
return true;
|
|
313
320
|
}
|
|
314
321
|
|
|
@@ -354,7 +361,7 @@ export function syncHomeMateSettingsTarget(msg) {
|
|
|
354
361
|
var mate = getMate(dialogMateId);
|
|
355
362
|
if (!mate) closeHomeMateSettings();
|
|
356
363
|
else {
|
|
357
|
-
confirmHomeMateAvatar(mate);
|
|
364
|
+
confirmHomeMateAvatar(mate, msg && msg.requestId);
|
|
358
365
|
dialog.querySelector(".home-mate-settings-header span").textContent = getMateName(mate);
|
|
359
366
|
renderDialogContent();
|
|
360
367
|
}
|
|
@@ -364,7 +371,7 @@ export function handleHomeMateAvatarError(msg) {
|
|
|
364
371
|
if (!dialog || !msg) return false;
|
|
365
372
|
var colorHandled = applyMateColorResult(msg);
|
|
366
373
|
var profileHandled = applyMateProfile(msg);
|
|
367
|
-
if (!colorHandled && !profileHandled && !failHomeMateAvatar(msg.mateId, msg.error)) return false;
|
|
374
|
+
if (!colorHandled && !profileHandled && !failHomeMateAvatar(msg.mateId, msg.error, msg.requestId)) return false;
|
|
368
375
|
renderDialogContent();
|
|
369
376
|
return true;
|
|
370
377
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderBrowserLink, enhanceBrowserLinks } from './clay-browser-links.js';
|
|
1
2
|
import { renderWireframeBlocks } from './wireframe-chat.js';
|
|
2
3
|
import { copyToClipboard, escapeHtml } from './utils.js';
|
|
3
4
|
import { refreshIcons } from './icons.js';
|
|
@@ -14,6 +15,8 @@ marked.use({ gfm: true, breaks: false });
|
|
|
14
15
|
marked.use({
|
|
15
16
|
renderer: {
|
|
16
17
|
link({ href, title, tokens, text }) {
|
|
18
|
+
var browserLink = renderBrowserLink(href, title, text);
|
|
19
|
+
if (browserLink) return browserLink;
|
|
17
20
|
var recordLink = renderClayRecordLink(href, text);
|
|
18
21
|
if (recordLink) return recordLink;
|
|
19
22
|
var localLink = renderLocalFileLink(href, title, text);
|
|
@@ -103,6 +106,7 @@ export function enhanceClaySessionLinks(root) {
|
|
|
103
106
|
}
|
|
104
107
|
enhanceClayLogLinks(root);
|
|
105
108
|
enhanceClayFileLinks(root);
|
|
109
|
+
enhanceBrowserLinks(root);
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
/**
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Shared appearance names; stable IDs remain internal catalog identities.
|
|
2
|
+
export var MATE_AVATAR_COLLECTIONS = [{"id":"B","name":"Oddballs","avatars":[{"id":"B01","name":"Indigo Cyclops"},
|
|
3
|
+
{"id":"B02","name":"Coral Sentinel"},
|
|
4
|
+
{"id":"B03","name":"Golden Visor"},
|
|
5
|
+
{"id":"B04","name":"Mint Wide Eyes"},
|
|
6
|
+
{"id":"B05","name":"Ivory Grin"},
|
|
7
|
+
{"id":"B06","name":"Blue Teardrop"},
|
|
8
|
+
{"id":"B07","name":"Peach Orbit"},
|
|
9
|
+
{"id":"B08","name":"Lilac Sleeper"},
|
|
10
|
+
{"id":"B09","name":"Coral Prism"},
|
|
11
|
+
{"id":"B10","name":"Mint Spring"},
|
|
12
|
+
{"id":"B11","name":"Golden Pebbles"},
|
|
13
|
+
{"id":"B12","name":"Periwinkle Orbit"},
|
|
14
|
+
{"id":"B13","name":"Coral Horizon"},
|
|
15
|
+
{"id":"B14","name":"Indigo Stack"},
|
|
16
|
+
{"id":"B15","name":"Mint Button Box"},
|
|
17
|
+
{"id":"B16","name":"Golden Curve"},
|
|
18
|
+
{"id":"B17","name":"Loop Listener"},
|
|
19
|
+
{"id":"B18","name":"Blue Twin Antenna"},
|
|
20
|
+
{"id":"B19","name":"Lilac Daydream"},
|
|
21
|
+
{"id":"B20","name":"Coral Toothsome"},
|
|
22
|
+
{"id":"B21","name":"Ivory Satellite"},
|
|
23
|
+
{"id":"B22","name":"Mint Coil"},
|
|
24
|
+
{"id":"B23","name":"Golden Wonder"},
|
|
25
|
+
{"id":"B24","name":"Slate Smile"},
|
|
26
|
+
{"id":"B25","name":"Lilac Shield"},
|
|
27
|
+
{"id":"B26","name":"Mint Diamond"},
|
|
28
|
+
{"id":"B27","name":"Coral Pebble"},
|
|
29
|
+
{"id":"B28","name":"Golden Horns"},
|
|
30
|
+
{"id":"B29","name":"Blue Trio"},
|
|
31
|
+
{"id":"B30","name":"Ivory Tower"},
|
|
32
|
+
{"id":"B31","name":"Charcoal Grin"},
|
|
33
|
+
{"id":"B32","name":"Teal Cheer"},
|
|
34
|
+
{"id":"B33","name":"Rose Satellite"},
|
|
35
|
+
{"id":"B34","name":"Blue Spool"},
|
|
36
|
+
{"id":"B35","name":"Golden Band"},
|
|
37
|
+
{"id":"B36","name":"Mint Beacon"},
|
|
38
|
+
{"id":"B37","name":"Charcoal Crescent"},
|
|
39
|
+
{"id":"B38","name":"Coral Cyclops"},
|
|
40
|
+
{"id":"B39","name":"Blue Scout"},
|
|
41
|
+
{"id":"B40","name":"Ivory Navigator"},
|
|
42
|
+
{"id":"B41","name":"Golden Porthole"},
|
|
43
|
+
{"id":"B42","name":"Lilac Windup"},
|
|
44
|
+
{"id":"B43","name":"Mint Receiver"},
|
|
45
|
+
{"id":"B44","name":"Coral Stack"},
|
|
46
|
+
{"id":"B45","name":"Indigo Porthole"},
|
|
47
|
+
{"id":"B46","name":"Stone Sprout"},
|
|
48
|
+
{"id":"B47","name":"Mint Sunbeam"},
|
|
49
|
+
{"id":"B48","name":"Golden Cubby"}]},
|
|
50
|
+
{"id":"C","name":"Kindred Bots","avatars":[{"id":"C01","name":"Violet Smile"},
|
|
51
|
+
{"id":"C02","name":"Apricot Joy"},
|
|
52
|
+
{"id":"C03","name":"Sage Curious"},
|
|
53
|
+
{"id":"C04","name":"Golden Wink"},
|
|
54
|
+
{"id":"C05","name":"Blue Sideglance"},
|
|
55
|
+
{"id":"C06","name":"Charcoal Wonder"},
|
|
56
|
+
{"id":"C07","name":"Lilac Specs"},
|
|
57
|
+
{"id":"C08","name":"Coral Giggle"},
|
|
58
|
+
{"id":"C09","name":"Mint Blush"},
|
|
59
|
+
{"id":"C10","name":"Golden Button"},
|
|
60
|
+
{"id":"C11","name":"Sky Smile"},
|
|
61
|
+
{"id":"C12","name":"Plum Joy"},
|
|
62
|
+
{"id":"C13","name":"Teal Diamond"},
|
|
63
|
+
{"id":"C14","name":"Peach Dimple"},
|
|
64
|
+
{"id":"C15","name":"Indigo Delight"},
|
|
65
|
+
{"id":"C16","name":"Sage Sprout"},
|
|
66
|
+
{"id":"C17","name":"Golden Specs"},
|
|
67
|
+
{"id":"C18","name":"Lilac Stargaze"},
|
|
68
|
+
{"id":"C19","name":"Blue Curiosity"},
|
|
69
|
+
{"id":"C20","name":"Coral Heart"},
|
|
70
|
+
{"id":"C21","name":"Mint Bud"},
|
|
71
|
+
{"id":"C22","name":"Ivory Surprise"},
|
|
72
|
+
{"id":"C23","name":"Rose Wink"},
|
|
73
|
+
{"id":"C24","name":"Sky Giggle"},
|
|
74
|
+
{"id":"C25","name":"Ivory Dreamer"},
|
|
75
|
+
{"id":"C26","name":"Mint Loop"},
|
|
76
|
+
{"id":"C27","name":"Violet Glasses"},
|
|
77
|
+
{"id":"C28","name":"Coral Sunshine"},
|
|
78
|
+
{"id":"C29","name":"Slate Sideglance"},
|
|
79
|
+
{"id":"C30","name":"Golden Dimple"},
|
|
80
|
+
{"id":"C31","name":"Teal Listener"},
|
|
81
|
+
{"id":"C32","name":"Peach Cheer"},
|
|
82
|
+
{"id":"C33","name":"Indigo Blush"},
|
|
83
|
+
{"id":"C34","name":"Sage Daydream"},
|
|
84
|
+
{"id":"C35","name":"Rose Wonder"},
|
|
85
|
+
{"id":"C36","name":"Sky Blush"},
|
|
86
|
+
{"id":"C37","name":"Peach Heart"},
|
|
87
|
+
{"id":"C38","name":"Blue Button"},
|
|
88
|
+
{"id":"C39","name":"Lilac Wink"},
|
|
89
|
+
{"id":"C40","name":"Mint Dish"},
|
|
90
|
+
{"id":"C41","name":"Ivory Wonder"},
|
|
91
|
+
{"id":"C42","name":"Violet Cheer"},
|
|
92
|
+
{"id":"C43","name":"Rose Specs"},
|
|
93
|
+
{"id":"C44","name":"Teal Delight"},
|
|
94
|
+
{"id":"C45","name":"Golden Diamond"},
|
|
95
|
+
{"id":"C46","name":"Lilac Surprise"},
|
|
96
|
+
{"id":"C47","name":"Sage Listener"},
|
|
97
|
+
{"id":"C48","name":"Coral Crown"}]}];
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// Shared Mate profile and removal flows used by project and Home surfaces.
|
|
2
2
|
|
|
3
3
|
import { getWs } from './ws-ref.js';
|
|
4
|
-
import {
|
|
4
|
+
import { store } from './store.js';
|
|
5
|
+
import { openHomeMateSettings } from './home-mate-settings.js';
|
|
5
6
|
import { showToast } from './utils.js';
|
|
6
7
|
import { showConfirm, hideConfirm } from './app-misc.js';
|
|
7
8
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
8
9
|
|
|
9
10
|
var mateMenu = null;
|
|
11
|
+
var mateMenuOpener = null;
|
|
10
12
|
|
|
11
13
|
function send(message) {
|
|
12
14
|
var ws = getWs();
|
|
@@ -15,9 +17,13 @@ function send(message) {
|
|
|
15
17
|
|
|
16
18
|
export function editMateProfile(anchorEl, mate) {
|
|
17
19
|
if (!anchorEl || !mate || mate.primary) return;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
openHomeMateSettings(mate.id, anchorEl);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function changeMateAvatar(anchorEl, mate) {
|
|
24
|
+
var current = (store.get('cachedMatesList') || []).find(function (item) { return mate && item.id === mate.id; });
|
|
25
|
+
if (!anchorEl || !current || current.primary || current.builtinKey === 'clay' || (current.profile || {}).avatarLocked) return false;
|
|
26
|
+
return openHomeMateSettings(current.id, anchorEl, { avatar: true });
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export function confirmMateRemoval(anchorEl, mate, onRemoved) {
|
|
@@ -76,9 +82,12 @@ export function confirmMateRemoval(anchorEl, mate, onRemoved) {
|
|
|
76
82
|
input.focus();
|
|
77
83
|
}
|
|
78
84
|
|
|
79
|
-
export function closeMateManagementMenu() {
|
|
85
|
+
export function closeMateManagementMenu(restoreFocus) {
|
|
86
|
+
var opener = mateMenuOpener;
|
|
80
87
|
if (mateMenu) mateMenu.remove();
|
|
81
|
-
mateMenu = null;
|
|
88
|
+
mateMenu = null; mateMenuOpener = null;
|
|
89
|
+
document.removeEventListener("keydown", handleMateMenuKeydown, true);
|
|
90
|
+
if (restoreFocus && opener && opener.isConnected) opener.focus({ preventScroll: true });
|
|
82
91
|
document.removeEventListener("click", handleMateMenuOutside, true);
|
|
83
92
|
}
|
|
84
93
|
|
|
@@ -86,6 +95,18 @@ function handleMateMenuOutside(event) {
|
|
|
86
95
|
if (mateMenu && !mateMenu.contains(event.target)) closeMateManagementMenu();
|
|
87
96
|
}
|
|
88
97
|
|
|
98
|
+
function handleMateMenuKeydown(event) {
|
|
99
|
+
if (!mateMenu) return;
|
|
100
|
+
if (event.key === 'Escape') {
|
|
101
|
+
event.preventDefault(); event.stopPropagation(); closeMateManagementMenu(true); return;
|
|
102
|
+
}
|
|
103
|
+
if (event.key === 'Tab') { closeMateManagementMenu(true); return; }
|
|
104
|
+
var items = Array.from(mateMenu.querySelectorAll('[role="menuitem"]:not(:disabled)'));
|
|
105
|
+
var index = items.indexOf(document.activeElement);
|
|
106
|
+
var next = event.key === 'ArrowDown' ? (index + 1) % items.length : event.key === 'ArrowUp' ? (index + items.length - 1) % items.length : event.key === 'Home' ? 0 : event.key === 'End' ? items.length - 1 : -1;
|
|
107
|
+
if (next >= 0) { event.preventDefault(); event.stopPropagation(); items[next].focus(); }
|
|
108
|
+
}
|
|
109
|
+
|
|
89
110
|
export function showMateManagementMenu(anchorEl, mate, options) {
|
|
90
111
|
if (!anchorEl || !mate || mate.primary) return;
|
|
91
112
|
options = options || {};
|
|
@@ -93,7 +114,16 @@ export function showMateManagementMenu(anchorEl, mate, options) {
|
|
|
93
114
|
if (options.beforeOpen) options.beforeOpen();
|
|
94
115
|
var menu = document.createElement("div");
|
|
95
116
|
menu.className = "project-ctx-menu";
|
|
117
|
+
menu.setAttribute('role', 'menu');
|
|
118
|
+
menu.setAttribute('aria-label', 'Actions for ' + ((mate.profile || {}).displayName || mate.name || 'Mate'));
|
|
119
|
+
if (!(mate.profile || {}).avatarLocked && mate.builtinKey !== 'clay') {
|
|
120
|
+
var avatar = document.createElement('button'); avatar.type = 'button'; avatar.className = 'project-ctx-item'; avatar.setAttribute('role', 'menuitem');
|
|
121
|
+
avatar.innerHTML = iconHtml('image') + ' <span>Change avatar</span>';
|
|
122
|
+
avatar.addEventListener('click', function (event) { event.stopPropagation(); closeMateManagementMenu(); changeMateAvatar(anchorEl, mate); });
|
|
123
|
+
menu.appendChild(avatar);
|
|
124
|
+
}
|
|
96
125
|
var edit = document.createElement("button");
|
|
126
|
+
edit.type = "button"; edit.setAttribute("role", "menuitem");
|
|
97
127
|
edit.className = "project-ctx-item";
|
|
98
128
|
edit.innerHTML = iconHtml("edit-2") + " <span>Edit Profile</span>";
|
|
99
129
|
edit.addEventListener("click", function (event) {
|
|
@@ -102,6 +132,7 @@ export function showMateManagementMenu(anchorEl, mate, options) {
|
|
|
102
132
|
editMateProfile(anchorEl, mate);
|
|
103
133
|
});
|
|
104
134
|
var remove = document.createElement("button");
|
|
135
|
+
remove.type = "button"; remove.setAttribute("role", "menuitem");
|
|
105
136
|
remove.className = "project-ctx-item project-ctx-delete";
|
|
106
137
|
remove.innerHTML = iconHtml(mate.builtinKey ? "minus-circle" : "trash-2")
|
|
107
138
|
+ " <span>" + (mate.builtinKey ? "Remove mate" : "Delete mate") + "</span>";
|
|
@@ -113,7 +144,9 @@ export function showMateManagementMenu(anchorEl, mate, options) {
|
|
|
113
144
|
menu.appendChild(edit);
|
|
114
145
|
menu.appendChild(remove);
|
|
115
146
|
document.body.appendChild(menu);
|
|
116
|
-
mateMenu = menu;
|
|
147
|
+
mateMenu = menu; mateMenuOpener = anchorEl;
|
|
148
|
+
document.addEventListener("keydown", handleMateMenuKeydown, true);
|
|
149
|
+
menu.querySelector("[role=menuitem]").focus();
|
|
117
150
|
refreshIcons();
|
|
118
151
|
requestAnimationFrame(function () {
|
|
119
152
|
var rect = anchorEl.getBoundingClientRect();
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { enhanceSettingsSelects } from './settings-select.js';
|
|
2
|
+
import { discoveryView, credentialsView } from './mcp-discovery-view.js';
|
|
1
3
|
import { escapeHtml } from './utils.js';
|
|
2
4
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
5
|
import { store } from './store.js';
|
|
@@ -13,11 +15,11 @@ function heading(title, subtitle, icon) {
|
|
|
13
15
|
}
|
|
14
16
|
function status(row) { return '<span class="mc-status" data-status="' + text(row.status) + '"><span aria-hidden="true"></span>' + text(labels[row.status] || 'Unavailable') + '</span>'; }
|
|
15
17
|
function switchControl(row, busy) {
|
|
16
|
-
return '<button type="button" class="mc-switch" role="switch" aria-checked="' + !!row.projectEnabled + '" aria-label="Enable ' + text(row.label || row.name) + ' in this project" data-toggle="' + text(row.id) + '"' + (busy ? ' disabled' : '') + '><span></span></button>';
|
|
18
|
+
return '<button type="button" class="mc-switch" role="switch" aria-checked="' + !!row.projectEnabled + '" aria-label="Enable ' + text(row.label || row.name) + (row.source === 'computer' ? ' in this project' : ' for your private sessions and Mates') + '" data-toggle="' + text(row.id) + '"' + (busy ? ' disabled' : '') + '><span></span></button>';
|
|
17
19
|
}
|
|
18
20
|
function rows(servers, busy) {
|
|
19
21
|
return '<div class="mc-list">' + servers.map(function (row) {
|
|
20
|
-
return '<div class="mc-row"><button type="button" class="mc-row-open" data-open="' + text(row.id) + '"><span class="mc-server-icon">' + iconHtml(row.source === 'url' ? 'globe' : 'monitor') + '</span><span class="mc-row-info"><strong>' + text(row.label || row.name) + '</strong><span>' + (row.source === 'url' ? 'Remote' : 'Your computer') + ' · ' + row.toolCount + ' tools</span>' + status(row) + '</span></button>' + switchControl(row, busy) + '</div>';
|
|
22
|
+
return '<div class="mc-row"><button type="button" class="mc-row-open" data-open="' + text(row.id) + '"><span class="mc-server-icon">' + iconHtml(row.source === 'url' ? 'globe' : 'monitor') + '</span><span class="mc-row-info"><strong>' + text(row.label || row.name) + '</strong><span>' + (row.source === 'url' ? 'Remote' : row.source === 'local' ? 'Clay server' : 'Your computer') + ' · ' + row.toolCount + ' tools</span>' + status(row) + '</span></button>' + switchControl(row, busy) + '</div>';
|
|
21
23
|
}).join('') + '</div>';
|
|
22
24
|
}
|
|
23
25
|
function search(value, placeholder) { return '<label class="mc-search">' + iconHtml('search') + '<input type="search" data-search value="' + text(value) + '" placeholder="' + placeholder + '" aria-label="' + placeholder + '"></label>'; }
|
|
@@ -25,11 +27,12 @@ function listView(state) {
|
|
|
25
27
|
if (!state.loaded) return heading('Your connections', 'Loading your MCP servers…', 'cable');
|
|
26
28
|
if (!state.servers.length) return '<div class="mc-empty">' + heading('Connect tools to Clay', 'Let your agent work with the services and tools you choose.', 'cable') + button('Add connection', 'add', true, 'plus') + '<p>Connect a remote service or tools on your computer.</p></div>';
|
|
27
29
|
var servers = state.servers.filter(function (row) { return (row.label || row.name).toLowerCase().includes((state.search || '').toLowerCase()); });
|
|
28
|
-
return search(state.search, 'Search servers…') + '<div class="mc-section-heading"><h2>Your connections</h2>' + button('Add', 'add', false, 'plus') + '</div>' + rows(servers, !!state.pending) + (!servers.length ? '<p class="mc-muted">No matching connections.</p>' : '') + '<p class="mc-footnote">
|
|
30
|
+
return search(state.search, 'Search servers…') + '<div class="mc-section-heading"><h2>Your connections</h2>' + button('Add', 'add', false, 'plus') + '</div>' + rows(servers, !!state.pending) + (!servers.length ? '<p class="mc-muted">No matching connections.</p>' : '') + '<p class="mc-footnote">Imported and remote connections are available to your private sessions and Mates. Computer preferences are scoped to this project. In multi-user Clay, shared conversations cannot use personal connections.</p>';
|
|
29
31
|
}
|
|
30
32
|
function addView() {
|
|
31
33
|
return heading('Add connection', 'Choose where your tools are available.') + '<div class="mc-choices">' +
|
|
32
34
|
'<button class="mc-choice" data-action="remote"><span class="mc-choice-icon">' + iconHtml('globe') + '</span><span><strong>Remote MCP URL</strong><small>Connect a hosted service using its MCP server address.</small></span>' + iconHtml('arrow-right') + '</button>' +
|
|
35
|
+
'<button class="mc-choice" data-action="discovery"><span class="mc-choice-icon">' + iconHtml('folder-search') + '</span><span><strong>Import vendor config</strong><small>Inspect Claude, Codex, Kiro and other supported runtime sources.</small></span>' + iconHtml('arrow-right') + '</button>' +
|
|
33
36
|
'<button class="mc-choice" data-action="computer"><span class="mc-choice-icon">' + iconHtml('monitor') + '</span><span><strong>On your computer</strong><small>Connect local tools through the Clay browser extension.</small></span>' + iconHtml('arrow-right') + '</button></div>';
|
|
34
37
|
}
|
|
35
38
|
function remoteView(state) {
|
|
@@ -53,10 +56,12 @@ function permissionsView(row) {
|
|
|
53
56
|
[['', 'Use session default'], ['auto', 'AI review'], ['default', 'Ask every time']].map(function (item) { return '<option value="' + item[0] + '"' + (selected === item[0] ? ' selected' : '') + '>' + item[1] + '</option>'; }).join('') + '</select></label>' + (!availability.applicable ? '<p class="mc-muted">' + text(availability.reason) + '</p>' : '') + '</div>';
|
|
54
57
|
}
|
|
55
58
|
function detailView(state, row) {
|
|
56
|
-
var remote = row.source === 'url';
|
|
57
|
-
var html = heading(row.label || row.name, remote ? 'Remote MCP connection' : 'Connected through your browser extension', remote ? 'globe' : 'monitor');
|
|
58
|
-
html += status(row) + '<div class="mc-enable"><span>Enabled in this project</span>' + switchControl(row, !!state.pending) + '</div>';
|
|
59
|
-
html += '<dl class="mc-metadata"><dt>Access</dt><dd>Your account only</dd><dt>' + (remote ? 'Server' : 'Location') + '</dt><dd>' + (remote ? text(row.url) : 'Your computer') + '</dd></dl>';
|
|
59
|
+
var remote = row.source === 'url', local = row.source === 'local', managed = remote || local;
|
|
60
|
+
var html = heading(row.label || row.name, remote ? 'Remote MCP connection' : local ? 'MCP process on the Clay server' : 'Connected through your browser extension', remote ? 'globe' : 'monitor');
|
|
61
|
+
html += status(row) + '<div class="mc-enable"><span>' + (managed ? 'Enabled for your private sessions and Mates' : 'Enabled in this project') + '</span>' + switchControl(row, !!state.pending) + '</div>';
|
|
62
|
+
html += '<dl class="mc-metadata"><dt>Access</dt><dd>Your account only</dd><dt>' + (remote ? 'Server' : 'Location') + '</dt><dd>' + (remote ? text(row.url) : (local ? 'Clay server · your account' : 'Your computer')) + '</dd></dl>';
|
|
63
|
+
if (row.provenance) html += '<p class="mc-muted">Imported from ' + text(row.provenance.vendor + ' · ' + row.provenance.scope + ' · ' + row.provenance.file) + '</p>';
|
|
64
|
+
html += credentialsView(row);
|
|
60
65
|
if (row.error) html += '<div class="mc-alert" role="status">' + iconHtml('circle-alert') + '<span>' + text(row.error) + '</span></div>';
|
|
61
66
|
if (remote && row.status === 'auth_required') html += '<div class="mc-auth">' + button('Sign in with Clay browser', 'signin', true, 'log-in') + '<p class="mc-muted">Only you can view and control this sign-in browser.</p></div>';
|
|
62
67
|
if (remote) html += '<details class="mc-token"><summary>Use an access token</summary><form data-form="token" class="mc-form"><label>Access token<input name="token" type="password" required maxlength="8192" autocomplete="new-password" placeholder="Paste your access token"></label><button type="submit" class="mc-button">Save and connect</button></form></details>';
|
|
@@ -67,15 +72,15 @@ function detailView(state, row) {
|
|
|
67
72
|
var tools = (row.tools || []).filter(function (tool) { return (tool.name + ' ' + (tool.description || '')).toLowerCase().includes((state.search || '').toLowerCase()); });
|
|
68
73
|
html += '<div class="mc-tools">' + tools.map(function (tool) { return '<details><summary>' + text(tool.name) + '</summary><p>' + text(tool.description || 'No description provided.') + '</p><h4>Inputs</h4><pre>' + text(JSON.stringify(tool.inputSchema || {}, null, 2)) + '</pre></details>'; }).join('') + (!tools.length ? '<p class="mc-muted">' + (row.toolCount ? 'No matching tools.' : 'No tools available. Test the connection to refresh its catalog.') + '</p>' : '') + '</div>';
|
|
69
74
|
}
|
|
70
|
-
html += '<div class="mc-detail-actions">' + (
|
|
71
|
-
if (
|
|
75
|
+
html += '<div class="mc-detail-actions">' + (managed ? button('Test connection', 'test', false, 'refresh-cw') + (!remote || row.status === 'auth_required' ? '' : button('Sign in again', 'signin', false, 'log-in')) : button('Computer setup', 'computer', false, 'monitor')) + '</div>';
|
|
76
|
+
if (managed) html += '<details class="mc-manage"><summary>Manage connection</summary><form data-form="rename" class="mc-form"><label>Name<input name="label" required maxlength="100" value="' + text(row.label) + '"></label><button type="submit" class="mc-button">Save name</button></form>' + button('Remove connection', 'remove', false, 'trash-2') + '</details>';
|
|
72
77
|
return html;
|
|
73
78
|
}
|
|
74
79
|
export function renderConnections(root, state, events) {
|
|
75
80
|
var sameScreen = root.dataset.screenKey === (state.screen || 'list') + ':' + (state.selected || '');
|
|
76
81
|
var disclosures = sameScreen ? Array.from(root.querySelectorAll('details[open]')).map(function (el) { return el.className || el.querySelector('summary').textContent; }) : [];
|
|
77
82
|
var drafts = {};
|
|
78
|
-
if (sameScreen) root.querySelectorAll('input[name]').forEach(function (el) { drafts[el.name] = el.value; });
|
|
83
|
+
if (sameScreen) root.querySelectorAll('input[name]:not([type="password"])').forEach(function (el) { drafts[el.name] = el.value; });
|
|
79
84
|
var focused = root.contains(document.activeElement) ? document.activeElement : null;
|
|
80
85
|
var searchFocused = focused && focused.hasAttribute('data-search');
|
|
81
86
|
var start = focused && focused.tagName === 'INPUT' ? focused.selectionStart : null;
|
|
@@ -84,18 +89,20 @@ export function renderConnections(root, state, events) {
|
|
|
84
89
|
var screen = state.screen || 'list';
|
|
85
90
|
var html = screen !== 'list' ? '<button type="button" class="mc-back" data-action="back">' + iconHtml('arrow-left') + 'MCP Servers</button>' : '';
|
|
86
91
|
if (state.error) html += '<div class="mc-alert" role="alert">' + iconHtml('circle-alert') + '<span>' + text(state.error) + '</span></div>';
|
|
92
|
+
html += '<p class="mc-muted">Clay connections work in supported native and ACP sessions. Antigravity’s current agy stream-json adapter cannot mount session-bound Clay tools; use another runtime for these connections.</p>';
|
|
87
93
|
if (state.notice) html += '<p class="mc-notice" role="status">' + text(state.notice) + '</p>';
|
|
88
94
|
if (screen === 'add') html += addView();
|
|
89
95
|
else if (screen === 'remote') html += remoteView(state);
|
|
96
|
+
else if (screen === 'discovery') html += discoveryView(state);
|
|
90
97
|
else if (screen === 'computer') html += computerView(state);
|
|
91
|
-
else if (screen === 'remove' && row) html += heading('Remove ' + row.label + '?', 'This removes saved credentials and disables
|
|
98
|
+
else if (screen === 'remove' && row) html += heading('Remove ' + row.label + '?', 'This removes saved credentials and disables this connection for your private sessions and Mates.') + '<div role="group" aria-label="Confirm removal">' + button('Cancel', 'cancel-remove', false) + button('Remove connection', 'confirm-remove', true, 'trash-2') + '</div>';
|
|
92
99
|
else if (screen === 'detail' && row) html += detailView(state, row);
|
|
93
100
|
else html += listView(state);
|
|
94
101
|
if (state.pending && screen !== 'remote') html += '<p class="mc-working" role="status">' + iconHtml('loader-circle') + 'Updating connection…</p>';
|
|
95
102
|
root.innerHTML = '<div class="mc-screen">' + html + '</div>';
|
|
96
103
|
root.dataset.screenKey = screen + ':' + (state.selected || '');
|
|
97
104
|
root.querySelectorAll('details').forEach(function (el) { el.open = disclosures.indexOf(el.className || el.querySelector('summary').textContent) >= 0; });
|
|
98
|
-
root.querySelectorAll('input[name]').forEach(function (el) {
|
|
105
|
+
root.querySelectorAll('input[name]:not([type="password"])').forEach(function (el) {
|
|
99
106
|
if (Object.prototype.hasOwnProperty.call(drafts, el.name)) el.value = drafts[el.name];
|
|
100
107
|
if (focusedName === el.name) { el.focus(); try { el.setSelectionRange(start, start); } catch (error) {} }
|
|
101
108
|
});
|
|
@@ -106,31 +113,42 @@ export function renderConnections(root, state, events) {
|
|
|
106
113
|
if (state.pending && ['signin', 'test', 'remove', 'confirm-remove'].includes(action)) el.disabled = true;
|
|
107
114
|
el.onclick = function () {
|
|
108
115
|
if (action === 'back') events.navigate('list');
|
|
109
|
-
else if (['add', 'remote', 'computer'].includes(action)) events.navigate(action);
|
|
116
|
+
else if (['add', 'remote', 'computer', 'discovery'].includes(action)) events.navigate(action);
|
|
110
117
|
else if (action === 'remove' && row) events.remove(row);
|
|
111
118
|
else if (action === 'cancel-remove') events.navigate('detail', row.id);
|
|
112
119
|
else if (action === 'confirm-remove') events.action('remove', { id: row.id });
|
|
120
|
+
else if (action === 'discover') events.discover();
|
|
113
121
|
else if (action === 'refresh') events.refresh();
|
|
114
122
|
else if (action === 'setup') events.setup();
|
|
115
123
|
else if (action === 'copy') navigator.clipboard.writeText('npx clay-mcp-bridge install ' + (state.extensionId || '<extension-id>')).then(function () { el.textContent = 'Copied'; }).catch(function () { el.textContent = 'Select command to copy'; });
|
|
116
124
|
else events.action(action);
|
|
117
125
|
};
|
|
118
126
|
});
|
|
127
|
+
root.querySelectorAll('[data-import]').forEach(function (el) { el.onclick = function () { events.action('import', { scanId: state.discovery.scanId, candidateId: el.dataset.import }); }; });
|
|
119
128
|
root.querySelectorAll('[data-open]').forEach(function (el) { el.onclick = function () { events.navigate('detail', el.dataset.open); }; });
|
|
120
129
|
root.querySelectorAll('[data-toggle]').forEach(function (el) { el.onclick = function () { var item = state.servers.find(function (server) { return server.id === el.dataset.toggle; }); if (item) events.toggle(item, !item.projectEnabled); }; });
|
|
121
130
|
root.querySelectorAll('[data-tab]').forEach(function (el) { el.onclick = function () { store.set({ mcpConnections: Object.assign({}, store.get('mcpConnections'), { detailTab: el.dataset.tab, search: '' }) }); }; });
|
|
122
131
|
var input = root.querySelector('[data-search]');
|
|
123
132
|
if (input) { input.oninput = function () { events.search(input.value); }; if (searchFocused) { input.focus(); try { input.setSelectionRange(start, start); } catch (error) {} } }
|
|
133
|
+
var discoveryForm = root.querySelector('[data-form="discovery"]');
|
|
134
|
+
if (discoveryForm) {
|
|
135
|
+
function saveSourceDraft() { events.sourceDraft({ vendor: discoveryForm.elements.vendor.value, file: discoveryForm.elements.file.value }); }
|
|
136
|
+
discoveryForm.elements.vendor.onchange = saveSourceDraft;
|
|
137
|
+
discoveryForm.elements.file.oninput = saveSourceDraft;
|
|
138
|
+
}
|
|
124
139
|
var urlInput = root.querySelector('[name="url"]'); if (urlInput) urlInput.oninput = function () { events.draft(urlInput.value); };
|
|
125
140
|
root.querySelectorAll('form').forEach(function (form) {
|
|
126
141
|
form.onsubmit = function (event) {
|
|
127
142
|
event.preventDefault(); if (state.pending) return;
|
|
128
143
|
if (form.dataset.form === 'remote') events.action('add', { url: form.elements.url.value });
|
|
129
144
|
if (form.dataset.form === 'token') { var token = form.elements.token.value; form.reset(); events.action('token', { token: token }); }
|
|
145
|
+
if (form.dataset.form === 'discovery') events.discover({ vendor: form.elements.vendor.value.trim(), file: form.elements.file.value.trim() });
|
|
146
|
+
if (form.dataset.form === 'credentials') { var values = {}; Array.from(form.querySelectorAll('input')).forEach(function (input) { values[input.name] = input.value; }); form.reset(); events.action('credentials', { values: values }); }
|
|
130
147
|
if (form.dataset.form === 'rename') events.action('rename', { label: form.elements.label.value });
|
|
131
148
|
};
|
|
132
149
|
});
|
|
133
150
|
var permission = root.querySelector('[data-permission]');
|
|
134
151
|
if (permission) permission.onchange = function () { sendMcpPermissionMode(store.get('currentSlug'), store.get('activeSessionId'), row.name, permission.value); };
|
|
152
|
+
enhanceSettingsSelects(root);
|
|
135
153
|
refreshIcons(root);
|
|
136
154
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { escapeHtml } from './utils.js';
|
|
2
|
+
import { iconHtml } from './icons.js';
|
|
3
|
+
function text(value) { return escapeHtml(String(value || '')); }
|
|
4
|
+
export function discoveryView(state) {
|
|
5
|
+
var discovery = state.discovery || {}, pending = !!state.pending;
|
|
6
|
+
var draft = state.sourceDraft || {};
|
|
7
|
+
var vendors = { claude: 'Claude Code', codex: 'Codex', kiro: 'Kiro', opencode: 'OpenCode', kimi: 'Kimi Code', copilot: 'GitHub Copilot', qwen: 'Qwen Code', junie: 'Junie', grok: 'Grok', antigravity: 'Antigravity', acp: 'ACP' };
|
|
8
|
+
var options = Object.keys(vendors).map(function (key) { return '<option value="' + key + '"' + ((draft.vendor || 'claude') === key ? ' selected' : '') + '>' + vendors[key] + '</option>'; }).join('');
|
|
9
|
+
var html = '<div class="mc-heading"><h2 tabindex="-1">Import vendor connections</h2><p>Inspect MCP configurations on the Clay server. Import saves a disabled copy for your account; enabling or testing can start its command or contact its service.</p></div>' +
|
|
10
|
+
'<button type="button" class="mc-button" data-action="discover"' + (pending ? ' disabled' : '') + '>' + iconHtml('refresh-cw') + 'Scan vendor configurations</button>' +
|
|
11
|
+
'<details class="mc-manage"><summary>Configured source file</summary><form data-form="discovery" class="mc-form"><label>Vendor format<select name="vendor" aria-label="Vendor format">' + options + '</select></label><label>Absolute config path<input name="file" value="' + text(draft.file) + '" required placeholder="/home/you/agent/mcp.json" maxlength="4096"></label><button type="submit" class="mc-button"' + (pending ? ' disabled' : '') + '>Inspect source</button></form></details>';
|
|
12
|
+
if (!discovery.scanId) return html + '<p class="mc-muted">Vendor OAuth tokens stay with their vendor. Use Clay sign-in or enter credentials after importing.</p>';
|
|
13
|
+
html += '<div class="mc-list">' + (discovery.candidates || []).map(function (row) {
|
|
14
|
+
return '<div class="mc-row"><span class="mc-row-info"><strong>' + text(row.name) + '</strong><span>' + text(row.vendor + ' · ' + row.scope + ' · ' + row.transport) + '</span><small>' + text(row.file) + '</small><small>' + text('Dependency: ' + row.dependency) + '</small>' +
|
|
15
|
+
(row.references.length ? '<small>Requires: ' + text(row.references.join(', ')) + '</small>' : '') +
|
|
16
|
+
(row.blocked ? '<small>' + text(row.blocked) + '</small>' : '') +
|
|
17
|
+
(row.pluginVersion ? '<small>Plugin version: ' + text(row.pluginVersion) + '</small>' : '') +
|
|
18
|
+
(row.conflict ? '<small>Different definitions use this name. Import the source you intend to use.</small>' : row.duplicate ? '<small>Matching definition found in another source.</small>' : '') +
|
|
19
|
+
'<small>' + (row.sourceEnabled === true ? 'Enabled in source' : row.sourceEnabled === false ? 'Disabled in source' : 'Source enablement unknown') + ' · imports disabled</small></span><button class="mc-button" type="button" data-import="' + text(row.id) + '"' + (pending || row.imported || row.blocked ? ' disabled' : '') + '>' + (row.imported ? 'Imported' : row.blocked ? 'Blocked' : 'Import') + '</button></div>';
|
|
20
|
+
}).join('') + '</div>';
|
|
21
|
+
if (!(discovery.candidates || []).length) html += '<p class="mc-muted">No supported MCP definitions found.</p>';
|
|
22
|
+
html += (discovery.diagnostics || []).map(function (item) { return '<p class="mc-muted">' + text(item.vendor + ' · ' + item.scope + (item.name ? ' · ' + item.name : '') + ': ' + item.message) + '</p>'; }).join('');
|
|
23
|
+
return html;
|
|
24
|
+
}
|
|
25
|
+
export function credentialsView(row) {
|
|
26
|
+
if (!row.references || !row.references.length) return '';
|
|
27
|
+
if (row.references.indexOf('Unsupported reference') >= 0) return '<p class="mc-muted">This definition needs a vendor-specific helper or file reference. Configure a direct Clay connection instead.</p>';
|
|
28
|
+
return '<details class="mc-token"><summary>Required credentials</summary><form data-form="credentials" class="mc-form">' + row.references.map(function (name) {
|
|
29
|
+
return '<label>' + text(name) + '<input type="password" name="' + text(name) + '" required maxlength="8192" autocomplete="new-password"></label>';
|
|
30
|
+
}).join('') + '<button type="submit" class="mc-button">Save credentials</button></form></details>';
|
|
31
|
+
}
|
|
@@ -9,7 +9,8 @@ function patch(value) { store.set({ mcpConnections: Object.assign({}, state(), v
|
|
|
9
9
|
function send(message) {
|
|
10
10
|
var ws = getWs();
|
|
11
11
|
if (!ws || ws.readyState !== 1) { patch({ error: 'Reconnect to Clay to manage connections.' }); return false; }
|
|
12
|
-
ws.send(JSON.stringify(message)); return true;
|
|
12
|
+
try { if (ws.send(JSON.stringify(message)) === false) throw new Error('Not sent'); return true; }
|
|
13
|
+
catch (error) { patch({ error: 'Could not send the request. Reconnect and try again.' }); return false; }
|
|
13
14
|
}
|
|
14
15
|
function requestState() { send({ type: 'mcp_state_request' }); }
|
|
15
16
|
function clearActionTimer() { var timer = store.get('mcpActionTimer'); if (timer) clearTimeout(timer); store.set({ mcpActionTimer: null }); }
|
|
@@ -24,8 +25,18 @@ function action(name, args) {
|
|
|
24
25
|
}, 90000) });
|
|
25
26
|
if (!send(Object.assign({ type: 'mcp_connection_action', action: name, requestId: requestId, id: state().selected }, args || {}))) { clearActionTimer(); patch({ pending: null }); }
|
|
26
27
|
}
|
|
28
|
+
function discover(options) {
|
|
29
|
+
if (state().pending) return;
|
|
30
|
+
var requestId = crypto.randomUUID();
|
|
31
|
+
patch({ pending: { requestId: requestId, action: 'discover' }, error: '' });
|
|
32
|
+
clearActionTimer();
|
|
33
|
+
store.set({ mcpActionTimer: setTimeout(function () {
|
|
34
|
+
if (state().pending && state().pending.requestId === requestId) { clearActionTimer(); patch({ pending: null, error: 'The scan did not respond. Your draft and previous results are retained. Reconnect or scan again.' }); }
|
|
35
|
+
}, 30000) });
|
|
36
|
+
if (!send(Object.assign({ type: 'mcp_discovery_request', requestId: requestId }, options || {}))) { clearActionTimer(); patch({ pending: null }); }
|
|
37
|
+
}
|
|
27
38
|
function toggle(row, value) {
|
|
28
|
-
if (row.source
|
|
39
|
+
if (row.source !== 'computer') action('enable', { id: row.id, enabled: value });
|
|
29
40
|
else send({ type: 'mcp_toggle_server', name: row.name, enabled: value });
|
|
30
41
|
}
|
|
31
42
|
function render() {
|
|
@@ -35,7 +46,8 @@ function render() {
|
|
|
35
46
|
var extension = document.getElementById('ext-pill-wrap');
|
|
36
47
|
if (extension) extension.hidden = current.screen !== 'computer';
|
|
37
48
|
renderConnections(element, current, {
|
|
38
|
-
navigate: navigate, action: action, toggle: toggle,
|
|
49
|
+
navigate: navigate, action: action, toggle: toggle, discover: discover,
|
|
50
|
+
sourceDraft: function (value) { patch({ sourceDraft: value }); },
|
|
39
51
|
search: function (value) { patch({ search: value }); },
|
|
40
52
|
draft: function (value) { store.set({ mcpConnectionDraft: value }); },
|
|
41
53
|
refresh: requestState,
|
|
@@ -72,12 +84,19 @@ export function setExtensionConnected(value) { patch({ extensionConnected: !!val
|
|
|
72
84
|
export function getMcpServers() { return state().servers; }
|
|
73
85
|
export function handleMcpConnectionMessage(msg) {
|
|
74
86
|
if (msg.slug && msg.slug !== store.get('currentSlug')) return false;
|
|
87
|
+
if (msg.type === 'mcp_discovery_result') {
|
|
88
|
+
var scanPending = state().pending;
|
|
89
|
+
if (!scanPending || scanPending.requestId !== msg.requestId) return true;
|
|
90
|
+
clearActionTimer();
|
|
91
|
+
patch({ pending: null, discovery: msg.ok ? { scanId: msg.scanId, candidates: msg.candidates, diagnostics: msg.diagnostics } : state().discovery, error: msg.ok ? '' : msg.error });
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
75
94
|
if (msg.type === 'mcp_action_result') {
|
|
76
95
|
var pending = state().pending;
|
|
77
96
|
if (msg.requestId && (!pending || msg.requestId !== pending.requestId)) return true;
|
|
78
97
|
clearActionTimer();
|
|
79
98
|
patch({ pending: null, error: msg.ok ? '' : msg.error || 'Could not update connection.' });
|
|
80
|
-
if (msg.ok && msg.action === 'add') { store.set({ mcpConnectionDraft: '' }); navigate('detail', msg.id); }
|
|
99
|
+
if (msg.ok && (msg.action === 'add' || msg.action === 'import')) { store.set({ mcpConnectionDraft: '' }); navigate('detail', msg.id); }
|
|
81
100
|
if (msg.ok && msg.action === 'remove') navigate('list');
|
|
82
101
|
if (msg.ok && msg.action === 'enable') patch({ notice: 'Saved. Tool availability updates on the next turn; some runtimes may need a new session.' });
|
|
83
102
|
return true;
|
|
@@ -482,6 +482,8 @@ export function handleMentionStart(msg) {
|
|
|
482
482
|
mateId: msg.mateId,
|
|
483
483
|
mateName: msg.mateName,
|
|
484
484
|
avatarColor: msg.avatarColor,
|
|
485
|
+
avatarCatalogId: msg.avatarCatalogId,
|
|
486
|
+
avatarCustom: msg.avatarCustom,
|
|
485
487
|
avatarStyle: msg.avatarStyle,
|
|
486
488
|
avatarSeed: msg.avatarSeed,
|
|
487
489
|
};
|
|
@@ -762,6 +764,7 @@ function buildMentionAvatarUrl(meta) {
|
|
|
762
764
|
return mateAvatarUrl({
|
|
763
765
|
id: meta.mateId,
|
|
764
766
|
profile: {
|
|
767
|
+
avatarCatalogId: meta.avatarCatalogId || "",
|
|
765
768
|
avatarStyle: meta.avatarStyle || "imprint",
|
|
766
769
|
avatarSeed: meta.avatarSeed || meta.mateId,
|
|
767
770
|
avatarColor: meta.avatarColor || "#5857fc",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { showToast } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export function forwardPaneBrowserLink(url) {
|
|
5
|
+
if (!store.get('paneMode') || window.parent === window) return false;
|
|
6
|
+
window.parent.postMessage({ type: 'clay-pane-open-browser', url: url, projectSlug: store.get('currentSlug'), sessionId: String(store.get('activeSessionId')) }, window.location.origin);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
export function handlePaneBrowserMessage(event, host) {
|
|
10
|
+
if (!event || event.origin !== window.location.origin || !host || !event.data || event.data.type !== 'clay-pane-open-browser') return false;
|
|
11
|
+
var msg = event.data, s = store.snap();
|
|
12
|
+
var frame = Array.from(host.querySelectorAll('.split-pane-frame')).find(function (item) { return item.contentWindow === event.source; });
|
|
13
|
+
var pane = frame && s.splitPanes && s.splitPanes.panes && s.splitPanes.panes.find(function (item) { return String(item.slug) === frame.dataset.projectSlug && String(item.sessionId) === frame.dataset.sessionId; });
|
|
14
|
+
if (!pane || msg.projectSlug !== s.currentSlug || msg.projectSlug !== frame.dataset.projectSlug || String(msg.sessionId) !== frame.dataset.sessionId) return true;
|
|
15
|
+
var expected = new URL('/p/' + encodeURIComponent(pane.slug) + '/?pane=1&session=' + encodeURIComponent(pane.sessionId), window.location.href).href;
|
|
16
|
+
if (frame.src !== expected) return true;
|
|
17
|
+
// The authenticated host owns the browser; a live same-project pane supplies only a URL.
|
|
18
|
+
import('./shared-browser.js').then(function (module) { module.openSharedBrowserAt(msg.url); }).catch(function (error) { showToast(error.message || 'Could not open the browser.', 'warn'); });
|
|
19
|
+
return true;
|
|
20
|
+
}
|