@realtimex/folio 0.1.14 → 0.1.16

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.
@@ -7,6 +7,7 @@ import crypto from "crypto";
7
7
  import { asyncHandler } from "../middleware/errorHandler.js";
8
8
  import { optionalAuth } from "../middleware/auth.js";
9
9
  import { IngestionService } from "../services/IngestionService.js";
10
+ import { SDKService } from "../services/SDKService.js";
10
11
 
11
12
  const router = Router();
12
13
  const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 20 * 1024 * 1024 } });
@@ -68,7 +69,11 @@ router.post(
68
69
  .eq("user_id", req.user.id)
69
70
  .maybeSingle();
70
71
 
71
- const dropzoneDir = settings?.storage_path || path.join(os.homedir(), ".realtimex", "folio", "dropzone");
72
+ const configuredStoragePath = typeof settings?.storage_path === "string" ? settings.storage_path.trim() : "";
73
+ const legacyDefaultDropzoneDir = path.join(os.homedir(), ".realtimex", "folio", "dropzone");
74
+ const dropzoneDir = !configuredStoragePath || configuredStoragePath === legacyDefaultDropzoneDir
75
+ ? await SDKService.getDefaultDropzoneDir()
76
+ : configuredStoragePath;
72
77
  await fs.mkdir(dropzoneDir, { recursive: true });
73
78
 
74
79
  // Compute SHA-256 hash before writing — used for deduplication
@@ -1,4 +1,6 @@
1
1
  import { RealtimeXSDK, ProvidersResponse } from "@realtimex/sdk";
2
+ import os from "node:os";
3
+ import path from "node:path";
2
4
 
3
5
  import { createLogger } from "../utils/logger.js";
4
6
 
@@ -48,7 +50,6 @@ export class SDKService {
48
50
 
49
51
  logger.info("RealTimeX SDK initialized successfully");
50
52
 
51
- // @ts-ignore ping available in desktop bridge
52
53
  this.instance.ping?.().catch(() => {
53
54
  logger.warn("Desktop ping failed during startup");
54
55
  });
@@ -77,7 +78,6 @@ export class SDKService {
77
78
 
78
79
  // Try to ping first (faster)
79
80
  try {
80
- // @ts-ignore ping available in desktop bridge
81
81
  await sdk.ping();
82
82
  return true;
83
83
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -111,6 +111,51 @@ export class SDKService {
111
111
  }
112
112
  }
113
113
 
114
+ private static appDataDir: string | null = null;
115
+
116
+ static async getAppDataDir(): Promise<string | null> {
117
+ if (this.appDataDir) {
118
+ return this.appDataDir;
119
+ }
120
+
121
+ const sdk = this.getSDK();
122
+ if (!sdk) {
123
+ return null;
124
+ }
125
+
126
+ try {
127
+ const dataDir = await this.withTimeout<string>(
128
+ sdk.getAppDataDir(),
129
+ 10000,
130
+ "App data directory fetch timed out"
131
+ );
132
+
133
+ if (!dataDir || !dataDir.trim()) {
134
+ return null;
135
+ }
136
+
137
+ this.appDataDir = dataDir;
138
+ return dataDir;
139
+ } catch (error) {
140
+ logger.warn("Failed to get app data directory from SDK", {
141
+ error: error instanceof Error ? error.message : String(error),
142
+ });
143
+ return null;
144
+ }
145
+ }
146
+
147
+ static async getDefaultDropzoneDir(): Promise<string> {
148
+ const sdkDataDir = await this.getAppDataDir();
149
+ if (sdkDataDir) {
150
+ return path.join(sdkDataDir, "dropzone");
151
+ }
152
+
153
+ const sdkAppId = this.getSDK()?.appId?.trim();
154
+ const envAppId = process.env.RTX_APP_ID?.trim();
155
+ const fallbackAppId = sdkAppId || envAppId || "folio";
156
+ return path.join(os.homedir(), ".realtimex.ai", "Resources", "local-apps", fallbackAppId, "dropzone");
157
+ }
158
+
114
159
  // Cache for default providers (avoid repeated SDK calls)
115
160
  private static defaultChatProvider: ProviderResult | null = null;
116
161
  private static defaultEmbedProvider: ProviderResult | null = null;
@@ -243,6 +288,7 @@ export class SDKService {
243
288
  }
244
289
 
245
290
  static clearProviderCache(): void {
291
+ this.appDataDir = null;
246
292
  this.defaultChatProvider = null;
247
293
  this.defaultEmbedProvider = null;
248
294
  }
@@ -7,6 +7,7 @@ import crypto from "crypto";
7
7
  import { asyncHandler } from "../middleware/errorHandler.js";
8
8
  import { optionalAuth } from "../middleware/auth.js";
9
9
  import { IngestionService } from "../services/IngestionService.js";
10
+ import { SDKService } from "../services/SDKService.js";
10
11
  const router = Router();
11
12
  const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 20 * 1024 * 1024 } });
