@xopcai/xopc 0.0.230 → 0.0.231

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 (150) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Bp_wmuGW.js → agent-avatar-dicebear-CBqUnlvL.js} +1 -1
  4. package/dist/gateway/static/root/assets/agents-7YO6znTT.js +68 -0
  5. package/dist/gateway/static/root/assets/{app-management-settings-panel-CwAPK4Jm.js → app-management-settings-panel-DP7YVRPn.js} +1 -1
  6. package/dist/gateway/static/root/assets/{appearance-settings-DsGLvqTb.js → appearance-settings-CVQ5n7y6.js} +1 -1
  7. package/dist/gateway/static/root/assets/{apps-page-Ee9qOQEx.js → apps-page-DSCia0DT.js} +1 -1
  8. package/dist/gateway/static/root/assets/{attachment-load-B4qqigAQ.js → attachment-load-mmr_mHRr.js} +1 -1
  9. package/dist/gateway/static/root/assets/{automations-page-B8ImehSW.js → automations-page-DfY-T6Ga.js} +1 -1
  10. package/dist/gateway/static/root/assets/automations-workspace-BpvD54D6.js +6 -0
  11. package/dist/gateway/static/root/assets/{binary-plugins-DaJbyYc2.js → binary-plugins-CO9QMEh2.js} +1 -1
  12. package/dist/gateway/static/root/assets/{block-editor-Kl25XWJc.js → block-editor-CE14CVOy.js} +3 -3
  13. package/dist/gateway/static/root/assets/browser-settings-page-CeESA4Oj.js +8 -0
  14. package/dist/gateway/static/root/assets/{browser-workflows-page-DjMxs33B.js → browser-workflows-page-1wgXyoSO.js} +2 -2
  15. package/dist/gateway/static/root/assets/capabilities-settings-panel-B_OXTojB.js +7 -0
  16. package/dist/gateway/static/root/assets/capability-presets-settings-panel-D5nZecxV.js +3 -0
  17. package/dist/gateway/static/root/assets/channels-settings-BzmnWx6X.js +1 -0
  18. package/dist/gateway/static/root/assets/{chat-terminal-dock-BiU_4Rq6.js → chat-terminal-dock-CdRW6Euc.js} +2 -2
  19. package/dist/gateway/static/root/assets/cloud-C4-yfnnk.js +1 -0
  20. package/dist/gateway/static/root/assets/connectors-page-DX6OkwFQ.js +2 -0
  21. package/dist/gateway/static/root/assets/{cron-expression-Cs_5pUPT.js → cron-expression-DYSMZsms.js} +1 -1
  22. package/dist/gateway/static/root/assets/{date-picker-y1kWrFVU.js → date-picker-DGCfX4tK.js} +1 -1
  23. package/dist/gateway/static/root/assets/{dependency-picker-skc62zrn.js → dependency-picker-DHDa4ht1.js} +1 -1
  24. package/dist/gateway/static/root/assets/{desktop-pet-ux9DxchT.js → desktop-pet-CMO-J3cG.js} +1 -1
  25. package/dist/gateway/static/root/assets/{desktop-pet-settings-BVVCBgYK.js → desktop-pet-settings-MmOR4A3u.js} +1 -1
  26. package/dist/gateway/static/root/assets/{directory-picker-path-field-V8vZP0Dy.js → directory-picker-path-field-B9qoxDcn.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-debug-page-Dh6DDuvQ.js → extension-debug-page-DdH0dG2_.js} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-page-8OKQDEat.js → extension-page-Clg-KktN.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-settings-page-BgSV0Nny.js → extension-settings-page-eP3fJ2M6.js} +1 -1
  30. package/dist/gateway/static/root/assets/files-Dp8_uGTO.js +1 -0
  31. package/dist/gateway/static/root/assets/{gateway-config-api-Dlc6eaTG.js → gateway-config-api-DEHnF4Wm.js} +1 -1
  32. package/dist/gateway/static/root/assets/{gateway-settings-DqQgiAUv.js → gateway-settings-DKkEXXj9.js} +1 -1
  33. package/dist/gateway/static/root/assets/{heartbeat-settings-Pxqk5GFH.js → heartbeat-settings-DlBPNubY.js} +1 -1
  34. package/dist/gateway/static/root/assets/home-page-BnOrLRfs.js +1 -0
  35. package/dist/gateway/static/root/assets/index-0xQO5B_Q.js +70 -0
  36. package/dist/gateway/static/root/assets/index-CCyIdGuQ.css +2 -0
  37. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BSFNqxzY.js → keyboard-shortcuts-settings-BvpEeHnm.js} +1 -1
  38. package/dist/gateway/static/root/assets/local-app-workbench-page-Ch1rdUo8.js +2 -0
  39. package/dist/gateway/static/root/assets/{local-apps-page-DzU6yZ9-.js → local-apps-page-Bzvn99eH.js} +1 -1
  40. package/dist/gateway/static/root/assets/logs-page-zxx2ExlY.js +2 -0
  41. package/dist/gateway/static/root/assets/{management-settings-B8hM56HS.js → management-settings-BQaeyOYm.js} +1 -1
  42. package/dist/gateway/static/root/assets/{markdown-split-DRbZjxMK.js → markdown-split-D9tmIiEm.js} +1 -1
  43. package/dist/gateway/static/root/assets/markdown-view-B1bGSQqt.css +1 -0
  44. package/dist/gateway/static/root/assets/{media-plugins-B8vMe5VX.js → media-plugins-D8I7OGnT.js} +1 -1
  45. package/dist/gateway/static/root/assets/models-hub-cache-0RjEwafI.js +1 -0
  46. package/dist/gateway/static/root/assets/{notes-api-BFsOGYkd.js → notes-api-EpgG-OQc.js} +1 -1
  47. package/dist/gateway/static/root/assets/{notes-page-B-YZ7iuK.js → notes-page-CCvjGMhN.js} +1 -1
  48. package/dist/gateway/static/root/assets/notes-workbench-9cgh77VH.js +4 -0
  49. package/dist/gateway/static/root/assets/oauth-api-BwNLwbI8.js +1 -0
  50. package/dist/gateway/static/root/assets/{product-open-page-DkWcyki5.js → product-open-page-MmMswmOU.js} +1 -1
  51. package/dist/gateway/static/root/assets/project-detail-page-hf6pmH4p.js +1 -0
  52. package/dist/gateway/static/root/assets/projects-page-By2U-Wdh.js +1 -0
  53. package/dist/gateway/static/root/assets/remote-access-hub-CUcpp7CQ.js +1 -0
  54. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Bq_ETeal.js → runtime-tools-settings-panel-Dx6o7sLu.js} +1 -1
  55. package/dist/gateway/static/root/assets/{schema-form-BogTlrsP.js → schema-form-Cdd9c4vt.js} +1 -1
  56. package/dist/gateway/static/root/assets/sessions-page-ByI82xS1.js +1 -0
  57. package/dist/gateway/static/root/assets/settings-advanced-gate-BCoI5IJ8.js +1 -0
  58. package/dist/gateway/static/root/assets/{settings-form-section-Coy1Cbp6.js → settings-form-section-D0YxMnhm.js} +1 -1
  59. package/dist/gateway/static/root/assets/{settings-loading-skeleton-B5GU7iCG.js → settings-loading-skeleton-y53gEKsF.js} +1 -1
  60. package/dist/gateway/static/root/assets/{settings-page-Dd-BRu12.js → settings-page-BoE_tToG.js} +1 -1
  61. package/dist/gateway/static/root/assets/{setup-status-panel-CwEFAVWx.js → setup-status-panel-BQmyb5KC.js} +1 -1
  62. package/dist/gateway/static/root/assets/share-preview-page-DiqBn-oE.js +2 -0
  63. package/dist/gateway/static/root/assets/shares-settings-Bf3VpZ-R.js +1 -0
  64. package/dist/gateway/static/root/assets/skill-reload-api-CXHlbuGS.js +1 -0
  65. package/dist/gateway/static/root/assets/{skills-page-Brxb6QnW.js → skills-page-CZwhWiRl.js} +1 -1
  66. package/dist/gateway/static/root/assets/{skills-page.utils-FB5LtEGm.js → skills-page.utils-kBGr8opZ.js} +1 -1
  67. package/dist/gateway/static/root/assets/{system-settings-panel-ChosWt8n.js → system-settings-panel-CPBs6lWi.js} +1 -1
  68. package/dist/gateway/static/root/assets/task-detail-page-Bo0KSZ2v.js +1 -0
  69. package/dist/gateway/static/root/assets/theme-store-Qgvo0Ga7.js +1 -0
  70. package/dist/gateway/static/root/assets/{use-autosave-2SqnmSKD.js → use-autosave-C4YcEiP9.js} +1 -1
  71. package/dist/gateway/static/root/assets/user-context-page-Cnj7gMX2.js +1 -0
  72. package/dist/gateway/static/root/assets/{voice-api-key-field-DtTIF6M8.js → voice-api-key-field-BML8zCwZ.js} +1 -1
  73. package/dist/gateway/static/root/assets/{work-discovery-overlay-CK3aZDdE.js → work-discovery-overlay-CKIdPEen.js} +1 -1
  74. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BjOuddkX.js → workflow-run-setup-panel-BK44Vm5W.js} +2 -2
  75. package/dist/gateway/static/root/assets/workflows-page-CB98MTwG.js +5 -0
  76. package/dist/gateway/static/root/index.html +10 -6
  77. package/dist/package.js +1 -1
  78. package/dist/src/agent/service.d.ts +1 -0
  79. package/dist/src/agent/service.js +5 -2
  80. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  81. package/dist/src/agent/tools/write.js +9 -12
  82. package/dist/src/agent/tools/xopc-use-tool.js +2 -2
  83. package/dist/src/auth/oauth/index.js +2 -2
  84. package/dist/src/auth/oauth/registry.d.ts +1 -1
  85. package/dist/src/auth/oauth/registry.js +5 -1
  86. package/dist/src/auth/oauth/xopc-cloud.d.ts +1 -0
  87. package/dist/src/auth/oauth/xopc-cloud.js +8 -2
  88. package/dist/src/files/file-service.d.ts +36 -0
  89. package/dist/src/files/file-service.js +282 -0
  90. package/dist/src/gateway/heartbeat/service.js +1 -1
  91. package/dist/src/gateway/hono/app.js +2 -2
  92. package/dist/src/gateway/hono/routes/files.d.ts +3 -0
  93. package/dist/src/gateway/hono/routes/files.js +172 -0
  94. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  95. package/dist/src/gateway/hono/routes/projects.js +0 -508
  96. package/dist/src/gateway/hono/routes/shares.js +410 -21
  97. package/dist/src/gateway/hono/routes/voice.js +1 -1
  98. package/dist/src/gateway/hono/routes/workspace.js +15 -974
  99. package/dist/src/gateway/service.js +9 -9
  100. package/dist/src/heartbeat/index.js +1 -1
  101. package/dist/src/share/hosted-session-share.d.ts +104 -0
  102. package/dist/src/share/hosted-session-share.js +276 -0
  103. package/dist/src/share/note-share-service.js +4 -24
  104. package/dist/src/share/session-share-projector.d.ts +31 -0
  105. package/dist/src/share/session-share-projector.js +79 -0
  106. package/dist/src/share/session-share-service.d.ts +89 -0
  107. package/dist/src/share/session-share-service.js +325 -0
  108. package/dist/src/share/share-asset-ticket.d.ts +7 -0
  109. package/dist/src/share/share-asset-ticket.js +30 -0
  110. package/dist/src/share/share-landing.d.ts +4 -3
  111. package/dist/src/share/share-landing.js +5 -5
  112. package/dist/src/share/share-store.d.ts +27 -2
  113. package/dist/src/share/share-store.js +71 -8
  114. package/dist/src/share/share-types.d.ts +17 -2
  115. package/dist/src/tui/backends/embedded-backend.js +1 -1
  116. package/dist/src/tui/backends/gateway-realtime-backend.js +11 -3
  117. package/package.json +1 -1
  118. package/dist/gateway/static/root/assets/agents-CKG93dlc.js +0 -68
  119. package/dist/gateway/static/root/assets/automations-workspace-BNFAfNwC.js +0 -6
  120. package/dist/gateway/static/root/assets/browser-settings-page-Cg6NgwE_.js +0 -8
  121. package/dist/gateway/static/root/assets/capabilities-settings-panel-NvJKgF1u.js +0 -7
  122. package/dist/gateway/static/root/assets/capability-presets-settings-panel-C2uBdALe.js +0 -3
  123. package/dist/gateway/static/root/assets/channels-settings-CIcYKMEs.js +0 -1
  124. package/dist/gateway/static/root/assets/connectors-page-CEEcmUiP.js +0 -2
  125. package/dist/gateway/static/root/assets/home-page-1xjo6wE4.js +0 -1
  126. package/dist/gateway/static/root/assets/index-Bbd_dXui.js +0 -70
  127. package/dist/gateway/static/root/assets/index-D_hrEKXM.css +0 -2
  128. package/dist/gateway/static/root/assets/local-app-workbench-page-heQ4-PJ7.js +0 -2
  129. package/dist/gateway/static/root/assets/logs-page-BblnS6Jy.js +0 -2
  130. package/dist/gateway/static/root/assets/markdown-view-CLzTdVLW.css +0 -1
  131. package/dist/gateway/static/root/assets/notes-workbench-CAM09C8b.js +0 -4
  132. package/dist/gateway/static/root/assets/project-detail-page-BIPIwgU2.js +0 -1
  133. package/dist/gateway/static/root/assets/projects-page-B_kzTn1K.js +0 -1
  134. package/dist/gateway/static/root/assets/remote-access-hub-CtD8DsD-.js +0 -1
  135. package/dist/gateway/static/root/assets/sessions-page-B8n9HI1y.js +0 -1
  136. package/dist/gateway/static/root/assets/settings-advanced-gate-xlhdRTME.js +0 -1
  137. package/dist/gateway/static/root/assets/share-preview-page-DzXw9UQJ.js +0 -2
  138. package/dist/gateway/static/root/assets/shares-settings-Bk6UQOwS.js +0 -1
  139. package/dist/gateway/static/root/assets/skill-reload-api-DrmQZ3Au.js +0 -1
  140. package/dist/gateway/static/root/assets/task-detail-page-CW2sJbdc.js +0 -1
  141. package/dist/gateway/static/root/assets/theme-store-DOdCjyuw.js +0 -1
  142. package/dist/gateway/static/root/assets/user-context-page-Bq9KB-Oj.js +0 -1
  143. package/dist/gateway/static/root/assets/workflows-page-CcbGEQol.js +0 -5
  144. package/dist/src/gateway/file-path-classifier.d.ts +0 -30
  145. package/dist/src/gateway/file-path-classifier.js +0 -105
  146. package/dist/src/gateway/file-reference-errors.d.ts +0 -6
  147. package/dist/src/gateway/file-reference-errors.js +0 -21
  148. package/dist/src/gateway/file-reference-registry.d.ts +0 -34
  149. package/dist/src/gateway/file-reference-registry.js +0 -45
  150. /package/dist/gateway/static/root/assets/{markdown-view-DvM5PWEH.js → markdown-view-RxNHwlNo.js} +0 -0
