nolo-cli 0.1.13 → 0.1.15

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.
Files changed (321) hide show
  1. package/README.md +9 -2
  2. package/agent-runtime/hostAdapter.ts +53 -0
  3. package/agent-runtime/index.ts +28 -0
  4. package/agent-runtime/localLoop.ts +62 -0
  5. package/agent-runtime/runtimeDecision.ts +70 -0
  6. package/agent-runtime/types.ts +87 -0
  7. package/agentRunCommand.ts +104 -0
  8. package/agentRuntimeCommands.ts +139 -22
  9. package/agentRuntimeLocal.ts +7 -0
  10. package/ai/agent/_executeModel.ts +118 -0
  11. package/ai/agent/agentSlice.ts +544 -1
  12. package/ai/agent/appWorkingMemory.ts +126 -0
  13. package/ai/agent/avatarUtils.ts +24 -0
  14. package/ai/agent/buildEditingContext.ts +373 -0
  15. package/ai/agent/buildSystemPrompt.ts +532 -0
  16. package/ai/agent/cleanAgentMessages.ts +140 -0
  17. package/ai/agent/cliChatClient.ts +119 -0
  18. package/ai/agent/contextCompiler.ts +107 -0
  19. package/ai/agent/contextLayerContract.ts +44 -0
  20. package/ai/agent/createAgentSchema.ts +234 -0
  21. package/ai/agent/executeToolCall.ts +58 -0
  22. package/ai/agent/fetchAgentContexts.ts +42 -0
  23. package/ai/agent/generatePrompt.ts +3 -0
  24. package/ai/agent/getFullChatContextKeys.ts +168 -0
  25. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  26. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  27. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  28. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  29. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  30. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  31. package/ai/agent/planSlice.ts +259 -0
  32. package/ai/agent/referenceUtils.ts +229 -0
  33. package/ai/agent/runAgentBackground.ts +238 -0
  34. package/ai/agent/runAgentClientLoop.ts +138 -0
  35. package/ai/agent/runtimeGuidance.ts +97 -0
  36. package/ai/agent/runtimeServerBase.ts +37 -0
  37. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  38. package/ai/agent/startParallelAgentStreams.ts +424 -0
  39. package/ai/agent/startupProtocol.ts +53 -0
  40. package/ai/agent/streamAgentChatTurn.ts +1299 -0
  41. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  42. package/ai/agent/types.ts +71 -0
  43. package/ai/agent/utils/imageOutput.ts +39 -0
  44. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  45. package/ai/agent/utils/sortUtils.ts +250 -0
  46. package/ai/agent/web/referencePickerUtils.ts +146 -0
  47. package/ai/ai.locale.ts +1083 -0
  48. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  49. package/ai/chat/fetchUtils.native.ts +276 -0
  50. package/ai/chat/fetchUtils.ts +153 -0
  51. package/ai/chat/inlineImageUrlsForCustomProvider.ts +117 -0
  52. package/ai/chat/parseApiError.ts +64 -0
  53. package/ai/chat/parseMultilineSSE.ts +95 -0
  54. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  55. package/ai/chat/sendOpenAICompletionsRequest.ts +712 -0
  56. package/ai/chat/sendOpenAIResponseRequest.ts +512 -0
  57. package/ai/chat/shouldUseServerProxy.ts +18 -0
  58. package/ai/chat/sseClient.native.ts +91 -0
  59. package/ai/chat/sseClient.ts +67 -0
  60. package/ai/chat/streamReader.native.ts +31 -0
  61. package/ai/chat/streamReader.ts +62 -0
  62. package/ai/chat/updateTotalUsage.ts +72 -0
  63. package/ai/context/buildReferenceContext.ts +437 -0
  64. package/ai/context/calculateContextUsage.ts +133 -0
  65. package/ai/context/retention.ts +165 -0
  66. package/ai/context/tokenUtils.ts +78 -0
  67. package/ai/index.ts +1 -1
  68. package/ai/llm/agentCapabilities.ts +74 -0
  69. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  70. package/ai/llm/deepinfra.ts +28 -0
  71. package/ai/llm/fireworks.ts +68 -0
  72. package/ai/llm/generateRequestBody.ts +165 -0
  73. package/ai/llm/getModelContextWindow.ts +84 -0
  74. package/ai/llm/getNoloKey.ts +37 -0
  75. package/ai/llm/getPricing.ts +232 -0
  76. package/ai/llm/hooks/useModelPricing.ts +75 -0
  77. package/ai/llm/imagePricing.ts +66 -0
  78. package/ai/llm/isResponseAPIModel.ts +13 -0
  79. package/ai/llm/kimi.ts +18 -0
  80. package/ai/llm/mimo.ts +71 -0
  81. package/ai/llm/mistral.ts +22 -0
  82. package/ai/llm/modelAvatar.ts +427 -0
  83. package/ai/llm/models.ts +45 -0
  84. package/ai/llm/openrouterModels.ts +141 -0
  85. package/ai/llm/providers.ts +307 -0
  86. package/ai/llm/reasoningModels.ts +28 -0
  87. package/ai/llm/types.ts +59 -0
  88. package/ai/llm/usageRequestOptions.ts +59 -0
  89. package/ai/memory/capture.ts +148 -0
  90. package/ai/memory/consolidate.ts +104 -0
  91. package/ai/memory/delete.ts +147 -0
  92. package/ai/memory/overlay.ts +84 -0
  93. package/ai/memory/query.ts +38 -0
  94. package/ai/memory/queryShared.ts +160 -0
  95. package/ai/memory/rank.ts +105 -0
  96. package/ai/memory/recentRelationshipRecap.ts +247 -0
  97. package/ai/memory/remember.ts +167 -0
  98. package/ai/memory/runtime.ts +76 -0
  99. package/ai/memory/store.ts +20 -0
  100. package/ai/memory/storeShared.ts +76 -0
  101. package/ai/memory/types.ts +46 -0
  102. package/ai/memory/understanding.ts +349 -0
  103. package/ai/memory/understandingGreeting.ts +264 -0
  104. package/ai/messages/type.ts +20 -0
  105. package/ai/policy/personalizationDialog.ts +333 -0
  106. package/ai/policy/runtimePolicy.ts +440 -0
  107. package/ai/policy/selfUpdateFields.ts +48 -0
  108. package/ai/policy/types.ts +64 -0
  109. package/ai/skills/referenceRuntime.ts +274 -0
  110. package/ai/skills/skillDiagnostics.ts +251 -0
  111. package/ai/skills/skillDocBuilder.ts +139 -0
  112. package/ai/skills/skillDocProtocol.ts +434 -0
  113. package/ai/skills/skillReferenceSummary.ts +63 -0
  114. package/ai/skills/skillSummaryMarker.ts +26 -0
  115. package/ai/token/calculatePrice.ts +546 -0
  116. package/ai/token/db.ts +98 -0
  117. package/ai/token/externalToolCost.ts +321 -0
  118. package/ai/token/hooks/useRecords.ts +65 -0
  119. package/ai/token/missingUsageEstimate.ts +42 -0
  120. package/ai/token/modelUsageQuery.ts +252 -0
  121. package/ai/token/normalizeUsage.ts +84 -0
  122. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  123. package/ai/token/prepareTokenUsageData.ts +88 -0
  124. package/ai/token/query.ts +88 -0
  125. package/ai/token/queryUserTokens.ts +59 -0
  126. package/ai/token/resolveBillingTarget.ts +52 -0
  127. package/ai/token/saveTokenRecord.ts +53 -0
  128. package/ai/token/serverDialogProjection.ts +78 -0
  129. package/ai/token/serverTokenWriter.ts +143 -0
  130. package/ai/token/stats.ts +21 -0
  131. package/ai/token/tokenThunks.ts +24 -0
  132. package/ai/token/types.ts +93 -0
  133. package/ai/tools/agent/agentTools.ts +176 -0
  134. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  135. package/ai/tools/agent/callAgentTool.ts +139 -0
  136. package/ai/tools/agent/createAgentTool.ts +512 -0
  137. package/ai/tools/agent/createDialogTool.ts +69 -0
  138. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  139. package/ai/tools/agent/parallelBudget.ts +221 -0
  140. package/ai/tools/agent/presets/appBuilderPreset.ts +147 -0
  141. package/ai/tools/agent/runLlmTool.ts +96 -0
  142. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  143. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  144. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  145. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  146. package/ai/tools/agent/updateAgentTool.ts +96 -0
  147. package/ai/tools/agent/updateSelfTool.ts +113 -0
  148. package/ai/tools/amazonProductScraperTool.ts +86 -0
  149. package/ai/tools/apifyActorClient.ts +45 -0
  150. package/ai/tools/appEditGuard.ts +372 -0
  151. package/ai/tools/appReadSnapshot.ts +153 -0
  152. package/ai/tools/appTools.ts +1549 -0
  153. package/ai/tools/applyEditTool.ts +256 -0
  154. package/ai/tools/applyLineEditsTool.ts +312 -0
  155. package/ai/tools/browserTools/click.ts +33 -0
  156. package/ai/tools/browserTools/closeSession.ts +29 -0
  157. package/ai/tools/browserTools/common.ts +27 -0
  158. package/ai/tools/browserTools/openSession.ts +48 -0
  159. package/ai/tools/browserTools/readContent.ts +38 -0
  160. package/ai/tools/browserTools/selectOption.ts +46 -0
  161. package/ai/tools/browserTools/typeText.ts +42 -0
  162. package/ai/tools/category/createCategoryTool.ts +66 -0
  163. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  164. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  165. package/ai/tools/cfBrowserTools.ts +319 -0
  166. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  167. package/ai/tools/checkEnvTool.ts +65 -0
  168. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  169. package/ai/tools/codeSearchTool.ts +111 -0
  170. package/ai/tools/codeTools.ts +101 -0
  171. package/ai/tools/createDocTool.ts +132 -0
  172. package/ai/tools/createPlanTool.ts +999 -0
  173. package/ai/tools/createSkillDocTool.ts +155 -0
  174. package/ai/tools/createWorkflowTool.ts +154 -0
  175. package/ai/tools/deepseekOcrTool.ts +34 -0
  176. package/ai/tools/delayTool.ts +31 -0
  177. package/ai/tools/deleteSpacesTool.ts +325 -0
  178. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  179. package/ai/tools/devReloadUtils.ts +29 -0
  180. package/ai/tools/dialogMessageSearch.ts +137 -0
  181. package/ai/tools/doctorSkillTool.ts +72 -0
  182. package/ai/tools/ecommerceScraperTool.ts +86 -0
  183. package/ai/tools/emailTools.ts +549 -0
  184. package/ai/tools/evalSkillTool.ts +92 -0
  185. package/ai/tools/exaSearchTool.ts +64 -0
  186. package/ai/tools/execBashTool.ts +379 -0
  187. package/ai/tools/executeSqlTool.ts +192 -0
  188. package/ai/tools/fetchWebpageSupport.ts +309 -0
  189. package/ai/tools/fetchWebpageTool.ts +84 -0
  190. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  191. package/ai/tools/generateDocxTool.ts +215 -0
  192. package/ai/tools/googleSearchScraperTool.ts +106 -0
  193. package/ai/tools/importDataTool.ts +133 -0
  194. package/ai/tools/importSkillTool.ts +162 -0
  195. package/ai/tools/index.ts +1927 -0
  196. package/ai/tools/listFilesTool.ts +82 -0
  197. package/ai/tools/listUserSpacesTool.ts +113 -0
  198. package/ai/tools/modelUsageTools.ts +199 -0
  199. package/ai/tools/olmOcrTool.ts +34 -0
  200. package/ai/tools/openaiImageTool.ts +267 -0
  201. package/ai/tools/prepareTools.ts +23 -0
  202. package/ai/tools/readDocTool.ts +84 -0
  203. package/ai/tools/readFileTool.ts +211 -0
  204. package/ai/tools/readTool.ts +163 -0
  205. package/ai/tools/readXPostTool.ts +233 -0
  206. package/ai/tools/rememberMemoryTool.ts +84 -0
  207. package/ai/tools/remotionVideoTool.ts +151 -0
  208. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  209. package/ai/tools/searchRepoTool.ts +115 -0
  210. package/ai/tools/searchWorkspaceTool.ts +259 -0
  211. package/ai/tools/skillFollowup.ts +86 -0
  212. package/ai/tools/surfWeatherTool.ts +169 -0
  213. package/ai/tools/table/addTableRowTool.ts +217 -0
  214. package/ai/tools/table/createTableTool.ts +315 -0
  215. package/ai/tools/table/rowTools.ts +366 -0
  216. package/ai/tools/table/schemaTools.ts +244 -0
  217. package/ai/tools/table/shareTableTool.ts +148 -0
  218. package/ai/tools/table/toolShared.ts +129 -0
  219. package/ai/tools/toolApiClient.ts +198 -0
  220. package/ai/tools/toolNameAliases.ts +57 -0
  221. package/ai/tools/toolResultError.ts +42 -0
  222. package/ai/tools/toolRunSlice.ts +303 -0
  223. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  224. package/ai/tools/toolVisibility.ts +4 -0
  225. package/ai/tools/types.ts +20 -0
  226. package/ai/tools/uiAskChoiceTool.ts +104 -0
  227. package/ai/tools/updateContentTitleTool.ts +84 -0
  228. package/ai/tools/updateDocTool.ts +105 -0
  229. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  230. package/ai/tools/whisperTool.ts +77 -0
  231. package/ai/tools/writeFileTool.ts +210 -0
  232. package/ai/tools/youtubeScraperTool.ts +116 -0
  233. package/ai/tools/ziweiChartTool.ts +678 -0
  234. package/ai/types.ts +55 -0
  235. package/ai/workflow/workflowExecutor.ts +323 -0
  236. package/ai/workflow/workflowSlice.ts +73 -0
  237. package/ai/workflow/workflowTypes.ts +106 -0
  238. package/client/agentRun.test.ts +240 -0
  239. package/client/agentRun.ts +182 -19
  240. package/client/compactDialog.test.ts +238 -0
  241. package/client/localRuntimeAdapter.test.ts +135 -0
  242. package/client/localRuntimeAdapter.ts +244 -0
  243. package/client/profileConfig.test.ts +40 -0
  244. package/client/streamingOutput.test.ts +22 -0
  245. package/client/streamingOutput.ts +38 -0
  246. package/commandRegistry.ts +11 -2
  247. package/connector-experimental/index.ts +5 -0
  248. package/database/actions/cacheMergedUserData.ts +64 -0
  249. package/database/actions/common.ts +242 -0
  250. package/database/actions/deleteFile.ts +40 -0
  251. package/database/actions/fetchUserData.ts +16 -0
  252. package/database/actions/fileContent.ts +125 -0
  253. package/database/actions/patch.ts +155 -0
  254. package/database/actions/read.ts +337 -0
  255. package/database/actions/readAndWait.ts +224 -0
  256. package/database/actions/readRequestManager.ts +120 -0
  257. package/database/actions/remove.ts +94 -0
  258. package/database/actions/replication.ts +366 -0
  259. package/database/actions/upload.ts +174 -0
  260. package/database/actions/upsert.ts +56 -0
  261. package/database/actions/write.ts +126 -0
  262. package/database/client/db.native.ts +73 -0
  263. package/database/client/db.ts +51 -0
  264. package/database/client/fetchUserData.ts +61 -0
  265. package/database/client/handleError.ts +19 -0
  266. package/database/client/queryRequest.ts +21 -0
  267. package/database/config.ts +21 -0
  268. package/database/dbActionThunks.ts +1 -0
  269. package/database/dbSlice.ts +149 -0
  270. package/database/email.ts +42 -0
  271. package/database/fileRing.ts +51 -0
  272. package/database/fileSharding.ts +70 -0
  273. package/database/fileStorage.native.ts +92 -0
  274. package/database/fileStorage.ts +232 -0
  275. package/database/fileUrl.ts +34 -0
  276. package/database/hooks/useUserData.ts +489 -0
  277. package/database/index.ts +1 -0
  278. package/database/keys.ts +765 -0
  279. package/database/queryPrefixes.ts +14 -0
  280. package/database/requests.ts +443 -0
  281. package/database/runtimeServerContext.ts +35 -0
  282. package/database/server/MemoryDB.ts +76 -0
  283. package/database/server/actorAccess.ts +76 -0
  284. package/database/server/agentDelegation.ts +124 -0
  285. package/database/server/coreDataOwnership.ts +13 -0
  286. package/database/server/coreDataProxy.ts +76 -0
  287. package/database/server/cybotReadonly.ts +18 -0
  288. package/database/server/dataHandlers.ts +111 -0
  289. package/database/server/db.ts +118 -0
  290. package/database/server/dbPath.ts +20 -0
  291. package/database/server/delete.ts +499 -0
  292. package/database/server/emailRepository.ts +1480 -0
  293. package/database/server/ensureDbOpen.ts +12 -0
  294. package/database/server/fileRead.ts +337 -0
  295. package/database/server/fileService.ts +436 -0
  296. package/database/server/handleTransaction.ts +86 -0
  297. package/database/server/patch.ts +282 -0
  298. package/database/server/query.ts +138 -0
  299. package/database/server/read.ts +325 -0
  300. package/database/server/resourceAccess.ts +211 -0
  301. package/database/server/routes.ts +110 -0
  302. package/database/server/spaceMemberAuthority.ts +67 -0
  303. package/database/server/upload.ts +159 -0
  304. package/database/server/write.ts +494 -0
  305. package/database/server/writeAuthority.ts +133 -0
  306. package/database/sqliteDb.ts +46 -0
  307. package/database/table/deleteTable.ts +120 -0
  308. package/database/tenantPlacement.ts +57 -0
  309. package/database/tombstones.ts +52 -0
  310. package/database/userDataLoadDecision.ts +17 -0
  311. package/database/userDataMerge.ts +95 -0
  312. package/database/userPreferenceRegister.ts +108 -0
  313. package/database/utils/dbPath.ts +47 -0
  314. package/database/utils/ulid.native.ts +6 -0
  315. package/database/utils/ulid.ts +1 -0
  316. package/index.ts +37 -19
  317. package/localRuntimeDb.ts +28 -0
  318. package/package.json +17 -4
  319. package/runtimeModeArgs.ts +33 -0
  320. package/tui/readlineWorkspace.ts +1 -0
  321. package/tui/session.ts +22 -0
