@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.
- package/api/src/routes/ingestions.ts +6 -1
- package/api/src/services/SDKService.ts +48 -2
- package/dist/api/src/routes/ingestions.js +6 -1
- package/dist/api/src/services/SDKService.js +37 -2
- package/dist/assets/{index-aI2VZJFA.js → index-dnBz6SWG.js} +2 -2
- package/dist/index.html +1 -1
- package/docs-dev/ingestion-engine.md +3 -3
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
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
|
}
|