@@ -18,15 +18,9 @@ import { TaskApplicationService } from "../../../tasks/task-application-service.
18
18
  import { defineTaskContract } from "../../../tasks/task-contract-definition.js";
19
19
  import { ProjectOperatingViewService, summarizeProjectOperatingView } from "../../../tasks/project-operating-view-service.js";
20
20
  import "../../../tasks/index.js";
21
- import { classifyFileReferenceFsError } from "../../file-reference-errors.js";
22
- import { createGatewayRouteLogger } from "../lib/route-logger.js";
23
21
  import { parseActivityIncludeRelated, parseActivityQuery } from "./activity.js";
24
- import { fuzzySearchWorkspaceFiles } from "../../workspace-file-search.js";
25
- import * as path$1 from "node:path";
26
22
  import { randomUUID } from "node:crypto";
27
- import { readFile, readdir, realpath, stat, writeFile } from "node:fs/promises";
28
23
  //#region src/gateway/hono/routes/projects.ts
29
- const log = createGatewayRouteLogger("Projects");
30
24
  function parseProjectStatus(raw) {
31
25
  return raw === "planned" || raw === "active" || raw === "paused" || raw === "completed" || raw === "cancelled" || raw === "archived" ? raw : void 0;
32
26
  }
@@ -89,171 +83,6 @@ function enrichProjectWorkspace(service, project) {
89
83
  effectiveWorkspaceRoot: resolveEffectiveWorkspaceRoot(service, project)
90
84
  };