12
13
  router.use(optionalAuth);
@@ -52,7 +53,11 @@ router.post("/upload", upload.single("file"), asyncHandler(async (req, res) => {
52
53
  .select("storage_path")
53
54
  .eq("user_id", req.user.id)
54
55
  .maybeSingle();
55
- const dropzoneDir = settings?.storage_path || path.join(os.homedir(), ".realtimex", "folio", "dropzone");
56
+ const configuredStoragePath = typeof settings?.storage_path === "string" ? settings.storage_path.trim() : "";
57
+ const legacyDefaultDropzoneDir = path.join(os.homedir(), ".realtimex", "folio", "dropzone");
58
+ const dropzoneDir = !configuredStoragePath || configuredStoragePath === legacyDefaultDropzoneDir
59
+ ? await SDKService.getDefaultDropzoneDir()
60
+ : configuredStoragePath;
56
61
  await fs.mkdir(dropzoneDir, { recursive: true });
57
62
  // Compute SHA-256 hash before writing — used for deduplication
58
63
  const fileHash = crypto.createHash("sha256").update(file.buffer).digest("hex");
@@ -1,4 +1,6 @@
1
1
  import { RealtimeXSDK } from "@realtimex/sdk";
2
+ import os from "node:os";
3
+ import path from "node:path";
2
4
  import { createLogger } from "../utils/logger.js";
3
5
  const logger = createLogger("SDKService");
4
6
  export class SDKService {
@@ -34,7 +36,6 @@ export class SDKService {
34
36
  ]
35
37
  });
36
38
  logger.info("RealTimeX SDK initialized successfully");
37
- // @ts-ignore ping available in desktop bridge
38
39
  this.instance.ping?.().catch(() => {
39
40
  logger.warn("Desktop ping failed during startup");
40
41
  });
@@ -61,7 +62,6 @@ export class SDKService {
61
62
  return false;
62
63
  // Try to ping first (faster)
63
64
  try {
64
- // @ts-ignore ping available in desktop bridge
65
65
  await sdk.ping();
66
66
  return true;
67
67
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -95,6 +95,40 @@ export class SDKService {
95
95
  clearTimeout(timeoutHandle);
96
96
  }
97
97
  }
98
+ static appDataDir = null;
99
+ static async getAppDataDir() {
100
+ if (this.appDataDir) {
101
+ return this.appDataDir;
102
+ }
103
+ const sdk = this.getSDK();
104
+ if (!sdk) {
105
+ return null;
106
+ }
107
+ try {
108
+ const dataDir = await this.withTimeout(sdk.getAppDataDir(), 10000, "App data directory fetch timed out");
109
+ if (!dataDir || !dataDir.trim()) {
110
+ return null;
111
+ }
112
+ this.appDataDir = dataDir;
113
+ return dataDir;
114
+ }
115
+ catch (error) {
116
+ logger.warn("Failed to get app data directory from SDK", {
117
+ error: error instanceof Error ? error.message : String(error),
118
+ });
119
+ return null;
120
+ }
121
+ }
122
+ static async getDefaultDropzoneDir() {
123
+ const sdkDataDir = await this.getAppDataDir();
124
+ if (sdkDataDir) {
125
+ return path.join(sdkDataDir, "dropzone");
126
+ }
127
+ const sdkAppId = this.getSDK()?.appId?.trim();
128
+ const envAppId = process.env.RTX_APP_ID?.trim();
129
+ const fallbackAppId = sdkAppId || envAppId || "folio";
130
+ return path.join(os.homedir(), ".realtimex.ai", "Resources", "local-apps", fallbackAppId, "dropzone");
131
+ }
98
132
  // Cache for default providers (avoid repeated SDK calls)
99
133
  static defaultChatProvider = null;
100
134
  static defaultEmbedProvider = null;
@@ -206,6 +240,7 @@ export class SDKService {
206
240
  return await this.getDefaultEmbedProvider();
207
241
  }
208
242
  static clearProviderCache() {
243
+ this.appDataDir = null;
209
244
  this.defaultChatProvider = null;
210
245
  this.defaultEmbedProvider = null;
211
246
  }