clay-server 3.8.1 → 4.0.0-beta.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/lib/ask-user-mcp-server.js +5 -119
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +1 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-connection.js +4 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +1 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-memory.js +14 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-vendor-login.js +397 -0
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +349 -102
- package/lib/public/app.js +85 -42
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +93 -74
- package/lib/public/modules/app-connection.js +10 -1
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +102 -37
- package/lib/public/modules/app-notifications.js +8 -119
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +158 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/pane-bridge.js +9 -0
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +7 -64
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/split-view.js +7 -0
- package/lib/public/modules/sticky-notes.js +7 -6
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/tui-attention.js +10 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/vendor-login.js +287 -0
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +13 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +10 -6
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +111 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +18 -3
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +286 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +93 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +73 -43
- package/lib/yoke/adapters/codex.js +40 -10
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-app-server.js +20 -2
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +3 -2
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- package/lib/server-clay-home.js +0 -245
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// vendor-login.js - Vendor login (auth_required) recovery flow.
|
|
2
|
+
//
|
|
3
|
+
// The server owns the login terminal (see lib/project-vendor-login.js): exactly
|
|
4
|
+
// one per vendor per project, tracked server-side, killed once the login lands.
|
|
5
|
+
// This module is the client half of that contract - it asks for the flow, opens
|
|
6
|
+
// the modal on whatever terminal the server hands back, and reflects the flow's
|
|
7
|
+
// state in a banner.
|
|
8
|
+
//
|
|
9
|
+
// Nothing here spawns a terminal speculatively. Every auth_required after the
|
|
10
|
+
// first is a no-op while a flow is live, which is what stops the login-terminal
|
|
11
|
+
// pile-up and the endless auth_required -> login -> 401 loop.
|
|
12
|
+
|
|
13
|
+
import { store } from './store.js';
|
|
14
|
+
import { getWs } from './ws-ref.js';
|
|
15
|
+
import { refreshIcons, iconHtml } from './icons.js';
|
|
16
|
+
import { openTuiModal, closeTuiModal, getTuiModalTerminalId } from './tui-attention.js';
|
|
17
|
+
import { forwardPaneAuthRequired } from './pane-bridge.js';
|
|
18
|
+
|
|
19
|
+
// vendor -> { terminalId, startedAt } as last broadcast by the server.
|
|
20
|
+
var activeFlows = Object.create(null);
|
|
21
|
+
// vendor -> true between sending vendor_login_start and hearing back.
|
|
22
|
+
var pendingStarts = Object.create(null);
|
|
23
|
+
// Vendor whose login terminal the modal is currently showing.
|
|
24
|
+
var modalVendor = null;
|
|
25
|
+
// Set while this module tears the modal down itself, so the modal's close hook
|
|
26
|
+
// does not read a programmatic close as "the user gave up".
|
|
27
|
+
var closingModalInternally = false;
|
|
28
|
+
|
|
29
|
+
function vendorDisplayName(vendor) {
|
|
30
|
+
var vendors = store.get('vendorInfo') || {};
|
|
31
|
+
var info = vendors[vendor];
|
|
32
|
+
if (info && info.displayName) return info.displayName;
|
|
33
|
+
return vendor === "codex" ? "Codex" : "Claude Code";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function currentProjectSlug() {
|
|
37
|
+
var slug = store.get('currentSlug') || "";
|
|
38
|
+
if (slug) return slug;
|
|
39
|
+
// Fallback: derive from the URL (/p/<slug>/...).
|
|
40
|
+
try {
|
|
41
|
+
var m = (window.location.pathname || "").match(/^\/p\/([a-z0-9_-]+)/);
|
|
42
|
+
if (m) return m[1];
|
|
43
|
+
} catch (e) {}
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The banner surface belongs to the notification center. Query it rather than
|
|
48
|
+
// importing app-notifications so the dependency stays one-directional
|
|
49
|
+
// (app-notifications -> vendor-login).
|
|
50
|
+
function bannerContainer() {
|
|
51
|
+
return document.querySelector(".notif-banner-container");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function removeBannerEl(el) {
|
|
55
|
+
if (!el) return;
|
|
56
|
+
el.classList.remove("show");
|
|
57
|
+
setTimeout(function () {
|
|
58
|
+
if (el.parentNode) el.parentNode.removeChild(el);
|
|
59
|
+
}, 300);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function clearLoginBanners() {
|
|
63
|
+
var container = bannerContainer();
|
|
64
|
+
if (!container) return;
|
|
65
|
+
var existing = container.querySelectorAll('[data-vendor-login="true"]');
|
|
66
|
+
for (var i = 0; i < existing.length; i++) removeBannerEl(existing[i]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function showLoginBanner(opts) {
|
|
70
|
+
var container = bannerContainer();
|
|
71
|
+
if (!container) return null;
|
|
72
|
+
clearLoginBanners();
|
|
73
|
+
|
|
74
|
+
var banner = document.createElement("div");
|
|
75
|
+
banner.className = "notif-banner notif-banner-update";
|
|
76
|
+
banner.setAttribute("data-notif-id", "_vendor_login");
|
|
77
|
+
banner.setAttribute("data-vendor-login", "true");
|
|
78
|
+
banner.innerHTML =
|
|
79
|
+
'<div class="notif-banner-icon">' + iconHtml(opts.icon || "check-circle") + '</div>' +
|
|
80
|
+
'<div class="notif-banner-body">' +
|
|
81
|
+
'<div class="notif-banner-project">CLAY</div>' +
|
|
82
|
+
'<div class="notif-banner-title">' + opts.title + '</div>' +
|
|
83
|
+
'<div class="notif-banner-text">' + opts.text + '</div>' +
|
|
84
|
+
'</div>' +
|
|
85
|
+
'<button class="notif-banner-close">' + iconHtml("x") + '</button>';
|
|
86
|
+
|
|
87
|
+
container.appendChild(banner);
|
|
88
|
+
refreshIcons();
|
|
89
|
+
requestAnimationFrame(function () { banner.classList.add("show"); });
|
|
90
|
+
|
|
91
|
+
var closeBtn = banner.querySelector(".notif-banner-close");
|
|
92
|
+
if (closeBtn) {
|
|
93
|
+
closeBtn.addEventListener("click", function (e) {
|
|
94
|
+
e.stopPropagation();
|
|
95
|
+
removeBannerEl(banner);
|
|
96
|
+
if (opts.onDismiss) opts.onDismiss();
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (typeof opts.autoDismissMs === "number") {
|
|
100
|
+
setTimeout(function () { removeBannerEl(banner); }, opts.autoDismissMs);
|
|
101
|
+
}
|
|
102
|
+
return banner;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function showLoginStartedBanner(vendor) {
|
|
106
|
+
showLoginBanner({
|
|
107
|
+
icon: "check-circle",
|
|
108
|
+
title: vendorDisplayName(vendor) + " login started",
|
|
109
|
+
text: "Finish the sign-in in the terminal. Clay reloads the credentials on its own, so your sessions keep working - no new session needed.",
|
|
110
|
+
onDismiss: function () { cancelVendorLogin(vendor); },
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function showLoginCompleteBanner(vendor) {
|
|
115
|
+
showLoginBanner({
|
|
116
|
+
icon: "check-circle",
|
|
117
|
+
title: "Signed in to " + vendorDisplayName(vendor),
|
|
118
|
+
text: "Credentials reloaded. Send your next message to continue.",
|
|
119
|
+
autoDismissMs: 6000,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function showLoginErrorBanner(vendor, error) {
|
|
124
|
+
showLoginBanner({
|
|
125
|
+
icon: "alert-triangle",
|
|
126
|
+
title: vendorDisplayName(vendor) + " login could not start",
|
|
127
|
+
text: error || "The login terminal could not be created.",
|
|
128
|
+
autoDismissMs: 8000,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function closeModalInternally() {
|
|
133
|
+
modalVendor = null;
|
|
134
|
+
closingModalInternally = true;
|
|
135
|
+
try {
|
|
136
|
+
closeTuiModal();
|
|
137
|
+
} finally {
|
|
138
|
+
closingModalInternally = false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function closeModalForVendor(vendor, terminalId) {
|
|
143
|
+
if (modalVendor !== vendor) return;
|
|
144
|
+
var openId = getTuiModalTerminalId();
|
|
145
|
+
if (typeof terminalId === "number" && openId != null && openId !== terminalId) return;
|
|
146
|
+
closeModalInternally();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ========================================================
|
|
150
|
+
// Outgoing
|
|
151
|
+
// ========================================================
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Ask the server to start (or hand back) the login terminal for a vendor.
|
|
155
|
+
* opts.auto marks a request triggered by an auth_required event; those never
|
|
156
|
+
* open a second prompt while a flow is already running.
|
|
157
|
+
*/
|
|
158
|
+
export function requestVendorLogin(vendor, opts) {
|
|
159
|
+
var options = opts || {};
|
|
160
|
+
var target = vendor || "claude";
|
|
161
|
+
if (options.auto && (activeFlows[target] || pendingStarts[target])) return false;
|
|
162
|
+
|
|
163
|
+
var ws = getWs();
|
|
164
|
+
if (!ws || ws.readyState !== 1) return false;
|
|
165
|
+
|
|
166
|
+
pendingStarts[target] = true;
|
|
167
|
+
ws.send(JSON.stringify({
|
|
168
|
+
type: "vendor_login_start",
|
|
169
|
+
vendor: target,
|
|
170
|
+
auto: !!options.auto,
|
|
171
|
+
sessionId: options.sessionId || store.get('activeSessionId') || null,
|
|
172
|
+
}));
|
|
173
|
+
showLoginStartedBanner(target);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function cancelVendorLogin(vendor) {
|
|
178
|
+
var target = vendor || modalVendor;
|
|
179
|
+
if (!target) return;
|
|
180
|
+
delete pendingStarts[target];
|
|
181
|
+
delete activeFlows[target];
|
|
182
|
+
clearLoginBanners();
|
|
183
|
+
if (modalVendor === target) closeModalInternally();
|
|
184
|
+
var ws = getWs();
|
|
185
|
+
if (ws && ws.readyState === 1) {
|
|
186
|
+
ws.send(JSON.stringify({ type: "vendor_login_cancel", vendor: target }));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Auto-open the login flow when the server reports the vendor isn't logged in.
|
|
192
|
+
*
|
|
193
|
+
* Split panes have no banner surface of their own (the parent shell owns the
|
|
194
|
+
* single visible one), so a pane hands the event up instead of running two
|
|
195
|
+
* competing flows.
|
|
196
|
+
*/
|
|
197
|
+
export function autoStartLoginIfNeeded(msg) {
|
|
198
|
+
if (!msg) return false;
|
|
199
|
+
if (store.get('paneMode')) {
|
|
200
|
+
return forwardPaneAuthRequired({
|
|
201
|
+
vendor: msg.vendor || "claude",
|
|
202
|
+
sessionId: store.get('activeSessionId') || null,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
var vendor = msg.vendor || "claude";
|
|
206
|
+
if (activeFlows[vendor] || pendingStarts[vendor]) return false;
|
|
207
|
+
return requestVendorLogin(vendor, { auto: true, sessionId: msg.sessionId || null });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ========================================================
|
|
211
|
+
// Incoming
|
|
212
|
+
// ========================================================
|
|
213
|
+
|
|
214
|
+
export function handleVendorLoginReady(msg) {
|
|
215
|
+
if (!msg || typeof msg.terminalId !== "number") return;
|
|
216
|
+
var vendor = msg.vendor || "claude";
|
|
217
|
+
delete pendingStarts[vendor];
|
|
218
|
+
activeFlows[vendor] = { terminalId: msg.terminalId, startedAt: Date.now() };
|
|
219
|
+
|
|
220
|
+
var slug = msg.slug || currentProjectSlug();
|
|
221
|
+
if (!slug) return;
|
|
222
|
+
modalVendor = vendor;
|
|
223
|
+
openTuiModal(msg.terminalId, slug, {
|
|
224
|
+
sessionTitle: vendorDisplayName(vendor) + " login",
|
|
225
|
+
projectName: slug,
|
|
226
|
+
compact: true,
|
|
227
|
+
// Dismissing the modal abandons the login: kill the terminal server-side
|
|
228
|
+
// so it never lingers in the sidebar terminal list.
|
|
229
|
+
onClose: function () {
|
|
230
|
+
if (closingModalInternally) return;
|
|
231
|
+
modalVendor = null;
|
|
232
|
+
cancelVendorLogin(vendor);
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function handleVendorLoginState(msg) {
|
|
238
|
+
if (!msg) return;
|
|
239
|
+
var next = Object.create(null);
|
|
240
|
+
var flows = msg.flows || [];
|
|
241
|
+
for (var i = 0; i < flows.length; i++) {
|
|
242
|
+
next[flows[i].vendor] = { terminalId: flows[i].terminalId, startedAt: flows[i].startedAt };
|
|
243
|
+
delete pendingStarts[flows[i].vendor];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// A flow the server dropped without an auth_refreshed (cancelled elsewhere,
|
|
247
|
+
// terminal killed from the sidebar) must not leave a stale modal behind.
|
|
248
|
+
var previous = Object.keys(activeFlows);
|
|
249
|
+
for (var p = 0; p < previous.length; p++) {
|
|
250
|
+
if (!next[previous[p]] && modalVendor === previous[p]) {
|
|
251
|
+
closeModalInternally();
|
|
252
|
+
clearLoginBanners();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
activeFlows = next;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function handleAuthRefreshed(msg) {
|
|
259
|
+
if (!msg) return;
|
|
260
|
+
var vendor = msg.vendor || "claude";
|
|
261
|
+
var flow = activeFlows[vendor];
|
|
262
|
+
delete pendingStarts[vendor];
|
|
263
|
+
delete activeFlows[vendor];
|
|
264
|
+
closeModalForVendor(vendor, flow ? flow.terminalId : undefined);
|
|
265
|
+
clearLoginBanners();
|
|
266
|
+
showLoginCompleteBanner(vendor);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function handleVendorLoginError(msg) {
|
|
270
|
+
if (!msg) return;
|
|
271
|
+
var vendor = msg.vendor || "claude";
|
|
272
|
+
delete pendingStarts[vendor];
|
|
273
|
+
delete activeFlows[vendor];
|
|
274
|
+
if (modalVendor === vendor) closeModalInternally();
|
|
275
|
+
clearLoginBanners();
|
|
276
|
+
showLoginErrorBanner(vendor, msg.error);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function requestVendorLoginState() {
|
|
280
|
+
var ws = getWs();
|
|
281
|
+
if (!ws || ws.readyState !== 1) return;
|
|
282
|
+
ws.send(JSON.stringify({ type: "vendor_login_state_request" }));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function getActiveLoginFlow(vendor) {
|
|
286
|
+
return activeFlows[vendor] || null;
|
|
287
|
+
}
|
|
@@ -158,7 +158,7 @@ export function renderWorkerProposal(msg) {
|
|
|
158
158
|
|
|
159
159
|
var summary = document.createElement("p");
|
|
160
160
|
summary.className = "worker-proposal-summary";
|
|
161
|
-
summary.textContent = msg.summary || "This task can be planned here and executed in a visible Worker session.";
|
|
161
|
+
summary.textContent = msg.summary || "This task can be planned here and executed in a visible Split Worker session.";
|
|
162
162
|
card.appendChild(summary);
|
|
163
163
|
|
|
164
164
|
var steps = planSteps(msg.plan);
|
|
@@ -225,7 +225,7 @@ export function renderWorkerProposal(msg) {
|
|
|
225
225
|
var accept = document.createElement("button");
|
|
226
226
|
accept.type = "button";
|
|
227
227
|
accept.className = "worker-proposal-action primary";
|
|
228
|
-
accept.innerHTML = iconHtml("panel-right-open") + "<span>Run with Worker</span>";
|
|
228
|
+
accept.innerHTML = iconHtml("panel-right-open") + "<span>Run with Split Worker</span>";
|
|
229
229
|
accept.addEventListener("click", function () { sendDecision(card, true); });
|
|
230
230
|
actions.appendChild(decline);
|
|
231
231
|
actions.appendChild(accept);
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { getWs } from './ws-ref.js';
|
|
2
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
|
+
|
|
4
|
+
var pendingFocus = {};
|
|
5
|
+
|
|
6
|
+
function textElement(tag, className, text) {
|
|
7
|
+
var element = document.createElement(tag);
|
|
8
|
+
element.className = className;
|
|
9
|
+
element.textContent = text || "";
|
|
10
|
+
return element;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function statusLabel(status) {
|
|
14
|
+
if (status === "starting") return "Starting";
|
|
15
|
+
if (status === "running") return "In progress";
|
|
16
|
+
if (status === "completed") return "Completed";
|
|
17
|
+
if (status === "failed") return "Failed";
|
|
18
|
+
if (status === "cancelled") return "Cancelled";
|
|
19
|
+
if (status === "interrupted") return "Interrupted";
|
|
20
|
+
return "Approval required";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sendDecision(assignment, action, surface, context, card, opener) {
|
|
24
|
+
var ws = getWs();
|
|
25
|
+
if (!ws || ws.readyState !== 1 || !assignment || assignment.status !== "proposed") return false;
|
|
26
|
+
pendingFocus[assignment.assignmentId] = opener || null;
|
|
27
|
+
card.setAttribute("aria-busy", "true");
|
|
28
|
+
var buttons = card.querySelectorAll("button");
|
|
29
|
+
for (var i = 0; i < buttons.length; i++) buttons[i].disabled = true;
|
|
30
|
+
ws.send(JSON.stringify({
|
|
31
|
+
type: "project_assignment_response",
|
|
32
|
+
assignmentId: assignment.assignmentId,
|
|
33
|
+
action: action,
|
|
34
|
+
surface: surface,
|
|
35
|
+
sourceProjectSlug: assignment.sourceProjectSlug,
|
|
36
|
+
sourceSessionRef: assignment.sourceSessionRef,
|
|
37
|
+
mateId: context && context.mateId || null,
|
|
38
|
+
sessionId: context && context.sessionId || null,
|
|
39
|
+
requestId: context && context.requestId || null,
|
|
40
|
+
}));
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function restoreWorkspaceAssignmentFocus(assignmentId, card) {
|
|
45
|
+
if (!assignmentId || !pendingFocus[assignmentId] || !card) return false;
|
|
46
|
+
delete pendingFocus[assignmentId];
|
|
47
|
+
var focusTarget = card.querySelector(".workspace-assignment-error") || card.querySelector('[role="status"]');
|
|
48
|
+
if (!focusTarget) return false;
|
|
49
|
+
focusTarget.tabIndex = -1;
|
|
50
|
+
requestAnimationFrame(function () { focusTarget.focus({ preventScroll: true }); });
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function createWorkspaceAssignmentCard(assignment, options) {
|
|
55
|
+
var config = options || {};
|
|
56
|
+
var followUp = assignment.delivery === "follow_up";
|
|
57
|
+
var card = document.createElement("section");
|
|
58
|
+
card.className = "workspace-assignment-card" + (config.home ? " home-workspace-assignment" : "");
|
|
59
|
+
card.dataset.assignmentId = assignment.assignmentId || "";
|
|
60
|
+
card.dataset.status = assignment.status || "proposed";
|
|
61
|
+
card.dataset.delivery = followUp ? "follow_up" : "new_session";
|
|
62
|
+
card.setAttribute("aria-label", (followUp ? "Project follow-up: " : "Project assignment: ") + (assignment.title || "Untitled assignment"));
|
|
63
|
+
|
|
64
|
+
var header = document.createElement("div");
|
|
65
|
+
header.className = "workspace-assignment-header";
|
|
66
|
+
var mark = document.createElement("span");
|
|
67
|
+
mark.className = "workspace-assignment-mark";
|
|
68
|
+
mark.innerHTML = iconHtml(followUp ? "message-square-plus" : "send");
|
|
69
|
+
mark.setAttribute("aria-hidden", "true");
|
|
70
|
+
header.appendChild(mark);
|
|
71
|
+
var heading = document.createElement("div");
|
|
72
|
+
heading.className = "workspace-assignment-heading";
|
|
73
|
+
heading.appendChild(textElement("span", "workspace-assignment-kicker", followUp ? "PROJECT FOLLOW-UP" : "PROJECT ASSIGNMENT"));
|
|
74
|
+
heading.appendChild(textElement("h3", "workspace-assignment-title", assignment.title || "Untitled assignment"));
|
|
75
|
+
header.appendChild(heading);
|
|
76
|
+
var status = textElement("span", "workspace-assignment-status", statusLabel(assignment.status));
|
|
77
|
+
status.setAttribute("role", "status");
|
|
78
|
+
header.appendChild(status);
|
|
79
|
+
card.appendChild(header);
|
|
80
|
+
|
|
81
|
+
var targetText = followUp
|
|
82
|
+
? "Continue “" + (assignment.targetSessionTitle || "Existing session") + "” in " + (assignment.projectTitle || assignment.projectSlug || "project")
|
|
83
|
+
: "New private session in " + (assignment.projectTitle || assignment.projectSlug || "project");
|
|
84
|
+
var target = textElement("p", "workspace-assignment-target", targetText);
|
|
85
|
+
card.appendChild(target);
|
|
86
|
+
card.appendChild(textElement("p", "workspace-assignment-task", assignment.task || ""));
|
|
87
|
+
|
|
88
|
+
if (assignment.error) {
|
|
89
|
+
var error = textElement("p", "workspace-assignment-error", assignment.error);
|
|
90
|
+
error.setAttribute("role", "alert");
|
|
91
|
+
card.appendChild(error);
|
|
92
|
+
}
|
|
93
|
+
if (assignment.resultSummary) card.appendChild(textElement("p", "workspace-assignment-result", assignment.resultSummary));
|
|
94
|
+
|
|
95
|
+
var actions = document.createElement("div");
|
|
96
|
+
actions.className = "workspace-assignment-actions";
|
|
97
|
+
if (assignment.status === "proposed") {
|
|
98
|
+
var cancel = textElement("button", "workspace-assignment-action secondary", "Cancel");
|
|
99
|
+
cancel.type = "button";
|
|
100
|
+
cancel.setAttribute("aria-label", "Cancel project assignment");
|
|
101
|
+
cancel.addEventListener("click", function () { sendDecision(assignment, "cancel", config.home ? "home" : "project", config.context, card, cancel); });
|
|
102
|
+
var approve = textElement("button", "workspace-assignment-action primary", "Approve");
|
|
103
|
+
approve.type = "button";
|
|
104
|
+
approve.setAttribute("aria-label", followUp ? "Approve follow-up in " + (assignment.targetSessionTitle || "the target session") : "Approve new session in " + (assignment.projectTitle || assignment.projectSlug || "the target project"));
|
|
105
|
+
approve.addEventListener("click", function () { sendDecision(assignment, "approve", config.home ? "home" : "project", config.context, card, approve); });
|
|
106
|
+
actions.appendChild(cancel);
|
|
107
|
+
actions.appendChild(approve);
|
|
108
|
+
} else {
|
|
109
|
+
var resolved = textElement("span", "workspace-assignment-resolved", statusLabel(assignment.status));
|
|
110
|
+
resolved.setAttribute("role", "status");
|
|
111
|
+
resolved.tabIndex = -1;
|
|
112
|
+
actions.appendChild(resolved);
|
|
113
|
+
}
|
|
114
|
+
card.appendChild(actions);
|
|
115
|
+
restoreWorkspaceAssignmentFocus(assignment.assignmentId, card);
|
|
116
|
+
refreshIcons();
|
|
117
|
+
return card;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function showWorkspaceAssignmentError(card, msg) {
|
|
121
|
+
if (!card) return false;
|
|
122
|
+
card.removeAttribute("aria-busy");
|
|
123
|
+
var buttons = card.querySelectorAll("button");
|
|
124
|
+
for (var i = 0; i < buttons.length; i++) buttons[i].disabled = false;
|
|
125
|
+
var error = card.querySelector(".workspace-assignment-error");
|
|
126
|
+
if (!error) {
|
|
127
|
+
error = textElement("p", "workspace-assignment-error", "");
|
|
128
|
+
error.setAttribute("role", "alert");
|
|
129
|
+
card.insertBefore(error, card.querySelector(".workspace-assignment-actions"));
|
|
130
|
+
}
|
|
131
|
+
error.textContent = msg.error || "The assignment could not be updated.";
|
|
132
|
+
error.tabIndex = -1;
|
|
133
|
+
error.focus({ preventScroll: true });
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function applyHomeWorkspaceAssignment(messages, msg) {
|
|
138
|
+
var assignment = msg && msg.assignment;
|
|
139
|
+
if (!assignment || !assignment.assignmentId) return messages;
|
|
140
|
+
var next = (messages || []).slice();
|
|
141
|
+
for (var i = 0; i < next.length; i++) {
|
|
142
|
+
if (next[i].role === "assignment" && next[i].assignment && next[i].assignment.assignmentId === assignment.assignmentId) {
|
|
143
|
+
next[i] = { role: "assignment", assignment: assignment };
|
|
144
|
+
return next;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
next.push({ role: "assignment", assignment: assignment });
|
|
148
|
+
return next;
|
|
149
|
+
}
|
package/lib/public/style.css
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
@import url("css/title-bar.css?v=20260828-project-header1");
|
|
4
4
|
@import url("css/pane.css");
|
|
5
5
|
@import url("css/worker-proposal.css");
|
|
6
|
+
@import url("css/workspace-assignment.css");
|
|
6
7
|
@import url("css/session-actions.css?v=20260828-contrast1");
|
|
7
8
|
@import url("css/sidebar.css?v=20260828-session-list3");
|
|
8
9
|
@import url("css/overlays.css?v=20260827-brand3");
|
|
@@ -23,7 +24,13 @@
|
|
|
23
24
|
@import url("css/scheduler.css");
|
|
24
25
|
@import url("css/scheduler-modal.css");
|
|
25
26
|
@import url("css/home-hub.css");
|
|
26
|
-
|
|
27
|
+
@import url("css/home-sidebar.css");
|
|
28
|
+
@import url("css/home-session-actions.css");
|
|
29
|
+
@import url("css/home-capsule-library.css");
|
|
30
|
+
@import url("css/home-capsule-source.css");
|
|
31
|
+
@import url("css/capsule-ui.css?v=20260831-v2");
|
|
32
|
+
@import url("css/home-mate-model-picker.css");
|
|
33
|
+
@import url("css/home-mate-settings.css");
|
|
27
34
|
@import url("css/playbook.css");
|
|
28
35
|
@import url("css/stt.css");
|
|
29
36
|
@import url("css/profile.css");
|
|
@@ -32,9 +39,13 @@
|
|
|
32
39
|
@import url("css/session-search.css");
|
|
33
40
|
@import url("css/tooltip.css");
|
|
34
41
|
@import url("css/mates.css?v=20260407");
|
|
35
|
-
@import url("css/command-palette.css");
|
|
42
|
+
@import url("css/command-palette.css?v=20260901-clay-chat2");
|
|
36
43
|
@import url("css/mention.css");
|
|
37
44
|
@import url("css/debate.css");
|
|
45
|
+
@import url("css/home-debate-planning.css");
|
|
46
|
+
@import url("css/home-mate-creation.css");
|
|
47
|
+
@import url("css/home-debate-live.css");
|
|
48
|
+
@import url("css/home-debates-archive.css");
|
|
38
49
|
@import url("css/notifications-center.css");
|
|
39
50
|
@import url("css/tui-attention.css?v=20260829-terminal-canvas1");
|
|
40
51
|
@import url("css/pwa-mobile.css?v=20260824f");
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var SAFE_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2
|
+
var PROTECTED_KEYS = { HOME: true, USER: true, LOGNAME: true, SHELL: true, PATH: true, XDG_RUNTIME_DIR: true, NODE_OPTIONS: true, TERM: true, COLORFGBG: true };
|
|
3
|
+
|
|
4
|
+
function isProtectedKey(key) {
|
|
5
|
+
return !!PROTECTED_KEYS[key] || key.indexOf("CLAY_") === 0 || key.indexOf("LD_") === 0 || key.indexOf("DYLD_") === 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function parseQuotedValue(value, lineNumber) {
|
|
9
|
+
var trimmed = value.trim();
|
|
10
|
+
if (!trimmed) return "";
|
|
11
|
+
var quote = trimmed.charAt(0);
|
|
12
|
+
if (quote !== "\"" && quote !== "'") {
|
|
13
|
+
if (/[;`|]|\$\(|&&/.test(trimmed)) throw new Error("Unsupported executable syntax at line " + lineNumber);
|
|
14
|
+
return trimmed;
|
|
15
|
+
}
|
|
16
|
+
if (trimmed.length < 2 || trimmed.charAt(trimmed.length - 1) !== quote) throw new Error("Invalid quoted value at line " + lineNumber);
|
|
17
|
+
var content = trimmed.substring(1, trimmed.length - 1);
|
|
18
|
+
if (quote === "'") return content;
|
|
19
|
+
try {
|
|
20
|
+
var parsed = JSON.parse(trimmed);
|
|
21
|
+
if (typeof parsed !== "string" || parsed.indexOf("\0") !== -1) throw new Error("Invalid");
|
|
22
|
+
return parsed;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
throw new Error("Invalid quoted value at line " + lineNumber);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseEnvrc(text) {
|
|
29
|
+
var env = {};
|
|
30
|
+
var source = typeof text === "string" ? text : "";
|
|
31
|
+
if (source.indexOf("\0") !== -1) throw new Error("Environment values cannot contain NUL bytes");
|
|
32
|
+
var lines = source.split(/\r?\n/);
|
|
33
|
+
for (var i = 0; i < lines.length; i++) {
|
|
34
|
+
var line = lines[i].trim();
|
|
35
|
+
if (!line || line.charAt(0) === "#") continue;
|
|
36
|
+
if (line.indexOf("export ") === 0) line = line.substring(7).trim();
|
|
37
|
+
var equals = line.indexOf("=");
|
|
38
|
+
if (equals < 1) throw new Error("Unsupported syntax at line " + (i + 1) + ": use KEY=VALUE or export KEY=VALUE");
|
|
39
|
+
var key = line.substring(0, equals).trim();
|
|
40
|
+
if (!SAFE_KEY.test(key)) throw new Error("Invalid variable name at line " + (i + 1) + ": " + key);
|
|
41
|
+
env[key] = parseQuotedValue(line.substring(equals + 1), i + 1);
|
|
42
|
+
}
|
|
43
|
+
return env;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function validateEnvString(text) {
|
|
47
|
+
try { parseEnvrc(text); return null; }
|
|
48
|
+
catch (e) { return e.message || String(e); }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function applyEnv(target, values) {
|
|
52
|
+
var keys = Object.keys(values || {});
|
|
53
|
+
for (var i = 0; i < keys.length; i++) if (!isProtectedKey(keys[i])) target[keys[i]] = values[keys[i]];
|
|
54
|
+
return target;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveRuntimeEnv(opts) {
|
|
58
|
+
opts = opts || {};
|
|
59
|
+
var result = Object.assign({}, opts.baseEnv || process.env);
|
|
60
|
+
applyEnv(result, parseEnvrc(opts.sharedEnvrc || ""));
|
|
61
|
+
applyEnv(result, parseEnvrc(opts.projectEnvrc || ""));
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { parseEnvrc: parseEnvrc, validateEnvString: validateEnvString, resolveRuntimeEnv: resolveRuntimeEnv, isProtectedKey: isProtectedKey };
|