dot-studio 0.0.1 → 0.0.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/README.md +20 -200
- package/client/assets/ActFrame-BYOBkLYW.js +1 -0
- package/client/assets/ActFrame-C_WEt6bv.css +1 -0
- package/client/assets/ActInspectorPanel-C3VlS7tB.js +1 -0
- package/client/assets/ActInspectorPanel-CE6s6GYv.css +1 -0
- package/client/assets/AssistantChat-BOyW0K79.js +1 -0
- package/client/assets/AssistantChat-DoVmHvMJ.css +1 -0
- package/client/assets/CanvasTerminalFrame-BC-79q9U.css +1 -0
- package/client/assets/CanvasTerminalFrame-DxKbexK6.js +4 -0
- package/client/assets/CanvasTrackingFrame-DumxhNwg.js +1 -0
- package/client/assets/CanvasTrackingFrame-G4rRrfne.css +1 -0
- package/client/assets/CanvasWindowFrame-ziJeVfHG.js +1 -0
- package/client/assets/DanceBundleEditorFrame-CH8VDUMK.js +1 -0
- package/client/assets/DanceBundleEditorFrame-DaLqMflT.css +1 -0
- package/client/assets/MarkdownEditorFrame-DVecIZpZ.css +1 -0
- package/client/assets/MarkdownEditorFrame-Dwpgs2GX.js +2 -0
- package/client/assets/MarkdownRenderer-Cz8A4AgP.js +1 -0
- package/client/assets/PublishModal-DUlHz0fT.js +1 -0
- package/client/assets/TodoDock-DcVf7zQG.js +1 -0
- package/client/assets/WorkspaceToolbar-CXYi_sMD.js +2 -0
- package/client/assets/WorkspaceToolbar-CiQvVocC.css +1 -0
- package/client/assets/chat-message-visibility-YwJ-AQno.js +11 -0
- package/client/assets/dnd-vendor-CIAZE2P2.js +5 -0
- package/client/assets/flow-vendor-BZV40eAE.css +1 -0
- package/client/assets/flow-vendor-C868rU-6.js +23 -0
- package/client/assets/icon-vendor-I2JVIi1s.js +501 -0
- package/client/assets/index-BMY4hrBP.js +3 -0
- package/client/assets/index-C-vnj9y3.js +1 -0
- package/client/assets/index-C9HTqfZw.css +1 -0
- package/client/assets/index-CWrv6O3o.js +64 -0
- package/client/assets/index-DMS12-Q2.js +8 -0
- package/client/assets/index-Dn7t_Y7G.js +1 -0
- package/client/assets/index-p-wk7iGH.css +1 -0
- package/client/assets/markdown-vendor-BSTcku12.css +10 -0
- package/client/assets/markdown-vendor-DnTJ9hmR.js +35 -0
- package/client/assets/participant-labels-Cf3qP3GB.js +1 -0
- package/client/assets/queries-Dm1jEHfc.js +1 -0
- package/client/assets/query-vendor-_taqgrbn.js +1 -0
- package/client/assets/react-vendor-DzpMUNDT.js +49 -0
- package/client/assets/settings-utils-l7KCS3Ev.js +1 -0
- package/client/assets/terminal-vendor-6GBZ9nXN.css +32 -0
- package/client/assets/terminal-vendor-D0xRnmbI.js +112 -0
- package/client/index.html +13 -3
- package/dist/cli.js +25 -3
- package/dist/server/app.js +72 -0
- package/dist/server/index.js +2 -62
- package/dist/server/lib/act-session-policy.js +31 -0
- package/dist/server/lib/chat-session.js +101 -0
- package/dist/server/lib/config.js +18 -4
- package/dist/server/lib/dot-authoring.js +171 -102
- package/dist/server/lib/dot-loader.js +9 -8
- package/dist/server/lib/dot-login.js +8 -190
- package/dist/server/lib/dot-source.js +11 -0
- package/dist/server/lib/model-catalog.js +74 -15
- package/dist/server/lib/opencode-auth.js +4 -1
- package/dist/server/lib/opencode-errors.js +70 -38
- package/dist/server/lib/opencode-sidecar.js +5 -2
- package/dist/server/lib/project-config.js +8 -0
- package/dist/server/lib/runtime-tools.js +46 -8
- package/dist/server/lib/safe-mode.js +410 -0
- package/dist/server/lib/session-execution.js +81 -0
- package/dist/server/lib/sse.js +22 -0
- package/dist/server/routes/act-runtime-threads.js +156 -0
- package/dist/server/routes/act-runtime-tools.js +157 -0
- package/dist/server/routes/act-runtime.js +7 -0
- package/dist/server/routes/adapter.js +32 -0
- package/dist/server/routes/assets-collection.js +16 -0
- package/dist/server/routes/assets-detail.js +38 -0
- package/dist/server/routes/assets.js +4 -158
- package/dist/server/routes/chat-messages.js +104 -0
- package/dist/server/routes/chat-sessions.js +104 -0
- package/dist/server/routes/chat-stream.js +15 -0
- package/dist/server/routes/chat.js +6 -353
- package/dist/server/routes/compile.js +5 -91
- package/dist/server/routes/dot-assets.js +77 -0
- package/dist/server/routes/dot-core.js +62 -0
- package/dist/server/routes/dot-performer.js +80 -0
- package/dist/server/routes/dot.js +6 -267
- package/dist/server/routes/drafts-collection.js +40 -0
- package/dist/server/routes/drafts-dance-bundle.js +113 -0
- package/dist/server/routes/drafts-item.js +86 -0
- package/dist/server/routes/drafts.js +9 -0
- package/dist/server/routes/health.js +18 -33
- package/dist/server/routes/opencode-core.js +120 -0
- package/dist/server/routes/opencode-file.js +67 -0
- package/dist/server/routes/opencode-mcp.js +74 -0
- package/dist/server/routes/opencode-provider.js +41 -0
- package/dist/server/routes/opencode.js +8 -418
- package/dist/server/routes/route-errors.js +10 -0
- package/dist/server/routes/safe-actions.js +60 -0
- package/dist/server/routes/safe-summary.js +20 -0
- package/dist/server/routes/safe.js +7 -0
- package/dist/server/routes/workspaces.js +47 -0
- package/dist/server/services/act-runtime/act-context-builder.js +81 -0
- package/dist/server/services/act-runtime/act-runtime-service.js +313 -0
- package/dist/server/services/act-runtime/act-runtime-utils.js +10 -0
- package/dist/server/services/act-runtime/act-tool-projection.js +26 -0
- package/dist/server/services/act-runtime/act-tools.js +151 -0
- package/dist/server/services/act-runtime/board-persistence.js +38 -0
- package/dist/server/services/act-runtime/event-logger.js +73 -0
- package/dist/server/services/act-runtime/event-router.js +102 -0
- package/dist/server/services/act-runtime/mailbox.js +149 -0
- package/dist/server/services/act-runtime/safety-guard.js +162 -0
- package/dist/server/services/act-runtime/session-queue.js +114 -0
- package/dist/server/services/act-runtime/thread-manager.js +351 -0
- package/dist/server/services/act-runtime/wake-cascade.js +306 -0
- package/dist/server/services/act-runtime/wake-evaluator.js +43 -0
- package/dist/server/services/act-runtime/wake-performer-resolver.js +68 -0
- package/dist/server/services/act-runtime/wake-prompt-builder.js +77 -0
- package/dist/server/services/adapter-view-service.js +6 -0
- package/dist/server/services/asset-service.js +366 -0
- package/dist/server/services/chat-event-stream-service.js +157 -0
- package/dist/server/services/chat-service.js +207 -0
- package/dist/server/services/chat-session-service.js +203 -0
- package/dist/server/services/compile-service.js +4 -0
- package/dist/server/services/dance-bundle-service.js +222 -0
- package/dist/server/services/dot-add-service.js +59 -0
- package/dist/server/services/dot-service.js +178 -0
- package/dist/server/services/draft-service.js +367 -0
- package/dist/server/services/opencode-projection/dance-compiler.js +164 -0
- package/dist/server/services/opencode-projection/performer-compiler.js +195 -0
- package/dist/server/services/opencode-projection/preview-service.js +31 -0
- package/dist/server/services/opencode-projection/projection-manifest.js +98 -0
- package/dist/server/services/opencode-projection/stage-projection-service.js +188 -0
- package/dist/server/services/opencode-service.js +338 -0
- package/dist/server/services/safe-service.js +33 -0
- package/dist/server/services/studio-assistant/assistant-service.js +172 -0
- package/dist/server/services/studio-service.js +69 -0
- package/dist/server/services/workspace-service.js +224 -0
- package/dist/server/terminal.js +57 -11
- package/dist/shared/act-types.js +4 -0
- package/dist/shared/adapter-view.js +1 -0
- package/dist/shared/asset-contracts.js +1 -0
- package/dist/shared/assistant-actions.js +1 -0
- package/dist/shared/chat-contracts.js +1 -0
- package/dist/shared/dot-contracts.js +1 -0
- package/dist/shared/dot-types.js +4 -0
- package/dist/shared/draft-contracts.js +2 -0
- package/dist/shared/model-types.js +2 -0
- package/dist/shared/performer-mcp-portability.js +10 -0
- package/dist/shared/safe-mode.js +1 -0
- package/dist/shared/session-metadata.js +4 -3
- package/package.json +6 -4
- package/client/assets/index-C2eIILoa.css +0 -41
- package/client/assets/index-DUPZ_Lw5.js +0 -616
- package/dist/server/lib/act-runtime.js +0 -1282
- package/dist/server/lib/prompt.js +0 -222
- package/dist/server/routes/stages.js +0 -137
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { getOpencode } from '../lib/opencode.js';
|
|
5
|
+
import { unwrapOpencodeResult } from '../lib/opencode-errors.js';
|
|
6
|
+
import { deleteSafeOwnerWorkspace } from '../lib/safe-mode.js';
|
|
7
|
+
import { listSessionExecutionContextsForWorkingDir, unregisterSessionExecutionContext, } from '../lib/session-execution.js';
|
|
8
|
+
import { workspacesDir, workspaceDir } from '../lib/config.js';
|
|
9
|
+
function isAllowedWorkspaceCharacter(char) {
|
|
10
|
+
const code = char.charCodeAt(0);
|
|
11
|
+
return !('/\\:*?"<>|'.includes(char) || code < 32);
|
|
12
|
+
}
|
|
13
|
+
function sanitizeWorkspaceId(id) {
|
|
14
|
+
return id
|
|
15
|
+
.replace(/\.\./g, '')
|
|
16
|
+
.split('')
|
|
17
|
+
.filter((char) => isAllowedWorkspaceCharacter(char))
|
|
18
|
+
.join('')
|
|
19
|
+
.trim();
|
|
20
|
+
}
|
|
21
|
+
function validateWorkspaceId(id) {
|
|
22
|
+
const clean = sanitizeWorkspaceId(id);
|
|
23
|
+
if (!clean || clean.length === 0)
|
|
24
|
+
return null;
|
|
25
|
+
if (clean.length > 128)
|
|
26
|
+
return null;
|
|
27
|
+
return clean;
|
|
28
|
+
}
|
|
29
|
+
function normalizeWorkingDir(input) {
|
|
30
|
+
const trimmed = input.trim().replace(/\/+$/, '');
|
|
31
|
+
if (!trimmed)
|
|
32
|
+
return null;
|
|
33
|
+
return path.resolve(trimmed);
|
|
34
|
+
}
|
|
35
|
+
function workspaceIdForWorkingDir(workingDir) {
|
|
36
|
+
return createHash('sha1').update(workingDir).digest('hex').slice(0, 16);
|
|
37
|
+
}
|
|
38
|
+
function workspacePathForId(id) {
|
|
39
|
+
return path.join(workspaceDir(id), 'workspace.json');
|
|
40
|
+
}
|
|
41
|
+
async function purgeLinkedOpencodeData(workspace) {
|
|
42
|
+
const workingDir = normalizeWorkingDir(workspace?.workingDir || '');
|
|
43
|
+
if (!workingDir) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const executionContexts = await listSessionExecutionContextsForWorkingDir(workingDir);
|
|
47
|
+
const directories = Array.from(new Set([
|
|
48
|
+
workingDir,
|
|
49
|
+
...executionContexts.map((context) => context.executionDir),
|
|
50
|
+
]));
|
|
51
|
+
const sessionDirectories = new Map(executionContexts.map((context) => [context.sessionId, context.executionDir]));
|
|
52
|
+
try {
|
|
53
|
+
const oc = await getOpencode();
|
|
54
|
+
for (const directory of directories) {
|
|
55
|
+
try {
|
|
56
|
+
const sessions = unwrapOpencodeResult(await oc.session.list({ directory })) || [];
|
|
57
|
+
for (const session of sessions) {
|
|
58
|
+
if (session?.id && !sessionDirectories.has(session.id)) {
|
|
59
|
+
sessionDirectories.set(session.id, directory);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.warn('[workspace-service] Failed to list OpenCode sessions for workspace delete', { workingDir, directory, error });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const [sessionId, directory] of Array.from(sessionDirectories.entries())) {
|
|
68
|
+
try {
|
|
69
|
+
unwrapOpencodeResult(await oc.session.delete({
|
|
70
|
+
sessionID: sessionId,
|
|
71
|
+
directory,
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.warn('[workspace-service] Failed to delete OpenCode session for workspace delete', { sessionId, directory, error });
|
|
76
|
+
}
|
|
77
|
+
await unregisterSessionExecutionContext(sessionId).catch(() => { });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.warn('[workspace-service] Failed to purge OpenCode data for workspace delete', { workingDir, error });
|
|
82
|
+
}
|
|
83
|
+
const ownerTargets = new Map();
|
|
84
|
+
for (const context of executionContexts) {
|
|
85
|
+
ownerTargets.set(`${context.ownerKind}:${context.ownerId}`, {
|
|
86
|
+
ownerKind: context.ownerKind,
|
|
87
|
+
ownerId: context.ownerId,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
for (const performer of Array.isArray(workspace?.performers) ? workspace.performers : []) {
|
|
91
|
+
if (typeof performer?.id === 'string' && performer.id) {
|
|
92
|
+
ownerTargets.set(`performer:${performer.id}`, { ownerKind: 'performer', ownerId: performer.id });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const act of Array.isArray(workspace?.acts) ? workspace.acts : []) {
|
|
96
|
+
if (typeof act?.id === 'string' && act.id) {
|
|
97
|
+
ownerTargets.set(`act:${act.id}`, { ownerKind: 'act', ownerId: act.id });
|
|
98
|
+
await fs.rm(path.join(workspaceDir(workspaceIdForWorkingDir(workingDir)), 'act-runtime', act.id), { recursive: true, force: true }).catch(() => { });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const { ownerKind, ownerId } of Array.from(ownerTargets.values())) {
|
|
102
|
+
await deleteSafeOwnerWorkspace(workingDir, ownerKind, ownerId).catch(() => { });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export async function listSavedWorkspaces(includeHidden = false) {
|
|
106
|
+
const dir = workspacesDir();
|
|
107
|
+
await fs.mkdir(dir, { recursive: true });
|
|
108
|
+
const entries = [];
|
|
109
|
+
let items;
|
|
110
|
+
try {
|
|
111
|
+
items = await fs.readdir(dir);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
await Promise.all(items.map(async (item) => {
|
|
117
|
+
const itemDir = path.join(dir, item);
|
|
118
|
+
try {
|
|
119
|
+
const itemStat = await fs.stat(itemDir);
|
|
120
|
+
if (!itemStat.isDirectory())
|
|
121
|
+
return;
|
|
122
|
+
const filePath = path.join(itemDir, 'workspace.json');
|
|
123
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
124
|
+
const parsed = JSON.parse(raw);
|
|
125
|
+
const wd = normalizeWorkingDir(parsed.workingDir || '') || '';
|
|
126
|
+
if (!wd)
|
|
127
|
+
return;
|
|
128
|
+
if (!includeHidden && parsed.hiddenFromList === true)
|
|
129
|
+
return;
|
|
130
|
+
const stat = await fs.stat(filePath);
|
|
131
|
+
entries.push({
|
|
132
|
+
id: item,
|
|
133
|
+
workingDir: wd,
|
|
134
|
+
updatedAt: stat.mtimeMs,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// skip invalid entries
|
|
139
|
+
}
|
|
140
|
+
}));
|
|
141
|
+
return entries.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
142
|
+
}
|
|
143
|
+
export async function getSavedWorkspace(rawId) {
|
|
144
|
+
const id = validateWorkspaceId(rawId);
|
|
145
|
+
if (!id) {
|
|
146
|
+
return { ok: false, status: 400, error: 'Invalid workspace id' };
|
|
147
|
+
}
|
|
148
|
+
const filePath = workspacePathForId(id);
|
|
149
|
+
if (!filePath.startsWith(workspacesDir())) {
|
|
150
|
+
return { ok: false, status: 400, error: 'Invalid workspace id' };
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
154
|
+
return { ok: true, workspace: JSON.parse(raw) };
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return { ok: false, status: 404, error: 'Workspace not found' };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export async function saveWorkspaceSnapshot(body) {
|
|
161
|
+
const workingDir = normalizeWorkingDir(body.workingDir || '');
|
|
162
|
+
if (!workingDir) {
|
|
163
|
+
return { ok: false, status: 400, error: 'workingDir is required' };
|
|
164
|
+
}
|
|
165
|
+
const id = workspaceIdForWorkingDir(workingDir);
|
|
166
|
+
const workspace = {
|
|
167
|
+
...body,
|
|
168
|
+
workingDir,
|
|
169
|
+
hiddenFromList: body.hiddenFromList === true,
|
|
170
|
+
};
|
|
171
|
+
const wsDir = workspaceDir(id);
|
|
172
|
+
await fs.mkdir(wsDir, { recursive: true });
|
|
173
|
+
const filePath = workspacePathForId(id);
|
|
174
|
+
if (!filePath.startsWith(workspacesDir())) {
|
|
175
|
+
return { ok: false, status: 400, error: 'Invalid workspace id' };
|
|
176
|
+
}
|
|
177
|
+
await fs.writeFile(filePath, JSON.stringify(workspace, null, 2), 'utf-8');
|
|
178
|
+
const stat = await fs.stat(filePath);
|
|
179
|
+
import('./studio-assistant/assistant-service.js').then(({ ensureAssistantAgent }) => ensureAssistantAgent(workingDir).catch(() => { }));
|
|
180
|
+
return {
|
|
181
|
+
ok: true,
|
|
182
|
+
id,
|
|
183
|
+
workingDir,
|
|
184
|
+
updatedAt: stat.mtimeMs,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
export async function setSavedWorkspaceHidden(rawId, hiddenFromList) {
|
|
188
|
+
const id = validateWorkspaceId(rawId);
|
|
189
|
+
if (!id) {
|
|
190
|
+
return { ok: false, status: 400, error: 'Invalid workspace id' };
|
|
191
|
+
}
|
|
192
|
+
const filePath = workspacePathForId(id);
|
|
193
|
+
if (!filePath.startsWith(workspacesDir())) {
|
|
194
|
+
return { ok: false, status: 400, error: 'Invalid workspace id' };
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
198
|
+
const workspace = JSON.parse(raw);
|
|
199
|
+
workspace.hiddenFromList = hiddenFromList === true;
|
|
200
|
+
await fs.writeFile(filePath, JSON.stringify(workspace, null, 2), 'utf-8');
|
|
201
|
+
return { ok: true, id, hiddenFromList: workspace.hiddenFromList };
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return { ok: false, status: 404, error: 'Workspace not found' };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export async function deleteSavedWorkspace(rawId) {
|
|
208
|
+
const id = validateWorkspaceId(rawId);
|
|
209
|
+
if (!id) {
|
|
210
|
+
return { ok: false, status: 400, error: 'Invalid workspace id' };
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const filePath = workspacePathForId(id);
|
|
214
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
215
|
+
const workspace = JSON.parse(raw);
|
|
216
|
+
await purgeLinkedOpencodeData(workspace);
|
|
217
|
+
// Delete entire workspace directory (includes act-runtime data)
|
|
218
|
+
await fs.rm(workspaceDir(id), { recursive: true, force: true });
|
|
219
|
+
return { ok: true };
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return { ok: false, status: 404, error: 'Workspace not found' };
|
|
223
|
+
}
|
|
224
|
+
}
|
package/dist/server/terminal.js
CHANGED
|
@@ -4,6 +4,56 @@ import { OPENCODE_URL } from './lib/config.js';
|
|
|
4
4
|
const MAX_BUFFER = 500;
|
|
5
5
|
const sessions = new Map();
|
|
6
6
|
let sessionCounter = 0;
|
|
7
|
+
function errorMessage(error, fallback = 'Unknown error') {
|
|
8
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
9
|
+
}
|
|
10
|
+
function stripTerminalNoise(input) {
|
|
11
|
+
let result = '';
|
|
12
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
13
|
+
const char = input[index];
|
|
14
|
+
const code = char.charCodeAt(0);
|
|
15
|
+
if (code === 27) {
|
|
16
|
+
const next = input[index + 1];
|
|
17
|
+
if (next === ']') {
|
|
18
|
+
index += 2;
|
|
19
|
+
while (index < input.length) {
|
|
20
|
+
const oscChar = input[index];
|
|
21
|
+
const oscCode = oscChar.charCodeAt(0);
|
|
22
|
+
if (oscCode === 7) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
if (oscCode === 27 && input[index + 1] === '\\') {
|
|
26
|
+
index += 1;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
index += 1;
|
|
30
|
+
}
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (next === '[') {
|
|
34
|
+
index += 2;
|
|
35
|
+
while (index < input.length) {
|
|
36
|
+
const csiCode = input[index].charCodeAt(0);
|
|
37
|
+
const isLetter = (csiCode >= 65 && csiCode <= 90) || (csiCode >= 97 && csiCode <= 122);
|
|
38
|
+
if (isLetter) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
index += 1;
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (next) {
|
|
46
|
+
index += 1;
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (char === '\r' || char === '\n') {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
result += char;
|
|
54
|
+
}
|
|
55
|
+
return result.trim();
|
|
56
|
+
}
|
|
7
57
|
async function opencodePtyRequest(path, init, directory) {
|
|
8
58
|
const url = new URL(path, OPENCODE_URL);
|
|
9
59
|
if (directory) {
|
|
@@ -87,9 +137,10 @@ export function setupTerminalWs(server, defaultCwd) {
|
|
|
87
137
|
setupWsHandlers(ws, session);
|
|
88
138
|
broadcastSessionList(session.cwd);
|
|
89
139
|
}
|
|
90
|
-
catch (
|
|
91
|
-
|
|
92
|
-
|
|
140
|
+
catch (error) {
|
|
141
|
+
const message = errorMessage(error);
|
|
142
|
+
console.error('Failed to create PTY via OpenCode:', message);
|
|
143
|
+
ws.send(JSON.stringify({ type: 'error', message: `Failed: ${message}` }));
|
|
93
144
|
ws.close();
|
|
94
145
|
}
|
|
95
146
|
}
|
|
@@ -109,12 +160,7 @@ export function setupTerminalWs(server, defaultCwd) {
|
|
|
109
160
|
// The data may arrive in multiple small chunks with ANSI sequences mixed in.
|
|
110
161
|
if (!session.initialized) {
|
|
111
162
|
// Strip ALL ANSI escape sequences (CSI, OSC, etc.)
|
|
112
|
-
const stripped = data
|
|
113
|
-
.replace(/\x1b\][^\x07]*(\x07|\x1b\\)/g, '') // OSC sequences
|
|
114
|
-
.replace(/\x1b\[[^a-zA-Z]*[a-zA-Z]/g, '') // CSI sequences
|
|
115
|
-
.replace(/\x1b[^[\]].?/g, '') // Other escapes
|
|
116
|
-
.replace(/[\r\n]/g, '')
|
|
117
|
-
.trim();
|
|
163
|
+
const stripped = stripTerminalNoise(data);
|
|
118
164
|
// Skip if it's cursor control noise, percent signs, or empty after stripping
|
|
119
165
|
if (!stripped || /\{"cursor":\d+\}/.test(stripped) || /^%+$/.test(stripped)) {
|
|
120
166
|
return;
|
|
@@ -208,8 +254,8 @@ export function setupTerminalWs(server, defaultCwd) {
|
|
|
208
254
|
}),
|
|
209
255
|
}, session.cwd);
|
|
210
256
|
}
|
|
211
|
-
catch (
|
|
212
|
-
console.error('PTY resize failed:',
|
|
257
|
+
catch (error) {
|
|
258
|
+
console.error('PTY resize failed:', errorMessage(error));
|
|
213
259
|
}
|
|
214
260
|
break;
|
|
215
261
|
case 'create':
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { extractMcpServerNamesFromConfig } from './mcp-config.js';
|
|
2
|
+
export function resolvePerformerMcpPortability(mcpConfig, projectMcpServerNames) {
|
|
3
|
+
const declaredMcpServerNames = extractMcpServerNamesFromConfig(mcpConfig);
|
|
4
|
+
const available = new Set(projectMcpServerNames.filter(Boolean));
|
|
5
|
+
return {
|
|
6
|
+
declaredMcpServerNames,
|
|
7
|
+
projectMcpMatches: declaredMcpServerNames.filter((name) => available.has(name)),
|
|
8
|
+
projectMcpMissing: declaredMcpServerNames.filter((name) => !available.has(name)),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const SESSION_TITLE_PREFIX = 'DOT Studio:';
|
|
2
|
-
const SESSION_METADATA_PATTERN = /^DOT Studio:\s*(.*?)\s*\[studio:([^:\]]+):(
|
|
3
|
-
export function buildStudioSessionTitle(performerId, performerName, configHash) {
|
|
4
|
-
|
|
2
|
+
const SESSION_METADATA_PATTERN = /^DOT Studio:\s*(.*?)\s*\[studio:([^:\]]+):(.*)\]\s*$/;
|
|
3
|
+
export function buildStudioSessionTitle(performerId, performerName, configHash, executionMode) {
|
|
4
|
+
const safeLabel = executionMode === 'safe' ? '[SAFE] ' : '';
|
|
5
|
+
return `${SESSION_TITLE_PREFIX} ${safeLabel}${performerName} [studio:${performerId}:${configHash}]`;
|
|
5
6
|
}
|
|
6
7
|
export function parseStudioSessionTitle(title) {
|
|
7
8
|
if (!title || !title.startsWith(SESSION_TITLE_PREFIX)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dot-studio",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "DOT Studio visual workspace for composing and running Dance of Tal performers and acts on top of OpenCode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "monarchjuno",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"start": "tsx server/start.ts",
|
|
32
32
|
"dev": "vite",
|
|
33
33
|
"kill-ports": "lsof -ti:5173,3001,4096 | xargs kill -9 2>/dev/null || true",
|
|
34
|
-
"dev:all": "npm run kill-ports && concurrently -k -n
|
|
34
|
+
"dev:all": "npm run kill-ports && concurrently -k -n opencode,server,vite -c magenta,yellow,cyan \"opencode --port 4096 .\" \"tsx --watch server/index.ts\" \"vite\"",
|
|
35
35
|
"server": "tsx server/index.ts",
|
|
36
36
|
"server:dev": "tsx --watch server/index.ts",
|
|
37
37
|
"clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true }); require('fs').rmSync('client', { recursive: true, force: true })\"",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"build:all": "npm run clean && npm run build:client && npm run build:server",
|
|
42
42
|
"pack:check": "npm run build:all && npm pack --dry-run",
|
|
43
43
|
"type-check": "tsc -b --noEmit && tsc -p tsconfig.server.json --noEmit",
|
|
44
|
+
"test": "vitest run",
|
|
44
45
|
"lint": "eslint .",
|
|
45
46
|
"preview": "vite preview",
|
|
46
47
|
"opencode": "opencode --port 4096 .",
|
|
@@ -66,7 +67,7 @@
|
|
|
66
67
|
"@xterm/addon-webgl": "^0.19.0",
|
|
67
68
|
"@xterm/xterm": "^6.0.0",
|
|
68
69
|
"@xyflow/react": "^12.10.1",
|
|
69
|
-
"dance-of-tal": "
|
|
70
|
+
"dance-of-tal": "4.0.1",
|
|
70
71
|
"elkjs": "^0.11.1",
|
|
71
72
|
"highlight.js": "^11.11.1",
|
|
72
73
|
"hono": "^4.12.5",
|
|
@@ -98,6 +99,7 @@
|
|
|
98
99
|
"globals": "^16.5.0",
|
|
99
100
|
"typescript": "~5.9.3",
|
|
100
101
|
"typescript-eslint": "^8.48.0",
|
|
101
|
-
"vite": "^6.4.1"
|
|
102
|
+
"vite": "^6.4.1",
|
|
103
|
+
"vitest": "^4.1.0"
|
|
102
104
|
}
|
|
103
105
|
}
|