@newsails/veil-studio 1.0.0

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 (87) hide show
  1. package/README.md +181 -0
  2. package/bin/veil-studio.js +142 -0
  3. package/nuxt-app/.output/public/200.html +13 -0
  4. package/nuxt-app/.output/public/404.html +13 -0
  5. package/nuxt-app/.output/public/_nuxt/builds/latest.json +1 -0
  6. package/nuxt-app/.output/public/_nuxt/builds/meta/6b28df26-54af-4fad-a1f0-38808960d9fe.json +1 -0
  7. package/nuxt-app/.output/public/_nuxt/entry.BrrOeBSX.js +120 -0
  8. package/nuxt-app/.output/public/_nuxt/entry.CYnp7zY5.css +1 -0
  9. package/nuxt-app/.output/public/_nuxt/error-404.BbdzCaXe.js +1 -0
  10. package/nuxt-app/.output/public/_nuxt/error-404.JekaaCis.css +1 -0
  11. package/nuxt-app/.output/public/_nuxt/error-500.CNP9nqm1.css +1 -0
  12. package/nuxt-app/.output/public/_nuxt/error-500.DbOlBIIY.js +1 -0
  13. package/nuxt-app/.output/public/_nuxt/index.BEoXSIOu.css +1 -0
  14. package/nuxt-app/.output/public/_nuxt/index.CNms2yAq.js +1 -0
  15. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.B7mPwVP_.ttf +0 -0
  16. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.CSr8KVlo.eot +0 -0
  17. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.Dp5v-WZN.woff2 +0 -0
  18. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.PXm3-2wK.woff +0 -0
  19. package/nuxt-app/.output/public/_nuxt/vue.-sixQ7xP.BlWffD__.js +1 -0
  20. package/nuxt-app/.output/public/index.html +13 -0
  21. package/package.json +37 -0
  22. package/server/index.js +184 -0
  23. package/server/routes/files.js +80 -0
  24. package/server/socket.js +507 -0
  25. package/server/utils/board-state.js +357 -0
  26. package/server/utils/config.js +51 -0
  27. package/server/utils/db.js +484 -0
  28. package/server/utils/element-instances.js +104 -0
  29. package/server/utils/element-registry.js +127 -0
  30. package/server/utils/elements/agent-instance.js +62 -0
  31. package/server/utils/elements/agent.js +93 -0
  32. package/server/utils/elements/annotation.js +30 -0
  33. package/server/utils/elements/approval-gate.js +49 -0
  34. package/server/utils/elements/assumption.js +32 -0
  35. package/server/utils/elements/blocker.js +36 -0
  36. package/server/utils/elements/chat-room.js +47 -0
  37. package/server/utils/elements/chat-view.js +33 -0
  38. package/server/utils/elements/code-block.js +39 -0
  39. package/server/utils/elements/collapsible-group.js +37 -0
  40. package/server/utils/elements/comparison-table.js +38 -0
  41. package/server/utils/elements/constraint.js +32 -0
  42. package/server/utils/elements/decision.js +39 -0
  43. package/server/utils/elements/diff-patch.js +38 -0
  44. package/server/utils/elements/divider.js +30 -0
  45. package/server/utils/elements/document-draft.js +35 -0
  46. package/server/utils/elements/fact-claim.js +36 -0
  47. package/server/utils/elements/feedback-request.js +43 -0
  48. package/server/utils/elements/file-reference.js +31 -0
  49. package/server/utils/elements/filter.js +48 -0
  50. package/server/utils/elements/generator.js +51 -0
  51. package/server/utils/elements/goal.js +36 -0
  52. package/server/utils/elements/html.js +35 -0
  53. package/server/utils/elements/idea.js +32 -0
  54. package/server/utils/elements/image-local.js +21 -0
  55. package/server/utils/elements/image.js +34 -0
  56. package/server/utils/elements/json-object.js +47 -0
  57. package/server/utils/elements/label-tag.js +30 -0
  58. package/server/utils/elements/markdown.js +37 -0
  59. package/server/utils/elements/merger.js +36 -0
  60. package/server/utils/elements/message.js +40 -0
  61. package/server/utils/elements/milestone.js +44 -0
  62. package/server/utils/elements/notification.js +34 -0
  63. package/server/utils/elements/outline.js +35 -0
  64. package/server/utils/elements/primitive.js +36 -0
  65. package/server/utils/elements/pro-con-list.js +39 -0
  66. package/server/utils/elements/processor.js +54 -0
  67. package/server/utils/elements/project.js +40 -0
  68. package/server/utils/elements/question.js +42 -0
  69. package/server/utils/elements/queue.js +85 -0
  70. package/server/utils/elements/research-note.js +41 -0
  71. package/server/utils/elements/section.js +35 -0
  72. package/server/utils/elements/source-collection.js +45 -0
  73. package/server/utils/elements/splitter.js +42 -0
  74. package/server/utils/elements/status-update.js +38 -0
  75. package/server/utils/elements/task.js +72 -0
  76. package/server/utils/elements/template.js +46 -0
  77. package/server/utils/elements/test-case.js +42 -0
  78. package/server/utils/elements/text.js +29 -0
  79. package/server/utils/elements/todo-list.js +57 -0
  80. package/server/utils/elements/url-card.js +37 -0
  81. package/server/utils/elements/web-search-query.js +46 -0
  82. package/server/utils/elements/web-snapshot.js +37 -0
  83. package/server/utils/file-utils.js +88 -0
  84. package/server/utils/session-watcher.js +108 -0
  85. package/server/utils/socket-io.js +14 -0
  86. package/server/utils/veil-client.js +185 -0
  87. package/server/utils/veil-ws.js +207 -0
