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.
Files changed (148) hide show
  1. package/README.md +20 -200
  2. package/client/assets/ActFrame-BYOBkLYW.js +1 -0
  3. package/client/assets/ActFrame-C_WEt6bv.css +1 -0
  4. package/client/assets/ActInspectorPanel-C3VlS7tB.js +1 -0
  5. package/client/assets/ActInspectorPanel-CE6s6GYv.css +1 -0
  6. package/client/assets/AssistantChat-BOyW0K79.js +1 -0
  7. package/client/assets/AssistantChat-DoVmHvMJ.css +1 -0
  8. package/client/assets/CanvasTerminalFrame-BC-79q9U.css +1 -0
  9. package/client/assets/CanvasTerminalFrame-DxKbexK6.js +4 -0
  10. package/client/assets/CanvasTrackingFrame-DumxhNwg.js +1 -0
  11. package/client/assets/CanvasTrackingFrame-G4rRrfne.css +1 -0
  12. package/client/assets/CanvasWindowFrame-ziJeVfHG.js +1 -0
  13. package/client/assets/DanceBundleEditorFrame-CH8VDUMK.js +1 -0
  14. package/client/assets/DanceBundleEditorFrame-DaLqMflT.css +1 -0
  15. package/client/assets/MarkdownEditorFrame-DVecIZpZ.css +1 -0
  16. package/client/assets/MarkdownEditorFrame-Dwpgs2GX.js +2 -0
  17. package/client/assets/MarkdownRenderer-Cz8A4AgP.js +1 -0
  18. package/client/assets/PublishModal-DUlHz0fT.js +1 -0
  19. package/client/assets/TodoDock-DcVf7zQG.js +1 -0
  20. package/client/assets/WorkspaceToolbar-CXYi_sMD.js +2 -0
  21. package/client/assets/WorkspaceToolbar-CiQvVocC.css +1 -0
  22. package/client/assets/chat-message-visibility-YwJ-AQno.js +11 -0
  23. package/client/assets/dnd-vendor-CIAZE2P2.js +5 -0
  24. package/client/assets/flow-vendor-BZV40eAE.css +1 -0
  25. package/client/assets/flow-vendor-C868rU-6.js +23 -0
  26. package/client/assets/icon-vendor-I2JVIi1s.js +501 -0
  27. package/client/assets/index-BMY4hrBP.js +3 -0
  28. package/client/assets/index-C-vnj9y3.js +1 -0
  29. package/client/assets/index-C9HTqfZw.css +1 -0
  30. package/client/assets/index-CWrv6O3o.js +64 -0
  31. package/client/assets/index-DMS12-Q2.js +8 -0
  32. package/client/assets/index-Dn7t_Y7G.js +1 -0
  33. package/client/assets/index-p-wk7iGH.css +1 -0
  34. package/client/assets/markdown-vendor-BSTcku12.css +10 -0
  35. package/client/assets/markdown-vendor-DnTJ9hmR.js +35 -0
  36. package/client/assets/participant-labels-Cf3qP3GB.js +1 -0
  37. package/client/assets/queries-Dm1jEHfc.js +1 -0
  38. package/client/assets/query-vendor-_taqgrbn.js +1 -0
  39. package/client/assets/react-vendor-DzpMUNDT.js +49 -0
  40. package/client/assets/settings-utils-l7KCS3Ev.js +1 -0
  41. package/client/assets/terminal-vendor-6GBZ9nXN.css +32 -0
  42. package/client/assets/terminal-vendor-D0xRnmbI.js +112 -0
  43. package/client/index.html +13 -3
  44. package/dist/cli.js +25 -3
  45. package/dist/server/app.js +72 -0
  46. package/dist/server/index.js +2 -62
  47. package/dist/server/lib/act-session-policy.js +31 -0
  48. package/dist/server/lib/chat-session.js +101 -0
  49. package/dist/server/lib/config.js +18 -4
  50. package/dist/server/lib/dot-authoring.js +171 -102
  51. package/dist/server/lib/dot-loader.js +9 -8
  52. package/dist/server/lib/dot-login.js +8 -190
  53. package/dist/server/lib/dot-source.js +11 -0
  54. package/dist/server/lib/model-catalog.js +74 -15
  55. package/dist/server/lib/opencode-auth.js +4 -1
  56. package/dist/server/lib/opencode-errors.js +70 -38
  57. package/dist/server/lib/opencode-sidecar.js +5 -2
  58. package/dist/server/lib/project-config.js +8 -0
  59. package/dist/server/lib/runtime-tools.js +46 -8
  60. package/dist/server/lib/safe-mode.js +410 -0
  61. package/dist/server/lib/session-execution.js +81 -0
  62. package/dist/server/lib/sse.js +22 -0
  63. package/dist/server/routes/act-runtime-threads.js +156 -0
  64. package/dist/server/routes/act-runtime-tools.js +157 -0
  65. package/dist/server/routes/act-runtime.js +7 -0
  66. package/dist/server/routes/adapter.js +32 -0
  67. package/dist/server/routes/assets-collection.js +16 -0
  68. package/dist/server/routes/assets-detail.js +38 -0
  69. package/dist/server/routes/assets.js +4 -158
  70. package/dist/server/routes/chat-messages.js +104 -0
  71. package/dist/server/routes/chat-sessions.js +104 -0
  72. package/dist/server/routes/chat-stream.js +15 -0
  73. package/dist/server/routes/chat.js +6 -353
  74. package/dist/server/routes/compile.js +5 -91
  75. package/dist/server/routes/dot-assets.js +77 -0
  76. package/dist/server/routes/dot-core.js +62 -0
  77. package/dist/server/routes/dot-performer.js +80 -0
  78. package/dist/server/routes/dot.js +6 -267
  79. package/dist/server/routes/drafts-collection.js +40 -0
  80. package/dist/server/routes/drafts-dance-bundle.js +113 -0
  81. package/dist/server/routes/drafts-item.js +86 -0
  82. package/dist/server/routes/drafts.js +9 -0
  83. package/dist/server/routes/health.js +18 -33
  84. package/dist/server/routes/opencode-core.js +120 -0
  85. package/dist/server/routes/opencode-file.js +67 -0
  86. package/dist/server/routes/opencode-mcp.js +74 -0
  87. package/dist/server/routes/opencode-provider.js +41 -0
  88. package/dist/server/routes/opencode.js +8 -418
  89. package/dist/server/routes/route-errors.js +10 -0
  90. package/dist/server/routes/safe-actions.js +60 -0
  91. package/dist/server/routes/safe-summary.js +20 -0
  92. package/dist/server/routes/safe.js +7 -0
  93. package/dist/server/routes/workspaces.js +47 -0
  94. package/dist/server/services/act-runtime/act-context-builder.js +81 -0
  95. package/dist/server/services/act-runtime/act-runtime-service.js +313 -0
  96. package/dist/server/services/act-runtime/act-runtime-utils.js +10 -0
  97. package/dist/server/services/act-runtime/act-tool-projection.js +26 -0
  98. package/dist/server/services/act-runtime/act-tools.js +151 -0
  99. package/dist/server/services/act-runtime/board-persistence.js +38 -0
  100. package/dist/server/services/act-runtime/event-logger.js +73 -0
  101. package/dist/server/services/act-runtime/event-router.js +102 -0
  102. package/dist/server/services/act-runtime/mailbox.js +149 -0
  103. package/dist/server/services/act-runtime/safety-guard.js +162 -0
  104. package/dist/server/services/act-runtime/session-queue.js +114 -0
  105. package/dist/server/services/act-runtime/thread-manager.js +351 -0
  106. package/dist/server/services/act-runtime/wake-cascade.js +306 -0
  107. package/dist/server/services/act-runtime/wake-evaluator.js +43 -0
  108. package/dist/server/services/act-runtime/wake-performer-resolver.js +68 -0
  109. package/dist/server/services/act-runtime/wake-prompt-builder.js +77 -0
  110. package/dist/server/services/adapter-view-service.js +6 -0
  111. package/dist/server/services/asset-service.js +366 -0
  112. package/dist/server/services/chat-event-stream-service.js +157 -0
  113. package/dist/server/services/chat-service.js +207 -0
  114. package/dist/server/services/chat-session-service.js +203 -0
  115. package/dist/server/services/compile-service.js +4 -0
  116. package/dist/server/services/dance-bundle-service.js +222 -0
  117. package/dist/server/services/dot-add-service.js +59 -0
  118. package/dist/server/services/dot-service.js +178 -0
  119. package/dist/server/services/draft-service.js +367 -0
  120. package/dist/server/services/opencode-projection/dance-compiler.js +164 -0
  121. package/dist/server/services/opencode-projection/performer-compiler.js +195 -0
  122. package/dist/server/services/opencode-projection/preview-service.js +31 -0
  123. package/dist/server/services/opencode-projection/projection-manifest.js +98 -0
  124. package/dist/server/services/opencode-projection/stage-projection-service.js +188 -0
  125. package/dist/server/services/opencode-service.js +338 -0
  126. package/dist/server/services/safe-service.js +33 -0
  127. package/dist/server/services/studio-assistant/assistant-service.js +172 -0
  128. package/dist/server/services/studio-service.js +69 -0
  129. package/dist/server/services/workspace-service.js +224 -0
  130. package/dist/server/terminal.js +57 -11
  131. package/dist/shared/act-types.js +4 -0
  132. package/dist/shared/adapter-view.js +1 -0
  133. package/dist/shared/asset-contracts.js +1 -0
  134. package/dist/shared/assistant-actions.js +1 -0
  135. package/dist/shared/chat-contracts.js +1 -0
  136. package/dist/shared/dot-contracts.js +1 -0
  137. package/dist/shared/dot-types.js +4 -0
  138. package/dist/shared/draft-contracts.js +2 -0
  139. package/dist/shared/model-types.js +2 -0
  140. package/dist/shared/performer-mcp-portability.js +10 -0
  141. package/dist/shared/safe-mode.js +1 -0
  142. package/dist/shared/session-metadata.js +4 -3
  143. package/package.json +6 -4
  144. package/client/assets/index-C2eIILoa.css +0 -41
  145. package/client/assets/index-DUPZ_Lw5.js +0 -616
  146. package/dist/server/lib/act-runtime.js +0 -1282
  147. package/dist/server/lib/prompt.js +0 -222
  148. 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