91
85
  }
92
- const SKIP_FILE_NAMES = new Set([".git", "node_modules"]);
93
- const PROJECT_FILE_MIME_BY_EXT = {
94
- png: "image/png",
95
- jpg: "image/jpeg",
96
- jpeg: "image/jpeg",
97
- gif: "image/gif",
98
- webp: "image/webp",
99
- bmp: "image/bmp",
100
- svg: "image/svg+xml",
101
- pdf: "application/pdf",
102
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
103
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
104
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
105
- txt: "text/plain",
106
- md: "text/markdown",
107
- json: "application/json",
108
- html: "text/html",
109
- css: "text/css",
110
- js: "text/javascript",
111
- ts: "text/typescript",
112
- mp3: "audio/mpeg",
113
- wav: "audio/wav",
114
- ogg: "audio/ogg",
115
- webm: "video/webm",
116
- mp4: "video/mp4",
117
- mov: "video/quicktime"
118
- };
119
- function normalizeRelativePath(raw) {
120
- return (raw ?? "").trim().replaceAll("\\", "/").replace(/^\/+/, "");
121
- }
122
- function isWithinDirectory(root, target) {
123
- const relativePath = path$1.relative(root, target);
124
- return relativePath === "" || !relativePath.startsWith("..") && !path$1.isAbsolute(relativePath);
125
- }
126
- async function resolveProjectWorkspaceRoot(service, projectId) {
127
- const project = service.projects.get(projectId);
128
- if (!project) return {
129
- ok: false,
130
- status: 404,
131
- error: "Project not found"
132
- };
133
- const workspaceRoot = resolveEffectiveWorkspaceRoot(service, project);
134
- if (!workspaceRoot?.trim()) return {
135
- ok: false,
136
- status: 400,
137
- error: "Project workspace root is not set"
138
- };
139
- try {
140
- const root = await realpath(workspaceRoot);
141
- if (!(await stat(root)).isDirectory()) return {
142
- ok: false,
143
- status: 400,
144
- error: "Project workspace root is not a directory"
145
- };
146
- return {
147
- ok: true,
148
- projectId: project.id,
149
- root
150
- };
151
- } catch (err) {
152
- log.warn({
153
- err,
154
- projectId: project.id,
155
- path: workspaceRoot
156
- }, "project workspace root unavailable");
157
- return {
158
- ok: false,
159
- status: 404,
160
- error: "Project workspace root is unavailable"
161
- };
162
- }
163
- }
164
- async function resolveProjectWorkspacePath(service, projectId, rawPath) {
165
- const rootResult = await resolveProjectWorkspaceRoot(service, projectId);
166
- if (rootResult.ok === false) return {
167
- ok: false,
168
- status: rootResult.status,
169
- error: rootResult.error
170
- };
171
- const requestedPath = normalizeRelativePath(rawPath);
172
- const candidate = path$1.resolve(rootResult.root, requestedPath);
173
- if (!isWithinDirectory(rootResult.root, candidate)) return {
174
- ok: false,
175
- status: 400,
176
- error: "Path is outside project workspace"
177
- };
178
- const relativePath = path$1.relative(rootResult.root, candidate).split(path$1.sep).join("/");
179
- return {
180
- ok: true,
181
- projectId: rootResult.projectId,
182
- root: rootResult.root,
183
- requestedPath,
184
- absolutePath: candidate,
185
- relativePath: relativePath === "." ? "" : relativePath
186
- };
187
- }
188
- async function resolveExistingProjectWorkspacePath(service, projectId, rawPath) {
189
- const resolved = await resolveProjectWorkspacePath(service, projectId, rawPath);
190
- if (resolved.ok === false) return resolved;
191
- if (!resolved.requestedPath.trim()) return {
192
- ok: false,
193
- status: 400,
194
- error: "Missing path"
195
- };
196
- let realTarget;
197
- try {
198
- realTarget = await realpath(resolved.absolutePath);
199
- } catch {
200
- return {
201
- ok: false,
202
- status: 404,
203
- error: "Not found"
204
- };
205
- }
206
- if (!isWithinDirectory(resolved.root, realTarget)) return {
207
- ok: false,
208
- status: 400,
209
- error: "Path is outside project workspace"
210
- };
211
- const targetStat = await stat(realTarget);
212
- return {
213
- ...resolved,
214
- absolutePath: realTarget,
215
- stat: targetStat
216
- };
217
- }
218
- async function resolveProjectWorkspaceWritePath(service, projectId, rawPath) {
219
- const resolved = await resolveProjectWorkspacePath(service, projectId, rawPath);
220
- if (resolved.ok === false) return resolved;
221
- if (!resolved.requestedPath.trim()) return {
222
- ok: false,
223
- status: 400,
224
- error: "Missing path"
225
- };
226
- try {
227
- const realTarget = await realpath(resolved.absolutePath);
228
- if (!isWithinDirectory(resolved.root, realTarget)) return {
229
- ok: false,
230
- status: 400,
231
- error: "Path is outside project workspace"
232
- };
233
- return {
234
- ...resolved,
235
- absolutePath: realTarget,
236
- existingStat: await stat(realTarget)
237
- };
238
- } catch {
239
- const parentPath = path$1.dirname(resolved.absolutePath);
240
- try {
241
- const realParent = await realpath(parentPath);
242
- if (!isWithinDirectory(resolved.root, realParent)) return {
243
- ok: false,
244
- status: 400,
245
- error: "Path is outside project workspace"
246
- };
247
- } catch {
248
- return {
249
- ok: false,
250
- status: 404,
251
- error: "Parent directory not found"
252
- };
253
- }
254
- return resolved;
255
- }
256
- }
257
86
  function textFromTranscriptContent(content) {
258
87
  if (typeof content === "string") return content;
259
88
  if (!Array.isArray(content)) return "";
@@ -846,343 +675,6 @@ function registerProjectsRoutes(authenticated, deps) {
846
675
  }, 404);
