@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.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Bp_wmuGW.js → agent-avatar-dicebear-CBqUnlvL.js} +1 -1
- package/dist/gateway/static/root/assets/agents-7YO6znTT.js +68 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CwAPK4Jm.js → app-management-settings-panel-DP7YVRPn.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-DsGLvqTb.js → appearance-settings-CVQ5n7y6.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-Ee9qOQEx.js → apps-page-DSCia0DT.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-B4qqigAQ.js → attachment-load-mmr_mHRr.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-B8ImehSW.js → automations-page-DfY-T6Ga.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BpvD54D6.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DaJbyYc2.js → binary-plugins-CO9QMEh2.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Kl25XWJc.js → block-editor-CE14CVOy.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-CeESA4Oj.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflows-page-DjMxs33B.js → browser-workflows-page-1wgXyoSO.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-B_OXTojB.js +7 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-D5nZecxV.js +3 -0
- package/dist/gateway/static/root/assets/channels-settings-BzmnWx6X.js +1 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BiU_4Rq6.js → chat-terminal-dock-CdRW6Euc.js} +2 -2
- package/dist/gateway/static/root/assets/cloud-C4-yfnnk.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-DX6OkwFQ.js +2 -0
- package/dist/gateway/static/root/assets/{cron-expression-Cs_5pUPT.js → cron-expression-DYSMZsms.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-y1kWrFVU.js → date-picker-DGCfX4tK.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-skc62zrn.js → dependency-picker-DHDa4ht1.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-ux9DxchT.js → desktop-pet-CMO-J3cG.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BVVCBgYK.js → desktop-pet-settings-MmOR4A3u.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-V8vZP0Dy.js → directory-picker-path-field-B9qoxDcn.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-Dh6DDuvQ.js → extension-debug-page-DdH0dG2_.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-8OKQDEat.js → extension-page-Clg-KktN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BgSV0Nny.js → extension-settings-page-eP3fJ2M6.js} +1 -1
- package/dist/gateway/static/root/assets/files-Dp8_uGTO.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-config-api-Dlc6eaTG.js → gateway-config-api-DEHnF4Wm.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-DqQgiAUv.js → gateway-settings-DKkEXXj9.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-Pxqk5GFH.js → heartbeat-settings-DlBPNubY.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-BnOrLRfs.js +1 -0
- package/dist/gateway/static/root/assets/index-0xQO5B_Q.js +70 -0
- package/dist/gateway/static/root/assets/index-CCyIdGuQ.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BSFNqxzY.js → keyboard-shortcuts-settings-BvpEeHnm.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-Ch1rdUo8.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-DzU6yZ9-.js → local-apps-page-Bzvn99eH.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-zxx2ExlY.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-B8hM56HS.js → management-settings-BQaeyOYm.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-DRbZjxMK.js → markdown-split-D9tmIiEm.js} +1 -1
- package/dist/gateway/static/root/assets/markdown-view-B1bGSQqt.css +1 -0
- package/dist/gateway/static/root/assets/{media-plugins-B8vMe5VX.js → media-plugins-D8I7OGnT.js} +1 -1
- package/dist/gateway/static/root/assets/models-hub-cache-0RjEwafI.js +1 -0
- package/dist/gateway/static/root/assets/{notes-api-BFsOGYkd.js → notes-api-EpgG-OQc.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-B-YZ7iuK.js → notes-page-CCvjGMhN.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-9cgh77VH.js +4 -0
- package/dist/gateway/static/root/assets/oauth-api-BwNLwbI8.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-DkWcyki5.js → product-open-page-MmMswmOU.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-hf6pmH4p.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-By2U-Wdh.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CUcpp7CQ.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Bq_ETeal.js → runtime-tools-settings-panel-Dx6o7sLu.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BogTlrsP.js → schema-form-Cdd9c4vt.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-ByI82xS1.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BCoI5IJ8.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-Coy1Cbp6.js → settings-form-section-D0YxMnhm.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-B5GU7iCG.js → settings-loading-skeleton-y53gEKsF.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Dd-BRu12.js → settings-page-BoE_tToG.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-CwEFAVWx.js → setup-status-panel-BQmyb5KC.js} +1 -1
- package/dist/gateway/static/root/assets/share-preview-page-DiqBn-oE.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-Bf3VpZ-R.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CXHlbuGS.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-Brxb6QnW.js → skills-page-CZwhWiRl.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-FB5LtEGm.js → skills-page.utils-kBGr8opZ.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-ChosWt8n.js → system-settings-panel-CPBs6lWi.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-Bo0KSZ2v.js +1 -0
- package/dist/gateway/static/root/assets/theme-store-Qgvo0Ga7.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-2SqnmSKD.js → use-autosave-C4YcEiP9.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-Cnj7gMX2.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-DtTIF6M8.js → voice-api-key-field-BML8zCwZ.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-CK3aZDdE.js → work-discovery-overlay-CKIdPEen.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BjOuddkX.js → workflow-run-setup-panel-BK44Vm5W.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-CB98MTwG.js +5 -0
- package/dist/gateway/static/root/index.html +10 -6
- package/dist/package.js +1 -1
- package/dist/src/agent/service.d.ts +1 -0
- package/dist/src/agent/service.js +5 -2
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/tools/write.js +9 -12
- package/dist/src/agent/tools/xopc-use-tool.js +2 -2
- package/dist/src/auth/oauth/index.js +2 -2
- package/dist/src/auth/oauth/registry.d.ts +1 -1
- package/dist/src/auth/oauth/registry.js +5 -1
- package/dist/src/auth/oauth/xopc-cloud.d.ts +1 -0
- package/dist/src/auth/oauth/xopc-cloud.js +8 -2
- package/dist/src/files/file-service.d.ts +36 -0
- package/dist/src/files/file-service.js +282 -0
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +2 -2
- package/dist/src/gateway/hono/routes/files.d.ts +3 -0
- package/dist/src/gateway/hono/routes/files.js +172 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/projects.js +0 -508
- package/dist/src/gateway/hono/routes/shares.js +410 -21
- package/dist/src/gateway/hono/routes/voice.js +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +15 -974
- package/dist/src/gateway/service.js +9 -9
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/share/hosted-session-share.d.ts +104 -0
- package/dist/src/share/hosted-session-share.js +276 -0
- package/dist/src/share/note-share-service.js +4 -24
- package/dist/src/share/session-share-projector.d.ts +31 -0
- package/dist/src/share/session-share-projector.js +79 -0
- package/dist/src/share/session-share-service.d.ts +89 -0
- package/dist/src/share/session-share-service.js +325 -0
- package/dist/src/share/share-asset-ticket.d.ts +7 -0
- package/dist/src/share/share-asset-ticket.js +30 -0
- package/dist/src/share/share-landing.d.ts +4 -3
- package/dist/src/share/share-landing.js +5 -5
- package/dist/src/share/share-store.d.ts +27 -2
- package/dist/src/share/share-store.js +71 -8
- package/dist/src/share/share-types.d.ts +17 -2
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/tui/backends/gateway-realtime-backend.js +11 -3
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-CKG93dlc.js +0 -68
- package/dist/gateway/static/root/assets/automations-workspace-BNFAfNwC.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-Cg6NgwE_.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-NvJKgF1u.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-C2uBdALe.js +0 -3
- package/dist/gateway/static/root/assets/channels-settings-CIcYKMEs.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CEEcmUiP.js +0 -2
- package/dist/gateway/static/root/assets/home-page-1xjo6wE4.js +0 -1
- package/dist/gateway/static/root/assets/index-Bbd_dXui.js +0 -70
- package/dist/gateway/static/root/assets/index-D_hrEKXM.css +0 -2
- package/dist/gateway/static/root/assets/local-app-workbench-page-heQ4-PJ7.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-BblnS6Jy.js +0 -2
- package/dist/gateway/static/root/assets/markdown-view-CLzTdVLW.css +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-CAM09C8b.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-BIPIwgU2.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-B_kzTn1K.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CtD8DsD-.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B8n9HI1y.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-xlhdRTME.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-DzXw9UQJ.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-Bk6UQOwS.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DrmQZ3Au.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-CW2sJbdc.js +0 -1
- package/dist/gateway/static/root/assets/theme-store-DOdCjyuw.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-Bq9KB-Oj.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CcbGEQol.js +0 -5
- package/dist/src/gateway/file-path-classifier.d.ts +0 -30
- package/dist/src/gateway/file-path-classifier.js +0 -105
- package/dist/src/gateway/file-reference-errors.d.ts +0 -6
- package/dist/src/gateway/file-reference-errors.js +0 -21
- package/dist/src/gateway/file-reference-registry.d.ts +0 -34
- package/dist/src/gateway/file-reference-registry.js +0 -45
- /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"));
|