@ultrst-armada/kloks 1.2.1 → 1.4.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.
@@ -1,12 +1,33 @@
1
+ import {
2
+ CompositeBackend,
3
+ EndpointGenerator,
4
+ LOCAL_STORAGE_GRAPHS_PREFIX,
5
+ NodeGraphPersistenceService,
6
+ createBackend,
7
+ createCompositeService,
8
+ createLocalService,
9
+ createRemoteService,
10
+ exportGraphAsJsonFile,
11
+ nodeGraphPersistence,
12
+ parseGraphJsonFile,
13
+ validateGraphDocument
14
+ } from "./chunk-BM4QMVPA.js";
1
15
  import {
2
16
  CANONICAL_SRKL_URL,
3
17
  SrklClient,
18
+ SrklCredentialRegistry,
19
+ SrklVaultAccessClient,
4
20
  createSrklClient,
21
+ decryptVaultBufferInBrowser,
5
22
  getPrimarySrklDeploymentUrl,
6
23
  getSrklAccounts,
24
+ globalCredentialRegistry,
25
+ hexToBytes,
7
26
  srklListBackblazeBuckets,
27
+ srklService,
28
+ syncSrklAccounts,
8
29
  useSrklServices
9
- } from "./chunk-MN54ZIKZ.js";
30
+ } from "./chunk-KX3OTBQ3.js";
10
31
  import "./chunk-LS3HWJBX.js";