847
676
  }
848
677
  });
849
- authenticated.get("/api/projects/:id/files", async (c) => {
850
- const resolved = await resolveProjectWorkspacePath(service, c.req.param("id"), c.req.query("path"));
851
- if (resolved.ok === false) return c.json({
852
- ok: false,
853
- error: resolved.error
854
- }, resolved.status);
855
- const { root, absolutePath: candidate } = resolved;
856
- let currentPath;
857
- try {
858
- currentPath = await realpath(candidate);
859
- if (!isWithinDirectory(root, currentPath)) return c.json({
860
- ok: false,
861
- error: "Path is outside project workspace"
862
- }, 400);
863
- if (!(await stat(currentPath)).isDirectory()) return c.json({
864
- ok: false,
865
- error: "Path is not a directory"
866
- }, 400);
867
- } catch (err) {
868
- log.warn({
869
- err,
870
- projectId: resolved.projectId,
871
- path: candidate
872
- }, "project file path unavailable");
873
- return c.json({
874
- ok: false,
875
- error: "Path not found"
876
- }, 404);
877
- }
878
- try {
879
- const dirents = await readdir(currentPath, { withFileTypes: true });
880
- const visibleEntries = (await Promise.all(dirents.filter((entry) => !SKIP_FILE_NAMES.has(entry.name)).map(async (entry) => {
881
- const displayPath = path$1.join(currentPath, entry.name);
882
- const absolutePath = await realpath(displayPath).catch(() => null);
883
- if (!absolutePath || !isWithinDirectory(root, absolutePath)) return null;
884
- const entryStat = await stat(absolutePath).catch(() => null);
885
- if (!entryStat) return null;
886
- const relativePath = path$1.relative(root, displayPath).split(path$1.sep).join("/");
887
- return {
888
- name: entry.name,
889
- path: relativePath,
890
- absolutePath,
891
- type: entryStat.isDirectory() ? "directory" : "file",
892
- size: entryStat?.isFile() ? entryStat.size : void 0,
893
- updatedAt: entryStat?.mtime.toISOString()
894
- };
895
- }))).filter((entry) => entry !== null);
896
- visibleEntries.sort((left, right) => {
897
- if (left.type !== right.type) return left.type === "directory" ? -1 : 1;
898
- return left.name.localeCompare(right.name);
899
- });
900
- const relativeCurrentPath = path$1.relative(root, currentPath).split(path$1.sep).join("/");
901
- const parentRelativePath = relativeCurrentPath ? path$1.dirname(relativeCurrentPath).replaceAll("\\", "/") : null;
902
- return c.json({
903
- ok: true,
904
- root,
905
- path: relativeCurrentPath === "." ? "" : relativeCurrentPath,
906
- parentPath: parentRelativePath === "." ? "" : parentRelativePath,
907
- entries: visibleEntries
908
- });
909
- } catch (err) {
910
- log.warn({
911
- err,
912
- projectId: resolved.projectId,
913
- path: currentPath
914
- }, "project file list failed");
915
- if (err?.code === "EACCES") return c.json({
916
- ok: false,
917
- error: "Permission denied"
918
- }, 403);
919
- return c.json({
920
- ok: false,
921
- error: "Failed to read project files"
922
- }, 500);
923
- }
924
- });
925
- /** Fuzzy filename / path search across the entire project workspace. */
926
- authenticated.get("/api/projects/:id/files/search", async (c) => {
927
- const q = typeof c.req.query("q") === "string" ? c.req.query("q").trim() : "";
928
- const limitRaw = c.req.query("limit");
929
- const limit = Math.min(Math.max(parseInt(typeof limitRaw === "string" ? limitRaw : "50", 10) || 50, 1), 50);
930
- const root = await resolveProjectWorkspaceRoot(service, c.req.param("id"));
931
- if (root.ok === false) return c.json({
932
- ok: false,
933
- error: root.error
934
- }, root.status);
935
- const entries = await fuzzySearchWorkspaceFiles(root.root, q, limit);
936
- return c.json({
937
- ok: true,
938
- entries
939
- });
940
- });
941
- authenticated.get("/api/projects/:id/files/read", async (c) => {
942
- const resolved = await resolveExistingProjectWorkspacePath(service, c.req.param("id"), c.req.query("path"));
943
- if (resolved.ok === false) return c.json({
944
- ok: false,
945
- error: { message: resolved.error }
946
- }, resolved.status);
947
- if (!resolved.stat.isFile()) return c.json({
948
- ok: false,
949
- error: { message: "Not a file" }
950
- }, 400);
951
- try {
952
- const content = await readFile(resolved.absolutePath, "utf-8");
953
- return c.json({
954
- ok: true,
955
- payload: {
956
- content,
957
- path: resolved.relativePath,
958
- absolutePath: resolved.absolutePath,
959
- mtimeMs: resolved.stat.mtimeMs
960
- }
961
- });
962
- } catch {
963
- return c.json({
964
- ok: false,
965
- error: { message: "Read failed" }
966
- }, 500);
967
- }
968
- });
969
- authenticated.get("/api/projects/:id/files/raw", async (c) => {
970
- const resolved = await resolveExistingProjectWorkspacePath(service, c.req.param("id"), c.req.query("path"));
971
- if (resolved.ok === false) return c.json({
972
- ok: false,
973
- error: { message: resolved.error }
974
- }, resolved.status);
975
- if (!resolved.stat.isFile()) return c.json({
976
- ok: false,
977
- error: { message: "Not a file" }
978
- }, 400);
979
- const contentType = PROJECT_FILE_MIME_BY_EXT[resolved.relativePath.split(".").pop()?.toLowerCase() ?? ""] || "application/octet-stream";
980
- try {
981
- const buf = await readFile(resolved.absolutePath);
982
- return new Response(buf, { headers: {
983
- "Content-Type": contentType,
984
- "Cache-Control": "private, max-age=3600"
985
- } });
986
- } catch {
987
- return c.json({
988
- ok: false,
989
- error: { message: "Read failed" }
990
- }, 500);
991
- }
992
- });
993
- authenticated.get("/api/projects/:id/files/resolve-reference", async (c) => {
994
- const rawPath = typeof c.req.query("path") === "string" ? c.req.query("path").trim() : "";
995
- if (!rawPath) return c.json({
996
- ok: false,
997
- error: {
998
- code: "INVALID_PATH",
999
- message: "Missing path"
1000
- }
1001
- }, 400);
1002
- const resolved = await resolveProjectWorkspacePath(service, c.req.param("id"), rawPath);
1003
- if (resolved.ok === false) return c.json({
1004
- ok: false,
1005
- error: {
1006
- code: "PROJECT_WORKSPACE_RESOLUTION_FAILED",
1007
- message: resolved.error
1008
- }
1009
- }, resolved.status);
1010
- let st = null;
1011
- let statError = null;
1012
- let absolutePath = resolved.absolutePath;
1013
- try {
1014
- absolutePath = await realpath(resolved.absolutePath);
1015
- if (!isWithinDirectory(resolved.root, absolutePath)) return c.json({
1016
- ok: false,
1017
- error: {
1018
- code: "PROJECT_WORKSPACE_RESOLUTION_FAILED",
1019
- message: "Path is outside project workspace"
1020
- }
1021
- }, 400);
1022
- st = await stat(absolutePath);
1023
- } catch (err) {
1024
- statError = err;
1025
- }
1026
- const displayName = path$1.basename(rawPath);
1027
- if (!st) {
1028
- const failure = classifyFileReferenceFsError(statError);
1029
- if (failure.code !== "FILE_NOT_FOUND") {
1030
- log.warn({
1031
- err: statError,
1032
- projectId: resolved.projectId,
1033
- workspaceRoot: resolved.root,
1034
- candidate: resolved.absolutePath,
1035
- errorCode: failure.code
1036
- }, `Project file reference access failed: ${failure.message}`);
1037
- return c.json({
1038
- ok: false,
1039
- error: {
1040
- code: failure.code,
1041
- message: failure.message
1042
- }
1043
- }, failure.status === 403 ? 403 : 500);
1044
- }
1045
- return c.json({
1046
- ok: true,
1047
- payload: {
1048
- inputPath: rawPath,
1049
- displayName,
1050
- scope: "missing",
1051
- exists: false,
1052
- absolutePath: resolved.absolutePath,
1053
- capabilities: ["copyPath"],
1054
- errorCode: "FILE_NOT_FOUND"
1055
- }
1056
- });
1057
- }
1058
- return c.json({
1059
- ok: true,
1060
- payload: {
1061
- inputPath: rawPath,
1062
- displayName,
1063
- scope: "workspace",
1064
- exists: true,
1065
- isDirectory: st.isDirectory(),
1066
- absolutePath,
1067
- workspaceRelativePath: resolved.relativePath,
1068
- capabilities: st.isDirectory() ? [
1069
- "openExternal",
1070
- "revealInFolder",
1071
- "copyPath"
1072
- ] : [
1073
- "preview",
1074
- "edit",
1075
- "openExternal",
1076
- "revealInFolder",
1077
- "copyPath"
1078
- ],
1079
- mtimeMs: st.mtimeMs
1080
- }
1081
- });
1082
- });
1083
- authenticated.put("/api/projects/:id/files/write", async (c) => {
1084
- let body;
1085
- try {
1086
- body = await c.req.json();
1087
- } catch {
1088
- return c.json({
1089
- ok: false,
1090
- error: { message: "Invalid JSON" }
1091
- }, 400);
1092
- }
1093
- const bodyRecord = typeof body === "object" && body !== null ? body : {};
1094
- const pathRel = typeof bodyRecord.path === "string" ? bodyRecord.path : "";
1095
- const content = typeof bodyRecord.content === "string" ? bodyRecord.content : "";
1096
- if (!pathRel.trim()) return c.json({
1097
- ok: false,
1098
- error: { message: "Missing path" }
1099
- }, 400);
1100
- const resolved = await resolveProjectWorkspaceWritePath(service, c.req.param("id"), pathRel);
1101
- if (resolved.ok === false) return c.json({
1102
- ok: false,
1103
- error: { message: resolved.error }
1104
- }, resolved.status);
1105
- if (resolved.existingStat && !resolved.existingStat.isFile()) return c.json({
1106
- ok: false,
1107
- error: { message: "Not a file" }
1108
- }, 400);
1109
- try {
1110
- await writeFile(resolved.absolutePath, content, "utf-8");
1111
- const mtimeMs = (await stat(resolved.absolutePath).catch(() => ({ mtimeMs: Date.now() }))).mtimeMs;
1112
- return c.json({
1113
- ok: true,
1114
- payload: {
1115
- path: resolved.relativePath,
1116
- mtimeMs
1117
- }
1118
- });
1119
- } catch (err) {
1120
- log.error({
1121
- err,
1122
- projectId: resolved.projectId,
1123
- path: resolved.absolutePath
1124
- }, "project file write failed");
1125
- return c.json({
1126
- ok: false,
1127
- error: { message: "Write failed" }
1128
- }, 500);
1129
- }
1130
- });
1131
- authenticated.post("/api/projects/:id/files/upload", async (c) => {
1132
- let body;
1133
- try {
1134
- body = await c.req.parseBody();
1135
- } catch {
1136
- return c.json({
1137
- ok: false,
1138
- error: { message: "Invalid multipart body" }
1139
- }, 400);
1140
- }
1141
- const file = body.file;
1142
- if (!(file instanceof File)) return c.json({
1143
- ok: false,
1144
- error: { message: "Missing file" }
1145
- }, 400);
1146
- const requestedPath = typeof body.path === "string" ? body.path.trim() : "";
1147
- if (!requestedPath) return c.json({
1148
- ok: false,
1149
- error: { message: "Missing path" }
1150
- }, 400);
1151
- const resolved = await resolveProjectWorkspaceWritePath(service, c.req.param("id"), requestedPath);
1152
- if (resolved.ok === false) return c.json({
1153
- ok: false,
1154
- error: { message: resolved.error }
1155
- }, resolved.status);
1156
- if (resolved.existingStat) return c.json({
1157
- ok: false,
1158
- error: { message: "File already exists" }
1159
- }, 409);
1160
- try {
1161
- await writeFile(resolved.absolutePath, Buffer.from(await file.arrayBuffer()), { flag: "wx" });
1162
- const entryStat = await stat(resolved.absolutePath);
1163
- return c.json({
1164
- ok: true,
1165
- entry: {
1166
- name: path$1.basename(resolved.relativePath),
1167
- path: resolved.relativePath,
1168
- absolutePath: resolved.absolutePath,
1169
- type: "file",
1170
- size: entryStat.size,
1171
- updatedAt: entryStat.mtime.toISOString()
1172
- }
1173
- }, 201);
1174
- } catch (err) {
1175
- log.error({
1176
- err,
1177
- projectId: resolved.projectId,
1178
- path: resolved.absolutePath
1179
- }, "project file upload failed");
1180
- return c.json({
1181
- ok: false,
1182
- error: { message: "Upload failed" }
1183
- }, 500);
1184
- }
1185
- });
1186
678
  authenticated.post("/api/projects/:id/sessions/:sessionKey", async (c) => {
1187
679
  try {
1188
680
  service.projects.attachSession(c.req.param("sessionKey"), c.req.param("id"));