@fileverse/api 0.0.1

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.
@@ -0,0 +1,1258 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // node_modules/tsup/assets/esm_shims.js
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ var init_esm_shims = __esm({
16
+ "node_modules/tsup/assets/esm_shims.js"() {
17
+ "use strict";
18
+ }
19
+ });
20
+
21
+ // src/cli/constants.generated.ts
22
+ var STATIC_CONFIG, BASE_CONFIG;
23
+ var init_constants_generated = __esm({
24
+ "src/cli/constants.generated.ts"() {
25
+ "use strict";
26
+ init_esm_shims();
27
+ STATIC_CONFIG = {
28
+ API_URL: "https://prod-apps-storage-5cdacc06ff79.herokuapp.com/",
29
+ SERVER_DID: "did:key:z6Mkroj9bxTin6Z5S9qwx2G2b87NPrCX7S85FhCpmBGPcDCz",
30
+ PROXY_SERVER_DID: "did:key:z6MkrZSmq8D6vQG87YbjUQatXeptaCCXWdTx8fYaWxWbRUHB",
31
+ NETWORK_NAME: "gnosis",
32
+ DEFAULT_PORT: "8001",
33
+ DEFAULT_RPC_URL: "https://rpc.gnosischain.com",
34
+ PIMLICO_PROXY_URL: "https://pimlico-proxy-0a326da116f8.herokuapp.com/",
35
+ SERVICE_NAME: "fileverse-api",
36
+ LOG_LEVEL: "info",
37
+ FRONTEND_URL: "https://docs.fileverse.io"
38
+ };
39
+ BASE_CONFIG = STATIC_CONFIG;
40
+ }
41
+ });
42
+
43
+ // src/cli/constants.ts
44
+ var init_constants = __esm({
45
+ "src/cli/constants.ts"() {
46
+ "use strict";
47
+ init_esm_shims();
48
+ init_constants_generated();
49
+ }
50
+ });
51
+
52
+ // src/config/index.ts
53
+ import dotenv from "dotenv";
54
+ import path4 from "path";
55
+ import fs2 from "fs";
56
+ import os2 from "os";
57
+ function getEnvPath2() {
58
+ if (fs2.existsSync(projectEnvPath)) {
59
+ return projectEnvPath;
60
+ }
61
+ return userEnvPath;
62
+ }
63
+ function loadConfig(override = true) {
64
+ const envPath = getEnvPath2();
65
+ dotenv.config({ path: envPath, override });
66
+ }
67
+ function getRuntimeConfig() {
68
+ return {
69
+ get API_KEY() {
70
+ return process.env.API_KEY;
71
+ },
72
+ get RPC_URL() {
73
+ return process.env.RPC_URL || STATIC_CONFIG.DEFAULT_RPC_URL;
74
+ },
75
+ get DB_PATH() {
76
+ return process.env.DB_PATH;
77
+ },
78
+ get PORT() {
79
+ return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
80
+ },
81
+ get NODE_ENV() {
82
+ return process.env.NODE_ENV || "production";
83
+ },
84
+ get FRONTEND_URL() {
85
+ return process.env.FRONTEND_URL || STATIC_CONFIG.FRONTEND_URL;
86
+ }
87
+ };
88
+ }
89
+ var projectEnvPath, userEnvPath, config;
90
+ var init_config = __esm({
91
+ "src/config/index.ts"() {
92
+ "use strict";
93
+ init_esm_shims();
94
+ init_constants();
95
+ projectEnvPath = path4.join(process.cwd(), "config", ".env");
96
+ userEnvPath = path4.join(os2.homedir(), ".fileverse", ".env");
97
+ loadConfig(false);
98
+ config = {
99
+ ...STATIC_CONFIG,
100
+ get SERVICE_NAME() {
101
+ return STATIC_CONFIG.SERVICE_NAME;
102
+ },
103
+ get LOG_LEVEL() {
104
+ return STATIC_CONFIG.LOG_LEVEL;
105
+ },
106
+ get NETWORK_NAME() {
107
+ return STATIC_CONFIG.NETWORK_NAME;
108
+ },
109
+ get UPLOAD_SERVER_URL() {
110
+ return STATIC_CONFIG.API_URL;
111
+ },
112
+ get UPLOAD_SERVER_DID() {
113
+ return STATIC_CONFIG.SERVER_DID;
114
+ },
115
+ get API_KEY() {
116
+ return process.env.API_KEY;
117
+ },
118
+ get RPC_URL() {
119
+ return process.env.RPC_URL || STATIC_CONFIG.DEFAULT_RPC_URL;
120
+ },
121
+ get DB_PATH() {
122
+ return process.env.DB_PATH;
123
+ },
124
+ get PORT() {
125
+ return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
126
+ },
127
+ get NODE_ENV() {
128
+ return process.env.NODE_ENV || "production";
129
+ },
130
+ get IP() {
131
+ return process.env.IP || "0.0.0.0";
132
+ },
133
+ get FRONTEND_URL() {
134
+ return process.env.FRONTEND_URL || STATIC_CONFIG.FRONTEND_URL;
135
+ }
136
+ };
137
+ }
138
+ });
139
+
140
+ // src/infra/logger.ts
141
+ import pino from "pino";
142
+ var isProduction, pinoInstance, createLogMethod, logger;
143
+ var init_logger = __esm({
144
+ "src/infra/logger.ts"() {
145
+ "use strict";
146
+ init_esm_shims();
147
+ init_constants();
148
+ init_config();
149
+ isProduction = config.NODE_ENV === "production";
150
+ pinoInstance = pino({
151
+ name: STATIC_CONFIG.SERVICE_NAME,
152
+ level: STATIC_CONFIG.LOG_LEVEL,
153
+ formatters: {
154
+ bindings: (bindings) => ({ name: bindings.name }),
155
+ level: (label) => ({ level: label })
156
+ },
157
+ serializers: {
158
+ err(err) {
159
+ if (!err) return err;
160
+ if (isProduction) {
161
+ return { type: err.name, message: err.message };
162
+ }
163
+ return {
164
+ type: err.name,
165
+ message: err.message,
166
+ stack: err.stack
167
+ };
168
+ }
169
+ },
170
+ transport: config.NODE_ENV !== "production" ? {
171
+ target: "pino-pretty",
172
+ options: {
173
+ colorize: true,
174
+ translateTime: "SYS:standard",
175
+ ignore: "pid,hostname",
176
+ errorProps: "*",
177
+ errorLikeObjectKeys: ["err", "error"]
178
+ }
179
+ } : void 0
180
+ });
181
+ createLogMethod = (level) => {
182
+ return (...args) => {
183
+ const [first, ...rest] = args;
184
+ const log = pinoInstance[level].bind(pinoInstance);
185
+ if (typeof first === "object" && first !== null && !(first instanceof Error)) {
186
+ log(first, ...rest);
187
+ return;
188
+ }
189
+ if (rest.length > 0) {
190
+ const last = rest[rest.length - 1];
191
+ if (last instanceof Error) {
192
+ log({ err: last }, first, ...rest.slice(0, -1));
193
+ return;
194
+ }
195
+ }
196
+ if (first instanceof Error) {
197
+ log({ err: first }, first.message);
198
+ return;
199
+ }
200
+ log(first, ...rest);
201
+ };
202
+ };
203
+ logger = {
204
+ trace: createLogMethod("trace"),
205
+ debug: createLogMethod("debug"),
206
+ info: createLogMethod("info"),
207
+ warn: createLogMethod("warn"),
208
+ error: createLogMethod("error"),
209
+ fatal: createLogMethod("fatal"),
210
+ get level() {
211
+ return pinoInstance.level;
212
+ },
213
+ set level(lvl) {
214
+ pinoInstance.level = lvl;
215
+ },
216
+ child: pinoInstance.child.bind(pinoInstance)
217
+ };
218
+ }
219
+ });
220
+
221
+ // src/infra/asyncHandler.ts
222
+ var init_asyncHandler = __esm({
223
+ "src/infra/asyncHandler.ts"() {
224
+ "use strict";
225
+ init_esm_shims();
226
+ }
227
+ });
228
+
229
+ // src/sdk/key-store.ts
230
+ import { eciesDecrypt, eciesEncrypt, generateECKeyPair } from "@fileverse/crypto/ecies";
231
+ var init_key_store = __esm({
232
+ "src/sdk/key-store.ts"() {
233
+ "use strict";
234
+ init_esm_shims();
235
+ }
236
+ });
237
+
238
+ // src/sdk/auth-token-provider.ts
239
+ import * as ucans from "@ucans/ucans";
240
+ var init_auth_token_provider = __esm({
241
+ "src/sdk/auth-token-provider.ts"() {
242
+ "use strict";
243
+ init_esm_shims();
244
+ }
245
+ });
246
+
247
+ // src/constants/chains.ts
248
+ import { sepolia, gnosis } from "viem/chains";
249
+ var init_chains = __esm({
250
+ "src/constants/chains.ts"() {
251
+ "use strict";
252
+ init_esm_shims();
253
+ }
254
+ });
255
+
256
+ // src/constants/events.ts
257
+ var init_events = __esm({
258
+ "src/constants/events.ts"() {
259
+ "use strict";
260
+ init_esm_shims();
261
+ }
262
+ });
263
+
264
+ // src/constants/methods.ts
265
+ var init_methods = __esm({
266
+ "src/constants/methods.ts"() {
267
+ "use strict";
268
+ init_esm_shims();
269
+ }
270
+ });
271
+
272
+ // src/constants/index.ts
273
+ var NETWORK_NAME, UPLOAD_SERVER_URL, CHAIN_MAP, CHAIN;
274
+ var init_constants2 = __esm({
275
+ "src/constants/index.ts"() {
276
+ "use strict";
277
+ init_esm_shims();
278
+ init_constants();
279
+ init_config();
280
+ init_chains();
281
+ init_events();
282
+ init_methods();
283
+ NETWORK_NAME = STATIC_CONFIG.NETWORK_NAME;
284
+ UPLOAD_SERVER_URL = STATIC_CONFIG.API_URL;
285
+ CHAIN_MAP = {
286
+ gnosis,
287
+ sepolia
288
+ };
289
+ CHAIN = CHAIN_MAP[NETWORK_NAME];
290
+ }
291
+ });
292
+
293
+ // src/sdk/pimlico-utils.ts
294
+ import { createPublicClient, http, hexToBigInt, toHex, toBytes } from "viem";
295
+ import { createPimlicoClient } from "permissionless/clients/pimlico";
296
+ import { createSmartAccountClient } from "permissionless";
297
+ import { toSafeSmartAccount } from "permissionless/accounts";
298
+ import { entryPoint07Address } from "viem/account-abstraction";
299
+ import { generatePrivateKey } from "viem/accounts";
300
+ var init_pimlico_utils = __esm({
301
+ "src/sdk/pimlico-utils.ts"() {
302
+ "use strict";
303
+ init_esm_shims();
304
+ init_constants2();
305
+ }
306
+ });
307
+
308
+ // src/sdk/smart-agent.ts
309
+ import { toHex as toHex2 } from "viem";
310
+ import { privateKeyToAccount } from "viem/accounts";
311
+ var init_smart_agent = __esm({
312
+ "src/sdk/smart-agent.ts"() {
313
+ "use strict";
314
+ init_esm_shims();
315
+ init_pimlico_utils();
316
+ init_constants();
317
+ }
318
+ });
319
+
320
+ // src/sdk/file-encryption.ts
321
+ import { generateRandomBytes } from "@fileverse/crypto/utils";
322
+ var init_file_encryption = __esm({
323
+ "src/sdk/file-encryption.ts"() {
324
+ "use strict";
325
+ init_esm_shims();
326
+ }
327
+ });
328
+
329
+ // src/sdk/file-utils.ts
330
+ import { getArgon2idHash } from "@fileverse/crypto/argon";
331
+ import { bytesToBase64, generateRandomBytes as generateRandomBytes2 } from "@fileverse/crypto/utils";
332
+ import { derivePBKDF2Key, encryptAesCBC } from "@fileverse/crypto/kdf";
333
+ import { secretBoxEncrypt } from "@fileverse/crypto/nacl";
334
+ import hkdf from "futoin-hkdf";
335
+ import tweetnacl from "tweetnacl";
336
+ import { fromUint8Array, toUint8Array as toUint8Array2 } from "js-base64";
337
+ import { toAESKey, aesEncrypt } from "@fileverse/crypto/webcrypto";
338
+ import axios2 from "axios";
339
+ import { encodeFunctionData, parseEventLogs } from "viem";
340
+ var init_file_utils = __esm({
341
+ "src/sdk/file-utils.ts"() {
342
+ "use strict";
343
+ init_esm_shims();
344
+ init_file_encryption();
345
+ init_constants2();
346
+ }
347
+ });
348
+
349
+ // src/sdk/file-manager.ts
350
+ import { fromUint8Array as fromUint8Array2, toUint8Array as toUint8Array3 } from "js-base64";
351
+ import { generateAESKey, exportAESKey } from "@fileverse/crypto/webcrypto";
352
+ import { markdownToYjs } from "@fileverse/content-processor";
353
+ var init_file_manager = __esm({
354
+ "src/sdk/file-manager.ts"() {
355
+ "use strict";
356
+ init_esm_shims();
357
+ init_file_utils();
358
+ init_constants();
359
+ init_constants2();
360
+ init_infra();
361
+ }
362
+ });
363
+
364
+ // src/domain/portal/publish.ts
365
+ import { fromUint8Array as fromUint8Array3, toUint8Array as toUint8Array4 } from "js-base64";
366
+ import { stringToBytes } from "viem";
367
+ import { deriveHKDFKey } from "@fileverse/crypto/kdf";
368
+ import { generateKeyPairFromSeed } from "@stablelib/ed25519";
369
+ import * as ucans2 from "@ucans/ucans";
370
+ var init_publish = __esm({
371
+ "src/domain/portal/publish.ts"() {
372
+ "use strict";
373
+ init_esm_shims();
374
+ init_models();
375
+ init_infra();
376
+ init_key_store();
377
+ init_auth_token_provider();
378
+ init_smart_agent();
379
+ init_file_manager();
380
+ init_config();
381
+ }
382
+ });
383
+
384
+ // src/domain/portal/saveApiKey.ts
385
+ function addApiKey(input) {
386
+ if (!input.apiKeySeed || !input.name || !input.collaboratorAddress || !input.portalAddress) {
387
+ throw new Error("apiKeySeed, name, collaboratorAddress, and portalAddress are required");
388
+ }
389
+ const portal = PortalsModel.findByPortalAddress(input.portalAddress);
390
+ if (!portal) {
391
+ throw new Error(`Portal with address ${input.portalAddress} does not exist`);
392
+ }
393
+ return ApiKeysModel.create(input);
394
+ }
395
+ var init_saveApiKey = __esm({
396
+ "src/domain/portal/saveApiKey.ts"() {
397
+ "use strict";
398
+ init_esm_shims();
399
+ init_models();
400
+ }
401
+ });
402
+
403
+ // src/domain/portal/removeApiKey.ts
404
+ var init_removeApiKey = __esm({
405
+ "src/domain/portal/removeApiKey.ts"() {
406
+ "use strict";
407
+ init_esm_shims();
408
+ init_models();
409
+ }
410
+ });
411
+
412
+ // src/domain/portal/index.ts
413
+ var init_portal = __esm({
414
+ "src/domain/portal/index.ts"() {
415
+ "use strict";
416
+ init_esm_shims();
417
+ init_publish();
418
+ init_savePortal();
419
+ init_saveApiKey();
420
+ init_removeApiKey();
421
+ }
422
+ });
423
+
424
+ // src/errors/rate-limit.ts
425
+ import { HttpRequestError } from "viem";
426
+ var init_rate_limit = __esm({
427
+ "src/errors/rate-limit.ts"() {
428
+ "use strict";
429
+ init_esm_shims();
430
+ }
431
+ });
432
+
433
+ // src/infra/worker/eventProcessor.ts
434
+ var init_eventProcessor = __esm({
435
+ "src/infra/worker/eventProcessor.ts"() {
436
+ "use strict";
437
+ init_esm_shims();
438
+ init_config();
439
+ init_portal();
440
+ init_models();
441
+ init_infra();
442
+ init_pimlico_utils();
443
+ init_file_utils();
444
+ init_constants2();
445
+ init_rate_limit();
446
+ }
447
+ });
448
+
449
+ // src/infra/worker/workerSignal.ts
450
+ import { EventEmitter } from "events";
451
+ var WorkerSignal, workerSignal;
452
+ var init_workerSignal = __esm({
453
+ "src/infra/worker/workerSignal.ts"() {
454
+ "use strict";
455
+ init_esm_shims();
456
+ WorkerSignal = class extends EventEmitter {
457
+ };
458
+ workerSignal = new WorkerSignal();
459
+ workerSignal.setMaxListeners(20);
460
+ }
461
+ });
462
+
463
+ // src/infra/worker/worker.ts
464
+ var STALE_THRESHOLD_MS;
465
+ var init_worker = __esm({
466
+ "src/infra/worker/worker.ts"() {
467
+ "use strict";
468
+ init_esm_shims();
469
+ init_infra();
470
+ init_eventProcessor();
471
+ init_workerSignal();
472
+ init_models();
473
+ init_rate_limit();
474
+ STALE_THRESHOLD_MS = 5 * 60 * 1e3;
475
+ }
476
+ });
477
+
478
+ // src/infra/worker/index.ts
479
+ var init_worker2 = __esm({
480
+ "src/infra/worker/index.ts"() {
481
+ "use strict";
482
+ init_esm_shims();
483
+ init_worker();
484
+ init_workerSignal();
485
+ }
486
+ });
487
+
488
+ // src/appWorker.ts
489
+ var init_appWorker = __esm({
490
+ "src/appWorker.ts"() {
491
+ "use strict";
492
+ init_esm_shims();
493
+ init_worker2();
494
+ }
495
+ });
496
+
497
+ // src/infra/reporter.ts
498
+ var Reporter, reporter_default;
499
+ var init_reporter = __esm({
500
+ "src/infra/reporter.ts"() {
501
+ "use strict";
502
+ init_esm_shims();
503
+ Reporter = class {
504
+ async reportError(message) {
505
+ console.error("Error reported:", message);
506
+ }
507
+ };
508
+ reporter_default = new Reporter();
509
+ }
510
+ });
511
+
512
+ // src/infra/index.ts
513
+ var init_infra = __esm({
514
+ "src/infra/index.ts"() {
515
+ "use strict";
516
+ init_esm_shims();
517
+ init_logger();
518
+ init_asyncHandler();
519
+ init_appWorker();
520
+ init_database();
521
+ init_reporter();
522
+ }
523
+ });
524
+
525
+ // src/infra/database/connection.ts
526
+ import Database from "better-sqlite3";
527
+ var DatabaseConnectionManager, databaseConnectionManager;
528
+ var init_connection = __esm({
529
+ "src/infra/database/connection.ts"() {
530
+ "use strict";
531
+ init_esm_shims();
532
+ init_config();
533
+ init_infra();
534
+ DatabaseConnectionManager = class _DatabaseConnectionManager {
535
+ static instance;
536
+ db = null;
537
+ constructor() {
538
+ }
539
+ static getInstance() {
540
+ if (!_DatabaseConnectionManager.instance) {
541
+ _DatabaseConnectionManager.instance = new _DatabaseConnectionManager();
542
+ }
543
+ return _DatabaseConnectionManager.instance;
544
+ }
545
+ getConnection() {
546
+ if (!this.db) {
547
+ const dbPath = config.DB_PATH;
548
+ this.db = new Database(dbPath, {
549
+ verbose: config.NODE_ENV === "development" ? (msg) => logger.debug(String(msg)) : void 0
550
+ });
551
+ this.db.pragma("journal_mode = WAL");
552
+ this.db.pragma("foreign_keys = ON");
553
+ this.db.prepare("SELECT 1").get();
554
+ logger.info(`SQLite database connected: ${dbPath}`);
555
+ }
556
+ return this.db;
557
+ }
558
+ async close() {
559
+ if (this.db) {
560
+ this.db.close();
561
+ this.db = null;
562
+ logger.info("Database connection closed");
563
+ }
564
+ }
565
+ isConnected() {
566
+ return this.db !== null && this.db.open;
567
+ }
568
+ };
569
+ databaseConnectionManager = DatabaseConnectionManager.getInstance();
570
+ }
571
+ });
572
+
573
+ // src/domain/file/constants.ts
574
+ var DEFAULT_LIST_LIMIT;
575
+ var init_constants3 = __esm({
576
+ "src/domain/file/constants.ts"() {
577
+ "use strict";
578
+ init_esm_shims();
579
+ DEFAULT_LIST_LIMIT = 10;
580
+ }
581
+ });
582
+
583
+ // src/infra/database/query-builder.ts
584
+ function getDb() {
585
+ return databaseConnectionManager.getConnection();
586
+ }
587
+ var QueryBuilder;
588
+ var init_query_builder = __esm({
589
+ "src/infra/database/query-builder.ts"() {
590
+ "use strict";
591
+ init_esm_shims();
592
+ init_connection();
593
+ init_constants3();
594
+ QueryBuilder = class {
595
+ static select(sql, params = []) {
596
+ const stmt = getDb().prepare(sql);
597
+ return stmt.all(params);
598
+ }
599
+ static selectOne(sql, params = []) {
600
+ const stmt = getDb().prepare(sql);
601
+ return stmt.get(params);
602
+ }
603
+ static execute(sql, params = []) {
604
+ const stmt = getDb().prepare(sql);
605
+ const result = stmt.run(params);
606
+ return {
607
+ changes: result.changes,
608
+ lastInsertRowid: result.lastInsertRowid
609
+ };
610
+ }
611
+ static transaction(callback) {
612
+ return getDb().transaction(callback)();
613
+ }
614
+ static paginate(sql, options = {}) {
615
+ let query = sql;
616
+ if (options.orderBy) {
617
+ query += ` ORDER BY ${options.orderBy} ${options.orderDirection || "ASC"}`;
618
+ }
619
+ const hasOffset = (options.offset ?? 0) > 0;
620
+ const limit = options.limit ?? (hasOffset ? DEFAULT_LIST_LIMIT : void 0);
621
+ if (limit) {
622
+ query += ` LIMIT ${limit}`;
623
+ }
624
+ if (hasOffset) {
625
+ query += ` OFFSET ${options.offset}`;
626
+ }
627
+ return query;
628
+ }
629
+ };
630
+ }
631
+ });
632
+
633
+ // src/infra/database/index.ts
634
+ function getDb2() {
635
+ return databaseConnectionManager.getConnection();
636
+ }
637
+ var database_default;
638
+ var init_database = __esm({
639
+ "src/infra/database/index.ts"() {
640
+ "use strict";
641
+ init_esm_shims();
642
+ init_connection();
643
+ init_query_builder();
644
+ database_default = getDb2;
645
+ }
646
+ });
647
+
648
+ // src/infra/database/models/files.model.ts
649
+ import { uuidv7 } from "uuidv7";
650
+ var init_files_model = __esm({
651
+ "src/infra/database/models/files.model.ts"() {
652
+ "use strict";
653
+ init_esm_shims();
654
+ init_database();
655
+ }
656
+ });
657
+
658
+ // src/infra/database/models/portals.model.ts
659
+ import { uuidv7 as uuidv72 } from "uuidv7";
660
+ var PortalsModel;
661
+ var init_portals_model = __esm({
662
+ "src/infra/database/models/portals.model.ts"() {
663
+ "use strict";
664
+ init_esm_shims();
665
+ init_database();
666
+ PortalsModel = class {
667
+ static TABLE = "portals";
668
+ static findByPortalAddress(portalAddress) {
669
+ const sql = `SELECT _id, portalAddress, portalSeed, ownerAddress, createdAt, updatedAt FROM ${this.TABLE} WHERE portalAddress = ?`;
670
+ return QueryBuilder.selectOne(sql, [portalAddress]);
671
+ }
672
+ static create(input) {
673
+ const _id = uuidv72();
674
+ const now = (/* @__PURE__ */ new Date()).toISOString();
675
+ const sql = `INSERT INTO ${this.TABLE} (_id, portalAddress, portalSeed, ownerAddress, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)`;
676
+ QueryBuilder.execute(sql, [_id, input.portalAddress, input.portalSeed, input.ownerAddress, now, now]);
677
+ const created = this.findByPortalAddress(input.portalAddress);
678
+ if (!created) {
679
+ throw new Error("Failed to create portal");
680
+ }
681
+ return created;
682
+ }
683
+ static update(portalAddress, input) {
684
+ const now = (/* @__PURE__ */ new Date()).toISOString();
685
+ const keys = [];
686
+ const values = [];
687
+ for (const [k, v] of Object.entries(input)) {
688
+ if (v !== void 0) {
689
+ keys.push(`${k} = ?`);
690
+ values.push(v);
691
+ }
692
+ }
693
+ keys.push("updatedAt = ?");
694
+ values.push(now);
695
+ const updateChain = keys.join(", ");
696
+ const sql = `UPDATE ${this.TABLE} SET ${updateChain} WHERE portalAddress = ?`;
697
+ values.push(portalAddress);
698
+ QueryBuilder.execute(sql, values);
699
+ const updated = this.findByPortalAddress(portalAddress);
700
+ if (!updated) {
701
+ throw new Error("Failed to update portal");
702
+ }
703
+ return updated;
704
+ }
705
+ static upsert(input) {
706
+ const existing = this.findByPortalAddress(input.portalAddress);
707
+ if (existing) {
708
+ return this.update(input.portalAddress, {
709
+ portalSeed: input.portalSeed,
710
+ ownerAddress: input.ownerAddress
711
+ });
712
+ }
713
+ return this.create(input);
714
+ }
715
+ };
716
+ }
717
+ });
718
+
719
+ // src/infra/database/models/apikeys.model.ts
720
+ import { uuidv7 as uuidv73 } from "uuidv7";
721
+ var ApiKeysModel;
722
+ var init_apikeys_model = __esm({
723
+ "src/infra/database/models/apikeys.model.ts"() {
724
+ "use strict";
725
+ init_esm_shims();
726
+ init_database();
727
+ ApiKeysModel = class {
728
+ static TABLE = "api_keys";
729
+ static create(input) {
730
+ const _id = uuidv73();
731
+ const now = (/* @__PURE__ */ new Date()).toISOString();
732
+ const sql = `INSERT INTO ${this.TABLE} (_id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt)
733
+ VALUES (?, ?, ?, ?, ?, ?)`;
734
+ const result = QueryBuilder.execute(sql, [
735
+ _id,
736
+ input.apiKeySeed,
737
+ input.name,
738
+ input.collaboratorAddress,
739
+ input.portalAddress,
740
+ now
741
+ ]);
742
+ if (result.changes === 0) {
743
+ throw new Error("Failed to create API key");
744
+ }
745
+ const created = this.findById(_id);
746
+ if (!created) {
747
+ throw new Error("Failed to create API key");
748
+ }
749
+ return created;
750
+ }
751
+ static findById(_id) {
752
+ const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE _id = ? AND isDeleted = 0`;
753
+ return QueryBuilder.selectOne(sql, [_id]);
754
+ }
755
+ static findByCollaboratorAddress(collaboratorAddress) {
756
+ const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE collaboratorAddress = ? AND isDeleted = 0 LIMIT 1`;
757
+ return QueryBuilder.selectOne(sql, [collaboratorAddress]);
758
+ }
759
+ static delete(_id) {
760
+ const sql = `UPDATE ${this.TABLE} SET isDeleted = 1 WHERE _id = ?`;
761
+ QueryBuilder.execute(sql, [_id]);
762
+ }
763
+ static findByPortalAddress(portalAddress) {
764
+ const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE portalAddress = ? AND isDeleted = 0`;
765
+ return QueryBuilder.selectOne(sql, [portalAddress]);
766
+ }
767
+ static findByApiKey(apiKey) {
768
+ const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE apiKeySeed = ? AND isDeleted = 0`;
769
+ return QueryBuilder.selectOne(sql, [apiKey]);
770
+ }
771
+ };
772
+ }
773
+ });
774
+
775
+ // src/infra/database/models/folders.model.ts
776
+ var init_folders_model = __esm({
777
+ "src/infra/database/models/folders.model.ts"() {
778
+ "use strict";
779
+ init_esm_shims();
780
+ init_database();
781
+ }
782
+ });
783
+
784
+ // src/infra/database/models/events.model.ts
785
+ import { uuidv7 as uuidv74 } from "uuidv7";
786
+ var init_events_model = __esm({
787
+ "src/infra/database/models/events.model.ts"() {
788
+ "use strict";
789
+ init_esm_shims();
790
+ init_database();
791
+ init_workerSignal();
792
+ }
793
+ });
794
+
795
+ // src/infra/database/models/index.ts
796
+ var init_models = __esm({
797
+ "src/infra/database/models/index.ts"() {
798
+ "use strict";
799
+ init_esm_shims();
800
+ init_files_model();
801
+ init_portals_model();
802
+ init_apikeys_model();
803
+ init_folders_model();
804
+ init_events_model();
805
+ }
806
+ });
807
+
808
+ // src/domain/portal/savePortal.ts
809
+ function savePortal(input) {
810
+ if (!input.portalAddress || !input.portalSeed || !input.ownerAddress) {
811
+ throw new Error("portalAddress, portalSeed, and ownerAddress are required");
812
+ }
813
+ return PortalsModel.upsert(input);
814
+ }
815
+ var init_savePortal = __esm({
816
+ "src/domain/portal/savePortal.ts"() {
817
+ "use strict";
818
+ init_esm_shims();
819
+ init_models();
820
+ }
821
+ });
822
+
823
+ // src/infra/database/migrations/index.ts
824
+ var migrations_exports = {};
825
+ __export(migrations_exports, {
826
+ runMigrations: () => runMigrations
827
+ });
828
+ function runMigrations() {
829
+ const db = database_default();
830
+ db.exec(STABLE_SCHEMA);
831
+ logger.debug("Database schema ready");
832
+ }
833
+ var STABLE_SCHEMA;
834
+ var init_migrations = __esm({
835
+ "src/infra/database/migrations/index.ts"() {
836
+ "use strict";
837
+ init_esm_shims();
838
+ init_database();
839
+ init_infra();
840
+ STABLE_SCHEMA = `
841
+ CREATE TABLE IF NOT EXISTS files (
842
+ _id TEXT PRIMARY KEY,
843
+ ddocId TEXT NOT NULL,
844
+ title TEXT NOT NULL,
845
+ content TEXT NOT NULL,
846
+ localVersion INTEGER NOT NULL DEFAULT 1,
847
+ onchainVersion INTEGER NOT NULL DEFAULT 0,
848
+ syncStatus TEXT NOT NULL DEFAULT 'pending',
849
+ createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
850
+ updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
851
+ isDeleted INTEGER NOT NULL DEFAULT 0,
852
+ portalAddress TEXT NOT NULL,
853
+ metadata TEXT DEFAULT '{}',
854
+ onChainFileId INTEGER,
855
+ commentKey TEXT,
856
+ linkKey TEXT,
857
+ linkKeyNonce TEXT,
858
+ link TEXT
859
+ );
860
+ CREATE INDEX IF NOT EXISTS idx_files_createdAt ON files(createdAt);
861
+ CREATE INDEX IF NOT EXISTS idx_files_syncStatus ON files(syncStatus);
862
+ CREATE INDEX IF NOT EXISTS idx_files_title ON files(title);
863
+ CREATE INDEX IF NOT EXISTS idx_files_portalAddress ON files(portalAddress);
864
+
865
+ CREATE TABLE IF NOT EXISTS portals (
866
+ _id TEXT PRIMARY KEY,
867
+ portalAddress TEXT NOT NULL UNIQUE,
868
+ portalSeed TEXT NOT NULL UNIQUE,
869
+ ownerAddress TEXT NOT NULL,
870
+ createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
871
+ updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
872
+ );
873
+
874
+ CREATE TABLE IF NOT EXISTS api_keys (
875
+ _id TEXT PRIMARY KEY,
876
+ apiKeySeed TEXT NOT NULL UNIQUE,
877
+ name TEXT NOT NULL,
878
+ collaboratorAddress TEXT NOT NULL UNIQUE,
879
+ portalAddress TEXT NOT NULL,
880
+ createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
881
+ isDeleted INTEGER NOT NULL DEFAULT 0
882
+ );
883
+
884
+ CREATE TABLE IF NOT EXISTS events (
885
+ _id TEXT PRIMARY KEY,
886
+ type TEXT NOT NULL CHECK (type IN ('create', 'update', 'delete')),
887
+ timestamp INTEGER NOT NULL,
888
+ fileId TEXT NOT NULL,
889
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'processed', 'failed')),
890
+ retryCount INTEGER NOT NULL DEFAULT 0,
891
+ lastError TEXT,
892
+ lockedAt INTEGER,
893
+ nextRetryAt INTEGER,
894
+ userOpHash TEXT,
895
+ pendingPayload TEXT,
896
+ portalAddress TEXT
897
+ );
898
+ CREATE INDEX IF NOT EXISTS idx_events_pending_eligible ON events (status, nextRetryAt, timestamp) WHERE status = 'pending';
899
+ CREATE INDEX IF NOT EXISTS idx_events_file_pending_ts ON events (fileId, status, timestamp) WHERE status = 'pending';
900
+ CREATE INDEX IF NOT EXISTS idx_events_processing_locked ON events (status, lockedAt) WHERE status = 'processing';
901
+ CREATE INDEX IF NOT EXISTS idx_events_failed_portal ON events (portalAddress, status) WHERE status = 'failed';
902
+
903
+ CREATE TABLE IF NOT EXISTS folders (
904
+ _id TEXT PRIMARY KEY,
905
+ onchainFileId INTEGER NOT NULL,
906
+ folderId TEXT NOT NULL,
907
+ folderRef TEXT NOT NULL,
908
+ folderName TEXT NOT NULL,
909
+ portalAddress TEXT NOT NULL,
910
+ metadataIPFSHash TEXT NOT NULL,
911
+ contentIPFSHash TEXT NOT NULL,
912
+ isDeleted INTEGER NOT NULL DEFAULT 0,
913
+ lastTransactionHash TEXT,
914
+ lastTransactionBlockNumber INTEGER NOT NULL,
915
+ lastTransactionBlockTimestamp INTEGER NOT NULL,
916
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
917
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
918
+ );
919
+ CREATE INDEX IF NOT EXISTS idx_folders_folderRef_folderId ON folders(folderRef, folderId);
920
+ CREATE INDEX IF NOT EXISTS idx_folders_folderRef ON folders(folderRef);
921
+ CREATE INDEX IF NOT EXISTS idx_folders_created_at ON folders(created_at);
922
+ `;
923
+ }
924
+ });
925
+
926
+ // src/cli/index.ts
927
+ init_esm_shims();
928
+ import { Command } from "commander";
929
+
930
+ // src/cli/fetch-api-key.ts
931
+ init_esm_shims();
932
+ init_constants();
933
+ import axios from "axios";
934
+ import { toUint8Array } from "js-base64";
935
+ import { sha256 } from "viem";
936
+ var fetchApiKeyData = async (apiKey) => {
937
+ try {
938
+ const keyHash = sha256(toUint8Array(apiKey));
939
+ const fullUrl = BASE_CONFIG.API_URL + `api-access/${keyHash}`;
940
+ const response = await axios.get(fullUrl);
941
+ const { encryptedKeyMaterial, encryptedAppMaterial, id } = response.data;
942
+ return { encryptedKeyMaterial, encryptedAppMaterial, id };
943
+ } catch (error) {
944
+ if (axios.isAxiosError(error)) {
945
+ if (error.response?.status === 401) {
946
+ throw new Error("Invalid API key");
947
+ }
948
+ if (error.response?.status === 404) {
949
+ throw new Error("API key not found");
950
+ }
951
+ if (error.code === "ECONNREFUSED") {
952
+ throw new Error(`Cannot connect to server at ${BASE_CONFIG.API_URL}`);
953
+ }
954
+ throw new Error(`Server error: ${error.response?.data?.message || error.message}`);
955
+ }
956
+ throw error;
957
+ }
958
+ };
959
+
960
+ // src/cli/scaffold-config.ts
961
+ init_esm_shims();
962
+ init_constants();
963
+ import fs from "fs";
964
+ import path2 from "path";
965
+ import os from "os";
966
+ function getFileverseDir() {
967
+ return path2.join(os.homedir(), ".fileverse");
968
+ }
969
+ function getDefaultDbPath() {
970
+ return path2.join(getFileverseDir(), "fileverse-api.db");
971
+ }
972
+ function getEnvPath() {
973
+ return path2.join(getFileverseDir(), ".env");
974
+ }
975
+ function scaffoldConfig(options = {}) {
976
+ const fileverseDir = getFileverseDir();
977
+ const envPath = getEnvPath();
978
+ if (!fs.existsSync(fileverseDir)) {
979
+ fs.mkdirSync(fileverseDir, { recursive: true });
980
+ }
981
+ const dbPath = options.dbPath || getDefaultDbPath();
982
+ const dbDir = path2.dirname(dbPath);
983
+ if (!fs.existsSync(dbDir)) {
984
+ fs.mkdirSync(dbDir, { recursive: true });
985
+ }
986
+ const envContent = `API_KEY=${options.apiKey}
987
+ RPC_URL=${options.rpcUrl || STATIC_CONFIG.DEFAULT_RPC_URL}
988
+ DB_PATH=${dbPath}
989
+ PORT=${options.port || STATIC_CONFIG.DEFAULT_PORT}
990
+ `;
991
+ fs.writeFileSync(envPath, envContent, "utf-8");
992
+ return envPath;
993
+ }
994
+
995
+ // src/cli/process-manager.ts
996
+ init_esm_shims();
997
+ import { spawn } from "child_process";
998
+ import path3 from "path";
999
+ import { fileURLToPath as fileURLToPath2 } from "url";
1000
+ import { existsSync } from "fs";
1001
+ var managedProcesses = [];
1002
+ function getDistDir() {
1003
+ const __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
1004
+ return path3.resolve(__dirname2, "..");
1005
+ }
1006
+ function isDevMode() {
1007
+ const distDir = getDistDir();
1008
+ return existsSync(path3.join(distDir, "index.ts"));
1009
+ }
1010
+ function prefixOutput(name, data) {
1011
+ const lines = data.toString().split("\n").filter(Boolean);
1012
+ for (const line of lines) {
1013
+ console.log(`[${name}] ${line}`);
1014
+ }
1015
+ }
1016
+ function spawnProcess(name, executable, scriptPath, extraEnv) {
1017
+ const child = spawn(executable, [scriptPath], {
1018
+ stdio: ["ignore", "pipe", "pipe"],
1019
+ env: { ...process.env, NODE_ENV: executable === "tsx" ? "development" : "production", ...extraEnv },
1020
+ detached: false
1021
+ });
1022
+ child.stdout?.on("data", (data) => prefixOutput(name, data));
1023
+ child.stderr?.on("data", (data) => prefixOutput(name, data));
1024
+ child.on("error", (error) => {
1025
+ console.error(`[${name}] Process error:`, error.message);
1026
+ });
1027
+ child.on("exit", (code, signal) => {
1028
+ if (signal) {
1029
+ console.log(`[${name}] Process terminated by signal ${signal}`);
1030
+ } else if (code !== 0) {
1031
+ console.error(`[${name}] Process exited with code ${code}`);
1032
+ }
1033
+ });
1034
+ managedProcesses.push({ name, process: child });
1035
+ return child;
1036
+ }
1037
+ function startApiServer() {
1038
+ const distDir = getDistDir();
1039
+ const dev = isDevMode();
1040
+ const executable = dev ? "tsx" : "node";
1041
+ const apiPath = path3.join(distDir, dev ? "index.ts" : "index.js");
1042
+ return spawnProcess("API", executable, apiPath, { IS_CLI: "1" });
1043
+ }
1044
+ function startWorker() {
1045
+ const distDir = getDistDir();
1046
+ const dev = isDevMode();
1047
+ const executable = dev ? "tsx" : "node";
1048
+ const workerPath = path3.join(distDir, dev ? "worker.ts" : "worker.js");
1049
+ return spawnProcess("WORKER", executable, workerPath);
1050
+ }
1051
+ function startAll() {
1052
+ const api = startApiServer();
1053
+ const worker = startWorker();
1054
+ return { api, worker };
1055
+ }
1056
+ function setupShutdownHandlers() {
1057
+ const shutdown = (signal) => {
1058
+ console.log(`
1059
+ Received ${signal}, shutting down...`);
1060
+ for (const { name, process: child } of managedProcesses) {
1061
+ if (child.pid && !child.killed) {
1062
+ console.log(`[${name}] Stopping...`);
1063
+ child.kill("SIGTERM");
1064
+ }
1065
+ }
1066
+ setTimeout(() => {
1067
+ for (const { name, process: child } of managedProcesses) {
1068
+ if (child.pid && !child.killed) {
1069
+ console.log(`[${name}] Force killing...`);
1070
+ child.kill("SIGKILL");
1071
+ }
1072
+ }
1073
+ process.exit(0);
1074
+ }, 5e3);
1075
+ };
1076
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
1077
+ process.on("SIGINT", () => shutdown("SIGINT"));
1078
+ }
1079
+ function waitForProcesses() {
1080
+ return new Promise((resolve) => {
1081
+ const checkInterval = setInterval(() => {
1082
+ const allExited = managedProcesses.every(({ process: child }) => child.exitCode !== null || child.killed);
1083
+ if (allExited) {
1084
+ clearInterval(checkInterval);
1085
+ resolve();
1086
+ }
1087
+ }, 1e3);
1088
+ });
1089
+ }
1090
+
1091
+ // src/cli/prompts.ts
1092
+ init_esm_shims();
1093
+ init_constants();
1094
+ init_config();
1095
+ import prompts from "prompts";
1096
+ var promptForConfig = async (existingOptions = {}) => {
1097
+ const savedConfig = getRuntimeConfig();
1098
+ const questions = [];
1099
+ if (!existingOptions.apiKey) {
1100
+ questions.push({
1101
+ type: "text",
1102
+ name: "apiKey",
1103
+ message: "Enter your API Key:",
1104
+ validate: (value) => value.length > 0 || "API Key is required",
1105
+ initial: savedConfig.API_KEY || ""
1106
+ });
1107
+ }
1108
+ if (!existingOptions.rpcUrl) {
1109
+ questions.push({
1110
+ type: "text",
1111
+ name: "rpcUrl",
1112
+ message: "Enter RPC URL (press Enter for default):",
1113
+ initial: savedConfig.RPC_URL || STATIC_CONFIG.DEFAULT_RPC_URL
1114
+ });
1115
+ }
1116
+ if (questions.length === 0) {
1117
+ return {
1118
+ apiKey: existingOptions.apiKey,
1119
+ rpcUrl: existingOptions.rpcUrl || STATIC_CONFIG.DEFAULT_RPC_URL
1120
+ };
1121
+ }
1122
+ const response = await prompts(questions, {
1123
+ onCancel: () => {
1124
+ console.log("\nSetup cancelled.");
1125
+ process.exit(1);
1126
+ }
1127
+ });
1128
+ return {
1129
+ apiKey: existingOptions.apiKey || response.apiKey,
1130
+ rpcUrl: existingOptions.rpcUrl || response.rpcUrl || STATIC_CONFIG.DEFAULT_RPC_URL
1131
+ };
1132
+ };
1133
+ function needsPrompting(options) {
1134
+ return !options.apiKey;
1135
+ }
1136
+
1137
+ // src/cli/index.ts
1138
+ init_config();
1139
+
1140
+ // src/init/index.ts
1141
+ init_esm_shims();
1142
+ init_savePortal();
1143
+ init_saveApiKey();
1144
+ init_apikeys_model();
1145
+ init_infra();
1146
+ import { deriveHKDFKey as deriveHKDFKey2 } from "@fileverse/crypto/hkdf";
1147
+ import { toUint8Array as toUint8Array5 } from "js-base64";
1148
+ import { stringToBytes as stringToBytes2 } from "viem";
1149
+ import { toAESKey as toAESKey2, aesDecrypt } from "@fileverse/crypto/webcrypto";
1150
+ var SAVED_DATA_ENCRYPTION_KEY_INFO = "SAVED_DATA_ENCRYPTION_KEY";
1151
+ function initializeWithData(data) {
1152
+ const { keyMaterial, appMaterial } = data;
1153
+ savePortal({
1154
+ portalAddress: appMaterial.portalAddress,
1155
+ portalSeed: appMaterial.portalSeed,
1156
+ ownerAddress: appMaterial.ownerAddress
1157
+ });
1158
+ const existingApiKey = ApiKeysModel.findByApiKey(keyMaterial.apiKeySeed);
1159
+ if (!existingApiKey) {
1160
+ addApiKey({
1161
+ apiKeySeed: keyMaterial.apiKeySeed,
1162
+ name: keyMaterial.name,
1163
+ collaboratorAddress: keyMaterial.collaboratorAddress,
1164
+ portalAddress: appMaterial.portalAddress
1165
+ });
1166
+ return { portalSaved: true, apiKeySaved: true };
1167
+ }
1168
+ return { portalSaved: true, apiKeySaved: false };
1169
+ }
1170
+ var getAesKeyFromApiKey = async (apiKey) => {
1171
+ const rawSecret = deriveHKDFKey2(
1172
+ toUint8Array5(apiKey),
1173
+ new Uint8Array([0]),
1174
+ stringToBytes2(SAVED_DATA_ENCRYPTION_KEY_INFO)
1175
+ );
1176
+ return await toAESKey2(rawSecret);
1177
+ };
1178
+ var bytestToJSON = (bytes) => {
1179
+ return JSON.parse(new TextDecoder().decode(bytes));
1180
+ };
1181
+ var decryptSavedData = async (apiKey, encryptedData) => {
1182
+ const aesKey = await getAesKeyFromApiKey(apiKey);
1183
+ const decryptedBytes = await aesDecrypt(aesKey, toUint8Array5(encryptedData));
1184
+ const data = bytestToJSON(decryptedBytes);
1185
+ return data;
1186
+ };
1187
+
1188
+ // src/cli/index.ts
1189
+ var program = new Command().name("fileverse-api").description("Run the Fileverse API server").version("0.0.1").option("--apiKey <key>", "API key for authentication").option("--rpcUrl <url>", "RPC URL for blockchain connection").option("--port <port>", "Port to run the server on", "8001").option("--db <path>", "Database path").action(async (options) => {
1190
+ try {
1191
+ console.log("Fileverse API - Starting initialization...\n");
1192
+ if (needsPrompting(options)) {
1193
+ const prompted = await promptForConfig({
1194
+ apiKey: options.apiKey,
1195
+ rpcUrl: options.rpcUrl
1196
+ });
1197
+ options.apiKey = prompted.apiKey;
1198
+ options.rpcUrl = prompted.rpcUrl;
1199
+ }
1200
+ const data = await fetchApiKeyData(options.apiKey);
1201
+ console.log("\u2713 API key data retrieved\n");
1202
+ const keyMaterial = await decryptSavedData(options.apiKey, data.encryptedKeyMaterial);
1203
+ const appMaterial = await decryptSavedData(options.apiKey, data.encryptedAppMaterial);
1204
+ console.log("Setting up configuration...");
1205
+ const envPath = scaffoldConfig({
1206
+ dbPath: options.db,
1207
+ port: options.port,
1208
+ apiKey: options.apiKey,
1209
+ rpcUrl: options.rpcUrl
1210
+ });
1211
+ loadConfig();
1212
+ console.log(`\u2713 Configuration saved to ${envPath}
1213
+ `);
1214
+ const { runMigrations: runMigrations2 } = await Promise.resolve().then(() => (init_migrations(), migrations_exports));
1215
+ runMigrations2();
1216
+ console.log("\u2713 Database migrations complete");
1217
+ const result = initializeWithData({
1218
+ keyMaterial,
1219
+ appMaterial,
1220
+ id: data.id
1221
+ });
1222
+ console.log("\u2713 Portal saved");
1223
+ if (result.apiKeySaved) {
1224
+ console.log("\u2713 API key saved");
1225
+ } else {
1226
+ console.log("\u2713 API key already exists");
1227
+ }
1228
+ console.log("\nStarting services...");
1229
+ setupShutdownHandlers();
1230
+ startAll();
1231
+ console.log(`
1232
+ \u2713 Fileverse API is running!
1233
+
1234
+ API Server: http://127.0.0.1:${options.port}
1235
+ Worker: Active
1236
+
1237
+ MCP: Add this to your AI agent's MCP config (e.g. .claude/mcp.json):
1238
+
1239
+ {
1240
+ "mcpServers": {
1241
+ "fileverse-api": {
1242
+ "command": "fileverse-api-mcp"
1243
+ }
1244
+ }
1245
+ }
1246
+
1247
+ Config is auto-read from ~/.fileverse/.env \u2014 no env vars needed.
1248
+
1249
+ Press Ctrl+C to stop.
1250
+ `);
1251
+ await waitForProcesses();
1252
+ } catch (error) {
1253
+ console.error("\n\u274C Error:", error instanceof Error ? error.message : error);
1254
+ process.exit(1);
1255
+ }
1256
+ });
1257
+ program.parse();
1258
+ //# sourceMappingURL=index.js.map