11
32
  import {
12
33
  authorizeB2,
@@ -16,16 +37,48 @@ import {
16
37
  b2UploadFile
17
38
  } from "./chunk-4QEMALXF.js";
18
39
  import {
19
- BaseApiRoute,
20
- createSSEResponse,
21
- withAuth,
22
- withCors
23
- } from "./chunk-TWCEVES2.js";
24
- import {
40
+ RemoteWorkspaceService,
25
41
  SyncEngine,
42
+ WorkingWorkspaceService,
26
43
  WorkspaceCloneSyncEngine,
27
- WorkspaceFS
28
- } from "./chunk-KTLD7Z4L.js";
44
+ WorkspaceFS,
45
+ b2DeleteFileVersion,
46
+ b2DeleteFolder,
47
+ b2ListBuckets as b2ListBuckets2,
48
+ cleanDbUrl,
49
+ createWorkspace,
50
+ deleteWorkspace,
51
+ deleteWorkspaceFile,
52
+ deleteWorkspaceFolder,
53
+ findUserWorkspace,
54
+ findWorkspaceRecord,
55
+ getB2BucketId,
56
+ getDefaultWorkingWorkspaceDir,
57
+ getUnifiedDbUrl,
58
+ getWorkingWorkspaceConfig,
59
+ getWorkingWorkspaceDir,
60
+ getWorkspaceFolderKey,
61
+ getWorkspaceFolderPrefix,
62
+ getWorkspaceStorageDetails,
63
+ getWorkspaces,
64
+ isB2Workspace,
65
+ listWorkspaceFiles,
66
+ loadWorkspaceData,
67
+ queryUnifiedDb,
68
+ readWorkspaceFile,
69
+ remoteWorkspaceService,
70
+ renameAppInWorkspaceData,
71
+ resolveWorkspaceConnection,
72
+ saveWorkspaceData,
73
+ setWorkingWorkspaceConfig,
74
+ syncWorkspace,
75
+ syncWorkspaceCreateToBucket,
76
+ syncWorkspaceDataToBucket,
77
+ syncWorkspaceDeleteToBucket,
78
+ updateWorkspace,
79
+ workingWorkspaceService,
80
+ writeWorkspaceFile
81
+ } from "./chunk-DZ4ZGIZM.js";
29
82
  import {
30
83
  AuthServiceDefinition,
31
84
  AuthServiceFacade,
@@ -41,7 +94,8 @@ import {
41
94
  StorageServiceFacade,
42
95
  WorkspaceServiceDefinition,
43
96
  WorkspaceServiceFacade
44
- } from "./chunk-OYQKQAEF.js";
97
+ } from "./chunk-CU7RZBMF.js";
98
+ import "./chunk-IPP4VURQ.js";
45
99
  import {
46
100
  BackblazeB2Adapter,
47
101
  BaseStorageAdapter,
@@ -61,6 +115,17 @@ import {
61
115
  formatCanonicalWorkspaceFolderName,
62
116
  sanitizeWorkspaceName
63
117
  } from "./chunk-FZKBTF34.js";
118
+ import {
119
+ BaseApiRoute,
120
+ createSSEResponse,
121
+ withAuth,
122
+ withCors
123
+ } from "./chunk-TWCEVES2.js";
124
+ import {
125
+ BackendProvider,
126
+ useBackend,
127
+ useBackendClient
128
+ } from "./chunk-G3ZOSXUR.js";
64
129
  import {
65
130
  AdminGuard,
66
131
  BaseAccountProvider,
@@ -74,10 +139,13 @@ import {
74
139
  SessionManager
75
140
  } from "./chunk-25Y7AVTP.js";
76
141
  import {
77
- BackendProvider,
78
- useBackend,
79
- useBackendClient
80
- } from "./chunk-G3ZOSXUR.js";
142
+ AuditLogger,
143
+ ControlPlaneManager,
144
+ DatabaseClusterManager,
145
+ detectDatabaseEngine,
146
+ getSystemDatabaseUrls,
147
+ resolveSystemDatabaseClusterNodes
148
+ } from "./chunk-RCCQ7IRM.js";
81
149
  import {
82
150
  BackendRegistry,
83
151
  ConcurrencyConflictError,
@@ -100,14 +168,6 @@ import {
100
168
  WebSocketTransport,
101
169
  createBackendClient
102
170
  } from "./chunk-NIP22V2G.js";
103
- import {
104
- AuditLogger,
105
- ControlPlaneManager,
106
- DatabaseClusterManager,
107
- detectDatabaseEngine,
108
- getSystemDatabaseUrls,
109
- resolveSystemDatabaseClusterNodes
110
- } from "./chunk-RCCQ7IRM.js";
111
171
  import {
112
172
  JobDependencyGraph,
113
173
  ResultFlowBridge,
@@ -124,15 +184,6 @@ import {
124
184
  import {
125
185
  IdempotencyManager
126
186
  } from "./chunk-3EPY34R6.js";
127
- import {
128
- CompositeBackend,
129
- EndpointGenerator,
130
- createBackend,
131
- createCompositeService,
132
- createLocalService,
133
- createRemoteService
134
- } from "./chunk-P4HL4C6V.js";
135
- import "./chunk-IPP4VURQ.js";
136
187
 
137
188
  // src/data/BaseDataProvider.ts
138
189
  import { SkipoBase } from "@ultrst-armada/kloks/functions";
@@ -205,40 +256,6 @@ function validateServiceDefinition(def) {
205
256
  // src/core/authCore.ts
206
257
  import crypto2 from "crypto";
207
258
  import bcrypt from "bcryptjs";
208
-
209
- // src/core/unifiedDb.ts
210
- import { neon } from "@neondatabase/serverless";
211
- function cleanDbUrl(url) {
212
- if (!url || typeof url !== "string") return "";
213
- return url.trim().replace(/^["']|["']$/g, "");
214
- }
215
- function getUnifiedDbUrl(env) {
216
- const fromEnv = cleanDbUrl(env?.SYSTEM_DATABASE_URL_1) || cleanDbUrl(env?.DATABASE_URL) || cleanDbUrl(env?.SYSTEM_DATABASE_URL_2);
217
- if (fromEnv) return fromEnv;
218
- if (typeof process !== "undefined" && process.env) {
219
- return cleanDbUrl(process.env.SYSTEM_DATABASE_URL_1) || cleanDbUrl(process.env.DATABASE_URL) || cleanDbUrl(process.env.SYSTEM_DATABASE_URL_2) || cleanDbUrl(process.env.SYSTEM_DATABASE_URL_3) || "";
220
- }
221
- return "";
222
- }
223
- async function queryUnifiedDb(sqlText, params = [], env) {
224
- const dbUrl = getUnifiedDbUrl(env);
225
- if (!dbUrl) {
226
- throw new Error("[UnifiedDB] No database URL configured in environment.");
227
- }
228
- const sql = neon(dbUrl);
229
- try {
230
- const rows = await (sql.query ? sql.query(sqlText, params) : sql(sqlText, params));
231
- return {
232
- rows: Array.isArray(rows) ? rows : [],
233
- rowCount: Array.isArray(rows) ? rows.length : 0
234
- };
235
- } catch (err) {
236
- console.error("[UnifiedDB] Query execution error:", err.message);
237
- throw err;
238
- }
239
- }
240
-
241
- // src/core/authCore.ts
242
259
  async function loginUser(email, password, isAdmin = false, env) {
243
260
  const normEmail = (email || "").trim().toLowerCase();
244
261
  const rawPassword = (password || "").trim();
@@ -314,473 +331,11 @@ async function getUsers(env) {
314
331
  }
315
332
  }
316
333
 
317
- // src/core/b2DirectCore.ts
318
- var authCache = /* @__PURE__ */ new Map();
319
- var bucketIdCache = /* @__PURE__ */ new Map();
320
- async function authorizeB22(keyId, applicationKey) {
321
- const cacheKey = `${keyId.trim()}:${applicationKey.trim()}`;
322
- const cached = authCache.get(cacheKey);
323
- if (cached && Date.now() < cached.expiresAt) return cached.auth;
324
- const authHeader = typeof Buffer !== "undefined" ? Buffer.from(`${keyId.trim()}:${applicationKey.trim()}`).toString("base64") : btoa(`${keyId.trim()}:${applicationKey.trim()}`);
325
- let res = await fetch("https://api.backblazeb2.com/b2api/v2/b2_authorize_account", {
326
- headers: { Authorization: `Basic ${authHeader}` },
327
- signal: AbortSignal.timeout(1e4)
328
- });
329
- if (!res.ok) {
330
- res = await fetch("https://api.backblazeb2.com/b2api/v4/b2_authorize_account", {
331
- headers: { Authorization: `Basic ${authHeader}` },
332
- signal: AbortSignal.timeout(1e4)
333
- });
334
- }
335
- if (!res.ok) {
336
- const errText = await res.text();
337
- throw new Error(`Backblaze B2 authorization failed (${res.status}): ${errText}`);
338
- }
339
- const data = await res.json();
340
- const auth = {
341
- accountId: data.accountId,
342
- authorizationToken: data.authorizationToken,
343
- apiUrl: (data.apiInfo?.storageApi?.apiUrl || data.apiUrl || "https://api.backblazeb2.com").replace(/\/+$/, ""),
344
- downloadUrl: (data.apiInfo?.storageApi?.downloadUrl || data.downloadUrl || data.apiUrl || "https://f003.backblazeb2.com").replace(/\/+$/, ""),
345
- s3ApiUrl: data.apiInfo?.storageApi?.s3ApiUrl || data.s3ApiUrl,
346
- allowed: data.apiInfo?.storageApi?.allowed || data.allowed
347
- };
348
- authCache.set(cacheKey, { auth, expiresAt: Date.now() + 20 * 60 * 1e3 });
349
- return auth;
350
- }
351
- async function b2ListBuckets2(keyId, applicationKey, bucketId, bucketName) {
352
- const auth = await authorizeB22(keyId, applicationKey);
353
- const rBucketId = bucketId || auth.allowed?.bucketId || auth.allowed?.buckets?.[0]?.bucketId;
354
- const rBucketName = bucketName || auth.allowed?.bucketName || auth.allowed?.buckets?.[0]?.bucketName;
355
- if (!auth.allowed?.bucketId && !auth.allowed?.bucketName) {
356
- try {
357
- const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_buckets`, {
358
- method: "POST",
359
- headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
360
- body: JSON.stringify({ accountId: auth.accountId }),
361
- signal: AbortSignal.timeout(8e3)
362
- });
363
- if (res.ok) {
364
- const data = await res.json();
365
- if (Array.isArray(data.buckets) && data.buckets.length > 0) return data.buckets;
366
- }
367
- } catch (err) {
368
- console.warn("[B2DirectCore] list_buckets query failed:", err.message);
369
- }
370
- }
371
- if (rBucketId || rBucketName) {
372
- const body = { accountId: auth.accountId };
373
- if (rBucketId) body.bucketId = rBucketId;
374
- else if (rBucketName) body.bucketName = rBucketName;
375
- try {
376
- const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_buckets`, {
377
- method: "POST",
378
- headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
379
- body: JSON.stringify(body),
380
- signal: AbortSignal.timeout(8e3)
381
- });
382
- if (res.ok) {
383
- const data = await res.json();
384
- if (Array.isArray(data.buckets) && data.buckets.length > 0) return data.buckets;
385
- }
386
- } catch {
387
- }
388
- return [{
389
- accountId: auth.accountId,
390
- bucketId: rBucketId || "restricted",
391
- bucketName: rBucketName || (bucketName || "default-bucket"),
392
- bucketType: "allPrivate"
393
- }];
394
- }
395
- return [];
396
- }
397
- async function getB2BucketId(keyId, applicationKey, bucketName) {
398
- const cacheKey = `${keyId.trim()}:${bucketName.trim()}`;
399
- if (bucketIdCache.has(cacheKey)) return bucketIdCache.get(cacheKey);
400
- const buckets = await b2ListBuckets2(keyId, applicationKey, void 0, bucketName);
401
- const match = buckets.find((b) => b.bucketName === bucketName);
402
- if (!match) throw new Error(`B2 bucket "${bucketName}" not found in account`);
403
- bucketIdCache.set(cacheKey, match.bucketId);
404
- return match.bucketId;
405
- }
406
- async function b2UploadFile2(keyId, applicationKey, bucketName, fileName, buffer, contentType = "b2/x-auto") {
407
- const auth = await authorizeB22(keyId, applicationKey);
408
- const bucketId = await getB2BucketId(keyId, applicationKey, bucketName);
409
- const urlRes = await fetch(`${auth.apiUrl}/b2api/v2/b2_get_upload_url`, {
410
- method: "POST",
411
- headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
412
- body: JSON.stringify({ bucketId })
413
- });
414
- if (!urlRes.ok) throw new Error(`B2 get upload url failed (${urlRes.status})`);
415
- const { uploadUrl, authorizationToken } = await urlRes.json();
416
- const encodedFileName = encodeURIComponent(fileName).replace(/%2F/g, "/");
417
- const uploadRes = await fetch(uploadUrl, {
418
- method: "POST",
419
- headers: {
420
- Authorization: authorizationToken,
421
- "X-Bz-File-Name": encodedFileName,
422
- "Content-Type": contentType,
423
- "Content-Length": String(buffer.byteLength),
424
- "X-Bz-Content-Sha1": "do_not_verify"
425
- },
426
- body: buffer
427
- });
428
- if (!uploadRes.ok) throw new Error(`B2 upload failed for ${fileName} (${uploadRes.status}): ${await uploadRes.text()}`);
429
- return uploadRes.json();
430
- }
431
- async function b2DownloadFile2(keyId, applicationKey, bucketName, fileName) {
432
- const auth = await authorizeB22(keyId, applicationKey);
433
- const encodedName = encodeURIComponent(fileName).replace(/%2F/g, "/");
434
- const res = await fetch(`${auth.downloadUrl}/file/${bucketName}/${encodedName}`, { headers: { Authorization: auth.authorizationToken } });
435
- if (res.status === 404) return null;
436
- if (!res.ok) throw new Error(`B2 download failed for ${fileName} (${res.status})`);
437
- return new Uint8Array(await res.arrayBuffer());
438
- }
439
- async function b2ListFileNames2(keyId, applicationKey, bucketName, prefix = "") {
440
- const auth = await authorizeB22(keyId, applicationKey);
441
- const bucketId = await getB2BucketId(keyId, applicationKey, bucketName);
442
- const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_list_file_names`, {
443
- method: "POST",
444
- headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
445
- body: JSON.stringify({ bucketId, prefix, maxFileCount: 1e3 })
446
- });
447
- if (!res.ok) throw new Error(`B2 list files failed (${res.status})`);
448
- const data = await res.json();
449
- return data.files || [];
450
- }
451
- async function b2DeleteFileVersion(keyId, applicationKey, fileName, fileId) {
452
- const auth = await authorizeB22(keyId, applicationKey);
453
- const res = await fetch(`${auth.apiUrl}/b2api/v2/b2_delete_file_version`, {
454
- method: "POST",
455
- headers: { Authorization: auth.authorizationToken, "Content-Type": "application/json" },
456
- body: JSON.stringify({ fileName, fileId })
457
- });
458
- if (!res.ok) throw new Error(`B2 delete file version failed (${res.status})`);
459
- return res.json();
460
- }
461
- async function b2DeleteFolder(keyId, applicationKey, bucketName, folderPrefix) {
462
- const cleanPrefix = folderPrefix.replace(/\/+$/, "") + "/";
463
- const files = await b2ListFileNames2(keyId, applicationKey, bucketName, cleanPrefix);
464
- let deletedCount = 0;
465
- for (const file of files) {
466
- await b2DeleteFileVersion(keyId, applicationKey, file.fileName, file.fileId);
467
- deletedCount++;
468
- }
469
- return { success: true, deletedCount };
470
- }
471
-
472
- // src/core/workspaceSyncEngine.ts
473
- import fs from "fs";
474
- import path from "path";
475
-
476
- // src/core/workspaceCanonical.ts
477
- function formatCanonicalWorkspaceFolderName2(appName, userName, workspaceName) {
478
- const safeApp = (appName || "qwnsr").replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
479
- const safeUser = (userName || "user").replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
480
- const safeWs = (workspaceName || "workspace").replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
481
- return `ultrst_${safeApp}_${safeUser}_${safeWs}`;
482
- }
483
-
484
- // src/core/workspaceSyncEngine.ts
485
- var ACCOUNTS_FILE = path.resolve(".skipo/accounts.json");
486
- var CONFIG_FILE = path.resolve(".skipo/workspace_config.json");
487
- function getDefaultWorkingWorkspaceDir() {
488
- if (process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME) {
489
- return path.join("/tmp", "workspace");
490
- }
491
- return path.join(process.cwd(), "workspace");
492
- }
493
- function getWorkingWorkspaceConfig() {
494
- try {
495
- if (fs.existsSync(CONFIG_FILE)) {
496
- const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
497
- const parsed = JSON.parse(raw);
498
- if (parsed.workingDirectory) {
499
- return {
500
- workingDirectory: parsed.workingDirectory,
501
- autoSyncIntervalMinutes: parsed.autoSyncIntervalMinutes || 5
502
- };
503
- }
504
- }
505
- } catch {
506
- }
507
- return {
508
- workingDirectory: getDefaultWorkingWorkspaceDir(),
509
- autoSyncIntervalMinutes: 5
510
- };
511
- }
512
- function setWorkingWorkspaceConfig(cfg) {
513
- const current = getWorkingWorkspaceConfig();
514
- const updated = {
515
- workingDirectory: (cfg.workingDirectory || current.workingDirectory).trim(),
516
- autoSyncIntervalMinutes: cfg.autoSyncIntervalMinutes || current.autoSyncIntervalMinutes
517
- };
518
- try {
519
- fs.mkdirSync(path.dirname(CONFIG_FILE), { recursive: true });
520
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(updated, null, 2), "utf-8");
521
- } catch (err) {
522
- console.warn("[WorkspaceSync] Failed to persist working config:", err);
523
- }
524
- return updated;
525
- }
526
- function getWorkingWorkspaceDir() {
527
- const dir = getWorkingWorkspaceConfig().workingDirectory;
528
- if (!fs.existsSync(dir)) {
529
- fs.mkdirSync(dir, { recursive: true });
530
- }
531
- return dir;
532
- }
533
- async function findWorkspaceRecord(workspaceId) {
534
- try {
535
- if (fs.existsSync(ACCOUNTS_FILE)) {
536
- const raw = fs.readFileSync(ACCOUNTS_FILE, "utf-8");
537
- const accounts = JSON.parse(raw);
538
- for (const u of Object.values(accounts)) {
539
- if (Array.isArray(u.workspaces)) {
540
- const ws = u.workspaces.find((w) => w.id === workspaceId);
541
- if (ws) {
542
- const conns = Array.isArray(u.storage_connections) ? u.storage_connections : [];
543
- const conn = conns.find((c) => c.id === ws.storageConnectionId) || conns[0] || null;
544
- return { user: u, workspace: ws, connection: conn };
545
- }
546
- }
547
- }
548
- }
549
- } catch {
550
- }
551
- try {
552
- const res = await queryUnifiedDb(`SELECT id, email, username, app_profiles FROM "ultrst-users"`);
553
- for (const u of res.rows) {
554
- const profiles = typeof u.app_profiles === "string" ? JSON.parse(u.app_profiles) : u.app_profiles || {};
555
- const qwnsr = profiles.qwnsr || {};
556
- const workspaces = Array.isArray(qwnsr.workspaces) ? qwnsr.workspaces : [];
557
- const ws = workspaces.find((w) => w.id === workspaceId);
558
- if (ws) {
559
- const conns = qwnsr.storage_connections || profiles.srkl?.storage_connections || [];
560
- const conn = conns.find((c) => c.id === ws.storageConnectionId) || conns[0] || null;
561
- return { user: u, workspace: ws, connection: conn };
562
- }
563
- }
564
- } catch {
565
- }
566
- return null;
567
- }
568
- function getWorkspaceFolderKey(workspace, user) {
569
- if (workspace.localFolderKey) return workspace.localFolderKey;
570
- if (workspace.localFolderName && workspace.localFolderName.startsWith("ultrst_")) return workspace.localFolderName;
571
- const userName = user?.username || user?.name || (user?.email ? user.email.split("@")[0] : "user");
572
- return formatCanonicalWorkspaceFolderName2("qwnsr", userName, workspace.name);
573
- }
574
- async function collectLocalFiles(dir, baseDir = dir) {
575
- if (!fs.existsSync(dir)) return [];
576
- const entries = await fs.promises.readdir(dir, { withFileTypes: true });
577
- const results = [];
578
- for (const entry of entries) {
579
- if (entry.name.startsWith(".") && entry.name !== ".bzEmpty") continue;
580
- const full = path.join(dir, entry.name);
581
- if (entry.isDirectory()) {
582
- const nested = await collectLocalFiles(full, baseDir);
583
- results.push(...nested);
584
- } else if (entry.isFile()) {
585
- const st = await fs.promises.stat(full);
586
- const rel = path.relative(baseDir, full).replace(/\\/g, "/");
587
- results.push({ relPath: rel, fullPath: full, mtimeMs: st.mtimeMs, sizeBytes: st.size });
588
- }
589
- }
590
- return results;
591
- }
592
- async function syncWorkspace(workspaceId) {
593
- const match = await findWorkspaceRecord(workspaceId);
594
- const nowIso = (/* @__PURE__ */ new Date()).toISOString();
595
- if (!match) {
596
- return {
597
- success: false,
598
- workspaceId,
599
- folderKey: workspaceId,
600
- workingDir: getWorkingWorkspaceDir(),
601
- persistenceType: "local_fs",
602
- targetLocation: "Unknown",
603
- syncedFiles: [],
604
- uploadedCount: 0,
605
- downloadedCount: 0,
606
- syncedAt: nowIso,
607
- error: `Workspace '${workspaceId}' not found.`
608
- };
609
- }
610
- const { workspace, user, connection } = match;
611
- const folderKey = getWorkspaceFolderKey(workspace, user);
612
- const workingDir = getWorkingWorkspaceDir();
613
- const isB2 = workspace.storageProvider === "backblaze_b2" || workspace.storageConfig?.type === "b2";
614
- const syncedFiles = [];
615
- let uploadedCount = 0;
616
- let downloadedCount = 0;
617
- if (isB2) {
618
- const creds = workspace.storageConfig || connection?.credentials || connection || {};
619
- const keyId = creds.keyId || creds.b2KeyId;
620
- const appKey = creds.applicationKey || creds.b2ApplicationKey || creds.appKey;
621
- const bucketName = creds.bucketName || creds.b2BucketName || workspace.b2BucketName || "ultrst-apps";
622
- if (!keyId || !appKey) {
623
- return {
624
- success: false,
625
- workspaceId,
626
- folderKey,
627
- workingDir,
628
- persistenceType: "backblaze_b2",
629
- targetLocation: bucketName,
630
- syncedFiles: [],
631
- uploadedCount: 0,
632
- downloadedCount: 0,
633
- syncedAt: nowIso,
634
- error: "Missing Backblaze B2 credentials for sync."
635
- };
636
- }
637
- try {
638
- const b2Prefix = `${folderKey}/`;
639
- const remoteFiles = await b2ListFileNames2(keyId, appKey, bucketName, b2Prefix);
640
- const remoteMap = /* @__PURE__ */ new Map();
641
- for (const rf of remoteFiles) {
642
- const cleanName = rf.fileName.slice(b2Prefix.length);
643
- if (cleanName) remoteMap.set(cleanName, { fileId: rf.fileId, size: rf.contentLength, timestamp: rf.uploadTimestamp });
644
- }
645
- const localFiles = await collectLocalFiles(workingDir);
646
- const localMap = /* @__PURE__ */ new Map();
647
- for (const lf of localFiles) {
648
- localMap.set(lf.relPath, lf);
649
- }
650
- for (const [relPath, rInfo] of remoteMap.entries()) {
651
- const lFile = localMap.get(relPath);
652
- const shouldDownload = !lFile || rInfo.timestamp > lFile.mtimeMs + 2e3;
653
- if (shouldDownload) {
654
- const buf = await b2DownloadFile2(keyId, appKey, bucketName, `${b2Prefix}${relPath}`);
655
- if (buf) {
656
- const destPath = path.join(workingDir, relPath);
657
- await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
658
- await fs.promises.writeFile(destPath, buf);
659
- try {
660
- const fileTime = new Date(rInfo.timestamp);
661
- await fs.promises.utimes(destPath, fileTime, fileTime);
662
- } catch {
663
- }
664
- syncedFiles.push({ fileName: relPath, action: "DOWNLOADED", sizeBytes: buf.byteLength });
665
- downloadedCount++;
666
- }
667
- }
668
- }
669
- for (const [relPath, lFile] of localMap.entries()) {
670
- const rInfo = remoteMap.get(relPath);
671
- const shouldUpload = !rInfo || lFile.mtimeMs > rInfo.timestamp + 2e3;
672
- if (shouldUpload) {
673
- const content = await fs.promises.readFile(lFile.fullPath);
674
- await b2UploadFile2(keyId, appKey, bucketName, `${b2Prefix}${relPath}`, new Uint8Array(content));
675
- syncedFiles.push({ fileName: relPath, action: "UPLOADED", sizeBytes: lFile.sizeBytes });
676
- uploadedCount++;
677
- } else if (!syncedFiles.some((f) => f.fileName === relPath)) {
678
- syncedFiles.push({ fileName: relPath, action: "UP_TO_DATE", sizeBytes: lFile.sizeBytes });
679
- }
680
- }
681
- return {
682
- success: true,
683
- workspaceId,
684
- folderKey,
685
- workingDir,
686
- persistenceType: "backblaze_b2",
687
- targetLocation: `B2://${bucketName}/${folderKey}/`,
688
- syncedFiles,
689
- uploadedCount,
690
- downloadedCount,
691
- syncedAt: nowIso
692
- };
693
- } catch (err) {
694
- return {
695
- success: false,
696
- workspaceId,
697
- folderKey,
698
- workingDir,
699
- persistenceType: "backblaze_b2",
700
- targetLocation: bucketName,
701
- syncedFiles,
702
- uploadedCount,
703
- downloadedCount,
704
- syncedAt: nowIso,
705
- error: err.message || "Backblaze sync failed."
706
- };
707
- }
708
- }
709
- const vaultDir = path.resolve(process.cwd(), folderKey);
710
- await fs.promises.mkdir(vaultDir, { recursive: true });
711
- try {
712
- const vaultFiles = await collectLocalFiles(vaultDir);
713
- const vaultMap = new Map(vaultFiles.map((f) => [f.relPath, f]));
714
- const workingFiles = await collectLocalFiles(workingDir);
715
- const workingMap = new Map(workingFiles.map((f) => [f.relPath, f]));
716
- for (const [relPath, vFile] of vaultMap.entries()) {
717
- const wFile = workingMap.get(relPath);
718
- const shouldDownload = !wFile || vFile.mtimeMs > wFile.mtimeMs + 1e3;
719
- if (shouldDownload) {
720
- const dest = path.join(workingDir, relPath);
721
- await fs.promises.mkdir(path.dirname(dest), { recursive: true });
722
- await fs.promises.copyFile(vFile.fullPath, dest);
723
- try {
724
- const vTime = new Date(vFile.mtimeMs);
725
- await fs.promises.utimes(dest, vTime, vTime);
726
- } catch {
727
- }
728
- syncedFiles.push({ fileName: relPath, action: "DOWNLOADED", sizeBytes: vFile.sizeBytes });
729
- downloadedCount++;
730
- }
731
- }
732
- for (const [relPath, wFile] of workingMap.entries()) {
733
- const vFile = vaultMap.get(relPath);
734
- const shouldUpload = !vFile || wFile.mtimeMs > vFile.mtimeMs + 1e3;
735
- if (shouldUpload) {
736
- const dest = path.join(vaultDir, relPath);
737
- await fs.promises.mkdir(path.dirname(dest), { recursive: true });
738
- await fs.promises.copyFile(wFile.fullPath, dest);
739
- try {
740
- const wTime = new Date(wFile.mtimeMs);
741
- await fs.promises.utimes(dest, wTime, wTime);
742
- } catch {
743
- }
744
- syncedFiles.push({ fileName: relPath, action: "UPLOADED", sizeBytes: wFile.sizeBytes });
745
- uploadedCount++;
746
- } else if (!syncedFiles.some((f) => f.fileName === relPath)) {
747
- syncedFiles.push({ fileName: relPath, action: "UP_TO_DATE", sizeBytes: wFile.sizeBytes });
748
- }
749
- }
750
- return {
751
- success: true,
752
- workspaceId,
753
- folderKey,
754
- workingDir,
755
- persistenceType: "local_fs",
756
- targetLocation: vaultDir,
757
- syncedFiles,
758
- uploadedCount,
759
- downloadedCount,
760
- syncedAt: nowIso
761
- };
762
- } catch (err) {
763
- return {
764
- success: false,
765
- workspaceId,
766
- folderKey,
767
- workingDir,
768
- persistenceType: "local_fs",
769
- targetLocation: vaultDir,
770
- syncedFiles,
771
- uploadedCount,
772
- downloadedCount,
773
- syncedAt: nowIso,
774
- error: err.message
775
- };
776
- }
777
- }
778
-
779
334
  // src/core/SrklStorageService.ts
780
335
  import { S3Client, ListBucketsCommand, HeadBucketCommand } from "@aws-sdk/client-s3";
781
- import { promises as fs2 } from "fs";
782
- import path2 from "path";
783
- var ACCOUNTS_FILE2 = path2.resolve(".skipo/accounts.json");
336
+ import { promises as fs } from "fs";
337
+ import path from "path";
338
+ var ACCOUNTS_FILE = path.resolve(".skipo/accounts.json");
784
339
  var SrklStorageService = class {
785
340
  srklBaseUrl;
786
341
  constructor(srklBaseUrl) {
@@ -893,10 +448,10 @@ var SrklStorageService = class {
893
448
  }
894
449
  async persistWorkspaceConfig(wsId, config) {
895
450
  try {
896
- await fs2.mkdir(path2.dirname(ACCOUNTS_FILE2), { recursive: true });
451
+ await fs.mkdir(path.dirname(ACCOUNTS_FILE), { recursive: true });
897
452
  let accounts = {};
898
453
  try {
899
- accounts = JSON.parse(await fs2.readFile(ACCOUNTS_FILE2, "utf-8"));
454
+ accounts = JSON.parse(await fs.readFile(ACCOUNTS_FILE, "utf-8"));
900
455
  } catch {
901
456
  accounts = {};
902
457
  }
@@ -907,7 +462,7 @@ var SrklStorageService = class {
907
462
  ws.storageConfig = config;
908
463
  ws.storageProvider = "backblaze_b2";
909
464
  ws.updatedAt = Date.now();
910
- await fs2.writeFile(ACCOUNTS_FILE2, JSON.stringify(accounts, null, 2), "utf-8");
465
+ await fs.writeFile(ACCOUNTS_FILE, JSON.stringify(accounts, null, 2), "utf-8");
911
466
  return;
912
467
  }
913
468
  }
@@ -932,7 +487,7 @@ var SrklStorageService = class {
932
487
  });
933
488
  }
934
489
  try {
935
- const raw = await fs2.readFile(ACCOUNTS_FILE2, "utf-8");
490
+ const raw = await fs.readFile(ACCOUNTS_FILE, "utf-8");
936
491
  const accounts = JSON.parse(raw);
937
492
  for (const user of Object.values(accounts)) {
938
493
  const conns = Array.isArray(user?.storage_connections) ? user.storage_connections : [];
@@ -1061,10 +616,10 @@ async function getSrklAccounts2(userId, service, env) {
1061
616
  }
1062
617
  }
1063
618
  try {
1064
- const fs10 = await import("fs/promises");
1065
- const path10 = await import("path");
1066
- const accPath = path10.resolve(".skipo/accounts.json");
1067
- const raw = await fs10.readFile(accPath, "utf-8");
619
+ const fs9 = await import("fs/promises");
620
+ const path9 = await import("path");
621
+ const accPath = path9.resolve(".skipo/accounts.json");
622
+ const raw = await fs9.readFile(accPath, "utf-8");
1068
623
  const localUsers = JSON.parse(raw);
1069
624
  for (const u of Object.values(localUsers)) {
1070
625
  const conns = Array.isArray(u.storage_connections) ? u.storage_connections : [];
@@ -1171,155 +726,6 @@ async function getSrklStatus(env) {
1171
726
  }
1172
727
  }
1173
728
 
1174
- // src/core/workspaceFileCore.ts
1175
- async function getFs() {
1176
- if (typeof process !== "undefined" && process.versions?.node) {
1177
- try {
1178
- const mod = await import("fs");
1179
- return mod.promises || mod.default?.promises || mod;
1180
- } catch {
1181
- }
1182
- }
1183
- return null;
1184
- }
1185
- async function getPath() {
1186
- if (typeof process !== "undefined" && process.versions?.node) {
1187
- try {
1188
- const mod = await import("path");
1189
- return mod.default || mod;
1190
- } catch {
1191
- }
1192
- }
1193
- return null;
1194
- }
1195
- function getWorkspaceFolderPrefix(workspace) {
1196
- return workspace.localFolderKey || workspace.localFolderName || (workspace.workspacePath ? workspace.workspacePath.replace(/^\.\//, "") : workspace.id);
1197
- }
1198
- function isB2Workspace(workspace, connection) {
1199
- return connection?.provider === "backblaze_b2" || connection?.provider === "BACKBLAZE_B2" || workspace?.storageProvider === "backblaze_b2";
1200
- }
1201
- async function writeWorkspaceFile(workspace, connection, relativePath, content, contentType = "application/json") {
1202
- const cleanRel = relativePath.replace(/^\/+/, "");
1203
- const prefix = getWorkspaceFolderPrefix(workspace);
1204
- const buffer = typeof content === "string" ? new TextEncoder().encode(content) : content;
1205
- if (isB2Workspace(workspace, connection)) {
1206
- const keyId = connection.b2KeyId || connection.keyId;
1207
- const appKey = connection.b2ApplicationKey || connection.appKey;
1208
- const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
1209
- const fullKey = `${prefix}/${cleanRel}`;
1210
- await b2UploadFile2(keyId, appKey, bucketName, fullKey, buffer, contentType);
1211
- return;
1212
- }
1213
- const fs10 = await getFs();
1214
- const path10 = await getPath();
1215
- if (!fs10 || !path10) throw new Error("Local filesystem operations not supported in this runtime.");
1216
- const localDir = path10.resolve(process.cwd(), prefix, path10.dirname(cleanRel));
1217
- await fs10.mkdir(localDir, { recursive: true });
1218
- await fs10.writeFile(path10.resolve(process.cwd(), prefix, cleanRel), buffer);
1219
- }
1220
- async function readWorkspaceFile(workspace, connection, relativePath) {
1221
- const cleanRel = relativePath.replace(/^\/+/, "");
1222
- const prefix = getWorkspaceFolderPrefix(workspace);
1223
- if (isB2Workspace(workspace, connection)) {
1224
- const keyId = connection.b2KeyId || connection.keyId;
1225
- const appKey = connection.b2ApplicationKey || connection.appKey;
1226
- const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
1227
- const fullKey = `${prefix}/${cleanRel}`;
1228
- return await b2DownloadFile2(keyId, appKey, bucketName, fullKey);
1229
- }
1230
- const fs10 = await getFs();
1231
- const path10 = await getPath();
1232
- if (!fs10 || !path10) return null;
1233
- const fullPath = path10.resolve(process.cwd(), prefix, cleanRel);
1234
- try {
1235
- const buf = await fs10.readFile(fullPath);
1236
- return new Uint8Array(buf);
1237
- } catch {
1238
- return null;
1239
- }
1240
- }
1241
- async function deleteWorkspaceFile(workspace, connection, relativePath) {
1242
- const cleanRel = relativePath.replace(/^\/+/, "");
1243
- const prefix = getWorkspaceFolderPrefix(workspace);
1244
- if (isB2Workspace(workspace, connection)) {
1245
- const keyId = connection.b2KeyId || connection.keyId;
1246
- const appKey = connection.b2ApplicationKey || connection.appKey;
1247
- const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
1248
- const fullKey = `${prefix}/${cleanRel}`;
1249
- const files = await b2ListFileNames2(keyId, appKey, bucketName, fullKey);
1250
- const match = files.find((f) => f.fileName === fullKey);
1251
- if (match) {
1252
- await b2DeleteFileVersion(keyId, appKey, match.fileName, match.fileId);
1253
- return true;
1254
- }
1255
- return false;
1256
- }
1257
- const fs10 = await getFs();
1258
- const path10 = await getPath();
1259
- if (!fs10 || !path10) return false;
1260
- const fullPath = path10.resolve(process.cwd(), prefix, cleanRel);
1261
- try {
1262
- await fs10.unlink(fullPath);
1263
- return true;
1264
- } catch {
1265
- return false;
1266
- }
1267
- }
1268
- async function listWorkspaceFiles(workspace, connection, subPrefix = "") {
1269
- const folderPrefix = getWorkspaceFolderPrefix(workspace);
1270
- const targetPrefix = subPrefix ? `${folderPrefix}/${subPrefix.replace(/^\/+/, "")}` : `${folderPrefix}/`;
1271
- if (isB2Workspace(workspace, connection)) {
1272
- const keyId = connection.b2KeyId || connection.keyId;
1273
- const appKey = connection.b2ApplicationKey || connection.appKey;
1274
- const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
1275
- const rawFiles = await b2ListFileNames2(keyId, appKey, bucketName, targetPrefix);
1276
- return rawFiles.map((f) => ({
1277
- fileName: f.fileName.slice(folderPrefix.length + 1),
1278
- sizeBytes: f.contentLength,
1279
- uploadTimestamp: f.uploadTimestamp
1280
- }));
1281
- }
1282
- const fs10 = await getFs();
1283
- const path10 = await getPath();
1284
- if (!fs10 || !path10) return [];
1285
- const baseDir = path10.resolve(process.cwd(), folderPrefix, subPrefix);
1286
- try {
1287
- const entries = await fs10.readdir(baseDir, { withFileTypes: true, recursive: true });
1288
- const result = [];
1289
- for (const entry of entries) {
1290
- if (entry.isFile()) {
1291
- const full = path10.join(entry.parentPath || baseDir, entry.name);
1292
- const st = await fs10.stat(full);
1293
- result.push({
1294
- fileName: path10.relative(path10.resolve(process.cwd(), folderPrefix), full),
1295
- sizeBytes: st.size
1296
- });
1297
- }
1298
- }
1299
- return result;
1300
- } catch {
1301
- return [];
1302
- }
1303
- }
1304
- async function deleteWorkspaceFolder(workspace, connection) {
1305
- const prefix = getWorkspaceFolderPrefix(workspace);
1306
- if (isB2Workspace(workspace, connection)) {
1307
- const keyId = connection.b2KeyId || connection.keyId;
1308
- const appKey = connection.b2ApplicationKey || connection.appKey;
1309
- const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
1310
- await b2DeleteFolder(keyId, appKey, bucketName, prefix);
1311
- return;
1312
- }
1313
- const fs10 = await getFs();
1314
- const path10 = await getPath();
1315
- if (!fs10 || !path10) return;
1316
- const localDir = path10.resolve(process.cwd(), prefix);
1317
- try {
1318
- await fs10.rm(localDir, { recursive: true, force: true });
1319
- } catch {
1320
- }
1321
- }
1322
-
1323
729
  // src/core/credentialVaultCore.ts
1324
730
  var MANIFEST_PATH = "credentials/manifest.json";
1325
731
  var SECRET_PREFIX = "credentials/secrets";
@@ -2684,385 +2090,6 @@ async function matchGatewayIngressAsync(urlPath, search, env) {
2684
2090
  return null;
2685
2091
  }
2686
2092
 
2687
- // src/core/workspaceBucketSyncCore.ts
2688
- function resolveWorkspaceConnection(profiles, workspace) {
2689
- const depm = profiles.depm || {};
2690
- const conns = depm.storage_connections?.length ? depm.storage_connections : profiles.srkl?.storage_connections || profiles.docustack?.storage_connections || [];
2691
- return conns.find((c) => c.id === workspace.storageConnectionId) || conns.find((c) => c.role === "primary") || conns[0] || null;
2692
- }
2693
- async function syncWorkspaceCreateToBucket(workspace, connection, env) {
2694
- if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") return;
2695
- try {
2696
- const wsMeta = {
2697
- id: workspace.id,
2698
- name: workspace.name,
2699
- createdAt: workspace.createdAt,
2700
- updatedAt: workspace.updatedAt,
2701
- storageProvider: "backblaze_b2",
2702
- bucket: connection.b2BucketName || "ultrst-apps"
2703
- };
2704
- await writeWorkspaceFile(workspace, connection, ".bzEmpty", new Uint8Array(0), "application/x-empty");
2705
- await writeWorkspaceFile(workspace, connection, "workspace.json", JSON.stringify(wsMeta, null, 2), "application/json");
2706
- await writeWorkspaceFile(
2707
- workspace,
2708
- connection,
2709
- "ecosystem.json",
2710
- JSON.stringify({ apps: [], nodes: [], edges: [], ecosystems: [] }, null, 2),
2711
- "application/json"
2712
- );
2713
- } catch (err) {
2714
- console.warn("[WorkspaceBucketSync] Create sync error:", err.message);
2715
- }
2716
- }
2717
- async function syncWorkspaceDeleteToBucket(workspace, connection, env) {
2718
- if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") return;
2719
- try {
2720
- await deleteWorkspaceFolder(workspace, connection);
2721
- } catch (err) {
2722
- console.warn("[WorkspaceBucketSync] Delete sync error:", err.message);
2723
- }
2724
- }
2725
- async function syncWorkspaceDataToBucket(workspace, connection, data, env) {
2726
- if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") return;
2727
- try {
2728
- await writeWorkspaceFile(workspace, connection, "ecosystem.json", JSON.stringify(data, null, 2), "application/json");
2729
- } catch (err) {
2730
- console.warn("[WorkspaceBucketSync] Data sync error:", err.message);
2731
- }
2732
- }
2733
-
2734
- // src/core/workspaceDataCore.ts
2735
- async function resolveUserRow(userId, env) {
2736
- if (userId) {
2737
- const res = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1 OR email = $1 LIMIT 1`, [userId], env);
2738
- if (res.rows.length > 0) return res.rows[0];
2739
- }
2740
- return null;
2741
- }
2742
- async function loadWorkspaceData(userId, workspaceId, env) {
2743
- if (!workspaceId) return { success: false, error: "workspaceId required", workspace: null };
2744
- const user = await resolveUserRow(userId, env);
2745
- if (!user) return { success: false, error: "User not found", workspace: null };
2746
- const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
2747
- const depm = profiles.depm || {};
2748
- const workspaceDataMap = depm.workspace_data || {};
2749
- let data = workspaceDataMap[workspaceId];
2750
- const isEmptyData = !data || (!data.nodes || data.nodes.length === 0) && (!data.apps || data.apps.length === 0);
2751
- if (isEmptyData) {
2752
- const ws = (depm.workspaces || []).find((w) => w.id === workspaceId);
2753
- if (ws) {
2754
- const conn = resolveWorkspaceConnection(profiles, ws);
2755
- if (conn) {
2756
- try {
2757
- const fileBuf = await readWorkspaceFile(ws, conn, "ecosystem.json");
2758
- if (fileBuf && fileBuf.length > 0) {
2759
- const parsed = JSON.parse(new TextDecoder().decode(fileBuf));
2760
- if (parsed.nodes && parsed.nodes.length > 0 || parsed.apps && parsed.apps.length > 0) {
2761
- if (!profiles.depm) profiles.depm = {};
2762
- if (!profiles.depm.workspace_data) profiles.depm.workspace_data = {};
2763
- profiles.depm.workspace_data[workspaceId] = parsed;
2764
- data = parsed;
2765
- await queryUnifiedDb(
2766
- `UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`,
2767
- [JSON.stringify(profiles), user.id],
2768
- env
2769
- );
2770
- return { success: true, workspace: data, workspaceId };
2771
- }
2772
- }
2773
- } catch (err) {
2774
- console.warn("[WorkspaceDataCore] Storage recovery check:", err.message);
2775
- }
2776
- }
2777
- }
2778
- }
2779
- if (!data) {
2780
- const ws = (depm.workspaces || []).find((w) => w.id === workspaceId);
2781
- const ecoName = ws?.ecosystemName || `${ws?.name || "Workspace"} Ecosystem`;
2782
- const fallback = {
2783
- apps: [],
2784
- accounts: [],
2785
- deployments: [],
2786
- nodes: [],
2787
- edges: [],
2788
- ecosystems: [
2789
- {
2790
- id: `eco_${workspaceId}`,
2791
- name: ecoName,
2792
- icon: "Globe",
2793
- health: "HEALTHY",
2794
- status: "SYSTEM ONLINE",
2795
- connectedAppsCount: 0,
2796
- meshLinkCount: 0,
2797
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2798
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2799
- }
2800
- ],
2801
- ecosystemName: ecoName,
2802
- ecosystemNodes: [
2803
- {
2804
- id: `node_eco_root_${workspaceId}`,
2805
- type: "ECOSYSTEM",
2806
- name: ecoName,
2807
- x: 0,
2808
- y: 0,
2809
- appId: "__ecosystem__",
2810
- icon: "Globe",
2811
- ecosystemData: { connectedAppsCount: 0, health: "HEALTHY", status: "SYSTEM ONLINE" }
2812
- }
2813
- ],
2814
- ecosystemEdges: []
2815
- };
2816
- return { success: true, workspace: fallback, workspaceId };
2817
- }
2818
- return { success: true, workspace: data, workspaceId };
2819
- }
2820
- async function saveWorkspaceData(userId, workspaceId, data, env) {
2821
- if (!workspaceId) return { success: false, error: "workspaceId required" };
2822
- const user = await resolveUserRow(userId, env);
2823
- if (!user) return { success: false, error: "User not found" };
2824
- const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
2825
- if (!profiles.depm) profiles.depm = {};
2826
- if (!profiles.depm.workspace_data) profiles.depm.workspace_data = {};
2827
- const existing = profiles.depm.workspace_data[workspaceId];
2828
- const incomingEmpty = (!data.nodes || data.nodes.length === 0) && (!data.apps || data.apps.length === 0);
2829
- const existingHasData = existing && (existing.nodes && existing.nodes.length > 0 || existing.apps && existing.apps.length > 0);
2830
- if (incomingEmpty && existingHasData && !data._forceEmptySave) {
2831
- console.warn(`[WorkspaceDataCore] Blocked empty overwrite for workspace ${workspaceId}.`);
2832
- return { success: true, workspaceId, workspace: existing, protected: true };
2833
- }
2834
- profiles.depm.workspace_data[workspaceId] = data;
2835
- if (Array.isArray(profiles.depm.workspaces)) {
2836
- const ws2 = profiles.depm.workspaces.find((w) => w.id === workspaceId);
2837
- if (ws2) {
2838
- ws2.appsCount = Array.isArray(data.apps) ? data.apps.length : 0;
2839
- ws2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2840
- }
2841
- }
2842
- await queryUnifiedDb(
2843
- `UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`,
2844
- [JSON.stringify(profiles), user.id],
2845
- env
2846
- );
2847
- const ws = Array.isArray(profiles.depm?.workspaces) ? profiles.depm.workspaces.find((w) => w.id === workspaceId) : null;
2848
- if (ws) {
2849
- const conn = resolveWorkspaceConnection(profiles, ws);
2850
- await syncWorkspaceDataToBucket(ws, conn, data, env);
2851
- }
2852
- return { success: true, workspaceId, workspace: data };
2853
- }
2854
- async function renameAppInWorkspaceData(userId, workspaceId, appId, newName, env) {
2855
- if (!workspaceId || !appId || !newName) {
2856
- return { success: false, error: "workspaceId, appId and newName required" };
2857
- }
2858
- const loadRes = await loadWorkspaceData(userId, workspaceId, env);
2859
- if (!loadRes.success || !loadRes.workspace) return loadRes;
2860
- const wsData = loadRes.workspace;
2861
- const trimmed = newName.trim();
2862
- const folderName = trimmed.replace(/[/\\?%*:|"<>]/g, "_");
2863
- if (Array.isArray(wsData.apps)) {
2864
- const targetApp = wsData.apps.find((a) => a.id === appId);
2865
- if (targetApp) {
2866
- targetApp.name = trimmed;
2867
- targetApp.folderName = folderName;
2868
- targetApp.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2869
- }
2870
- }
2871
- if (Array.isArray(wsData.nodes)) {
2872
- wsData.nodes = wsData.nodes.map((n) => {
2873
- if (n.appId === appId && (n.type === "APP" || n.type === "APP_NODE" || n.type === "APP_GRAPH_NODE")) {
2874
- return { ...n, name: trimmed, appData: n.appData ? { ...n.appData, name: trimmed, folderName } : void 0 };
2875
- }
2876
- return n;
2877
- });
2878
- }
2879
- return await saveWorkspaceData(userId, workspaceId, wsData, env);
2880
- }
2881
-
2882
- // src/core/workspaceStorageDetailsCore.ts
2883
- async function getWorkspaceStorageDetails(workspaceId, env) {
2884
- if (!workspaceId) return { success: false, error: "workspaceId is required" };
2885
- const { workspace, connection } = await findUserWorkspace(workspaceId, env);
2886
- const folderPrefix = workspace?.localFolderKey || workspace?.localFolderName || (workspace?.workspacePath ? workspace.workspacePath.replace(/^\.\//, "") : workspaceId);
2887
- if (!connection || connection.provider !== "backblaze_b2" && connection.provider !== "BACKBLAZE_B2") {
2888
- return {
2889
- success: true,
2890
- workspaceId,
2891
- storageProvider: workspace?.storageProvider || "local_fs",
2892
- connection: connection || null,
2893
- folderPrefix,
2894
- localStats: { exists: false, appsCount: 0, ecosystemsCount: 0 },
2895
- bucket: null,
2896
- files: []
2897
- };
2898
- }
2899
- const keyId = connection.b2KeyId || connection.keyId;
2900
- const applicationKey = connection.b2ApplicationKey || connection.appKey;
2901
- const bucketName = connection.b2BucketName || workspace.b2BucketName || "ultrst-apps";
2902
- try {
2903
- const buckets = await b2ListBuckets2(keyId, applicationKey, void 0, bucketName);
2904
- const bucketObj = buckets.find((b) => b.bucketName === bucketName);
2905
- if (!bucketObj) {
2906
- return {
2907
- success: true,
2908
- workspaceId,
2909
- storageProvider: "backblaze_b2",
2910
- connection: { id: connection.id, label: connection.label, provider: connection.provider },
2911
- folderPrefix,
2912
- bucket: { name: bucketName, status: "BUCKET_NOT_FOUND" },
2913
- files: []
2914
- };
2915
- }
2916
- const rawFiles = await b2ListFileNames2(keyId, applicationKey, bucketName, `${folderPrefix}/`);
2917
- return {
2918
- success: true,
2919
- workspaceId,
2920
- storageProvider: "backblaze_b2",
2921
- connection: { id: connection.id, label: connection.label, provider: connection.provider },
2922
- folderPrefix,
2923
- bucket: {
2924
- id: bucketObj.bucketId,
2925
- name: bucketObj.bucketName,
2926
- type: bucketObj.bucketType,
2927
- endpoint: connection.b2Endpoint || "s3.eu-central-003.backblazeb2.com",
2928
- region: connection.b2Region || "eu-central-003",
2929
- status: "CONNECTED"
2930
- },
2931
- fileCount: rawFiles.length,
2932
- files: rawFiles.map((f) => ({
2933
- fileName: f.fileName,
2934
- sizeBytes: f.contentLength,
2935
- uploadTimestamp: f.uploadTimestamp
2936
- }))
2937
- };
2938
- } catch (err) {
2939
- return {
2940
- success: false,
2941
- workspaceId,
2942
- storageProvider: "backblaze_b2",
2943
- error: err.message,
2944
- folderPrefix
2945
- };
2946
- }
2947
- }
2948
-
2949
- // src/core/workspaceCore.ts
2950
- async function findUserWorkspace(workspaceId, env) {
2951
- const res = await queryUnifiedDb(`SELECT id, email, username, app_profiles FROM "ultrst-users"`, [], env);
2952
- for (const user of res.rows) {
2953
- const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
2954
- const depm = profiles.depm || {};
2955
- const workspaces = Array.isArray(depm.workspaces) ? depm.workspaces : [];
2956
- const ws = workspaces.find((w) => w.id === workspaceId);
2957
- if (ws) {
2958
- const conns = depm.storage_connections || profiles.srkl?.storage_connections || profiles.docustack?.storage_connections || [];
2959
- const conn = conns.find((c) => c.id === ws.storageConnectionId) || conns.find((c) => c.role === "primary") || conns[0] || null;
2960
- return { user, workspace: ws, connection: conn };
2961
- }
2962
- }
2963
- return { user: null, workspace: null, connection: null };
2964
- }
2965
- async function getWorkspaces(userId, env) {
2966
- if (!userId) {
2967
- return { success: true, workspaces: [], activeWorkspaceId: null, storage_connections: [] };
2968
- }
2969
- const res = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1 OR email = $1 LIMIT 1`, [userId], env);
2970
- const userRow = res.rows[0];
2971
- if (!userRow) {
2972
- return { success: true, workspaces: [], activeWorkspaceId: null, storage_connections: [] };
2973
- }
2974
- const profiles = typeof userRow.app_profiles === "string" ? JSON.parse(userRow.app_profiles) : userRow.app_profiles || {};
2975
- const depm = profiles.depm || {};
2976
- const workspaces = Array.isArray(depm.workspaces) ? depm.workspaces : [];
2977
- const activeWorkspaceId = depm.activeWorkspaceId || null;
2978
- const conns = depm.storage_connections?.length ? depm.storage_connections : profiles.srkl?.storage_connections || [];
2979
- return { success: true, workspaces, activeWorkspaceId, storage_connections: conns };
2980
- }
2981
- async function createWorkspace(userId, wsData, env) {
2982
- const uRes = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1`, [userId], env);
2983
- const user = uRes.rows[0];
2984
- if (!user) throw new Error("User account not found.");
2985
- const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
2986
- if (!profiles.depm) profiles.depm = {};
2987
- const workspaces = Array.isArray(profiles.depm.workspaces) ? profiles.depm.workspaces : [];
2988
- const wsName = (wsData.name || "New Workspace").trim();
2989
- const wsId = wsData.id || `ws-depm-${Date.now()}`;
2990
- const userName = user.username || user.name || "user";
2991
- const folderKey = `ultrst_depm_${userName.toLowerCase().replace(/[^a-z0-9]/g, "_")}_${wsName.toLowerCase().replace(/[^a-z0-9]/g, "_")}`;
2992
- const newWs = {
2993
- ...wsData,
2994
- id: wsId,
2995
- name: wsName,
2996
- localFolderName: folderKey,
2997
- localFolderKey: folderKey,
2998
- workspacePath: `./${folderKey}`,
2999
- storageProvider: wsData.storageProvider || "backblaze_b2",
3000
- b2BucketName: wsData.b2BucketName || "ultrst-apps",
3001
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3002
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3003
- };
3004
- workspaces.push(newWs);
3005
- profiles.depm.workspaces = workspaces;
3006
- profiles.depm.activeWorkspaceId = wsId;
3007
- await queryUnifiedDb(`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`, [JSON.stringify(profiles), user.id], env);
3008
- const conn = resolveWorkspaceConnection(profiles, newWs);
3009
- await syncWorkspaceCreateToBucket(newWs, conn, env);
3010
- return { success: true, workspace: newWs, workspaces };
3011
- }
3012
- async function updateWorkspace(userId, params, env) {
3013
- const uRes = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1`, [userId], env);
3014
- const user = uRes.rows[0];
3015
- if (!user) throw new Error("User not found.");
3016
- const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
3017
- if (!profiles.depm) profiles.depm = {};
3018
- const workspaces = Array.isArray(profiles.depm.workspaces) ? profiles.depm.workspaces : [];
3019
- const { action, workspaceId, updates } = params;
3020
- const targetId = workspaceId || updates?.id;
3021
- const match = workspaces.find((w) => w.id === targetId);
3022
- if (!match) throw new Error("Workspace not found.");
3023
- if (action === "switch") {
3024
- profiles.depm.activeWorkspaceId = targetId;
3025
- } else {
3026
- const safeUpdates = { ...updates || params };
3027
- delete safeUpdates.storageProvider;
3028
- delete safeUpdates.storageConnectionId;
3029
- delete safeUpdates.b2BucketName;
3030
- delete safeUpdates.b2Endpoint;
3031
- delete safeUpdates.b2Region;
3032
- delete safeUpdates.localFolderName;
3033
- delete safeUpdates.localFolderKey;
3034
- delete safeUpdates.workspacePath;
3035
- delete safeUpdates.primaryStorage;
3036
- delete safeUpdates.id;
3037
- delete safeUpdates.action;
3038
- delete safeUpdates.workspaceId;
3039
- Object.assign(match, safeUpdates, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
3040
- }
3041
- await queryUnifiedDb(`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`, [JSON.stringify(profiles), user.id], env);
3042
- return { success: true, workspace: match, workspaces };
3043
- }
3044
- async function deleteWorkspace(userId, wsId, env) {
3045
- const uRes = await queryUnifiedDb(`SELECT * FROM "ultrst-users" WHERE id = $1`, [userId], env);
3046
- const user = uRes.rows[0];
3047
- if (!user) throw new Error("User not found.");
3048
- const profiles = typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {};
3049
- if (!profiles.depm?.workspaces) return { success: true, workspaces: [] };
3050
- const targetWs = profiles.depm.workspaces.find((w) => w.id === wsId);
3051
- const conn = targetWs ? resolveWorkspaceConnection(profiles, targetWs) : null;
3052
- profiles.depm.workspaces = profiles.depm.workspaces.filter((w) => w.id !== wsId);
3053
- if (profiles.depm.activeWorkspaceId === wsId) {
3054
- profiles.depm.activeWorkspaceId = profiles.depm.workspaces[0]?.id || null;
3055
- }
3056
- if (profiles.depm.workspace_data?.[wsId]) {
3057
- delete profiles.depm.workspace_data[wsId];
3058
- }
3059
- await queryUnifiedDb(`UPDATE "ultrst-users" SET app_profiles = $1, updated_at = NOW() WHERE id = $2`, [JSON.stringify(profiles), user.id], env);
3060
- if (targetWs && conn) {
3061
- await syncWorkspaceDeleteToBucket(targetWs, conn, env);
3062
- }
3063
- return { success: true, workspaces: profiles.depm.workspaces };
3064
- }
3065
-
3066
2093
  // src/core/cloudflareIntegrationCore.ts
3067
2094
  var cfFetcher = new CloudflareResourceFetcher();
3068
2095
  function buildHeaders(token, authKey, authEmail) {
@@ -3529,8 +2556,8 @@ function createWavBuffer(sampleRate, numChannels, samples) {
3529
2556
  }
3530
2557
 
3531
2558
  // src/server/sampleGenerator.ts
3532
- import fs3 from "fs";
3533
- import path3 from "path";
2559
+ import fs2 from "fs";
2560
+ import path2 from "path";
3534
2561
  var GLOBAL_SAMPLE_DEFS = [
3535
2562
  {
3536
2563
  filename: "808_sub_boom.wav",
@@ -3627,13 +2654,13 @@ var GLOBAL_SAMPLE_DEFS = [
3627
2654
  }
3628
2655
  ];
3629
2656
  async function ensureGlobalSamples(globalSamplesDir) {
3630
- if (!fs3.existsSync(globalSamplesDir)) {
3631
- fs3.mkdirSync(globalSamplesDir, { recursive: true });
2657
+ if (!fs2.existsSync(globalSamplesDir)) {
2658
+ fs2.mkdirSync(globalSamplesDir, { recursive: true });
3632
2659
  }
3633
2660
  const sampleRate = 44100;
3634
2661
  for (const def of GLOBAL_SAMPLE_DEFS) {
3635
- const filePath = path3.join(globalSamplesDir, def.filename);
3636
- if (!fs3.existsSync(filePath)) {
2662
+ const filePath = path2.join(globalSamplesDir, def.filename);
2663
+ if (!fs2.existsSync(filePath)) {
3637
2664
  const numSamples = Math.floor(sampleRate * def.durationSec);
3638
2665
  const chL = new Float32Array(numSamples);
3639
2666
  const chR = new Float32Array(numSamples);
@@ -3645,16 +2672,16 @@ async function ensureGlobalSamples(globalSamplesDir) {
3645
2672
  chR[i] = r;
3646
2673
  }
3647
2674
  const wavBuf = createWavBuffer(sampleRate, 2, [chL, chR]);
3648
- await fs3.promises.writeFile(filePath, wavBuf);
2675
+ await fs2.promises.writeFile(filePath, wavBuf);
3649
2676
  }
3650
2677
  }
3651
2678
  }
3652
2679
 
3653
2680
  // src/server/services/assetPoolService.ts
3654
- import fs4 from "fs";
3655
- import path4 from "path";
3656
- var WORKSPACE_ASSETS_ROOT = path4.join(process.cwd(), "assets");
3657
- var PROJECTS_ROOT = path4.join(process.cwd(), "projects");
2681
+ import fs3 from "fs";
2682
+ import path3 from "path";
2683
+ var WORKSPACE_ASSETS_ROOT = path3.join(process.cwd(), "assets");
2684
+ var PROJECTS_ROOT = path3.join(process.cwd(), "projects");
3658
2685
  var SUPPORTED_AUDIO_EXTS = [
3659
2686
  ".wav",
3660
2687
  ".mp3",
@@ -3672,27 +2699,27 @@ var SUPPORTED_AUDIO_EXTS = [
3672
2699
  var SUPPORTED_MIDI_EXTS = [".mid", ".midi", ".json"];
3673
2700
  async function ensureAssetPoolDirectories(rootDir) {
3674
2701
  const dirs = [
3675
- path4.join(rootDir, "audio", "stereo", "default"),
3676
- path4.join(rootDir, "audio", "stereo", "userdefined"),
3677
- path4.join(rootDir, "audio", "mono", "default"),
3678
- path4.join(rootDir, "audio", "mono", "userdefined"),
3679
- path4.join(rootDir, "midi", "default"),
3680
- path4.join(rootDir, "midi", "userdefined")
2702
+ path3.join(rootDir, "audio", "stereo", "default"),
2703
+ path3.join(rootDir, "audio", "stereo", "userdefined"),
2704
+ path3.join(rootDir, "audio", "mono", "default"),
2705
+ path3.join(rootDir, "audio", "mono", "userdefined"),
2706
+ path3.join(rootDir, "midi", "default"),
2707
+ path3.join(rootDir, "midi", "userdefined")
3681
2708
  ];
3682
2709
  for (const dir of dirs) {
3683
- if (!fs4.existsSync(dir)) {
3684
- await fs4.promises.mkdir(dir, { recursive: true });
2710
+ if (!fs3.existsSync(dir)) {
2711
+ await fs3.promises.mkdir(dir, { recursive: true });
3685
2712
  }
3686
2713
  }
3687
2714
  }
3688
2715
  async function seedDefaultAssets() {
3689
2716
  await ensureAssetPoolDirectories(WORKSPACE_ASSETS_ROOT);
3690
- const stereoDefaultDir = path4.join(WORKSPACE_ASSETS_ROOT, "audio", "stereo", "default");
3691
- const monoDefaultDir = path4.join(WORKSPACE_ASSETS_ROOT, "audio", "mono", "default");
3692
- const midiDefaultDir = path4.join(WORKSPACE_ASSETS_ROOT, "midi", "default");
2717
+ const stereoDefaultDir = path3.join(WORKSPACE_ASSETS_ROOT, "audio", "stereo", "default");
2718
+ const monoDefaultDir = path3.join(WORKSPACE_ASSETS_ROOT, "audio", "mono", "default");
2719
+ const midiDefaultDir = path3.join(WORKSPACE_ASSETS_ROOT, "midi", "default");
3693
2720
  const sampleRate = 44100;
3694
- const subBoomPath = path4.join(stereoDefaultDir, "808_sub_boom.wav");
3695
- if (!fs4.existsSync(subBoomPath)) {
2721
+ const subBoomPath = path3.join(stereoDefaultDir, "808_sub_boom.wav");
2722
+ if (!fs3.existsSync(subBoomPath)) {
3696
2723
  const numSamples = Math.floor(sampleRate * 1.8);
3697
2724
  const chL = new Float32Array(numSamples);
3698
2725
  const chR = new Float32Array(numSamples);
@@ -3703,10 +2730,10 @@ async function seedDefaultAssets() {
3703
2730
  chL[i] = s;
3704
2731
  chR[i] = s;
3705
2732
  }
3706
- await fs4.promises.writeFile(subBoomPath, createWavBuffer(sampleRate, 2, [chL, chR]));
2733
+ await fs3.promises.writeFile(subBoomPath, createWavBuffer(sampleRate, 2, [chL, chR]));
3707
2734
  }
3708
- const leadPath = path4.join(stereoDefaultDir, "celestial_lead.wav");
3709
- if (!fs4.existsSync(leadPath)) {
2735
+ const leadPath = path3.join(stereoDefaultDir, "celestial_lead.wav");
2736
+ if (!fs3.existsSync(leadPath)) {
3710
2737
  const numSamples = Math.floor(sampleRate * 2.5);
3711
2738
  const chL = new Float32Array(numSamples);
3712
2739
  const chR = new Float32Array(numSamples);
@@ -3720,10 +2747,10 @@ async function seedDefaultAssets() {
3720
2747
  chL[i] = s1 * env;
3721
2748
  chR[i] = (s1 + s2 * 0.5) * env;
3722
2749
  }
3723
- await fs4.promises.writeFile(leadPath, createWavBuffer(sampleRate, 2, [chL, chR]));
2750
+ await fs3.promises.writeFile(leadPath, createWavBuffer(sampleRate, 2, [chL, chR]));
3724
2751
  }
3725
- const padPath = path4.join(stereoDefaultDir, "ethereal_pad.wav");
3726
- if (!fs4.existsSync(padPath)) {
2752
+ const padPath = path3.join(stereoDefaultDir, "ethereal_pad.wav");
2753
+ if (!fs3.existsSync(padPath)) {
3727
2754
  const numSamples = Math.floor(sampleRate * 3);
3728
2755
  const chL = new Float32Array(numSamples);
3729
2756
  const chR = new Float32Array(numSamples);
@@ -3742,10 +2769,10 @@ async function seedDefaultAssets() {
3742
2769
  chL[i] = left * env;
3743
2770
  chR[i] = right * env;
3744
2771
  }
3745
- await fs4.promises.writeFile(padPath, createWavBuffer(sampleRate, 2, [chL, chR]));
2772
+ await fs3.promises.writeFile(padPath, createWavBuffer(sampleRate, 2, [chL, chR]));
3746
2773
  }
3747
- const snarePath = path4.join(monoDefaultDir, "snare_snappy.wav");
3748
- if (!fs4.existsSync(snarePath)) {
2774
+ const snarePath = path3.join(monoDefaultDir, "snare_snappy.wav");
2775
+ if (!fs3.existsSync(snarePath)) {
3749
2776
  const numSamples = Math.floor(sampleRate * 0.8);
3750
2777
  const chM = new Float32Array(numSamples);
3751
2778
  for (let i = 0; i < numSamples; i++) {
@@ -3755,10 +2782,10 @@ async function seedDefaultAssets() {
3755
2782
  const noise = (Math.random() * 2 - 1) * Math.exp(-p * 4.5) * 0.6;
3756
2783
  chM[i] = tone + noise;
3757
2784
  }
3758
- await fs4.promises.writeFile(snarePath, createWavBuffer(sampleRate, 1, [chM]));
2785
+ await fs3.promises.writeFile(snarePath, createWavBuffer(sampleRate, 1, [chM]));
3759
2786
  }
3760
- const hihatPath = path4.join(monoDefaultDir, "hihat_closed.wav");
3761
- if (!fs4.existsSync(hihatPath)) {
2787
+ const hihatPath = path3.join(monoDefaultDir, "hihat_closed.wav");
2788
+ if (!fs3.existsSync(hihatPath)) {
3762
2789
  const numSamples = Math.floor(sampleRate * 0.25);
3763
2790
  const chM = new Float32Array(numSamples);
3764
2791
  for (let i = 0; i < numSamples; i++) {
@@ -3766,10 +2793,10 @@ async function seedDefaultAssets() {
3766
2793
  const noise = (Math.random() * 2 - 1) * Math.exp(-p * 20);
3767
2794
  chM[i] = noise * 0.5;
3768
2795
  }
3769
- await fs4.promises.writeFile(hihatPath, createWavBuffer(sampleRate, 1, [chM]));
2796
+ await fs3.promises.writeFile(hihatPath, createWavBuffer(sampleRate, 1, [chM]));
3770
2797
  }
3771
- const midiChordPath = path4.join(midiDefaultDir, "lydian_celestial_pad.json");
3772
- if (!fs4.existsSync(midiChordPath)) {
2798
+ const midiChordPath = path3.join(midiDefaultDir, "lydian_celestial_pad.json");
2799
+ if (!fs3.existsSync(midiChordPath)) {
3773
2800
  const starterMidiPattern = {
3774
2801
  id: "pat-default-lydian",
3775
2802
  name: "Lydian Celestial Pad",
@@ -3782,31 +2809,31 @@ async function seedDefaultAssets() {
3782
2809
  { note: 71, name: "B4", velocity: 0.75, time: 0, duration: 3.8 }
3783
2810
  ]
3784
2811
  };
3785
- await fs4.promises.writeFile(midiChordPath, JSON.stringify(starterMidiPattern, null, 2), "utf-8");
2812
+ await fs3.promises.writeFile(midiChordPath, JSON.stringify(starterMidiPattern, null, 2), "utf-8");
3786
2813
  }
3787
2814
  await ensureProjectAssets("default-kingdom");
3788
2815
  }
3789
2816
  async function ensureProjectAssets(projectId) {
3790
- const safeId = path4.basename(projectId);
3791
- const projectAssetsDir = path4.join(PROJECTS_ROOT, safeId, "assets");
2817
+ const safeId = path3.basename(projectId);
2818
+ const projectAssetsDir = path3.join(PROJECTS_ROOT, safeId, "assets");
3792
2819
  await ensureAssetPoolDirectories(projectAssetsDir);
3793
2820
  return projectAssetsDir;
3794
2821
  }
3795
2822
  function getAssetPoolLocationPath(scope, projectId, dataType, channelType, subfolder = "userdefined") {
3796
- const root = scope === "project" && projectId ? path4.join(PROJECTS_ROOT, path4.basename(projectId), "assets") : WORKSPACE_ASSETS_ROOT;
3797
- const safeSub = path4.basename(subfolder).replace(/[^a-zA-Z0-9._-]/g, "_");
2823
+ const root = scope === "project" && projectId ? path3.join(PROJECTS_ROOT, path3.basename(projectId), "assets") : WORKSPACE_ASSETS_ROOT;
2824
+ const safeSub = path3.basename(subfolder).replace(/[^a-zA-Z0-9._-]/g, "_");
3798
2825
  if (dataType === "audio") {
3799
2826
  const safeChannel = channelType === "mono" ? "mono" : "stereo";
3800
- return path4.join(root, "audio", safeChannel, safeSub);
2827
+ return path3.join(root, "audio", safeChannel, safeSub);
3801
2828
  } else {
3802
- return path4.join(root, "midi", safeSub);
2829
+ return path3.join(root, "midi", safeSub);
3803
2830
  }
3804
2831
  }
3805
2832
  function getAssetPoolPublicUrl(scope, projectId, dataType, channelType, subfolder, filename) {
3806
- const safeFilename = path4.basename(filename);
3807
- const safeSub = path4.basename(subfolder);
2833
+ const safeFilename = path3.basename(filename);
2834
+ const safeSub = path3.basename(subfolder);
3808
2835
  if (scope === "project" && projectId) {
3809
- const safeProj = path4.basename(projectId);
2836
+ const safeProj = path3.basename(projectId);
3810
2837
  if (dataType === "audio") {
3811
2838
  const channel = channelType === "mono" ? "mono" : "stereo";
3812
2839
  return `/projects/${safeProj}/assets/audio/${channel}/${safeSub}/${safeFilename}`;
@@ -3828,31 +2855,31 @@ async function scanAssetPool(scope, projectId) {
3828
2855
  } else {
3829
2856
  await ensureAssetPoolDirectories(WORKSPACE_ASSETS_ROOT);
3830
2857
  }
3831
- const rootDir = scope === "project" && projectId ? path4.join(PROJECTS_ROOT, path4.basename(projectId), "assets") : WORKSPACE_ASSETS_ROOT;
2858
+ const rootDir = scope === "project" && projectId ? path3.join(PROJECTS_ROOT, path3.basename(projectId), "assets") : WORKSPACE_ASSETS_ROOT;
3832
2859
  const folders = [];
3833
2860
  const items = [];
3834
2861
  async function scanCategoryFolder(dataType, channelType) {
3835
- const catDir = dataType === "audio" ? path4.join(rootDir, "audio", channelType === "mono" ? "mono" : "stereo") : path4.join(rootDir, "midi");
3836
- if (!fs4.existsSync(catDir)) {
3837
- await fs4.promises.mkdir(catDir, { recursive: true });
2862
+ const catDir = dataType === "audio" ? path3.join(rootDir, "audio", channelType === "mono" ? "mono" : "stereo") : path3.join(rootDir, "midi");
2863
+ if (!fs3.existsSync(catDir)) {
2864
+ await fs3.promises.mkdir(catDir, { recursive: true });
3838
2865
  }
3839
- const subentries = await fs4.promises.readdir(catDir, { withFileTypes: true });
2866
+ const subentries = await fs3.promises.readdir(catDir, { withFileTypes: true });
3840
2867
  for (const entry of subentries) {
3841
2868
  if (!entry.isDirectory()) continue;
3842
2869
  const subfolderName = entry.name;
3843
- const subfolderDir = path4.join(catDir, subfolderName);
3844
- const files = await fs4.promises.readdir(subfolderDir);
2870
+ const subfolderDir = path3.join(catDir, subfolderName);
2871
+ const files = await fs3.promises.readdir(subfolderDir);
3845
2872
  let validFileCount = 0;
3846
2873
  for (const file of files) {
3847
2874
  if (file.startsWith(".")) continue;
3848
- const ext = path4.extname(file).toLowerCase();
2875
+ const ext = path3.extname(file).toLowerCase();
3849
2876
  const isAudio = SUPPORTED_AUDIO_EXTS.includes(ext);
3850
2877
  const isMidi = SUPPORTED_MIDI_EXTS.includes(ext);
3851
2878
  if (dataType === "audio" && !isAudio || dataType === "midi" && !isMidi) continue;
3852
2879
  validFileCount++;
3853
- const filePath = path4.join(subfolderDir, file);
3854
- const stat = await fs4.promises.stat(filePath);
3855
- const cleanName = path4.basename(file, ext).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2880
+ const filePath = path3.join(subfolderDir, file);
2881
+ const stat = await fs3.promises.stat(filePath);
2882
+ const cleanName = path3.basename(file, ext).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
3856
2883
  const relPath = dataType === "audio" ? `audio/${channelType === "mono" ? "mono" : "stereo"}/${subfolderName}/${file}` : `midi/${subfolderName}/${file}`;
3857
2884
  const url = getAssetPoolPublicUrl(
3858
2885
  scope,
@@ -3893,7 +2920,7 @@ async function scanAssetPool(scope, projectId) {
3893
2920
  return { folders, items };
3894
2921
  }
3895
2922
  async function createAssetSubfolder(params) {
3896
- const safeName = path4.basename(params.folderName).trim().replace(/[^a-zA-Z0-9._-]/g, "_");
2923
+ const safeName = path3.basename(params.folderName).trim().replace(/[^a-zA-Z0-9._-]/g, "_");
3897
2924
  if (!safeName) throw new Error("Invalid subfolder name");
3898
2925
  const dirPath = getAssetPoolLocationPath(
3899
2926
  params.scope,
@@ -3902,8 +2929,8 @@ async function createAssetSubfolder(params) {
3902
2929
  params.channelType,
3903
2930
  safeName
3904
2931
  );
3905
- if (!fs4.existsSync(dirPath)) {
3906
- await fs4.promises.mkdir(dirPath, { recursive: true });
2932
+ if (!fs3.existsSync(dirPath)) {
2933
+ await fs3.promises.mkdir(dirPath, { recursive: true });
3907
2934
  }
3908
2935
  return {
3909
2936
  name: safeName,
@@ -3922,16 +2949,16 @@ async function saveAssetToPool(params) {
3922
2949
  params.channelType,
3923
2950
  subfolder
3924
2951
  );
3925
- if (!fs4.existsSync(targetDir)) {
3926
- await fs4.promises.mkdir(targetDir, { recursive: true });
2952
+ if (!fs3.existsSync(targetDir)) {
2953
+ await fs3.promises.mkdir(targetDir, { recursive: true });
3927
2954
  }
3928
- const safeFilename = path4.basename(params.filename).replace(/[^a-zA-Z0-9._-]/g, "_");
3929
- const ext = path4.extname(safeFilename).toLowerCase() || (params.dataType === "audio" ? ".wav" : ".json");
3930
- const filePath = path4.join(targetDir, safeFilename);
2955
+ const safeFilename = path3.basename(params.filename).replace(/[^a-zA-Z0-9._-]/g, "_");
2956
+ const ext = path3.extname(safeFilename).toLowerCase() || (params.dataType === "audio" ? ".wav" : ".json");
2957
+ const filePath = path3.join(targetDir, safeFilename);
3931
2958
  const buffer = Buffer.from(params.base64Content, "base64");
3932
- await fs4.promises.writeFile(filePath, buffer);
3933
- const stat = await fs4.promises.stat(filePath);
3934
- const cleanName = path4.basename(safeFilename, ext).replace(/[-_]/g, " ");
2959
+ await fs3.promises.writeFile(filePath, buffer);
2960
+ const stat = await fs3.promises.stat(filePath);
2961
+ const cleanName = path3.basename(safeFilename, ext).replace(/[-_]/g, " ");
3935
2962
  const relPath = params.dataType === "audio" ? `audio/${params.channelType === "mono" ? "mono" : "stereo"}/${subfolder}/${safeFilename}` : `midi/${subfolder}/${safeFilename}`;
3936
2963
  const url = getAssetPoolPublicUrl(
3937
2964
  params.scope,
@@ -3965,19 +2992,19 @@ async function deleteAssetFromPool(params) {
3965
2992
  params.channelType,
3966
2993
  params.subfolder
3967
2994
  );
3968
- const filePath = path4.join(targetDir, path4.basename(params.filename));
3969
- if (fs4.existsSync(filePath)) {
3970
- await fs4.promises.unlink(filePath);
2995
+ const filePath = path3.join(targetDir, path3.basename(params.filename));
2996
+ if (fs3.existsSync(filePath)) {
2997
+ await fs3.promises.unlink(filePath);
3971
2998
  return true;
3972
2999
  }
3973
3000
  return false;
3974
3001
  }
3975
3002
 
3976
3003
  // src/server/services/graphAssetService.ts
3977
- import fs5 from "fs";
3978
- import path5 from "path";
3979
- var WORKSPACE_ROOT = path5.join(process.cwd(), "workspaces", "default");
3980
- var GRAPHS_ROOT = path5.join(WORKSPACE_ROOT, "graphs");
3004
+ import fs4 from "fs";
3005
+ import path4 from "path";
3006
+ var WORKSPACE_ROOT = path4.join(process.cwd(), "workspaces", "default");
3007
+ var GRAPHS_ROOT = path4.join(WORKSPACE_ROOT, "graphs");
3981
3008
  var SUPPORTED_AUDIO_EXTS2 = [
3982
3009
  ".wav",
3983
3010
  ".mp3",
@@ -3995,26 +3022,26 @@ var SUPPORTED_AUDIO_EXTS2 = [
3995
3022
  var SUPPORTED_MIDI_EXTS2 = [".mid", ".midi", ".json"];
3996
3023
  function ensureGraphAssetDirectories(graphDir) {
3997
3024
  const dirs = [
3998
- path5.join(graphDir, "assets", "audio", "stereo", "default"),
3999
- path5.join(graphDir, "assets", "audio", "stereo", "userdefined"),
4000
- path5.join(graphDir, "assets", "audio", "mono", "default"),
4001
- path5.join(graphDir, "assets", "audio", "mono", "userdefined"),
4002
- path5.join(graphDir, "assets", "midi", "default"),
4003
- path5.join(graphDir, "assets", "midi", "userdefined")
3025
+ path4.join(graphDir, "assets", "audio", "stereo", "default"),
3026
+ path4.join(graphDir, "assets", "audio", "stereo", "userdefined"),
3027
+ path4.join(graphDir, "assets", "audio", "mono", "default"),
3028
+ path4.join(graphDir, "assets", "audio", "mono", "userdefined"),
3029
+ path4.join(graphDir, "assets", "midi", "default"),
3030
+ path4.join(graphDir, "assets", "midi", "userdefined")
4004
3031
  ];
4005
3032
  for (const dir of dirs) {
4006
- if (!fs5.existsSync(dir)) {
4007
- fs5.mkdirSync(dir, { recursive: true });
3033
+ if (!fs4.existsSync(dir)) {
3034
+ fs4.mkdirSync(dir, { recursive: true });
4008
3035
  }
4009
3036
  }
4010
3037
  }
4011
3038
  function seedStarterGraphAssets(graphDir) {
4012
- const stereoDefaultDir = path5.join(graphDir, "assets", "audio", "stereo", "default");
4013
- const monoDefaultDir = path5.join(graphDir, "assets", "audio", "mono", "default");
4014
- const midiDefaultDir = path5.join(graphDir, "assets", "midi", "default");
3039
+ const stereoDefaultDir = path4.join(graphDir, "assets", "audio", "stereo", "default");
3040
+ const monoDefaultDir = path4.join(graphDir, "assets", "audio", "mono", "default");
3041
+ const midiDefaultDir = path4.join(graphDir, "assets", "midi", "default");
4015
3042
  const sampleRate = 44100;
4016
- const subBoomPath = path5.join(stereoDefaultDir, "808_sub_boom.wav");
4017
- if (!fs5.existsSync(subBoomPath)) {
3043
+ const subBoomPath = path4.join(stereoDefaultDir, "808_sub_boom.wav");
3044
+ if (!fs4.existsSync(subBoomPath)) {
4018
3045
  const numSamples = Math.floor(sampleRate * 1.8);
4019
3046
  const chL = new Float32Array(numSamples);
4020
3047
  const chR = new Float32Array(numSamples);
@@ -4025,10 +3052,10 @@ function seedStarterGraphAssets(graphDir) {
4025
3052
  chL[i] = s;
4026
3053
  chR[i] = s;
4027
3054
  }
4028
- fs5.writeFileSync(subBoomPath, createWavBuffer(sampleRate, 2, [chL, chR]));
3055
+ fs4.writeFileSync(subBoomPath, createWavBuffer(sampleRate, 2, [chL, chR]));
4029
3056
  }
4030
- const leadPath = path5.join(stereoDefaultDir, "celestial_lead.wav");
4031
- if (!fs5.existsSync(leadPath)) {
3057
+ const leadPath = path4.join(stereoDefaultDir, "celestial_lead.wav");
3058
+ if (!fs4.existsSync(leadPath)) {
4032
3059
  const numSamples = Math.floor(sampleRate * 2.5);
4033
3060
  const chL = new Float32Array(numSamples);
4034
3061
  const chR = new Float32Array(numSamples);
@@ -4042,10 +3069,10 @@ function seedStarterGraphAssets(graphDir) {
4042
3069
  chL[i] = s1 * env;
4043
3070
  chR[i] = (s1 + s2 * 0.5) * env;
4044
3071
  }
4045
- fs5.writeFileSync(leadPath, createWavBuffer(sampleRate, 2, [chL, chR]));
3072
+ fs4.writeFileSync(leadPath, createWavBuffer(sampleRate, 2, [chL, chR]));
4046
3073
  }
4047
- const padPath = path5.join(stereoDefaultDir, "ethereal_pad.wav");
4048
- if (!fs5.existsSync(padPath)) {
3074
+ const padPath = path4.join(stereoDefaultDir, "ethereal_pad.wav");
3075
+ if (!fs4.existsSync(padPath)) {
4049
3076
  const numSamples = Math.floor(sampleRate * 3);
4050
3077
  const chL = new Float32Array(numSamples);
4051
3078
  const chR = new Float32Array(numSamples);
@@ -4064,10 +3091,10 @@ function seedStarterGraphAssets(graphDir) {
4064
3091
  chL[i] = left * env;
4065
3092
  chR[i] = right * env;
4066
3093
  }
4067
- fs5.writeFileSync(padPath, createWavBuffer(sampleRate, 2, [chL, chR]));
3094
+ fs4.writeFileSync(padPath, createWavBuffer(sampleRate, 2, [chL, chR]));
4068
3095
  }
4069
- const snarePath = path5.join(monoDefaultDir, "snare_snappy.wav");
4070
- if (!fs5.existsSync(snarePath)) {
3096
+ const snarePath = path4.join(monoDefaultDir, "snare_snappy.wav");
3097
+ if (!fs4.existsSync(snarePath)) {
4071
3098
  const numSamples = Math.floor(sampleRate * 0.8);
4072
3099
  const chM = new Float32Array(numSamples);
4073
3100
  for (let i = 0; i < numSamples; i++) {
@@ -4077,10 +3104,10 @@ function seedStarterGraphAssets(graphDir) {
4077
3104
  const noise = (Math.random() * 2 - 1) * Math.exp(-p * 4.5) * 0.6;
4078
3105
  chM[i] = tone + noise;
4079
3106
  }
4080
- fs5.writeFileSync(snarePath, createWavBuffer(sampleRate, 1, [chM]));
3107
+ fs4.writeFileSync(snarePath, createWavBuffer(sampleRate, 1, [chM]));
4081
3108
  }
4082
- const hihatPath = path5.join(monoDefaultDir, "hihat_closed.wav");
4083
- if (!fs5.existsSync(hihatPath)) {
3109
+ const hihatPath = path4.join(monoDefaultDir, "hihat_closed.wav");
3110
+ if (!fs4.existsSync(hihatPath)) {
4084
3111
  const numSamples = Math.floor(sampleRate * 0.25);
4085
3112
  const chM = new Float32Array(numSamples);
4086
3113
  for (let i = 0; i < numSamples; i++) {
@@ -4088,10 +3115,10 @@ function seedStarterGraphAssets(graphDir) {
4088
3115
  const noise = (Math.random() * 2 - 1) * Math.exp(-p * 20);
4089
3116
  chM[i] = noise * 0.5;
4090
3117
  }
4091
- fs5.writeFileSync(hihatPath, createWavBuffer(sampleRate, 1, [chM]));
3118
+ fs4.writeFileSync(hihatPath, createWavBuffer(sampleRate, 1, [chM]));
4092
3119
  }
4093
- const midiChordPath = path5.join(midiDefaultDir, "lydian_celestial_pad.json");
4094
- if (!fs5.existsSync(midiChordPath)) {
3120
+ const midiChordPath = path4.join(midiDefaultDir, "lydian_celestial_pad.json");
3121
+ if (!fs4.existsSync(midiChordPath)) {
4095
3122
  const starterMidiPattern = {
4096
3123
  id: "pat-default-lydian",
4097
3124
  name: "Lydian Celestial Pad",
@@ -4104,43 +3131,43 @@ function seedStarterGraphAssets(graphDir) {
4104
3131
  { note: 71, name: "B4", velocity: 0.75, time: 0, duration: 3.8 }
4105
3132
  ]
4106
3133
  };
4107
- fs5.writeFileSync(midiChordPath, JSON.stringify(starterMidiPattern, null, 2), "utf-8");
3134
+ fs4.writeFileSync(midiChordPath, JSON.stringify(starterMidiPattern, null, 2), "utf-8");
4108
3135
  }
4109
3136
  }
4110
3137
  async function scanGraphAssets(graphId, includeImported = true) {
4111
- const safeGraphId = path5.basename(graphId);
4112
- const graphDir = path5.join(GRAPHS_ROOT, safeGraphId);
4113
- if (!fs5.existsSync(graphDir)) {
4114
- await fs5.promises.mkdir(graphDir, { recursive: true });
3138
+ const safeGraphId = path4.basename(graphId);
3139
+ const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
3140
+ if (!fs4.existsSync(graphDir)) {
3141
+ await fs4.promises.mkdir(graphDir, { recursive: true });
4115
3142
  }
4116
3143
  ensureGraphAssetDirectories(graphDir);
4117
3144
  const folders = [];
4118
3145
  const items = [];
4119
3146
  const importedItems = [];
4120
3147
  async function scanSingleGraph(gId, isImported, ownerName) {
4121
- const currentGraphDir = path5.join(GRAPHS_ROOT, path5.basename(gId));
4122
- const assetsDir = path5.join(currentGraphDir, "assets");
4123
- if (!fs5.existsSync(assetsDir)) return;
3148
+ const currentGraphDir = path4.join(GRAPHS_ROOT, path4.basename(gId));
3149
+ const assetsDir = path4.join(currentGraphDir, "assets");
3150
+ if (!fs4.existsSync(assetsDir)) return;
4124
3151
  async function scanCategoryFolder(dataType, channelType) {
4125
- const catDir = dataType === "audio" ? path5.join(assetsDir, "audio", channelType === "mono" ? "mono" : "stereo") : path5.join(assetsDir, "midi");
4126
- if (!fs5.existsSync(catDir)) return;
4127
- const subentries = await fs5.promises.readdir(catDir, { withFileTypes: true });
3152
+ const catDir = dataType === "audio" ? path4.join(assetsDir, "audio", channelType === "mono" ? "mono" : "stereo") : path4.join(assetsDir, "midi");
3153
+ if (!fs4.existsSync(catDir)) return;
3154
+ const subentries = await fs4.promises.readdir(catDir, { withFileTypes: true });
4128
3155
  for (const entry of subentries) {
4129
3156
  if (!entry.isDirectory()) continue;
4130
3157
  const subfolderName = entry.name;
4131
- const subfolderDir = path5.join(catDir, subfolderName);
4132
- const files = await fs5.promises.readdir(subfolderDir);
3158
+ const subfolderDir = path4.join(catDir, subfolderName);
3159
+ const files = await fs4.promises.readdir(subfolderDir);
4133
3160
  let validFileCount = 0;
4134
3161
  for (const file of files) {
4135
3162
  if (file.startsWith(".")) continue;
4136
- const ext = path5.extname(file).toLowerCase();
3163
+ const ext = path4.extname(file).toLowerCase();
4137
3164
  const isAudio = SUPPORTED_AUDIO_EXTS2.includes(ext);
4138
3165
  const isMidi = SUPPORTED_MIDI_EXTS2.includes(ext);
4139
3166
  if (dataType === "audio" && !isAudio || dataType === "midi" && !isMidi) continue;
4140
3167
  validFileCount++;
4141
- const filePath = path5.join(subfolderDir, file);
4142
- const stat = await fs5.promises.stat(filePath);
4143
- const cleanName = path5.basename(file, ext).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
3168
+ const filePath = path4.join(subfolderDir, file);
3169
+ const stat = await fs4.promises.stat(filePath);
3170
+ const cleanName = path4.basename(file, ext).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
4144
3171
  const relPath = dataType === "audio" ? `audio/${channelType === "mono" ? "mono" : "stereo"}/${subfolderName}/${file}` : `midi/${subfolderName}/${file}`;
4145
3172
  const url = dataType === "audio" ? `/graphs/${gId}/assets/audio/${channelType === "mono" ? "mono" : "stereo"}/${subfolderName}/${file}` : `/graphs/${gId}/assets/midi/${subfolderName}/${file}`;
4146
3173
  const item = {
@@ -4183,18 +3210,18 @@ async function scanGraphAssets(graphId, includeImported = true) {
4183
3210
  }
4184
3211
  await scanSingleGraph(safeGraphId, false);
4185
3212
  if (includeImported) {
4186
- const qmsFile = path5.join(graphDir, "graph.qms");
4187
- if (fs5.existsSync(qmsFile)) {
3213
+ const qmsFile = path4.join(graphDir, "graph.qms");
3214
+ if (fs4.existsSync(qmsFile)) {
4188
3215
  try {
4189
- const raw = await fs5.promises.readFile(qmsFile, "utf-8");
3216
+ const raw = await fs4.promises.readFile(qmsFile, "utf-8");
4190
3217
  const pkg = JSON.parse(raw);
4191
3218
  const importedIds = pkg?.metadata?.importedGraphIds || [];
4192
3219
  for (const impId of importedIds) {
4193
- const impQms = path5.join(GRAPHS_ROOT, impId, "graph.qms");
3220
+ const impQms = path4.join(GRAPHS_ROOT, impId, "graph.qms");
4194
3221
  let impName = impId;
4195
- if (fs5.existsSync(impQms)) {
3222
+ if (fs4.existsSync(impQms)) {
4196
3223
  try {
4197
- const impRaw = await fs5.promises.readFile(impQms, "utf-8");
3224
+ const impRaw = await fs4.promises.readFile(impQms, "utf-8");
4198
3225
  const impPkg = JSON.parse(impRaw);
4199
3226
  impName = impPkg?.metadata?.name || impId;
4200
3227
  } catch {
@@ -4210,14 +3237,14 @@ async function scanGraphAssets(graphId, includeImported = true) {
4210
3237
  return { folders, items, importedItems };
4211
3238
  }
4212
3239
  async function createGraphAssetFolder(params) {
4213
- const safeGraphId = path5.basename(params.graphId);
4214
- const safeFolderName = path5.basename(params.folderName).trim().replace(/[^a-zA-Z0-9._-]/g, "_");
3240
+ const safeGraphId = path4.basename(params.graphId);
3241
+ const safeFolderName = path4.basename(params.folderName).trim().replace(/[^a-zA-Z0-9._-]/g, "_");
4215
3242
  if (!safeFolderName) throw new Error("Invalid subfolder name");
4216
- const graphDir = path5.join(GRAPHS_ROOT, safeGraphId);
3243
+ const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
4217
3244
  ensureGraphAssetDirectories(graphDir);
4218
- const targetDir = params.dataType === "audio" ? path5.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", safeFolderName) : path5.join(graphDir, "assets", "midi", safeFolderName);
4219
- if (!fs5.existsSync(targetDir)) {
4220
- await fs5.promises.mkdir(targetDir, { recursive: true });
3245
+ const targetDir = params.dataType === "audio" ? path4.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", safeFolderName) : path4.join(graphDir, "assets", "midi", safeFolderName);
3246
+ if (!fs4.existsSync(targetDir)) {
3247
+ await fs4.promises.mkdir(targetDir, { recursive: true });
4221
3248
  }
4222
3249
  return {
4223
3250
  name: safeFolderName,
@@ -4229,21 +3256,21 @@ async function createGraphAssetFolder(params) {
4229
3256
  };
4230
3257
  }
4231
3258
  async function saveAssetToGraph(params) {
4232
- const safeGraphId = path5.basename(params.graphId);
3259
+ const safeGraphId = path4.basename(params.graphId);
4233
3260
  const subfolder = params.subfolder || "userdefined";
4234
- const graphDir = path5.join(GRAPHS_ROOT, safeGraphId);
3261
+ const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
4235
3262
  ensureGraphAssetDirectories(graphDir);
4236
- const targetDir = params.dataType === "audio" ? path5.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", subfolder) : path5.join(graphDir, "assets", "midi", subfolder);
4237
- if (!fs5.existsSync(targetDir)) {
4238
- await fs5.promises.mkdir(targetDir, { recursive: true });
3263
+ const targetDir = params.dataType === "audio" ? path4.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", subfolder) : path4.join(graphDir, "assets", "midi", subfolder);
3264
+ if (!fs4.existsSync(targetDir)) {
3265
+ await fs4.promises.mkdir(targetDir, { recursive: true });
4239
3266
  }
4240
- const safeFilename = path5.basename(params.filename).replace(/[^a-zA-Z0-9._-]/g, "_");
4241
- const ext = path5.extname(safeFilename).toLowerCase() || (params.dataType === "audio" ? ".wav" : ".json");
4242
- const filePath = path5.join(targetDir, safeFilename);
3267
+ const safeFilename = path4.basename(params.filename).replace(/[^a-zA-Z0-9._-]/g, "_");
3268
+ const ext = path4.extname(safeFilename).toLowerCase() || (params.dataType === "audio" ? ".wav" : ".json");
3269
+ const filePath = path4.join(targetDir, safeFilename);
4243
3270
  const buffer = Buffer.from(params.base64Content, "base64");
4244
- await fs5.promises.writeFile(filePath, buffer);
4245
- const stat = await fs5.promises.stat(filePath);
4246
- const cleanName = path5.basename(safeFilename, ext).replace(/[-_]/g, " ");
3271
+ await fs4.promises.writeFile(filePath, buffer);
3272
+ const stat = await fs4.promises.stat(filePath);
3273
+ const cleanName = path4.basename(safeFilename, ext).replace(/[-_]/g, " ");
4247
3274
  const relPath = params.dataType === "audio" ? `audio/${params.channelType === "mono" ? "mono" : "stereo"}/${subfolder}/${safeFilename}` : `midi/${subfolder}/${safeFilename}`;
4248
3275
  const url = params.dataType === "audio" ? `/graphs/${safeGraphId}/assets/audio/${params.channelType === "mono" ? "mono" : "stereo"}/${subfolder}/${safeFilename}` : `/graphs/${safeGraphId}/assets/midi/${subfolder}/${safeFilename}`;
4249
3276
  return {
@@ -4262,32 +3289,32 @@ async function saveAssetToGraph(params) {
4262
3289
  };
4263
3290
  }
4264
3291
  async function deleteAssetFromGraph(params) {
4265
- const safeGraphId = path5.basename(params.graphId);
4266
- const graphDir = path5.join(GRAPHS_ROOT, safeGraphId);
4267
- const targetDir = params.dataType === "audio" ? path5.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", params.subfolder) : path5.join(graphDir, "assets", "midi", params.subfolder);
4268
- const filePath = path5.join(targetDir, path5.basename(params.filename));
4269
- if (fs5.existsSync(filePath)) {
4270
- await fs5.promises.unlink(filePath);
3292
+ const safeGraphId = path4.basename(params.graphId);
3293
+ const graphDir = path4.join(GRAPHS_ROOT, safeGraphId);
3294
+ const targetDir = params.dataType === "audio" ? path4.join(graphDir, "assets", "audio", params.channelType === "mono" ? "mono" : "stereo", params.subfolder) : path4.join(graphDir, "assets", "midi", params.subfolder);
3295
+ const filePath = path4.join(targetDir, path4.basename(params.filename));
3296
+ if (fs4.existsSync(filePath)) {
3297
+ await fs4.promises.unlink(filePath);
4271
3298
  return true;
4272
3299
  }
4273
3300
  return false;
4274
3301
  }
4275
3302
 
4276
3303
  // src/server/services/graphPackageService.ts
4277
- import fs6 from "fs";
4278
- import path6 from "path";
3304
+ import fs5 from "fs";
3305
+ import path5 from "path";
4279
3306
  import { validateGraphImport } from "@ultrst-armada/kloks/state-graph";
4280
- var WORKSPACE_ROOT2 = path6.join(process.cwd(), "workspaces", "default");
4281
- var GRAPHS_ROOT2 = path6.join(WORKSPACE_ROOT2, "graphs");
4282
- var LEGACY_PROJECTS_ROOT = path6.join(process.cwd(), "projects");
3307
+ var WORKSPACE_ROOT2 = path5.join(process.cwd(), "workspaces", "default");
3308
+ var GRAPHS_ROOT2 = path5.join(WORKSPACE_ROOT2, "graphs");
3309
+ var LEGACY_PROJECTS_ROOT = path5.join(process.cwd(), "projects");
4283
3310
  function ensureWorkspaceStructure() {
4284
- if (!fs6.existsSync(GRAPHS_ROOT2)) {
4285
- fs6.mkdirSync(GRAPHS_ROOT2, { recursive: true });
3311
+ if (!fs5.existsSync(GRAPHS_ROOT2)) {
3312
+ fs5.mkdirSync(GRAPHS_ROOT2, { recursive: true });
4286
3313
  }
4287
3314
  seedStarterGraphsIfEmpty();
4288
3315
  }
4289
3316
  function seedStarterGraphsIfEmpty() {
4290
- const existing = fs6.existsSync(GRAPHS_ROOT2) ? fs6.readdirSync(GRAPHS_ROOT2) : [];
3317
+ const existing = fs5.existsSync(GRAPHS_ROOT2) ? fs5.readdirSync(GRAPHS_ROOT2) : [];
4291
3318
  if (existing.length > 0) return;
4292
3319
  const introId = "intro-motif-starter";
4293
3320
  createInitialGraphPackageSync(
@@ -4556,9 +3583,9 @@ function seedStarterGraphsIfEmpty() {
4556
3583
  );
4557
3584
  }
4558
3585
  function createInitialGraphPackageSync(id, name, description, type, bpm, masterVolume, currentBiome, nodes, connections, importedGraphIds = []) {
4559
- const graphDir = path6.join(GRAPHS_ROOT2, id);
4560
- if (!fs6.existsSync(graphDir)) {
4561
- fs6.mkdirSync(graphDir, { recursive: true });
3586
+ const graphDir = path5.join(GRAPHS_ROOT2, id);
3587
+ if (!fs5.existsSync(graphDir)) {
3588
+ fs5.mkdirSync(graphDir, { recursive: true });
4562
3589
  }
4563
3590
  ensureGraphAssetDirectories(graphDir);
4564
3591
  seedStarterGraphAssets(graphDir);
@@ -4591,29 +3618,29 @@ function createInitialGraphPackageSync(id, name, description, type, bpm, masterV
4591
3618
  connections
4592
3619
  }
4593
3620
  };
4594
- fs6.writeFileSync(path6.join(graphDir, "graph.qms"), JSON.stringify(graphPackage, null, 2), "utf-8");
3621
+ fs5.writeFileSync(path5.join(graphDir, "graph.qms"), JSON.stringify(graphPackage, null, 2), "utf-8");
4595
3622
  }
4596
3623
  async function listAllGraphPackages() {
4597
3624
  ensureWorkspaceStructure();
4598
- const dirs = await fs6.promises.readdir(GRAPHS_ROOT2);
3625
+ const dirs = await fs5.promises.readdir(GRAPHS_ROOT2);
4599
3626
  const packages = [];
4600
3627
  for (const dir of dirs) {
4601
- const graphDir = path6.join(GRAPHS_ROOT2, dir);
4602
- const stat = await fs6.promises.stat(graphDir);
3628
+ const graphDir = path5.join(GRAPHS_ROOT2, dir);
3629
+ const stat = await fs5.promises.stat(graphDir);
4603
3630
  if (!stat.isDirectory()) continue;
4604
- const qmsFile = path6.join(graphDir, "graph.qms");
4605
- const legacyProjectFile = path6.join(graphDir, "project.json");
3631
+ const qmsFile = path5.join(graphDir, "graph.qms");
3632
+ const legacyProjectFile = path5.join(graphDir, "project.json");
4606
3633
  let packageData = null;
4607
- if (fs6.existsSync(qmsFile)) {
3634
+ if (fs5.existsSync(qmsFile)) {
4608
3635
  try {
4609
- const raw = await fs6.promises.readFile(qmsFile, "utf-8");
3636
+ const raw = await fs5.promises.readFile(qmsFile, "utf-8");
4610
3637
  packageData = JSON.parse(raw);
4611
3638
  } catch (err) {
4612
3639
  console.warn(`Corrupted graph.qms in ${dir}:`, err);
4613
3640
  }
4614
- } else if (fs6.existsSync(legacyProjectFile)) {
3641
+ } else if (fs5.existsSync(legacyProjectFile)) {
4615
3642
  try {
4616
- const raw = await fs6.promises.readFile(legacyProjectFile, "utf-8");
3643
+ const raw = await fs5.promises.readFile(legacyProjectFile, "utf-8");
4617
3644
  const legacy = JSON.parse(raw);
4618
3645
  packageData = {
4619
3646
  format: "qms_graph_package",
@@ -4643,7 +3670,7 @@ async function listAllGraphPackages() {
4643
3670
  mapIoConfig: legacy.mapIoConfig
4644
3671
  }
4645
3672
  };
4646
- await fs6.promises.writeFile(qmsFile, JSON.stringify(packageData, null, 2), "utf-8");
3673
+ await fs5.promises.writeFile(qmsFile, JSON.stringify(packageData, null, 2), "utf-8");
4647
3674
  } catch (err) {
4648
3675
  console.warn(`Failed to auto-migrate legacy project ${dir}:`, err);
4649
3676
  }
@@ -4665,13 +3692,13 @@ async function listAllGraphPackages() {
4665
3692
  }
4666
3693
  async function getGraphPackageById(id) {
4667
3694
  ensureWorkspaceStructure();
4668
- const safeId = path6.basename(id);
4669
- const graphDir = path6.join(GRAPHS_ROOT2, safeId);
4670
- const qmsFile = path6.join(graphDir, "graph.qms");
4671
- if (!fs6.existsSync(qmsFile)) {
4672
- const legacyProject = path6.join(LEGACY_PROJECTS_ROOT, safeId, "project.json");
4673
- if (fs6.existsSync(legacyProject)) {
4674
- const raw2 = await fs6.promises.readFile(legacyProject, "utf-8");
3695
+ const safeId = path5.basename(id);
3696
+ const graphDir = path5.join(GRAPHS_ROOT2, safeId);
3697
+ const qmsFile = path5.join(graphDir, "graph.qms");
3698
+ if (!fs5.existsSync(qmsFile)) {
3699
+ const legacyProject = path5.join(LEGACY_PROJECTS_ROOT, safeId, "project.json");
3700
+ if (fs5.existsSync(legacyProject)) {
3701
+ const raw2 = await fs5.promises.readFile(legacyProject, "utf-8");
4675
3702
  const legacy = JSON.parse(raw2);
4676
3703
  return {
4677
3704
  format: "qms_graph_package",
@@ -4704,7 +3731,7 @@ async function getGraphPackageById(id) {
4704
3731
  }
4705
3732
  return null;
4706
3733
  }
4707
- const raw = await fs6.promises.readFile(qmsFile, "utf-8");
3734
+ const raw = await fs5.promises.readFile(qmsFile, "utf-8");
4708
3735
  return JSON.parse(raw);
4709
3736
  }
4710
3737
  async function createGraphPackage(params) {
@@ -4712,8 +3739,8 @@ async function createGraphPackage(params) {
4712
3739
  const name = params.name.trim();
4713
3740
  const safeSlug = name.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-");
4714
3741
  const id = `${safeSlug}-${Date.now().toString(36)}`;
4715
- const graphDir = path6.join(GRAPHS_ROOT2, id);
4716
- await fs6.promises.mkdir(graphDir, { recursive: true });
3742
+ const graphDir = path5.join(GRAPHS_ROOT2, id);
3743
+ await fs5.promises.mkdir(graphDir, { recursive: true });
4717
3744
  await ensureGraphAssetDirectories(graphDir);
4718
3745
  seedStarterGraphAssets(graphDir);
4719
3746
  const now = Date.now();
@@ -4748,8 +3775,8 @@ async function createGraphPackage(params) {
4748
3775
  connections: params.connections || []
4749
3776
  }
4750
3777
  };
4751
- await fs6.promises.writeFile(
4752
- path6.join(graphDir, "graph.qms"),
3778
+ await fs5.promises.writeFile(
3779
+ path5.join(graphDir, "graph.qms"),
4753
3780
  JSON.stringify(graphPackage, null, 2),
4754
3781
  "utf-8"
4755
3782
  );
@@ -4757,10 +3784,10 @@ async function createGraphPackage(params) {
4757
3784
  }
4758
3785
  async function saveGraphPackage(id, payload) {
4759
3786
  ensureWorkspaceStructure();
4760
- const safeId = path6.basename(id);
4761
- const graphDir = path6.join(GRAPHS_ROOT2, safeId);
4762
- if (!fs6.existsSync(graphDir)) {
4763
- await fs6.promises.mkdir(graphDir, { recursive: true });
3787
+ const safeId = path5.basename(id);
3788
+ const graphDir = path5.join(GRAPHS_ROOT2, safeId);
3789
+ if (!fs5.existsSync(graphDir)) {
3790
+ await fs5.promises.mkdir(graphDir, { recursive: true });
4764
3791
  await ensureGraphAssetDirectories(graphDir);
4765
3792
  }
4766
3793
  const existing = await getGraphPackageById(safeId);
@@ -4795,18 +3822,18 @@ async function saveGraphPackage(id, payload) {
4795
3822
  mapIoConfig: payload.mapIoConfig || existing?.graph?.mapIoConfig
4796
3823
  }
4797
3824
  };
4798
- await fs6.promises.writeFile(
4799
- path6.join(graphDir, "graph.qms"),
3825
+ await fs5.promises.writeFile(
3826
+ path5.join(graphDir, "graph.qms"),
4800
3827
  JSON.stringify(graphPackage, null, 2),
4801
3828
  "utf-8"
4802
3829
  );
4803
3830
  return graphPackage;
4804
3831
  }
4805
3832
  async function deleteGraphPackage(id) {
4806
- const safeId = path6.basename(id);
4807
- const graphDir = path6.join(GRAPHS_ROOT2, safeId);
4808
- if (!fs6.existsSync(graphDir)) return false;
4809
- await fs6.promises.rm(graphDir, { recursive: true, force: true });
3833
+ const safeId = path5.basename(id);
3834
+ const graphDir = path5.join(GRAPHS_ROOT2, safeId);
3835
+ if (!fs5.existsSync(graphDir)) return false;
3836
+ await fs5.promises.rm(graphDir, { recursive: true, force: true });
4810
3837
  return true;
4811
3838
  }
4812
3839
  async function importGraphDependency(targetGraphId, candidateImportId) {
@@ -4848,26 +3875,26 @@ async function removeGraphDependency(targetGraphId, importIdToRemove) {
4848
3875
  }
4849
3876
 
4850
3877
  // src/server/services/projectService.ts
4851
- import fs7 from "fs";
4852
- import path7 from "path";
4853
- var PROJECTS_ROOT2 = path7.join(process.cwd(), "projects");
3878
+ import fs6 from "fs";
3879
+ import path6 from "path";
3880
+ var PROJECTS_ROOT2 = path6.join(process.cwd(), "projects");
4854
3881
  var customStarterProjectTemplate = null;
4855
3882
  function configureStarterProjectTemplate(template) {
4856
3883
  customStarterProjectTemplate = template;
4857
3884
  }
4858
3885
  function ensureProjectsRoot(starterTemplate) {
4859
- if (!fs7.existsSync(PROJECTS_ROOT2)) {
4860
- fs7.mkdirSync(PROJECTS_ROOT2, { recursive: true });
3886
+ if (!fs6.existsSync(PROJECTS_ROOT2)) {
3887
+ fs6.mkdirSync(PROJECTS_ROOT2, { recursive: true });
4861
3888
  }
4862
3889
  const template = starterTemplate || customStarterProjectTemplate;
4863
3890
  if (template && template.id) {
4864
- const defaultProjDir = path7.join(PROJECTS_ROOT2, String(template.id));
4865
- if (!fs7.existsSync(defaultProjDir)) {
4866
- fs7.mkdirSync(defaultProjDir, { recursive: true });
4867
- fs7.mkdirSync(path7.join(defaultProjDir, "samples"), { recursive: true });
4868
- fs7.mkdirSync(path7.join(defaultProjDir, "assets"), { recursive: true });
4869
- fs7.writeFileSync(
4870
- path7.join(defaultProjDir, "project.json"),
3891
+ const defaultProjDir = path6.join(PROJECTS_ROOT2, String(template.id));
3892
+ if (!fs6.existsSync(defaultProjDir)) {
3893
+ fs6.mkdirSync(defaultProjDir, { recursive: true });
3894
+ fs6.mkdirSync(path6.join(defaultProjDir, "samples"), { recursive: true });
3895
+ fs6.mkdirSync(path6.join(defaultProjDir, "assets"), { recursive: true });
3896
+ fs6.writeFileSync(
3897
+ path6.join(defaultProjDir, "project.json"),
4871
3898
  JSON.stringify({ ...template, createdAt: Date.now(), updatedAt: Date.now() }, null, 2),
4872
3899
  "utf-8"
4873
3900
  );
@@ -4876,22 +3903,22 @@ function ensureProjectsRoot(starterTemplate) {
4876
3903
  }
4877
3904
  async function listAllProjects() {
4878
3905
  ensureProjectsRoot();
4879
- const dirs = await fs7.promises.readdir(PROJECTS_ROOT2);
3906
+ const dirs = await fs6.promises.readdir(PROJECTS_ROOT2);
4880
3907
  const projects = [];
4881
3908
  for (const dir of dirs) {
4882
- const projDir = path7.join(PROJECTS_ROOT2, dir);
4883
- const stat = await fs7.promises.stat(projDir);
3909
+ const projDir = path6.join(PROJECTS_ROOT2, dir);
3910
+ const stat = await fs6.promises.stat(projDir);
4884
3911
  if (!stat.isDirectory()) continue;
4885
- const projectFile = path7.join(projDir, "project.json");
4886
- const samplesDir = path7.join(projDir, "samples");
3912
+ const projectFile = path6.join(projDir, "project.json");
3913
+ const samplesDir = path6.join(projDir, "samples");
4887
3914
  let sampleCount = 0;
4888
- if (fs7.existsSync(samplesDir)) {
4889
- const samples = await fs7.promises.readdir(samplesDir);
3915
+ if (fs6.existsSync(samplesDir)) {
3916
+ const samples = await fs6.promises.readdir(samplesDir);
4890
3917
  sampleCount = samples.filter((f) => !f.startsWith(".")).length;
4891
3918
  }
4892
- if (fs7.existsSync(projectFile)) {
3919
+ if (fs6.existsSync(projectFile)) {
4893
3920
  try {
4894
- const raw = await fs7.promises.readFile(projectFile, "utf-8");
3921
+ const raw = await fs6.promises.readFile(projectFile, "utf-8");
4895
3922
  const data = JSON.parse(raw);
4896
3923
  projects.push({
4897
3924
  id: data.id || dir,
@@ -4911,24 +3938,24 @@ async function listAllProjects() {
4911
3938
  return projects.sort((a, b) => b.updatedAt - a.updatedAt);
4912
3939
  }
4913
3940
  async function getProjectById(id) {
4914
- const safeId = path7.basename(id);
4915
- const projectFile = path7.join(PROJECTS_ROOT2, safeId, "project.json");
4916
- if (!fs7.existsSync(projectFile)) return null;
4917
- const raw = await fs7.promises.readFile(projectFile, "utf-8");
3941
+ const safeId = path6.basename(id);
3942
+ const projectFile = path6.join(PROJECTS_ROOT2, safeId, "project.json");
3943
+ if (!fs6.existsSync(projectFile)) return null;
3944
+ const raw = await fs6.promises.readFile(projectFile, "utf-8");
4918
3945
  return JSON.parse(raw);
4919
3946
  }
4920
3947
  async function saveProject(id, payload) {
4921
- const safeId = path7.basename(id);
4922
- const projDir = path7.join(PROJECTS_ROOT2, safeId);
4923
- const projectFile = path7.join(projDir, "project.json");
4924
- if (!fs7.existsSync(projDir)) {
4925
- await fs7.promises.mkdir(projDir, { recursive: true });
4926
- await fs7.promises.mkdir(path7.join(projDir, "samples"), { recursive: true });
4927
- await fs7.promises.mkdir(path7.join(projDir, "assets"), { recursive: true });
3948
+ const safeId = path6.basename(id);
3949
+ const projDir = path6.join(PROJECTS_ROOT2, safeId);
3950
+ const projectFile = path6.join(projDir, "project.json");
3951
+ if (!fs6.existsSync(projDir)) {
3952
+ await fs6.promises.mkdir(projDir, { recursive: true });
3953
+ await fs6.promises.mkdir(path6.join(projDir, "samples"), { recursive: true });
3954
+ await fs6.promises.mkdir(path6.join(projDir, "assets"), { recursive: true });
4928
3955
  }
4929
3956
  let existingData = {};
4930
- if (fs7.existsSync(projectFile)) {
4931
- const raw = await fs7.promises.readFile(projectFile, "utf-8");
3957
+ if (fs6.existsSync(projectFile)) {
3958
+ const raw = await fs6.promises.readFile(projectFile, "utf-8");
4932
3959
  existingData = JSON.parse(raw);
4933
3960
  }
4934
3961
  const updatedData = {
@@ -4937,22 +3964,22 @@ async function saveProject(id, payload) {
4937
3964
  id: safeId,
4938
3965
  updatedAt: Date.now()
4939
3966
  };
4940
- await fs7.promises.writeFile(projectFile, JSON.stringify(updatedData, null, 2), "utf-8");
3967
+ await fs6.promises.writeFile(projectFile, JSON.stringify(updatedData, null, 2), "utf-8");
4941
3968
  return updatedData;
4942
3969
  }
4943
3970
  async function deleteProject(id) {
4944
- const safeId = path7.basename(id);
4945
- const projDir = path7.join(PROJECTS_ROOT2, safeId);
4946
- if (!fs7.existsSync(projDir)) return false;
4947
- await fs7.promises.rm(projDir, { recursive: true, force: true });
3971
+ const safeId = path6.basename(id);
3972
+ const projDir = path6.join(PROJECTS_ROOT2, safeId);
3973
+ if (!fs6.existsSync(projDir)) return false;
3974
+ await fs6.promises.rm(projDir, { recursive: true, force: true });
4948
3975
  return true;
4949
3976
  }
4950
3977
 
4951
3978
  // src/server/services/sampleService.ts
4952
- import fs8 from "fs";
4953
- import path8 from "path";
4954
- var SAMPLES_GLOBAL_ROOT = path8.join(process.cwd(), "samples", "global");
4955
- var PROJECTS_ROOT3 = path8.join(process.cwd(), "projects");
3979
+ import fs7 from "fs";
3980
+ import path7 from "path";
3981
+ var SAMPLES_GLOBAL_ROOT = path7.join(process.cwd(), "samples", "global");
3982
+ var PROJECTS_ROOT3 = path7.join(process.cwd(), "projects");
4956
3983
  var SUPPORTED_AUDIO_EXTS3 = [".wav", ".mp3", ".ogg", ".flac", ".m4a", ".webm", ".weba", ".aac", ".aiff", ".aif", ".opus", ".mp4"];
4957
3984
  function initGlobalSamplesDirectory() {
4958
3985
  ensureGlobalSamples(SAMPLES_GLOBAL_ROOT).catch((err) => {
@@ -4960,17 +3987,17 @@ function initGlobalSamplesDirectory() {
4960
3987
  });
4961
3988
  }
4962
3989
  async function listGlobalSamples() {
4963
- if (!fs8.existsSync(SAMPLES_GLOBAL_ROOT)) {
4964
- await fs8.promises.mkdir(SAMPLES_GLOBAL_ROOT, { recursive: true });
3990
+ if (!fs7.existsSync(SAMPLES_GLOBAL_ROOT)) {
3991
+ await fs7.promises.mkdir(SAMPLES_GLOBAL_ROOT, { recursive: true });
4965
3992
  }
4966
- const files = await fs8.promises.readdir(SAMPLES_GLOBAL_ROOT);
3993
+ const files = await fs7.promises.readdir(SAMPLES_GLOBAL_ROOT);
4967
3994
  const samples = [];
4968
3995
  for (const file of files) {
4969
- const ext = path8.extname(file).toLowerCase();
3996
+ const ext = path7.extname(file).toLowerCase();
4970
3997
  if (!SUPPORTED_AUDIO_EXTS3.includes(ext)) continue;
4971
- const filePath = path8.join(SAMPLES_GLOBAL_ROOT, file);
4972
- const stat = await fs8.promises.stat(filePath);
4973
- const baseName = path8.basename(file, ext);
3998
+ const filePath = path7.join(SAMPLES_GLOBAL_ROOT, file);
3999
+ const stat = await fs7.promises.stat(filePath);
4000
+ const baseName = path7.basename(file, ext);
4974
4001
  const cleanName = baseName.replace(/[-_]/g, " ");
4975
4002
  let category = "synth";
4976
4003
  if (/808|kick|snare|hihat|drum|perc/i.test(baseName)) category = "drums";
@@ -4990,15 +4017,15 @@ async function listGlobalSamples() {
4990
4017
  return samples;
4991
4018
  }
4992
4019
  async function saveGlobalSample(filename, base64Content, category) {
4993
- const safeFilename = path8.basename(filename).replace(/[^a-zA-Z0-9._-]/g, "_");
4994
- const ext = path8.extname(safeFilename).toLowerCase() || ".wav";
4995
- const filePath = path8.join(SAMPLES_GLOBAL_ROOT, safeFilename);
4020
+ const safeFilename = path7.basename(filename).replace(/[^a-zA-Z0-9._-]/g, "_");
4021
+ const ext = path7.extname(safeFilename).toLowerCase() || ".wav";
4022
+ const filePath = path7.join(SAMPLES_GLOBAL_ROOT, safeFilename);
4996
4023
  const buffer = Buffer.from(base64Content, "base64");
4997
- await fs8.promises.writeFile(filePath, buffer);
4998
- const stat = await fs8.promises.stat(filePath);
4024
+ await fs7.promises.writeFile(filePath, buffer);
4025
+ const stat = await fs7.promises.stat(filePath);
4999
4026
  return {
5000
4027
  filename: safeFilename,
5001
- name: path8.basename(safeFilename, ext).replace(/[-_]/g, " "),
4028
+ name: path7.basename(safeFilename, ext).replace(/[-_]/g, " "),
5002
4029
  category: category || "custom",
5003
4030
  format: ext.replace(".", "").toUpperCase(),
5004
4031
  url: `/samples/global/${safeFilename}`,
@@ -5008,28 +4035,28 @@ async function saveGlobalSample(filename, base64Content, category) {
5008
4035
  };
5009
4036
  }
5010
4037
  async function deleteGlobalSample(filename) {
5011
- const safeFilename = path8.basename(filename);
5012
- const filePath = path8.join(SAMPLES_GLOBAL_ROOT, safeFilename);
5013
- if (fs8.existsSync(filePath)) {
5014
- await fs8.promises.unlink(filePath);
4038
+ const safeFilename = path7.basename(filename);
4039
+ const filePath = path7.join(SAMPLES_GLOBAL_ROOT, safeFilename);
4040
+ if (fs7.existsSync(filePath)) {
4041
+ await fs7.promises.unlink(filePath);
5015
4042
  return true;
5016
4043
  }
5017
4044
  return false;
5018
4045
  }
5019
4046
  async function listProjectSamples(projectId) {
5020
- const safeId = path8.basename(projectId);
5021
- const samplesDir = path8.join(PROJECTS_ROOT3, safeId, "samples");
5022
- if (!fs8.existsSync(samplesDir)) {
5023
- await fs8.promises.mkdir(samplesDir, { recursive: true });
4047
+ const safeId = path7.basename(projectId);
4048
+ const samplesDir = path7.join(PROJECTS_ROOT3, safeId, "samples");
4049
+ if (!fs7.existsSync(samplesDir)) {
4050
+ await fs7.promises.mkdir(samplesDir, { recursive: true });
5024
4051
  }
5025
- const files = await fs8.promises.readdir(samplesDir);
4052
+ const files = await fs7.promises.readdir(samplesDir);
5026
4053
  const samples = [];
5027
4054
  for (const file of files) {
5028
- const ext = path8.extname(file).toLowerCase();
4055
+ const ext = path7.extname(file).toLowerCase();
5029
4056
  if (!SUPPORTED_AUDIO_EXTS3.includes(ext)) continue;
5030
- const filePath = path8.join(samplesDir, file);
5031
- const stat = await fs8.promises.stat(filePath);
5032
- const baseName = path8.basename(file, ext);
4057
+ const filePath = path7.join(samplesDir, file);
4058
+ const stat = await fs7.promises.stat(filePath);
4059
+ const baseName = path7.basename(file, ext);
5033
4060
  samples.push({
5034
4061
  filename: file,
5035
4062
  name: baseName.replace(/[-_]/g, " "),
@@ -5045,21 +4072,21 @@ async function listProjectSamples(projectId) {
5045
4072
  return samples;
5046
4073
  }
5047
4074
  async function saveProjectSample(projectId, filename, base64Content, isRecording) {
5048
- const safeId = path8.basename(projectId);
5049
- const samplesDir = path8.join(PROJECTS_ROOT3, safeId, "samples");
5050
- if (!fs8.existsSync(samplesDir)) {
5051
- await fs8.promises.mkdir(samplesDir, { recursive: true });
4075
+ const safeId = path7.basename(projectId);
4076
+ const samplesDir = path7.join(PROJECTS_ROOT3, safeId, "samples");
4077
+ if (!fs7.existsSync(samplesDir)) {
4078
+ await fs7.promises.mkdir(samplesDir, { recursive: true });
5052
4079
  }
5053
4080
  const defaultName = isRecording ? `recording_take_${Date.now()}.wav` : `clip_${Date.now()}.wav`;
5054
- const targetFilename = filename ? path8.basename(filename).replace(/[^a-zA-Z0-9._-]/g, "_") : defaultName;
5055
- const ext = path8.extname(targetFilename).toLowerCase() || ".wav";
5056
- const filePath = path8.join(samplesDir, targetFilename);
4081
+ const targetFilename = filename ? path7.basename(filename).replace(/[^a-zA-Z0-9._-]/g, "_") : defaultName;
4082
+ const ext = path7.extname(targetFilename).toLowerCase() || ".wav";
4083
+ const filePath = path7.join(samplesDir, targetFilename);
5057
4084
  const buffer = Buffer.from(base64Content, "base64");
5058
- await fs8.promises.writeFile(filePath, buffer);
5059
- const stat = await fs8.promises.stat(filePath);
4085
+ await fs7.promises.writeFile(filePath, buffer);
4086
+ const stat = await fs7.promises.stat(filePath);
5060
4087
  return {
5061
4088
  filename: targetFilename,
5062
- name: path8.basename(targetFilename, ext).replace(/[-_]/g, " "),
4089
+ name: path7.basename(targetFilename, ext).replace(/[-_]/g, " "),
5063
4090
  category: isRecording ? "recording" : "import",
5064
4091
  format: ext.replace(".", "").toUpperCase(),
5065
4092
  url: `/projects/${safeId}/samples/${targetFilename}`,
@@ -5070,26 +4097,26 @@ async function saveProjectSample(projectId, filename, base64Content, isRecording
5070
4097
  };
5071
4098
  }
5072
4099
  async function deleteProjectSample(projectId, filename) {
5073
- const safeId = path8.basename(projectId);
5074
- const safeFilename = path8.basename(filename);
5075
- const filePath = path8.join(PROJECTS_ROOT3, safeId, "samples", safeFilename);
5076
- if (fs8.existsSync(filePath)) {
5077
- await fs8.promises.unlink(filePath);
4100
+ const safeId = path7.basename(projectId);
4101
+ const safeFilename = path7.basename(filename);
4102
+ const filePath = path7.join(PROJECTS_ROOT3, safeId, "samples", safeFilename);
4103
+ if (fs7.existsSync(filePath)) {
4104
+ await fs7.promises.unlink(filePath);
5078
4105
  return true;
5079
4106
  }
5080
4107
  return false;
5081
4108
  }
5082
4109
 
5083
4110
  // src/server/services/workspaceGraphService.ts
5084
- import fs9 from "fs";
5085
- import path9 from "path";
5086
- var AUTH_FILE_PATH = path9.resolve(".skipo/accounts.json");
4111
+ import fs8 from "fs";
4112
+ import path8 from "path";
4113
+ var AUTH_FILE_PATH = path8.resolve(".skipo/accounts.json");
5087
4114
  async function resolveWorkspaceDir(workspaceIdOrSlug) {
5088
4115
  const workingDir = getWorkingWorkspaceDir();
5089
4116
  let targetWs = null;
5090
4117
  try {
5091
- if (fs9.existsSync(AUTH_FILE_PATH)) {
5092
- const raw = await fs9.promises.readFile(AUTH_FILE_PATH, "utf-8");
4118
+ if (fs8.existsSync(AUTH_FILE_PATH)) {
4119
+ const raw = await fs8.promises.readFile(AUTH_FILE_PATH, "utf-8");
5093
4120
  const accounts = JSON.parse(raw);
5094
4121
  for (const user of Object.values(accounts)) {
5095
4122
  if (Array.isArray(user.workspaces)) {
@@ -5109,35 +4136,35 @@ async function resolveWorkspaceDir(workspaceIdOrSlug) {
5109
4136
  if (targetWs) {
5110
4137
  const rawFolderName = targetWs.localFolderName || targetWs.name.replace(/[/\\?%*:|"<>]/g, "_");
5111
4138
  const folderName = rawFolderName.trim();
5112
- await fs9.promises.mkdir(workingDir, { recursive: true });
4139
+ await fs8.promises.mkdir(workingDir, { recursive: true });
5113
4140
  return { wsDir: workingDir, wsName: targetWs.name, wsFolder: folderName, wsId: targetWs.id };
5114
4141
  }
5115
- await fs9.promises.mkdir(workingDir, { recursive: true });
4142
+ await fs8.promises.mkdir(workingDir, { recursive: true });
5116
4143
  return { wsDir: workingDir, wsName: "Active Workspace", wsFolder: "workspace", wsId: workspaceIdOrSlug || "default" };
5117
4144
  }
5118
4145
  async function listWorkspaceGraphs(workspaceIdOrSlug) {
5119
4146
  const { wsDir, wsFolder } = await resolveWorkspaceDir(workspaceIdOrSlug);
5120
4147
  const graphs = [];
5121
4148
  const inspectDirForGraph = async (dirPath, folderName) => {
5122
- const projectJson = path9.join(dirPath, "project.json");
5123
- const graphQms = path9.join(dirPath, "graph.qms");
4149
+ const projectJson = path8.join(dirPath, "project.json");
4150
+ const graphQms = path8.join(dirPath, "graph.qms");
5124
4151
  let data = null;
5125
- if (fs9.existsSync(projectJson)) {
4152
+ if (fs8.existsSync(projectJson)) {
5126
4153
  try {
5127
- const raw = await fs9.promises.readFile(projectJson, "utf-8");
4154
+ const raw = await fs8.promises.readFile(projectJson, "utf-8");
5128
4155
  data = JSON.parse(raw);
5129
4156
  } catch {
5130
4157
  }
5131
- } else if (fs9.existsSync(graphQms)) {
4158
+ } else if (fs8.existsSync(graphQms)) {
5132
4159
  try {
5133
- const raw = await fs9.promises.readFile(graphQms, "utf-8");
4160
+ const raw = await fs8.promises.readFile(graphQms, "utf-8");
5134
4161
  const pkg = JSON.parse(raw);
5135
4162
  data = { ...pkg.metadata || {}, ...pkg.graph || {} };
5136
4163
  } catch {
5137
4164
  }
5138
4165
  }
5139
4166
  if (!data) return null;
5140
- const stat = await fs9.promises.stat(dirPath);
4167
+ const stat = await fs8.promises.stat(dirPath);
5141
4168
  const nodeCount = Array.isArray(data.nodes) ? data.nodes.length : 0;
5142
4169
  const connectionCount = Array.isArray(data.connections) ? data.connections.length : 0;
5143
4170
  return {
@@ -5145,7 +4172,7 @@ async function listWorkspaceGraphs(workspaceIdOrSlug) {
5145
4172
  name: data.name || folderName,
5146
4173
  description: data.description || "",
5147
4174
  folderName,
5148
- folderPath: path9.relative(process.cwd(), dirPath).replace(/\\/g, "/"),
4175
+ folderPath: path8.relative(process.cwd(), dirPath).replace(/\\/g, "/"),
5149
4176
  createdAt: data.createdAt || stat.birthtimeMs,
5150
4177
  updatedAt: data.updatedAt || stat.mtimeMs,
5151
4178
  bpm: data.bpm || 120,
@@ -5158,18 +4185,18 @@ async function listWorkspaceGraphs(workspaceIdOrSlug) {
5158
4185
  connectionCount
5159
4186
  };
5160
4187
  };
5161
- const entries = await fs9.promises.readdir(wsDir);
4188
+ const entries = await fs8.promises.readdir(wsDir);
5162
4189
  for (const entry of entries) {
5163
4190
  if (entry.startsWith(".") || entry === "samples" || entry === "assets") continue;
5164
- const fullPath = path9.join(wsDir, entry);
5165
- const stat = await fs9.promises.stat(fullPath);
4191
+ const fullPath = path8.join(wsDir, entry);
4192
+ const stat = await fs8.promises.stat(fullPath);
5166
4193
  if (!stat.isDirectory()) continue;
5167
4194
  if (entry === "graphs") {
5168
- const subEntries = await fs9.promises.readdir(fullPath);
4195
+ const subEntries = await fs8.promises.readdir(fullPath);
5169
4196
  for (const sub of subEntries) {
5170
4197
  if (sub.startsWith(".")) continue;
5171
- const subPath = path9.join(fullPath, sub);
5172
- const subStat = await fs9.promises.stat(subPath);
4198
+ const subPath = path8.join(fullPath, sub);
4199
+ const subStat = await fs8.promises.stat(subPath);
5173
4200
  if (subStat.isDirectory()) {
5174
4201
  const g2 = await inspectDirForGraph(subPath, sub);
5175
4202
  if (g2) graphs.push(g2);
@@ -5186,10 +4213,10 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
5186
4213
  const { wsDir, wsFolder, wsId } = await resolveWorkspaceDir(workspaceIdOrSlug);
5187
4214
  const rawName = params.name.trim();
5188
4215
  const folderName = rawName.replace(/[/\\?%*:|"<>]/g, "_");
5189
- const graphDir = path9.join(wsDir, folderName);
5190
- await fs9.promises.mkdir(graphDir, { recursive: true });
5191
- await fs9.promises.mkdir(path9.join(graphDir, "samples"), { recursive: true });
5192
- await fs9.promises.mkdir(path9.join(graphDir, "assets"), { recursive: true });
4216
+ const graphDir = path8.join(wsDir, folderName);
4217
+ await fs8.promises.mkdir(graphDir, { recursive: true });
4218
+ await fs8.promises.mkdir(path8.join(graphDir, "samples"), { recursive: true });
4219
+ await fs8.promises.mkdir(path8.join(graphDir, "assets"), { recursive: true });
5193
4220
  const now = Date.now();
5194
4221
  const nodes = params.templateData?.nodes || [];
5195
4222
  const connections = params.templateData?.connections || [];
@@ -5200,7 +4227,7 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
5200
4227
  id: folderName,
5201
4228
  name: rawName,
5202
4229
  description: params.description || `Workspace graph in ${wsFolder}`,
5203
- folderPath: path9.relative(process.cwd(), graphDir).replace(/\\/g, "/"),
4230
+ folderPath: path8.relative(process.cwd(), graphDir).replace(/\\/g, "/"),
5204
4231
  createdAt: now,
5205
4232
  updatedAt: now,
5206
4233
  bpm,
@@ -5209,7 +4236,7 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
5209
4236
  nodes,
5210
4237
  connections
5211
4238
  };
5212
- await fs9.promises.writeFile(path9.join(graphDir, "project.json"), JSON.stringify(projectData, null, 2), "utf-8");
4239
+ await fs8.promises.writeFile(path8.join(graphDir, "project.json"), JSON.stringify(projectData, null, 2), "utf-8");
5213
4240
  const graphPackage = {
5214
4241
  format: "qms_graph_package",
5215
4242
  version: "2.0.0",
@@ -5227,27 +4254,27 @@ async function createWorkspaceGraph(workspaceIdOrSlug, params) {
5227
4254
  },
5228
4255
  graph: { bpm, masterVolume, currentBiome, nodes, connections }
5229
4256
  };
5230
- await fs9.promises.writeFile(path9.join(graphDir, "graph.qms"), JSON.stringify(graphPackage, null, 2), "utf-8");
4257
+ await fs8.promises.writeFile(path8.join(graphDir, "graph.qms"), JSON.stringify(graphPackage, null, 2), "utf-8");
5231
4258
  return projectData;
5232
4259
  }
5233
4260
  async function getWorkspaceGraph(workspaceIdOrSlug, graphIdOrName) {
5234
4261
  const { wsDir } = await resolveWorkspaceDir(workspaceIdOrSlug);
5235
4262
  const safeTarget = graphIdOrName.replace(/[/\\?%*:|"<>]/g, "_");
5236
4263
  const candidates = [
5237
- path9.join(wsDir, safeTarget),
5238
- path9.join(wsDir, graphIdOrName),
5239
- path9.join(wsDir, "graphs", safeTarget)
4264
+ path8.join(wsDir, safeTarget),
4265
+ path8.join(wsDir, graphIdOrName),
4266
+ path8.join(wsDir, "graphs", safeTarget)
5240
4267
  ];
5241
4268
  for (const cand of candidates) {
5242
- if (fs9.existsSync(cand)) {
5243
- const pJson = path9.join(cand, "project.json");
5244
- if (fs9.existsSync(pJson)) {
5245
- const raw = await fs9.promises.readFile(pJson, "utf-8");
4269
+ if (fs8.existsSync(cand)) {
4270
+ const pJson = path8.join(cand, "project.json");
4271
+ if (fs8.existsSync(pJson)) {
4272
+ const raw = await fs8.promises.readFile(pJson, "utf-8");
5246
4273
  return JSON.parse(raw);
5247
4274
  }
5248
- const gQms = path9.join(cand, "graph.qms");
5249
- if (fs9.existsSync(gQms)) {
5250
- const raw = await fs9.promises.readFile(gQms, "utf-8");
4275
+ const gQms = path8.join(cand, "graph.qms");
4276
+ if (fs8.existsSync(gQms)) {
4277
+ const raw = await fs8.promises.readFile(gQms, "utf-8");
5251
4278
  const pkg = JSON.parse(raw);
5252
4279
  return { ...pkg.metadata || {}, ...pkg.graph || {} };
5253
4280
  }
@@ -5258,39 +4285,39 @@ async function getWorkspaceGraph(workspaceIdOrSlug, graphIdOrName) {
5258
4285
  async function saveWorkspaceGraph(workspaceIdOrSlug, graphIdOrName, payload) {
5259
4286
  const { wsDir } = await resolveWorkspaceDir(workspaceIdOrSlug);
5260
4287
  const safeTarget = graphIdOrName.replace(/[/\\?%*:|"<>]/g, "_");
5261
- let targetDir = path9.join(wsDir, safeTarget);
5262
- if (!fs9.existsSync(targetDir) && fs9.existsSync(path9.join(wsDir, "graphs", safeTarget))) {
5263
- targetDir = path9.join(wsDir, "graphs", safeTarget);
4288
+ let targetDir = path8.join(wsDir, safeTarget);
4289
+ if (!fs8.existsSync(targetDir) && fs8.existsSync(path8.join(wsDir, "graphs", safeTarget))) {
4290
+ targetDir = path8.join(wsDir, "graphs", safeTarget);
5264
4291
  }
5265
- await fs9.promises.mkdir(targetDir, { recursive: true });
5266
- const pJson = path9.join(targetDir, "project.json");
4292
+ await fs8.promises.mkdir(targetDir, { recursive: true });
4293
+ const pJson = path8.join(targetDir, "project.json");
5267
4294
  let existing = {};
5268
- if (fs9.existsSync(pJson)) {
4295
+ if (fs8.existsSync(pJson)) {
5269
4296
  try {
5270
- existing = JSON.parse(await fs9.promises.readFile(pJson, "utf-8"));
4297
+ existing = JSON.parse(await fs8.promises.readFile(pJson, "utf-8"));
5271
4298
  } catch {
5272
4299
  }
5273
4300
  }
5274
4301
  const updated = {
5275
4302
  ...existing,
5276
4303
  ...payload,
5277
- id: existing.id || path9.basename(targetDir),
4304
+ id: existing.id || path8.basename(targetDir),
5278
4305
  updatedAt: Date.now()
5279
4306
  };
5280
- await fs9.promises.writeFile(pJson, JSON.stringify(updated, null, 2), "utf-8");
4307
+ await fs8.promises.writeFile(pJson, JSON.stringify(updated, null, 2), "utf-8");
5281
4308
  return updated;
5282
4309
  }
5283
4310
  async function deleteWorkspaceGraph(workspaceIdOrSlug, graphIdOrName) {
5284
4311
  const { wsDir } = await resolveWorkspaceDir(workspaceIdOrSlug);
5285
4312
  const safeTarget = graphIdOrName.replace(/[/\\?%*:|"<>]/g, "_");
5286
4313
  const candidates = [
5287
- path9.join(wsDir, safeTarget),
5288
- path9.join(wsDir, graphIdOrName),
5289
- path9.join(wsDir, "graphs", safeTarget)
4314
+ path8.join(wsDir, safeTarget),
4315
+ path8.join(wsDir, graphIdOrName),
4316
+ path8.join(wsDir, "graphs", safeTarget)
5290
4317
  ];
5291
4318
  for (const cand of candidates) {
5292
- if (fs9.existsSync(cand)) {
5293
- await fs9.promises.rm(cand, { recursive: true, force: true });
4319
+ if (fs8.existsSync(cand)) {
4320
+ await fs8.promises.rm(cand, { recursive: true, force: true });
5294
4321
  return true;
5295
4322
  }
5296
4323
  }
@@ -5340,14 +4367,17 @@ export {
5340
4367
  JobDependencyGraph,
5341
4368
  JobScheduler,
5342
4369
  LEGACY_PROJECTS_ROOT,
4370
+ LOCAL_STORAGE_GRAPHS_PREFIX,
5343
4371
  LoadBalancer,
5344
4372
  LocalAccountProvider,
5345
4373
  LocalFSAdapter,
5346
4374
  LockManager,
4375
+ NodeGraphPersistenceService,
5347
4376
  PROJECTS_ROOT,
5348
4377
  PasswordHasher,
5349
4378
  RateLimiter,
5350
4379
  RemoteExecutor,
4380
+ RemoteWorkspaceService,
5351
4381
  ResourceFetcherRegistry,
5352
4382
  ResultFlowBridge,
5353
4383
  S3Adapter,
@@ -5358,7 +4388,9 @@ export {
5358
4388
  ServiceDispatcher,
5359
4389
  SessionManager,
5360
4390
  SrklClient,
4391
+ SrklCredentialRegistry,
5361
4392
  SrklStorageService,
4393
+ SrklVaultAccessClient,
5362
4394
  StorageAdapterFactory,
5363
4395
  StorageServiceDefinition,
5364
4396
  StorageServiceFacade,
@@ -5369,6 +4401,7 @@ export {
5369
4401
  WORKSPACE_ROOT,
5370
4402
  WebSocketTransport,
5371
4403
  WorkerExecutionBoundary,
4404
+ WorkingWorkspaceService,
5372
4405
  WorkspaceCloneSyncEngine,
5373
4406
  WorkspaceFS,
5374
4407
  WorkspaceServiceDefinition,
@@ -5383,6 +4416,8 @@ export {
5383
4416
  buildProjectObservability,
5384
4417
  buildProjectsList,
5385
4418
  cleanDbUrl,
4419
+ computeDeploymentFinalWeight,
4420
+ computeUsageWeightScore,
5386
4421
  configureStarterProjectTemplate,
5387
4422
  createAssetSubfolder,
5388
4423
  createBackend,
@@ -5402,6 +4437,7 @@ export {
5402
4437
  createWorkspace,
5403
4438
  createWorkspaceGraph,
5404
4439
  decryptSecretFromBlob,
4440
+ decryptVaultBufferInBrowser,
5405
4441
  deleteAssetFromGraph,
5406
4442
  deleteAssetFromPool,
5407
4443
  deleteCredential,
@@ -5424,6 +4460,7 @@ export {
5424
4460
  ensureS3DirectoryMarker,
5425
4461
  ensureWorkspaceStructure,
5426
4462
  exportEncryptedCredentials,
4463
+ exportGraphAsJsonFile,
5427
4464
  fetchCloudflareData,
5428
4465
  fetchVercelData,
5429
4466
  findUserWorkspace,
@@ -5434,6 +4471,8 @@ export {
5434
4471
  getAssetPoolLocationPath,
5435
4472
  getAssetPoolPublicUrl,
5436
4473
  getB2BucketId,
4474
+ getConnectedDeploymentNode,
4475
+ getConnectedWeightModifiers,
5437
4476
  getCredential,
5438
4477
  getDefaultWorkingWorkspaceDir,
5439
4478
  getExecutions,
@@ -5450,6 +4489,7 @@ export {
5450
4489
  getSystemDatabaseUrls,
5451
4490
  getUnifiedDbUrl,
5452
4491
  getUsers,
4492
+ getWeightModifierScore,
5453
4493
  getWorkingWorkspaceConfig,
5454
4494
  getWorkingWorkspaceDir,
5455
4495
  getWorkspaceFolderKey,
@@ -5457,6 +4497,8 @@ export {
5457
4497
  getWorkspaceGraph,
5458
4498
  getWorkspaceStorageDetails,
5459
4499
  getWorkspaces,
4500
+ globalCredentialRegistry,
4501
+ hexToBytes,
5460
4502
  importEncryptedCredentials,
5461
4503
  importGraphDependency,
5462
4504
  initGlobalSamplesDirectory,
@@ -5476,12 +4518,15 @@ export {
5476
4518
  loginUser,
5477
4519
  matchGatewayIngress,
5478
4520
  matchGatewayIngressAsync,
4521
+ nodeGraphPersistence,
5479
4522
  normalizeBackblazeCredentials,
4523
+ parseGraphJsonFile,
5480
4524
  permanentDeleteS3Folder,
5481
4525
  permanentDeleteS3Object,
5482
4526
  queryUnifiedDb,
5483
4527
  readWorkspaceFile,
5484
4528
  recordExecution,
4529
+ remoteWorkspaceService,
5485
4530
  removeGraphDependency,
5486
4531
  renameAppInWorkspaceData,
5487
4532
  renameS3Folder,
@@ -5510,6 +4555,8 @@ export {
5510
4555
  setWorkingWorkspaceConfig,
5511
4556
  srklListBackblazeBuckets2 as srklCoreListBackblazeBuckets,
5512
4557
  srklListBackblazeBuckets,
4558
+ srklService,
4559
+ syncSrklAccounts,
5513
4560
  syncWorkspace,
5514
4561
  syncWorkspaceCreateToBucket,
5515
4562
  syncWorkspaceDataToBucket,
@@ -5520,8 +4567,10 @@ export {
5520
4567
  useBackend,
5521
4568
  useBackendClient,
5522
4569
  useSrklServices,
4570
+ validateGraphDocument,
5523
4571
  validateServiceDefinition,
5524
4572
  withAuth,
5525
4573
  withCors,
4574
+ workingWorkspaceService,
5526
4575
  writeWorkspaceFile
5527
4576
  };