@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.
- package/README.md +181 -0
- package/bin/veil-studio.js +142 -0
- package/nuxt-app/.output/public/200.html +13 -0
- package/nuxt-app/.output/public/404.html +13 -0
- package/nuxt-app/.output/public/_nuxt/builds/latest.json +1 -0
- package/nuxt-app/.output/public/_nuxt/builds/meta/6b28df26-54af-4fad-a1f0-38808960d9fe.json +1 -0
- package/nuxt-app/.output/public/_nuxt/entry.BrrOeBSX.js +120 -0
- package/nuxt-app/.output/public/_nuxt/entry.CYnp7zY5.css +1 -0
- package/nuxt-app/.output/public/_nuxt/error-404.BbdzCaXe.js +1 -0
- package/nuxt-app/.output/public/_nuxt/error-404.JekaaCis.css +1 -0
- package/nuxt-app/.output/public/_nuxt/error-500.CNP9nqm1.css +1 -0
- package/nuxt-app/.output/public/_nuxt/error-500.DbOlBIIY.js +1 -0
- package/nuxt-app/.output/public/_nuxt/index.BEoXSIOu.css +1 -0
- package/nuxt-app/.output/public/_nuxt/index.CNms2yAq.js +1 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.B7mPwVP_.ttf +0 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.CSr8KVlo.eot +0 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.Dp5v-WZN.woff2 +0 -0
- package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.PXm3-2wK.woff +0 -0
- package/nuxt-app/.output/public/_nuxt/vue.-sixQ7xP.BlWffD__.js +1 -0
- package/nuxt-app/.output/public/index.html +13 -0
- package/package.json +37 -0
- package/server/index.js +184 -0
- package/server/routes/files.js +80 -0
- package/server/socket.js +507 -0
- package/server/utils/board-state.js +357 -0
- package/server/utils/config.js +51 -0
- package/server/utils/db.js +484 -0
- package/server/utils/element-instances.js +104 -0
- package/server/utils/element-registry.js +127 -0
- package/server/utils/elements/agent-instance.js +62 -0
- package/server/utils/elements/agent.js +93 -0
- package/server/utils/elements/annotation.js +30 -0
- package/server/utils/elements/approval-gate.js +49 -0
- package/server/utils/elements/assumption.js +32 -0
- package/server/utils/elements/blocker.js +36 -0
- package/server/utils/elements/chat-room.js +47 -0
- package/server/utils/elements/chat-view.js +33 -0
- package/server/utils/elements/code-block.js +39 -0
- package/server/utils/elements/collapsible-group.js +37 -0
- package/server/utils/elements/comparison-table.js +38 -0
- package/server/utils/elements/constraint.js +32 -0
- package/server/utils/elements/decision.js +39 -0
- package/server/utils/elements/diff-patch.js +38 -0
- package/server/utils/elements/divider.js +30 -0
- package/server/utils/elements/document-draft.js +35 -0
- package/server/utils/elements/fact-claim.js +36 -0
- package/server/utils/elements/feedback-request.js +43 -0
- package/server/utils/elements/file-reference.js +31 -0
- package/server/utils/elements/filter.js +48 -0
- package/server/utils/elements/generator.js +51 -0
- package/server/utils/elements/goal.js +36 -0
- package/server/utils/elements/html.js +35 -0
- package/server/utils/elements/idea.js +32 -0
- package/server/utils/elements/image-local.js +21 -0
- package/server/utils/elements/image.js +34 -0
- package/server/utils/elements/json-object.js +47 -0
- package/server/utils/elements/label-tag.js +30 -0
- package/server/utils/elements/markdown.js +37 -0
- package/server/utils/elements/merger.js +36 -0
- package/server/utils/elements/message.js +40 -0
- package/server/utils/elements/milestone.js +44 -0
- package/server/utils/elements/notification.js +34 -0
- package/server/utils/elements/outline.js +35 -0
- package/server/utils/elements/primitive.js +36 -0
- package/server/utils/elements/pro-con-list.js +39 -0
- package/server/utils/elements/processor.js +54 -0
- package/server/utils/elements/project.js +40 -0
- package/server/utils/elements/question.js +42 -0
- package/server/utils/elements/queue.js +85 -0
- package/server/utils/elements/research-note.js +41 -0
- package/server/utils/elements/section.js +35 -0
- package/server/utils/elements/source-collection.js +45 -0
- package/server/utils/elements/splitter.js +42 -0
- package/server/utils/elements/status-update.js +38 -0
- package/server/utils/elements/task.js +72 -0
- package/server/utils/elements/template.js +46 -0
- package/server/utils/elements/test-case.js +42 -0
- package/server/utils/elements/text.js +29 -0
- package/server/utils/elements/todo-list.js +57 -0
- package/server/utils/elements/url-card.js +37 -0
- package/server/utils/elements/web-search-query.js +46 -0
- package/server/utils/elements/web-snapshot.js +37 -0
- package/server/utils/file-utils.js +88 -0
- package/server/utils/session-watcher.js +108 -0
- package/server/utils/socket-io.js +14 -0
- package/server/utils/veil-client.js +185 -0
- 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
|
+
}
|