clay-server 5.3.0-beta.5 → 5.3.0-beta.7
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/clay-product-control-tools.js +2 -2
- package/lib/clay-product-control.js +1 -1
- package/lib/clay-screen-controls.js +1 -0
- package/lib/clay-split-view-awareness.js +5 -2
- package/lib/clay-split-view-controls.js +14 -7
- package/lib/clay-split-view-input.js +5 -4
- package/lib/clay-split-view-render-proof.js +14 -4
- package/lib/clay-view-workspace-input.js +71 -0
- package/lib/project-admin-overview.js +22 -11
- package/lib/project-background-tasks.js +76 -0
- package/lib/project-connection.js +1 -1
- package/lib/project-file-history.js +11 -9
- package/lib/project-file-watch.js +26 -7
- package/lib/project-filesystem.js +5 -4
- package/lib/project-sessions.js +12 -5
- package/lib/project-shared-browser.js +7 -3
- package/lib/project-split-view-document.js +16 -2
- package/lib/project-split-views.js +27 -297
- package/lib/project-user-message.js +4 -3
- package/lib/project-vendor-login.js +2 -2
- package/lib/project-view-input.js +11 -0
- package/lib/project-view-pair.js +26 -0
- package/lib/project-view-store.js +348 -0
- package/lib/project-view-terminal-binding.js +16 -0
- package/lib/project-view-workspace.js +126 -0
- package/lib/project-worker-conversation.js +5 -0
- package/lib/project.js +1 -0
- package/lib/public/app.js +9 -0
- package/lib/public/css/contextual-clay.css +2 -1
- package/lib/public/css/issues.css +3 -0
- package/lib/public/css/mate-conversation.css +1 -1
- package/lib/public/css/pane.css +5 -1
- package/lib/public/css/pending-message-queue.css +24 -19
- package/lib/public/css/project-admin-overview.css +42 -13
- package/lib/public/css/right-workbench.css +0 -1
- package/lib/public/css/scheduled-tasks.css +7 -0
- package/lib/public/css/shared-browser.css +31 -8
- package/lib/public/css/split-view-layout.css +129 -10
- package/lib/public/css/tool-pane.css +124 -0
- package/lib/public/index.html +6 -6
- package/lib/public/modules/agent-message-bubbles.js +96 -0
- package/lib/public/modules/app-header.js +6 -0
- package/lib/public/modules/app-messages.js +26 -9
- package/lib/public/modules/background-task-previews.js +8 -0
- package/lib/public/modules/background-task-viewer.js +97 -0
- package/lib/public/modules/background-tasks-ui.js +34 -125
- package/lib/public/modules/chat-bubble-renderer.js +11 -5
- package/lib/public/modules/clay-file-links.js +2 -1
- package/lib/public/modules/clay-split-view-screen.js +6 -3
- package/lib/public/modules/clay-tool-pane-screen.js +35 -0
- package/lib/public/modules/contextual-clay-geometry.js +7 -3
- package/lib/public/modules/file-history-snapshot.js +12 -0
- package/lib/public/modules/file-pane-entrypoints.js +55 -0
- package/lib/public/modules/file-pane-history.js +78 -0
- package/lib/public/modules/file-pane-instances.js +140 -0
- package/lib/public/modules/file-pane-links.js +41 -0
- package/lib/public/modules/file-pane-render.js +48 -0
- package/lib/public/modules/file-pane-review.js +26 -0
- package/lib/public/modules/file-pane-toolbar.js +54 -0
- package/lib/public/modules/file-viewer-source.js +70 -0
- package/lib/public/modules/filebrowser.js +43 -82
- package/lib/public/modules/git-panel.js +5 -0
- package/lib/public/modules/issues.js +3 -4
- package/lib/public/modules/linear-panel.js +2 -2
- package/lib/public/modules/mate-knowledge-workbench.js +10 -6
- package/lib/public/modules/notifications.js +4 -1
- package/lib/public/modules/pane-activation.js +7 -3
- package/lib/public/modules/pane-binding.js +5 -1
- package/lib/public/modules/pane-browser-bridge.js +6 -2
- package/lib/public/modules/pane-browser-transport.js +110 -0
- package/lib/public/modules/pane-header-tooltips.js +32 -0
- package/lib/public/modules/pending-message-queue-drag.js +69 -0
- package/lib/public/modules/pending-message-queue.js +3 -33
- package/lib/public/modules/permission-control.js +6 -5
- package/lib/public/modules/plantuml-preview.js +12 -12
- package/lib/public/modules/project-admin-overview.js +125 -10
- package/lib/public/modules/project-grouping.js +9 -0
- package/lib/public/modules/right-workbench.js +34 -15
- package/lib/public/modules/scheduled-tasks.js +7 -6
- package/lib/public/modules/server-settings-controls.js +33 -4
- package/lib/public/modules/session-folder-toolbar.js +1 -1
- package/lib/public/modules/session-tool-item.js +86 -0
- package/lib/public/modules/settings-select.js +2 -1
- package/lib/public/modules/shared-browser-tabs.js +37 -21
- package/lib/public/modules/shared-browser.js +69 -9
- package/lib/public/modules/sidebar-projects.js +1 -10
- package/lib/public/modules/split-pane-renderer.js +1 -0
- package/lib/public/modules/split-session-empty-pane.js +47 -0
- package/lib/public/modules/split-view-create-menu.js +74 -65
- package/lib/public/modules/split-view-drag-preview.js +90 -0
- package/lib/public/modules/split-view-layout-client.js +1 -1
- package/lib/public/modules/split-view-layout-scope.js +4 -0
- package/lib/public/modules/split-view-picker-actions.js +71 -0
- package/lib/public/modules/split-view-picker-render.js +81 -0
- package/lib/public/modules/split-view-presentation-drag.js +36 -13
- package/lib/public/modules/split-view-presentation-drop.js +21 -18
- package/lib/public/modules/split-view-presentation-renderer.js +22 -17
- package/lib/public/modules/split-view-presentations.js +25 -33
- package/lib/public/modules/split-view-worker-activity.js +2 -2
- package/lib/public/modules/split-view-worker-transcript.js +70 -33
- package/lib/public/modules/split-view-workers.js +6 -4
- package/lib/public/modules/split-view.js +9 -2
- package/lib/public/modules/sticky-notes-browser.js +2 -2
- package/lib/public/modules/terminal-viewport.js +14 -0
- package/lib/public/modules/terminal.js +49 -23
- package/lib/public/modules/theme-palette.js +177 -0
- package/lib/public/modules/theme.js +2 -180
- package/lib/public/modules/tool-pane-chrome.js +88 -0
- package/lib/public/modules/tool-pane-drag.js +134 -0
- package/lib/public/modules/tool-pane-layout.js +95 -0
- package/lib/public/modules/tool-pane-legacy.js +60 -0
- package/lib/public/modules/tool-pane-tree.js +51 -0
- package/lib/public/modules/tools.js +5 -89
- package/lib/public/modules/tooltip.js +32 -6
- package/lib/public/modules/view-legacy-coordinates.js +16 -0
- package/lib/public/modules/view-mutations.js +24 -0
- package/lib/public/modules/view-pair-presentation.js +74 -0
- package/lib/public/modules/view-pair-validation.js +12 -0
- package/lib/public/modules/view-presentation-state.js +37 -0
- package/lib/public/modules/view-workspace-controller.js +210 -0
- package/lib/public/modules/view-workspace-state.js +103 -0
- package/lib/public/modules/view-workspace-validation.js +47 -0
- package/lib/public/modules/wireframe-workbench.js +13 -2
- package/lib/public/modules/workbench-layout-preference.js +12 -5
- package/lib/public/modules/workbench-panels.js +21 -0
- package/lib/public/modules/worker-message-previews.js +39 -0
- package/lib/public/pane-assets/app-5W6KJXK5.js +1 -0
- package/lib/public/pane-assets/chunk-2IJUTAB6.js +2 -0
- package/lib/public/pane-assets/chunk-6QFLYOVY.js +1 -0
- package/lib/public/pane-assets/chunk-7OVNC5HT.js +1 -0
- package/lib/public/pane-assets/chunk-BQN7B62H.js +3 -0
- package/lib/public/pane-assets/chunk-EMLN2Y7L.js +1 -0
- package/lib/public/pane-assets/chunk-ENPAHXLB.js +1 -0
- package/lib/public/pane-assets/chunk-EPBGGZRT.js +1 -0
- package/lib/public/pane-assets/chunk-EYTE56IA.js +1 -0
- package/lib/public/pane-assets/chunk-FQTNR4ZS.js +1 -0
- package/lib/public/pane-assets/chunk-JIV7O5GN.js +1 -0
- package/lib/public/pane-assets/chunk-L6LSA5WI.js +1 -0
- package/lib/public/pane-assets/chunk-M57UIGZV.js +1 -0
- package/lib/public/pane-assets/chunk-MGTFTEJP.js +1 -0
- package/lib/public/pane-assets/chunk-MMG6MYCN.js +1 -0
- package/lib/public/pane-assets/chunk-MZLMEXMW.js +1 -0
- package/lib/public/pane-assets/chunk-OG3E5SVA.js +166 -0
- package/lib/public/pane-assets/chunk-R7QVVFUG.js +1 -0
- package/lib/public/pane-assets/chunk-RGAFZ3T2.js +1 -0
- package/lib/public/pane-assets/chunk-RNDRURMF.js +1 -0
- package/lib/public/pane-assets/chunk-SJOT74VW.js +2 -0
- package/lib/public/pane-assets/chunk-SJTGHIN5.js +1 -0
- package/lib/public/pane-assets/chunk-SLMAFKI2.js +1 -0
- package/lib/public/pane-assets/chunk-U3I5KY3B.js +1 -0
- package/lib/public/pane-assets/chunk-UPXU2IPC.js +1 -0
- package/lib/public/pane-assets/chunk-VEBYD7TL.js +1 -0
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +1 -0
- package/lib/public/pane-assets/chunk-XBHYNXXT.js +1 -0
- package/lib/public/pane-assets/manifest.json +438 -381
- package/lib/public/pane-assets/style-BSKKK54Q.css +1 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-message-processor.js +5 -2
- package/lib/server-storage.js +27 -3
- package/lib/server-workbench-layout-settings.js +3 -3
- package/lib/server.js +4 -2
- package/lib/sessions.js +1 -1
- package/lib/split-view-layout.js +11 -7
- package/lib/storage-usage.js +29 -7
- package/lib/terminal-manager.js +2 -2
- package/lib/users-workbench-layout-preferences.js +14 -4
- package/lib/workbench-window-layout.js +30 -0
- package/lib/worker-message-previews.js +50 -0
- package/lib/ws-schema.js +1 -1
- package/lib/yoke/adapters/claude-worker.js +8 -0
- package/lib/yoke/adapters/claude.js +17 -2
- package/lib/yoke/claude-task-reader.js +94 -0
- package/lib/yoke/interface.js +1 -0
- package/package.json +1 -1
- package/lib/public/pane-assets/app-6H5S2Y3Z.js +0 -1
- package/lib/public/pane-assets/chunk-3QV5SDC4.js +0 -1
- package/lib/public/pane-assets/chunk-4FM3U3DW.js +0 -1
- package/lib/public/pane-assets/chunk-7GZH3FZY.js +0 -1
- package/lib/public/pane-assets/chunk-AGJTF5WQ.js +0 -1
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +0 -1
- package/lib/public/pane-assets/chunk-I57URYSL.js +0 -1
- package/lib/public/pane-assets/chunk-NPKRTUNG.js +0 -2
- package/lib/public/pane-assets/chunk-VGA2Y3D2.js +0 -2
- package/lib/public/pane-assets/chunk-WKO3T75E.js +0 -167
- package/lib/public/pane-assets/style-TSRDU54Z.css +0 -1
|
@@ -31,8 +31,7 @@ function attachFileWatch(ctx) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// --- File watcher ---
|
|
34
|
-
//
|
|
35
|
-
// one browser tab silently replace another tab's live preview subscription.
|
|
34
|
+
// Bounded instance subscriptions belong to the authenticated socket.
|
|
36
35
|
var fileWatchers = new Map();
|
|
37
36
|
|
|
38
37
|
function settleFileWatchReady(entry, ready) {
|
|
@@ -76,6 +75,7 @@ function attachFileWatch(ctx) {
|
|
|
76
75
|
var latest = fileWatchers.get(key);
|
|
77
76
|
if (!latest || latest.relPath !== relPath) return;
|
|
78
77
|
try {
|
|
78
|
+
if (latest.authority !== JSON.stringify([client && client._clayUser && client._clayUser.id || null, scopeFor(client)])) { closeFileWatch(key); return; }
|
|
79
79
|
var snapshot = readFileSnapshot(client, relPath);
|
|
80
80
|
if (!snapshot) return;
|
|
81
81
|
if (latest.hasSnapshot && latest.content === snapshot.content && latest.size === snapshot.size) return;
|
|
@@ -84,6 +84,8 @@ function attachFileWatch(ctx) {
|
|
|
84
84
|
latest.size = snapshot.size;
|
|
85
85
|
sendFileChanged(client, {
|
|
86
86
|
type: "fs_file_changed",
|
|
87
|
+
instanceId: latest.instanceId,
|
|
88
|
+
requestId: latest.requestId,
|
|
87
89
|
path: relPath,
|
|
88
90
|
content: snapshot.content,
|
|
89
91
|
size: snapshot.size,
|
|
@@ -95,7 +97,7 @@ function attachFileWatch(ctx) {
|
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
function startFileWatch(client, relPath) {
|
|
100
|
+
function startFileWatch(client, relPath, binding) {
|
|
99
101
|
// Preserve the old single-argument API for callers outside the websocket
|
|
100
102
|
// file browser. They share one legacy subscription.
|
|
101
103
|
if (typeof relPath !== "string") {
|
|
@@ -104,9 +106,16 @@ function attachFileWatch(ctx) {
|
|
|
104
106
|
}
|
|
105
107
|
var absPath;
|
|
106
108
|
try { absPath = checkedPath(relPath, scopeFor(client)); } catch (error) { return Promise.resolve(false); }
|
|
109
|
+
if (binding && (!/^[A-Za-z0-9_-]{1,128}$/.test(binding.instanceId || '') || typeof binding.requestId !== 'string' || binding.requestId.length > 128)) return Promise.resolve(false);
|
|
107
110
|
var key = client || "_legacy";
|
|
111
|
+
if (binding) {
|
|
112
|
+
var entries = Array.from(fileWatchers.entries()).filter(function (pair) { return pair[1].client === client; });
|
|
113
|
+
var match = entries.find(function (pair) { return pair[1].instanceId === binding.instanceId; });
|
|
114
|
+
if (!match && entries.length >= 8) return Promise.resolve(false);
|
|
115
|
+
key = match ? match[0] : {};
|
|
116
|
+
}
|
|
108
117
|
var existing = fileWatchers.get(key);
|
|
109
|
-
if (existing && existing.relPath === relPath) return existing.ready;
|
|
118
|
+
if (existing && existing.relPath === relPath && existing.requestId === (binding ? binding.requestId : undefined)) return existing.ready;
|
|
110
119
|
closeFileWatch(key);
|
|
111
120
|
|
|
112
121
|
// Watch the parent directory rather than the file inode. Editors and agent
|
|
@@ -132,6 +141,10 @@ function attachFileWatch(ctx) {
|
|
|
132
141
|
var ready = new Promise(function (resolve) { resolveReady = resolve; });
|
|
133
142
|
var entry = {
|
|
134
143
|
watcher: watcher,
|
|
144
|
+
client: client,
|
|
145
|
+
authority: JSON.stringify([client && client._clayUser && client._clayUser.id || null, scopeFor(client)]),
|
|
146
|
+
instanceId: binding ? binding.instanceId : undefined,
|
|
147
|
+
requestId: binding ? binding.requestId : undefined,
|
|
135
148
|
relPath: relPath,
|
|
136
149
|
debounce: null,
|
|
137
150
|
reconcile: null,
|
|
@@ -158,7 +171,7 @@ function attachFileWatch(ctx) {
|
|
|
158
171
|
publishFileChanged(key, client, relPath);
|
|
159
172
|
if (fileWatchers.get(key) === entry) settleFileWatchReady(entry, true);
|
|
160
173
|
});
|
|
161
|
-
watcher.on("error", function () { closeFileWatch(key); });
|
|
174
|
+
watcher.on("error", function () { if (fileWatchers.get(key) === entry) closeFileWatch(key); });
|
|
162
175
|
return ready;
|
|
163
176
|
} catch (e) {
|
|
164
177
|
closeFileWatch(key);
|
|
@@ -166,9 +179,15 @@ function attachFileWatch(ctx) {
|
|
|
166
179
|
}
|
|
167
180
|
}
|
|
168
181
|
|
|
169
|
-
function stopFileWatch(client) {
|
|
182
|
+
function stopFileWatch(client, binding) {
|
|
170
183
|
if (arguments.length > 0) {
|
|
171
|
-
|
|
184
|
+
Array.from(fileWatchers.entries()).forEach(function (pair) {
|
|
185
|
+
var entry = pair[1];
|
|
186
|
+
if (entry.client !== client) return;
|
|
187
|
+
if (binding && (entry.instanceId !== binding.instanceId || entry.requestId !== binding.requestId)) return;
|
|
188
|
+
closeFileWatch(pair[0]);
|
|
189
|
+
});
|
|
190
|
+
if (!binding) closeFileWatch(client || "_legacy");
|
|
172
191
|
return;
|
|
173
192
|
}
|
|
174
193
|
var keys = Array.from(fileWatchers.keys());
|
|
@@ -45,13 +45,14 @@ function attachFilesystem(ctx) {
|
|
|
45
45
|
// Resolve live authority before every operation; never trust message roles.
|
|
46
46
|
if (/^fs_/.test(msg.type) && msg.type !== "fs_unwatch") {
|
|
47
47
|
try {
|
|
48
|
+
if (msg.instanceId && (msg.projectSlug !== slug || (msg.accountId || null) !== (ws._clayUser && ws._clayUser.id || null))) throw new Error('File pane context changed');
|
|
48
49
|
fileScope = access.fileScope(ws);
|
|
49
50
|
if (/^fs_(list|read|write|watch)$/.test(msg.type)) {
|
|
50
51
|
fileTarget = resolveFilePath(cwd, msg.type === "fs_list" ? msg.path || "." : msg.path, fileScope);
|
|
51
52
|
}
|
|
52
53
|
} catch (e) {
|
|
53
54
|
var denied = fileError(e);
|
|
54
|
-
sendTo(ws, { type: msg.type + "_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, ok: false, error: denied.message, errorCode: denied.code });
|
|
55
|
+
sendTo(ws, { type: msg.type + "_result", path: msg.path, instanceId: msg.instanceId, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, ok: false, error: denied.message, errorCode: denied.code });
|
|
55
56
|
return true;
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -115,7 +116,7 @@ function attachFilesystem(ctx) {
|
|
|
115
116
|
|
|
116
117
|
// --- fs_read ---
|
|
117
118
|
if (msg.type === "fs_read") {
|
|
118
|
-
var metadata = { type: 'fs_read_result', path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId };
|
|
119
|
+
var metadata = { type: 'fs_read_result', path: msg.path, instanceId: msg.instanceId, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId };
|
|
119
120
|
try {
|
|
120
121
|
var readResult = operations.read(fileScope, msg.path);
|
|
121
122
|
try { var fileSnapshot = operations.snapshot(fileScope, msg.path); if (fileSnapshot.hash === require('crypto').createHash('sha256').update(readResult.content).digest('hex')) readResult.controlRevision = require('./clay-control-target-projection').digest(fileSnapshot); } catch (ignored) {}
|
|
@@ -210,12 +211,12 @@ function attachFilesystem(ctx) {
|
|
|
210
211
|
|
|
211
212
|
// --- File watcher ---
|
|
212
213
|
if (msg.type === "fs_watch") {
|
|
213
|
-
if (msg.path) startFileWatch(ws, msg.path);
|
|
214
|
+
if (msg.path) startFileWatch(ws, msg.path, msg.instanceId ? { instanceId: msg.instanceId, requestId: msg.requestId } : null);
|
|
214
215
|
return true;
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
if (msg.type === "fs_unwatch") {
|
|
218
|
-
stopFileWatch(ws);
|
|
219
|
+
stopFileWatch(ws, msg.instanceId ? { instanceId: msg.instanceId, requestId: msg.requestId } : { instanceId: undefined, requestId: undefined });
|
|
219
220
|
return true;
|
|
220
221
|
}
|
|
221
222
|
|
package/lib/project-sessions.js
CHANGED
|
@@ -93,6 +93,7 @@ function formatAskUserAnswerAsMessage(input, answers) {
|
|
|
93
93
|
function attachSessions(ctx) {
|
|
94
94
|
var mateDefaultsToken = {};
|
|
95
95
|
var contextUsage = contextUsageModule.attachContextUsage(ctx);
|
|
96
|
+
var backgroundTaskReader = require("./project-background-tasks").attach(ctx);
|
|
96
97
|
var cwd = ctx.cwd;
|
|
97
98
|
var slug = ctx.slug;
|
|
98
99
|
var isMate = ctx.isMate;
|
|
@@ -606,6 +607,7 @@ function attachSessions(ctx) {
|
|
|
606
607
|
if (workerRemoval.handleMessage(ws, msg)) return true;
|
|
607
608
|
if (serverSettingsMessages.handleServerSettings({ opts: opts, usersModule: usersModule, sendTo: sendTo, send: send }, ws, msg)) return true;
|
|
608
609
|
if (contextUsage.handleMessage(ws, msg)) return true;
|
|
610
|
+
if (backgroundTaskReader.handleMessage(ws, msg)) return true;
|
|
609
611
|
|
|
610
612
|
if (msg.type === "push_subscribe") {
|
|
611
613
|
var _pushUserId = ws._clayUser ? ws._clayUser.id : null;
|
|
@@ -681,6 +683,9 @@ function attachSessions(ctx) {
|
|
|
681
683
|
if (creationRequestId) sendTo(ws, { type: "new_session_result", requestId: creationRequestId, ok: false, error: text });
|
|
682
684
|
else sendTo(ws, { type: "error", text: text });
|
|
683
685
|
};
|
|
686
|
+
if (msg.activate === false && (!creationRequestId || isMate || ws._clayPane || msg.slug !== slug)) {
|
|
687
|
+
creationFailed("Inactive creation requires this exact project and an ordinary session request."); return true;
|
|
688
|
+
}
|
|
684
689
|
// The same request id is created at most once per socket.
|
|
685
690
|
if (creationRequestId) {
|
|
686
691
|
if (!ws._createdRequests) ws._createdRequests = [];
|
|
@@ -723,7 +728,8 @@ function attachSessions(ctx) {
|
|
|
723
728
|
// Reuse an existing blank GUI session instead of stacking another
|
|
724
729
|
// one. A vendor-less blank is acceptable: nothing has happened in it,
|
|
725
730
|
// so stamping the requested vendor is equivalent to a fresh create.
|
|
726
|
-
var
|
|
731
|
+
var inactiveCreation = msg.activate === false && !!creationRequestId && !isMate && !ws._clayPane;
|
|
732
|
+
var reusable = msg.forceNew === true || inactiveCreation ? null : sm.findReusableBlankSession({
|
|
727
733
|
vendor: sessionOpts.vendor || null,
|
|
728
734
|
ownerId: sessionOpts.ownerId || null,
|
|
729
735
|
});
|
|
@@ -734,9 +740,10 @@ function attachSessions(ctx) {
|
|
|
734
740
|
sm.switchSession(reusable.localId, ws);
|
|
735
741
|
newSess = reusable;
|
|
736
742
|
} else {
|
|
737
|
-
newSess = sm.createSession(sessionOpts, ws);
|
|
743
|
+
newSess = inactiveCreation ? sm.createSessionRaw(sessionOpts) : sm.createSession(sessionOpts, ws);
|
|
738
744
|
}
|
|
739
|
-
ws._clayActiveSession = newSess.localId;
|
|
745
|
+
if (!inactiveCreation) ws._clayActiveSession = newSess.localId;
|
|
746
|
+
else { sm.saveSessionFile(newSess); sm.broadcastSessionList(); }
|
|
740
747
|
var folderPlaced = msg.folderId === undefined ? null : sessionFolders.placeNewSession(ws, newSess, msg.folderId, msg.folderSlug);
|
|
741
748
|
if (msg.requestId) {
|
|
742
749
|
var created = { type: "new_session_result", requestId: String(msg.requestId).substring(0, 200), ok: true, sessionId: newSess.localId };
|
|
@@ -758,8 +765,8 @@ function attachSessions(ctx) {
|
|
|
758
765
|
send({ type: "last_vendor", vendor: msg.vendor });
|
|
759
766
|
}
|
|
760
767
|
var nsPresKey = ws._clayUser ? ws._clayUser.id : "_default";
|
|
761
|
-
if (!ws._clayPane) userPresence.setPresence(slug, nsPresKey, userPresence.sessionIdForPersistence(newSess), null);
|
|
762
|
-
if (usersModule.isMultiUser() && !ws._clayPane) {
|
|
768
|
+
if (!inactiveCreation && !ws._clayPane) userPresence.setPresence(slug, nsPresKey, userPresence.sessionIdForPersistence(newSess), null);
|
|
769
|
+
if (!inactiveCreation && usersModule.isMultiUser() && !ws._clayPane) {
|
|
763
770
|
broadcastPresence();
|
|
764
771
|
}
|
|
765
772
|
return true;
|
|
@@ -52,9 +52,13 @@ function attachSharedBrowser(ctx) {
|
|
|
52
52
|
if (tabs.length > 1) throw new Error("Choose a browserId from status before operating a tab");
|
|
53
53
|
return tabs[0] || null;
|
|
54
54
|
}
|
|
55
|
+
function sendBrowserMessage(ws, session, message) {
|
|
56
|
+
var payload = require('./pane-session-transport').outbound(ws, message, session.localId);
|
|
57
|
+
if (payload) ctx.sendTo(ws, payload);
|
|
58
|
+
}
|
|
55
59
|
function sendState(ws, session, record, present, focus) {
|
|
56
60
|
if (!ws || ws.readyState !== 1 || !allowed(session, ws) || (record && !recordAllowed(record, ws))) return;
|
|
57
|
-
|
|
61
|
+
sendBrowserMessage(ws, session, { type: "shared_browser_state", sessionId: session.localId, browser: projection(record), browsers: sessionRecords(session).map(projection), present: !!present, focus: !!focus });
|
|
58
62
|
}
|
|
59
63
|
function broadcast(record, present) {
|
|
60
64
|
ctx.clients.forEach(function (ws) {
|
|
@@ -110,7 +114,7 @@ function attachSharedBrowser(ctx) {
|
|
|
110
114
|
record.frame = { type: "shared_browser_frame", sessionId: record.session.localId, browserId: record.id,
|
|
111
115
|
data: message.data, width: record.width, height: record.height };
|
|
112
116
|
viewers.forEach(function (id, ws) {
|
|
113
|
-
if (watching(record, ws) && ws.bufferedAmount < 1024 * 1024)
|
|
117
|
+
if (watching(record, ws) && ws.bufferedAmount < 1024 * 1024) sendBrowserMessage(ws, record.session, record.frame);
|
|
114
118
|
});
|
|
115
119
|
visibility(record);
|
|
116
120
|
} else broadcast(record);
|
|
@@ -287,7 +291,7 @@ function attachSharedBrowser(ctx) {
|
|
|
287
291
|
record = selectRecord(session, msg.browserId);
|
|
288
292
|
if (!record || !recordAllowed(record, ws) || msg.browserId !== record.id) throw new Error("Browser session expired");
|
|
289
293
|
if (msg.type === "shared_browser_view") {
|
|
290
|
-
if (msg.visible) { var previous = records.get(viewers.get(ws)); viewers.set(ws, record.id); if (previous && previous !== record) visibility(previous); if (record.frame)
|
|
294
|
+
if (msg.visible) { var previous = records.get(viewers.get(ws)); viewers.set(ws, record.id); if (previous && previous !== record) visibility(previous); if (record.frame) sendBrowserMessage(ws, record.session, record.frame); }
|
|
291
295
|
else viewers.delete(ws);
|
|
292
296
|
visibility(record);
|
|
293
297
|
} else if (msg.type === "shared_browser_control") {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
var layout = require("./split-view-layout");
|
|
2
|
+
var workspace = require("./project-view-workspace");
|
|
2
3
|
|
|
3
4
|
function validDocument(value) {
|
|
4
5
|
if (!value || value.version !== 1 || !value.projects || typeof value.projects !== "object" || Array.isArray(value.projects)) return false;
|
|
5
6
|
var projectSlugs = Object.keys(value.projects);
|
|
6
7
|
for (var i = 0; i < projectSlugs.length; i++) {
|
|
8
|
+
if (["__proto__", "constructor", "prototype"].indexOf(projectSlugs[i]) >= 0) return false;
|
|
7
9
|
var project = value.projects[projectSlugs[i]];
|
|
8
10
|
if (!project || !project.owners || typeof project.owners !== "object" || Array.isArray(project.owners)) return false;
|
|
9
11
|
var ownerKeys = Object.keys(project.owners);
|
|
@@ -11,7 +13,7 @@ function validDocument(value) {
|
|
|
11
13
|
var record = project.owners[ownerKeys[j]];
|
|
12
14
|
if (!record || !(record.ownerId === null || typeof record.ownerId === "string") ||
|
|
13
15
|
!(record.linuxUser === null || typeof record.linuxUser === "string") ||
|
|
14
|
-
!Number.
|
|
16
|
+
!Number.isSafeInteger(record.revision) || record.revision < 0 || !Array.isArray(record.views)) return false;
|
|
15
17
|
var ids = [], memberships = [];
|
|
16
18
|
var ownerIdForKey = ownerKeys[j] === "default" ? null : ownerKeys[j].slice(5);
|
|
17
19
|
if (ownerKeys[j] !== "default" && (ownerKeys[j].slice(0, 5) !== "user:" || !ownerIdForKey || ownerIdForKey !== record.ownerId)) return false;
|
|
@@ -25,9 +27,21 @@ function validDocument(value) {
|
|
|
25
27
|
memberships.push(checked.origins[m]);
|
|
26
28
|
}
|
|
27
29
|
}
|
|
30
|
+
if (record.activeViewId != null && ids.indexOf(record.activeViewId) < 0) return false;
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
return true;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
// Normalize copies only. Legacy records keep their original revision and bytes
|
|
37
|
+
// until an explicit mutation; no resource lookup or implicit migration write.
|
|
38
|
+
function normalizeDocument(value) {
|
|
39
|
+
if (!validDocument(value)) throw new TypeError('Invalid saved View document');
|
|
40
|
+
var result = JSON.parse(JSON.stringify(value));
|
|
41
|
+
Object.keys(result.projects).forEach(function (slug) {
|
|
42
|
+
var owners = result.projects[slug].owners;
|
|
43
|
+
Object.keys(owners).forEach(function (key) { owners[key].views = owners[key].views.map(workspace.normalized); });
|
|
44
|
+
});
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
module.exports = { validDocument: validDocument, normalizeDocument: normalizeDocument };
|