@@ -0,0 +1,12 @@
1
+ export type DbWithOpenStatus = {
2
+ status?: string | null;
3
+ open(): Promise<unknown>;
4
+ };
5
+
6
+ export async function ensureDbOpen(db: DbWithOpenStatus) {
7
+ if (db.status === "open") {
8
+ return;
9
+ }
10
+
11
+ await db.open();
12
+ }
@@ -0,0 +1,337 @@
1
+ // 文件路径: database/server/fileRead.ts
2
+
3
+ import { Request } from "bun";
4
+ import {
5
+ getFileMetadataById,
6
+ getFileContentById,
7
+ hasFileTombstoneById,
8
+ saveBufferAsFile,
9
+ FileMetadata,
10
+ } from "./fileService";
11
+
12
+ const UPSTREAM_DB_BASE = process.env.FILE_UPSTREAM_DB_BASE || "";
13
+
14
+ /**
15
+ * 从 Request.url 中解析 fileId
16
+ */
17
+ const extractFileIdFromRequest = (req: Request): string | null => {
18
+ try {
19
+ const url = new URL(req.url);
20
+ const segments = url.pathname.split("/").filter(Boolean);
21
+ if (!segments.length) return null;
22
+ return segments[segments.length - 1] || null;
23
+ } catch {
24
+ return null;
25
+ }
26
+ };
27
+
28
+ const buildFileContentHeaders = (
29
+ metadata: FileMetadata,
30
+ contentLength = metadata.size
31
+ ): HeadersInit => {
32
+ const headers: Record<string, string> = {
33
+ "Content-Type": metadata.mimeType || "application/octet-stream",
34
+ };
35
+
36
+ if (Number.isFinite(contentLength) && contentLength >= 0) {
37
+ headers["Content-Length"] = String(contentLength);
38
+ }
39
+
40
+ return headers;
41
+ };
42
+
43
+ const fetchUpstreamMetadata = async (
44
+ fileId: string,
45
+ { logWhenUnavailable = false }: { logWhenUnavailable?: boolean } = {}
46
+ ): Promise<FileMetadata | null> => {
47
+ if (!UPSTREAM_DB_BASE) {
48
+ if (logWhenUnavailable) {
49
+ console.warn(
50
+ "[fileRead] FILE_UPSTREAM_DB_BASE is not set, skip upstream sync."
51
+ );
52
+ }
53
+ return null;
54
+ }
55
+
56
+ const metaUrl = `${UPSTREAM_DB_BASE}/file/metadata/${fileId}`;
57
+ console.debug("[fileRead] Fetching upstream metadata:", metaUrl);
58
+ const metaRes = await fetch(metaUrl, {
59
+ method: "GET",
60
+ headers: { accept: "application/json" },
61
+ });
62
+
63
+ if (!metaRes.ok) {
64
+ console.warn(
65
+ "[fileRead] Upstream metadata fetch failed:",
66
+ metaRes.status,
67
+ await metaRes.text().catch(() => "")
68
+ );
69
+ return null;
70
+ }
71
+
72
+ const upstreamMeta = (await metaRes.json()) as any;
73
+ if (!upstreamMeta || !upstreamMeta.id) {
74
+ console.warn(
75
+ "[fileRead] Upstream metadata invalid for fileId:",
76
+ fileId,
77
+ upstreamMeta
78
+ );
79
+ return null;
80
+ }
81
+
82
+ return upstreamMeta as FileMetadata;
83
+ };
84
+
85
+ /**
86
+ * 尝试从上游服务器拉取文件,并写入本地 Blob/FileMetadata。
87
+ *
88
+ * 成功时返回 { buffer, metadata },失败时返回 null。
89
+ */
90
+ const syncFileFromUpstream = async (
91
+ fileId: string
92
+ ): Promise<{ buffer: Buffer; metadata: FileMetadata } | null> => {
93
+ try {
94
+ if (await hasFileTombstoneById(fileId)) {
95
+ console.info("[fileRead] Skip upstream sync for tombstoned file:", fileId);
96
+ return null;
97
+ }
98
+
99
+ const upstreamMeta = await fetchUpstreamMetadata(fileId, {
100
+ logWhenUnavailable: true,
101
+ });
102
+ if (!upstreamMeta) {
103
+ return null;
104
+ }
105
+
106
+ const contentUrl = `${UPSTREAM_DB_BASE}/file/content/${fileId}`;
107
+ console.debug("[fileRead] Fetching upstream content:", contentUrl);
108
+ const contentRes = await fetch(contentUrl, {
109
+ method: "GET",
110
+ });
111
+
112
+ if (!contentRes.ok || !contentRes.body) {
113
+ console.warn(
114
+ "[fileRead] Upstream content fetch failed:",
115
+ contentRes.status,
116
+ await contentRes.text().catch(() => "")
117
+ );
118
+ return null;
119
+ }
120
+
121
+ const arrayBuffer = await contentRes.arrayBuffer();
122
+ const buffer = Buffer.from(arrayBuffer);
123
+
124
+ const tenantId = upstreamMeta.tenantId || "default";
125
+ const ownerDbKey =
126
+ upstreamMeta.ownerDbKey ?? upstreamMeta.dbKey ?? undefined;
127
+
128
+ const { metadata } = await saveBufferAsFile(buffer, {
129
+ tenantId,
130
+ originalName: upstreamMeta.originalName || "upstream-import",
131
+ mimeType: upstreamMeta.mimeType || "application/octet-stream",
132
+
133
+ ownerType: upstreamMeta.ownerType,
134
+ ownerId: upstreamMeta.ownerId,
135
+ uploadedBy: upstreamMeta.uploadedBy,
136
+
137
+ ownerDbKey,
138
+ source: upstreamMeta.source,
139
+ model: upstreamMeta.model,
140
+ prompt: upstreamMeta.prompt,
141
+ tags: upstreamMeta.tags,
142
+
143
+ clientMetadata: upstreamMeta,
144
+ clientProvidedId: upstreamMeta.id,
145
+ });
146
+
147
+ console.info(
148
+ "[fileRead] Synced file from upstream:",
149
+ fileId,
150
+ "tenantId=",
151
+ metadata.tenantId
152
+ );
153
+
154
+ return { buffer, metadata };
155
+ } catch (err) {
156
+ console.error(
157
+ "[fileRead] syncFileFromUpstream error for fileId=",
158
+ fileId,
159
+ err
160
+ );
161
+ return null;
162
+ }
163
+ };
164
+
165
+ /**
166
+ * GET /database/file/metadata/:id
167
+ */
168
+ export const handleGetFileMetadata = async (
169
+ req: Request
170
+ ): Promise<Response> => {
171
+ const fileId = extractFileIdFromRequest(req);
172
+ if (!fileId) {
173
+ return new Response(JSON.stringify({ error: "Invalid file id" }), {
174
+ status: 400,
175
+ headers: { "Content-Type": "application/json" },
176
+ });
177
+ }
178
+
179
+ try {
180
+ const metadata = await getFileMetadataById(fileId);
181
+ if (!metadata) {
182
+ if (await hasFileTombstoneById(fileId)) {
183
+ return new Response(JSON.stringify({ error: "File not found" }), {
184
+ status: 404,
185
+ headers: { "Content-Type": "application/json" },
186
+ });
187
+ }
188
+
189
+ const upstreamMetadata = await fetchUpstreamMetadata(fileId);
190
+ if (upstreamMetadata) {
191
+ return new Response(JSON.stringify(upstreamMetadata satisfies FileMetadata), {
192
+ status: 200,
193
+ headers: { "Content-Type": "application/json" },
194
+ });
195
+ }
196
+
197
+ return new Response(JSON.stringify({ error: "File not found" }), {
198
+ status: 404,
199
+ headers: { "Content-Type": "application/json" },
200
+ });
201
+ }
202
+
203
+ return new Response(JSON.stringify(metadata satisfies FileMetadata), {
204
+ status: 200,
205
+ headers: { "Content-Type": "application/json" },
206
+ });
207
+ } catch (error: any) {
208
+ console.error("[handleGetFileMetadata] Error:", error);
209
+ return new Response(
210
+ JSON.stringify({
211
+ error: "Failed to read file metadata",
212
+ details: error?.message || "Unknown error",
213
+ }),
214
+ {
215
+ status: 500,
216
+ headers: { "Content-Type": "application/json" },
217
+ }
218
+ );
219
+ }
220
+ };
221
+
222
+ const getLocalFileHead = async (
223
+ fileId: string
224
+ ): Promise<{ metadata: FileMetadata; contentLength: number }> => {
225
+ const metadata = await getFileMetadataById(fileId);
226
+ if (!metadata) {
227
+ throw new Error(`File not found: ${fileId}`);
228
+ }
229
+
230
+ const file = Bun.file(metadata.filePath);
231
+ const exists = await file.exists();
232
+ if (!exists) {
233
+ throw new Error(`File content missing on disk: ${fileId}`);
234
+ }
235
+
236
+ return {
237
+ metadata,
238
+ contentLength: metadata.size,
239
+ };
240
+ };
241
+
242
+ /**
243
+ * GET /database/file/content/:id
244
+ */
245
+ export const handleGetFileContent = async (
246
+ req: Request
247
+ ): Promise<Response> => {
248
+ const fileId = extractFileIdFromRequest(req);
249
+ if (!fileId) {
250
+ return new Response(JSON.stringify({ error: "Invalid file id" }), {
251
+ status: 400,
252
+ headers: { "Content-Type": "application/json" },
253
+ });
254
+ }
255
+
256
+ try {
257
+ const { buffer, metadata } = await getFileContentById(fileId);
258
+
259
+ return new Response(buffer, {
260
+ status: 200,
261
+ headers: buildFileContentHeaders(metadata, buffer.byteLength),
262
+ });
263
+ } catch (error: any) {
264
+ console.warn(
265
+ "[handleGetFileContent] Local read failed for",
266
+ fileId,
267
+ "error=",
268
+ error?.message
269
+ );
270
+
271
+ const upstreamResult = await syncFileFromUpstream(fileId);
272
+ if (upstreamResult) {
273
+ const { buffer, metadata } = upstreamResult;
274
+
275
+ return new Response(buffer, {
276
+ status: 200,
277
+ headers: buildFileContentHeaders(metadata, buffer.byteLength),
278
+ });
279
+ }
280
+
281
+ const message = error?.message || "Unknown error";
282
+ const status = /not found/i.test(message) ? 404 : 500;
283
+
284
+ return new Response(
285
+ JSON.stringify({
286
+ error: "Failed to read file content",
287
+ details: message,
288
+ }),
289
+ {
290
+ status,
291
+ headers: { "Content-Type": "application/json" },
292
+ }
293
+ );
294
+ }
295
+ };
296
+
297
+ export const handleHeadFileContent = async (
298
+ req: Request
299
+ ): Promise<Response> => {
300
+ const fileId = extractFileIdFromRequest(req);
301
+ if (!fileId) {
302
+ return new Response(null, { status: 400 });
303
+ }
304
+
305
+ let localError: any = null;
306
+ try {
307
+ const { metadata, contentLength } = await getLocalFileHead(fileId);
308
+ return new Response(null, {
309
+ status: 200,
310
+ headers: buildFileContentHeaders(metadata, contentLength),
311
+ });
312
+ } catch (error: any) {
313
+ localError = error;
314
+ console.warn(
315
+ "[handleHeadFileContent] Local head failed for",
316
+ fileId,
317
+ "error=",
318
+ error?.message
319
+ );
320
+ }
321
+
322
+ if (await hasFileTombstoneById(fileId)) {
323
+ return new Response(null, { status: 404 });
324
+ }
325
+
326
+ const upstreamMetadata = await fetchUpstreamMetadata(fileId);
327
+ if (upstreamMetadata) {
328
+ return new Response(null, {
329
+ status: 200,
330
+ headers: buildFileContentHeaders(upstreamMetadata),
331
+ });
332
+ }
333
+
334
+ const message = localError?.message || `File not found: ${fileId}`;
335
+ const status = /not found/i.test(message) ? 404 : 500;
336
+ return new Response(null, { status });
337
+ };