+ }
@@ -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 (err) {
91
- console.error('Failed to create PTY via OpenCode:', err.message);
92
- ws.send(JSON.stringify({ type: 'error', message: `Failed: ${err.message}` }));
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 (e) {
212
- console.error('PTY resize failed:', e.message);
257
+ catch (error) {
258
+ console.error('PTY resize failed:', errorMessage(error));
213
259
  }
214
260
  break;
215
261
  case 'create':
@@ -0,0 +1,4 @@
1
+ // Choreography Act — Shared Types (PRD-005)
2
+ // dot contract types are Source of Truth for the serialization schema.
3
+ // Studio extends them with runtime-only fields (id, performerRef, mailbox, etc.)
4
+ export {};
@@ -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,4 @@
1
+ // dot Contract Types — Single Source of Truth for all dot type imports
2
+ // This is the ONLY file that imports types directly from 'dance-of-tal'.
3
+ // Both shared/ and server/ code should import dot types from here.
4
+ export {};
@@ -0,0 +1,2 @@
1
+ // Draft CRUD — Shared contracts between client and server
2
+ export {};
@@ -0,0 +1,2 @@
1
+ // Canonical ModelSelection type shared by client and server.
2
+ 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:([^:\]]+):([^\]]+)\]\s*$/;
3
- export function buildStudioSessionTitle(performerId, performerName, configHash) {
4
- return `${SESSION_TITLE_PREFIX} ${performerName} [studio:${performerId}:${configHash}]`;
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.1",
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 vite,server,opencode -c cyan,yellow,magenta \"vite\" \"tsx --watch server/index.ts\" \"opencode --port 4096 .\"",
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": "^3.2.3",
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
  }