@@ -0,0 +1,357 @@
1
+ 'use strict'
2
+
3
+ const { randomUUID } = require('crypto')
4
+ const { getIO } = require('./socket-io.js')
5
+ const {
6
+ dbGetElements, dbGetElement, dbInsertElement, dbUpdateElement, dbDeleteElement,
7
+ dbGetLinks, dbInsertLink, dbDeleteLink,
8
+ dbGetZones, dbInsertZone, dbUpdateZone, dbDeleteZone,
9
+ dbGetViews, dbInsertView, dbDeleteView,
10
+ dbGetArchive, dbInsertArchive, dbRemoveArchive,
11
+ dbGetDeleted, dbInsertDeleted, dbRemoveDeleted,
12
+ dbGetMeta, dbSetMeta,
13
+ dbGetShapes, dbInsertShape, dbUpdateShape, dbDeleteShape,
14
+ } = require('./db.js')
15
+
16
+ // In-memory Maps (source of truth at runtime)
17
+ const elements = new Map()
18
+ const links = new Map()
19
+ const zones = new Map()
20
+ const views = new Map()
21
+ const shapes = new Map()
22
+
23
+ function loadFromDB() {
24
+ elements.clear()
25
+ links.clear()
26
+ zones.clear()
27
+ views.clear()
28
+ shapes.clear()
29
+
30
+ for (const el of dbGetElements()) elements.set(el.id, el)
31
+ for (const l of dbGetLinks()) links.set(l.id, l)
32
+ for (const z of dbGetZones()) zones.set(z.id, z)
33
+ for (const v of dbGetViews()) views.set(v.id, v)
34
+ for (const s of dbGetShapes()) shapes.set(s.id, s)
35
+ }
36
+
37
+ function getSnapshot() {
38
+ const meta = {}
39
+ for (const key of ['created_at', 'first_opened', 'projectDir']) {
40
+ const val = dbGetMeta(key)
41
+ if (val !== null) meta[key] = val
42
+ }
43
+ return {
44
+ elements: Array.from(elements.values()),
45
+ links: Array.from(links.values()),
46
+ zones: Array.from(zones.values()),
47
+ views: Array.from(views.values()),
48
+ shapes: Array.from(shapes.values()),
49
+ meta,
50
+ }
51
+ }
52
+
53
+ function broadcast(delta) {
54
+ try {
55
+ getIO().to('board').emit('board:delta', delta)
56
+ } catch (_) {
57
+ // Socket.IO not yet initialized during startup; safe to ignore
58
+ }
59
+ }
60
+
61
+ // ─── Elements ──────────────────────────────────────────────────────────────
62
+
63
+ function createElement(partial) {
64
+ const now = Date.now()
65
+ const el = {
66
+ id: randomUUID(),
67
+ type: partial.type,
68
+ x: partial.x ?? 0,
69
+ y: partial.y ?? 0,
70
+ width: partial.width ?? 160,
71
+ height: partial.height ?? 80,
72
+ data: partial.data ?? {},
73
+ zoneId: partial.zoneId ?? null,
74
+ expansionMode: partial.expansionMode ?? 'inline',
75
+ expanded: partial.expanded ?? true,
76
+ state: partial.state ?? 'default',
77
+ createdAt: now,
78
+ updatedAt: now,
79
+ }
80
+ dbInsertElement(el)
81
+ elements.set(el.id, el)
82
+ broadcast({ type: 'element:add', element: el })
83
+ return el
84
+ }
85
+
86
+ function updateElement(id, changes) {
87
+ const existing = elements.get(id)
88
+ if (!existing) throw new Error(`Element not found: ${id}`)
89
+ const updated = { ...existing, ...changes, id, updatedAt: Date.now() }
90
+ dbUpdateElement(id, updated)
91
+ elements.set(id, updated)
92
+ broadcast({ type: 'element:update', id, changes: { ...changes, updatedAt: updated.updatedAt } })
93
+ return updated
94
+ }
95
+
96
+ function removeElement(id) {
97
+ const el = elements.get(id)
98
+ if (!el) throw new Error(`Element not found: ${id}`)
99
+ const item = { id, elementData: el, timestamp: Date.now() }
100
+ dbInsertDeleted(item)
101
+ dbDeleteElement(id)
102
+ elements.delete(id)
103
+ // Remove associated links from memory (DB cascade handles DB)
104
+ for (const [lid, link] of links) {
105
+ if (link.fromElementId === id || link.toElementId === id) links.delete(lid)
106
+ }
107
+ broadcast({ type: 'element:remove', id })
108
+ return item
109
+ }
110
+
111
+ function archiveElement(id) {
112
+ const el = elements.get(id)
113
+ if (!el) throw new Error(`Element not found: ${id}`)
114
+ const item = { id, elementData: el, timestamp: Date.now() }
115
+ dbInsertArchive(item)
116
+ dbDeleteElement(id)
117
+ elements.delete(id)
118
+ // Remove associated links from memory (DB cascade handles DB)
119
+ for (const [lid, link] of links) {
120
+ if (link.fromElementId === id || link.toElementId === id) links.delete(lid)
121
+ }
122
+ broadcast({ type: 'element:remove', id })
123
+ return item
124
+ }
125
+
126
+ function duplicateElement(id, offsetX = 40, offsetY = 40) {
127
+ const el = elements.get(id)
128
+ if (!el) throw new Error(`Element not found: ${id}`)
129
+ return createElement({
130
+ ...el,
131
+ id: undefined,
132
+ x: el.x + offsetX,
133
+ y: el.y + offsetY,
134
+ data: { ...el.data },
135
+ state: 'default',
136
+ })
137
+ }
138
+
139
+ function updateElementState(id, state) {
140
+ const el = elements.get(id)
141
+ if (!el) return
142
+ el.state = state
143
+ el.updatedAt = Date.now()
144
+ dbUpdateElement(id, el)
145
+ broadcast({ type: 'element:state', id, state })
146
+ }
147
+
148
+ // ─── Links ─────────────────────────────────────────────────────────────────
149
+
150
+ function createLink(partial) {
151
+ if (!elements.has(partial.fromElementId)) throw new Error(`Source element not found: ${partial.fromElementId}`)
152
+ if (!elements.has(partial.toElementId)) throw new Error(`Target element not found: ${partial.toElementId}`)
153
+
154
+ const link = {
155
+ id: randomUUID(),
156
+ fromElementId: partial.fromElementId,
157
+ fromPortKey: partial.fromPortKey,
158
+ toElementId: partial.toElementId,
159
+ toPortKey: partial.toPortKey,
160
+ type: partial.type || 'element-element',
161
+ createdAt: Date.now(),
162
+ }
163
+ dbInsertLink(link)
164
+ links.set(link.id, link)
165
+ broadcast({ type: 'link:add', link })
166
+ return link
167
+ }
168
+
169
+ function removeLink(id) {
170
+ const link = links.get(id)
171
+ if (!link) throw new Error(`Link not found: ${id}`)
172
+ dbDeleteLink(id)
173
+ links.delete(id)
174
+ broadcast({ type: 'link:remove', id })
175
+ }
176
+
177
+ // ─── Zones ─────────────────────────────────────────────────────────────────
178
+
179
+ function createZone(partial) {
180
+ const now = Date.now()
181
+ const zone = {
182
+ id: randomUUID(),
183
+ name: partial.name || 'Zone',
184
+ x: partial.x ?? 0,
185
+ y: partial.y ?? 0,
186
+ width: partial.width ?? 300,
187
+ height: partial.height ?? 200,
188
+ color: partial.color || '#3b82f6',
189
+ config: partial.config ?? {},
190
+ collapsed: partial.collapsed ?? false,
191
+ createdAt: now,
192
+ updatedAt: now,
193
+ }
194
+ dbInsertZone(zone)
195
+ zones.set(zone.id, zone)
196
+ broadcast({ type: 'zone:add', zone })
197
+ return zone
198
+ }
199
+
200
+ function updateZone(id, changes) {
201
+ const existing = zones.get(id)
202
+ if (!existing) throw new Error(`Zone not found: ${id}`)
203
+ const updated = { ...existing, ...changes, id, updatedAt: Date.now() }
204
+ dbUpdateZone(id, updated)
205
+ zones.set(id, updated)
206
+ broadcast({ type: 'zone:update', id, changes: { ...changes, updatedAt: updated.updatedAt } })
207
+ return updated
208
+ }
209
+
210
+ function removeZone(id) {
211
+ const zone = zones.get(id)
212
+ if (!zone) throw new Error(`Zone not found: ${id}`)
213
+ dbDeleteZone(id)
214
+ zones.delete(id)
215
+ broadcast({ type: 'zone:remove', id })
216
+ }
217
+
218
+ // ─── Views ─────────────────────────────────────────────────────────────────
219
+
220
+ function createView(partial) {
221
+ const view = {
222
+ id: randomUUID(),
223
+ name: partial.name || 'View',
224
+ cameraX: partial.cameraX ?? 0,
225
+ cameraY: partial.cameraY ?? 0,
226
+ zoom: partial.zoom ?? 1,
227
+ createdAt: Date.now(),
228
+ }
229
+ dbInsertView(view)
230
+ views.set(view.id, view)
231
+ broadcast({ type: 'view:add', view })
232
+ return view
233
+ }
234
+
235
+ function removeView(id) {
236
+ const view = views.get(id)
237
+ if (!view) throw new Error(`View not found: ${id}`)
238
+ dbDeleteView(id)
239
+ views.delete(id)
240
+ broadcast({ type: 'view:remove', id })
241
+ }
242
+
243
+ // ─── Archive / Deleted bins ────────────────────────────────────────────────
244
+
245
+ function getArchive() { return dbGetArchive() }
246
+ function getDeleted() { return dbGetDeleted() }
247
+
248
+ function restoreFromArchive(id) {
249
+ const items = dbGetArchive()
250
+ const item = items.find(i => i.id === id)
251
+ if (!item) throw new Error(`Archive item not found: ${id}`)
252
+ const el = { ...item.elementData, state: 'default', updatedAt: Date.now() }
253
+ dbInsertElement(el)
254
+ elements.set(el.id, el)
255
+ dbRemoveArchive(id)
256
+ broadcast({ type: 'element:add', element: el })
257
+ return el
258
+ }
259
+
260
+ function restoreFromDeleted(id) {
261
+ const items = dbGetDeleted()
262
+ const item = items.find(i => i.id === id)
263
+ if (!item) throw new Error(`Deleted item not found: ${id}`)
264
+ const el = { ...item.elementData, state: 'default', updatedAt: Date.now() }
265
+ dbInsertElement(el)
266
+ elements.set(el.id, el)
267
+ dbRemoveDeleted(id)
268
+ broadcast({ type: 'element:add', element: el })
269
+ return el
270
+ }
271
+
272
+ // ─── Meta ──────────────────────────────────────────────────────────────────
273
+
274
+ function getMeta(key) { return dbGetMeta(key) }
275
+ function setMeta(key, value) { dbSetMeta(key, value) }
276
+
277
+ // ─── Shapes ─────────────────────────────────────────────────────────────────
278
+
279
+ function createShape(partial) {
280
+ const now = Date.now()
281
+ const s = {
282
+ id: randomUUID(),
283
+ subtype: partial.subtype || 'rectangle',
284
+ x: partial.x ?? 100,
285
+ y: partial.y ?? 100,
286
+ width: partial.width ?? 160,
287
+ height: partial.height ?? 120,
288
+ fillColor: partial.fillColor || 'transparent',
289
+ strokeColor: partial.strokeColor || '#6b7280',
290
+ strokeWidth: partial.strokeWidth ?? 1.5,
291
+ text: partial.text || '',
292
+ title: partial.title || '',
293
+ textAlign: partial.textAlign || 'center',
294
+ createdAt: now,
295
+ updatedAt: now,
296
+ }
297
+ dbInsertShape(s)
298
+ shapes.set(s.id, s)
299
+ broadcast({ type: 'shape:add', shape: s })
300
+ return s
301
+ }
302
+
303
+ function updateShape(id, changes) {
304
+ const existing = shapes.get(id)
305
+ if (!existing) throw new Error(`Shape not found: ${id}`)
306
+ const updated = { ...existing, ...changes, id, updatedAt: Date.now() }
307
+ dbUpdateShape(id, updated)
308
+ shapes.set(id, updated)
309
+ broadcast({ type: 'shape:update', id, changes: { ...changes, updatedAt: updated.updatedAt } })
310
+ return updated
311
+ }
312
+
313
+ function removeShape(id) {
314
+ const s = shapes.get(id)
315
+ if (!s) throw new Error(`Shape not found: ${id}`)
316
+ dbDeleteShape(id)
317
+ shapes.delete(id)
318
+ broadcast({ type: 'shape:remove', id })
319
+ }
320
+
321
+ // ─── Read helpers for ElementContext ───────────────────────────────────────
322
+
323
+ function getElements() { return Array.from(elements.values()) }
324
+ function getLinks() { return Array.from(links.values()) }
325
+ function getZones() { return Array.from(zones.values()) }
326
+ function getShapes() { return Array.from(shapes.values()) }
327
+
328
+ module.exports = {
329
+ loadFromDB,
330
+ getSnapshot,
331
+ createElement,
332
+ updateElement,
333
+ removeElement,
334
+ archiveElement,
335
+ duplicateElement,
336
+ updateElementState,
337
+ createLink,
338
+ removeLink,
339
+ createZone,
340
+ updateZone,
341
+ removeZone,
342
+ createView,
343
+ removeView,
344
+ getArchive,
345
+ getDeleted,
346
+ restoreFromArchive,
347
+ restoreFromDeleted,
348
+ getMeta,
349
+ setMeta,
350
+ getElements,
351
+ getLinks,
352
+ getZones,
353
+ createShape,
354
+ updateShape,
355
+ removeShape,
356
+ getShapes,
357
+ }
@@ -0,0 +1,51 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+ const fs = require('fs')
5
+
6
+ const PROJECT_DIR = process.env.PROJECT_DIR
7
+ ? path.resolve(process.env.PROJECT_DIR)
8
+ : process.cwd()
9
+
10
+ // Try to read .veil/settings.json for fallback values
11
+ let veilSettings = {}
12
+ try {
13
+ const settingsPath = path.join(PROJECT_DIR, '.veil', 'settings.json')
14
+ veilSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
15
+ } catch (_) {
16
+ // Silent — file may not exist
17
+ }
18
+
19
+ const VEIL_API = process.env.VEIL_API
20
+ || (veilSettings.port ? `http://localhost:${veilSettings.port}` : null)
21
+ || 'http://localhost:5050'
22
+
23
+ const VEIL_SECRET = process.env.VEIL_SECRET
24
+ || veilSettings.secret
25
+ || ''
26
+
27
+ const STUDIO_PORT = parseInt(process.env.STUDIO_PORT || '4444', 10)
28
+
29
+ // Studio-level secret token (protects the Studio itself, separate from VEIL_SECRET)
30
+ let studioConfig = {}
31
+ try {
32
+ const studioConfigPath = path.join(PROJECT_DIR, '.veil-studio', 'config.json')
33
+ studioConfig = JSON.parse(fs.readFileSync(studioConfigPath, 'utf8'))
34
+ } catch (_) {}
35
+
36
+ const STUDIO_SECRET = process.env.STUDIO_SECRET || studioConfig.secret || ''
37
+
38
+ const DB_PATH = path.join(PROJECT_DIR, '.veil-studio', 'studio.db')
39
+ const ELEMENTS_DIR = path.join(PROJECT_DIR, '.veil-studio', 'elements')
40
+ const VEIL_WS_URL = VEIL_API.replace(/^http:\/\//, 'ws://').replace(/^https:\/\//, 'wss://')
41
+
42
+ module.exports = {
43
+ PROJECT_DIR,
44
+ VEIL_API,
45
+ VEIL_SECRET,
46
+ STUDIO_SECRET,
47
+ STUDIO_PORT,
48
+ DB_PATH,
49
+ ELEMENTS_DIR,
50
+ VEIL_WS_URL,
51
+ }