@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
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { FileServiceError, FileSpaceService, fileResourceFromPath, resolveFilePath } from "../../../files/file-service.js";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
//#region src/gateway/hono/routes/files.ts
|
|
5
|
+
const services = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
const mutationLocks = /* @__PURE__ */ new Map();
|
|
7
|
+
async function withMutationLock(key, action) {
|
|
8
|
+
const previous = mutationLocks.get(key) ?? Promise.resolve();
|
|
9
|
+
let release;
|
|
10
|
+
const gate = new Promise((resolve) => {
|
|
11
|
+
release = resolve;
|
|
12
|
+
});
|
|
13
|
+
const tail = previous.then(() => gate);
|
|
14
|
+
mutationLocks.set(key, tail);
|
|
15
|
+
await previous;
|
|
16
|
+
try {
|
|
17
|
+
return await action();
|
|
18
|
+
} finally {
|
|
19
|
+
release();
|
|
20
|
+
if (mutationLocks.get(key) === tail) mutationLocks.delete(key);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function getFiles(deps) {
|
|
24
|
+
const existing = services.get(deps.service);
|
|
25
|
+
if (existing) return existing;
|
|
26
|
+
const created = new FileSpaceService(() => deps.service.currentConfig, deps.service.projects, (sessionKey) => deps.service.agentService.getResolvedWorkspaceForSession(sessionKey));
|
|
27
|
+
services.set(deps.service, created);
|
|
28
|
+
return created;
|
|
29
|
+
}
|
|
30
|
+
function publicSpace(space) {
|
|
31
|
+
const { root: _root, ...value } = space;
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
function errorResponse(c, error) {
|
|
35
|
+
if (error instanceof FileServiceError) return c.json({ error: { message: error.message } }, error.status);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
async function collectFiles(files, spaceId, query, max) {
|
|
39
|
+
const output = [];
|
|
40
|
+
const directories = [""];
|
|
41
|
+
while (directories.length && output.length < max) {
|
|
42
|
+
const directory = directories.shift();
|
|
43
|
+
const children = await files.children(spaceId, directory);
|
|
44
|
+
for (const child of children) {
|
|
45
|
+
if (child.kind === "directory") directories.push(child.relativePath);
|
|
46
|
+
else if (!query || child.name.toLocaleLowerCase().includes(query)) output.push(child);
|
|
47
|
+
if (output.length >= max) break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return output;
|
|
51
|
+
}
|
|
52
|
+
function registerFilesRoutes(authenticated, deps) {
|
|
53
|
+
const files = getFiles(deps);
|
|
54
|
+
authenticated.get("/api/files/spaces", async (c) => {
|
|
55
|
+
try {
|
|
56
|
+
return c.json({ spaces: (await files.list()).map(publicSpace) });
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return errorResponse(c, error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
authenticated.get("/api/files/contexts/:kind/:id", async (c) => {
|
|
62
|
+
try {
|
|
63
|
+
const kind = c.req.param("kind");
|
|
64
|
+
if (kind !== "agent" && kind !== "project" && kind !== "session") throw new FileServiceError(400, "Invalid context kind");
|
|
65
|
+
return c.json({ space: publicSpace(await files.forContext(kind, c.req.param("id"))) });
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return errorResponse(c, error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
authenticated.get("/api/files/spaces/:id/children", async (c) => {
|
|
71
|
+
try {
|
|
72
|
+
return c.json({ items: await files.children(c.req.param("id"), c.req.query("path") ?? "") });
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return errorResponse(c, error);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
authenticated.get("/api/files/recent", async (c) => {
|
|
78
|
+
try {
|
|
79
|
+
const limit = Math.min(100, Math.max(1, Number(c.req.query("limit")) || 50));
|
|
80
|
+
const spaces = await files.list();
|
|
81
|
+
const items = (await Promise.all(spaces.map((space) => collectFiles(files, space.id, "", 5e3)))).flat().sort((a, b) => b.modifiedAt - a.modifiedAt).slice(0, limit);
|
|
82
|
+
return c.json({ items });
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return errorResponse(c, error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
authenticated.get("/api/files/search", async (c) => {
|
|
88
|
+
try {
|
|
89
|
+
const query = (c.req.query("q") ?? "").trim().toLocaleLowerCase();
|
|
90
|
+
if (!query) return c.json({ items: [] });
|
|
91
|
+
const limit = Math.min(100, Math.max(1, Number(c.req.query("limit")) || 50));
|
|
92
|
+
const requestedSpaceId = c.req.query("spaceId");
|
|
93
|
+
const spaces = requestedSpaceId ? [await files.get(requestedSpaceId)] : await files.list();
|
|
94
|
+
const items = (await Promise.all(spaces.map((space) => collectFiles(files, space.id, query, 5e3)))).flat().sort((a, b) => b.modifiedAt - a.modifiedAt).slice(0, limit);
|
|
95
|
+
return c.json({ items });
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return errorResponse(c, error);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
authenticated.post("/api/files/resolve", async (c) => {
|
|
101
|
+
try {
|
|
102
|
+
const body = await c.req.json();
|
|
103
|
+
if (!body.spaceId || !body.path) throw new FileServiceError(400, "spaceId and path are required");
|
|
104
|
+
const space = await files.get(body.spaceId);
|
|
105
|
+
const absolutePath = await resolveFilePath(space.root, body.path);
|
|
106
|
+
return c.json({ resource: await fileResourceFromPath(space, absolutePath) });
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return errorResponse(c, error);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
authenticated.get("/api/files/:id", async (c) => {
|
|
112
|
+
try {
|
|
113
|
+
return c.json({ resource: (await files.resource(c.req.param("id"))).resource });
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return errorResponse(c, error);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
authenticated.get("/api/files/:id/content", async (c) => {
|
|
119
|
+
try {
|
|
120
|
+
const { resource, absolutePath } = await files.resource(c.req.param("id"));
|
|
121
|
+
if (resource.kind !== "file") throw new FileServiceError(400, "Resource is not a file");
|
|
122
|
+
if (c.req.header("if-none-match") === resource.revision) return c.body(null, 304);
|
|
123
|
+
c.header("Content-Type", resource.mimeType);
|
|
124
|
+
c.header("ETag", resource.revision);
|
|
125
|
+
c.header("Content-Disposition", `inline; filename="${resource.name.replace(/["\r\n]/g, "_")}"`);
|
|
126
|
+
return c.body(await readFile(absolutePath));
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return errorResponse(c, error);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
authenticated.put("/api/files/:id/content", async (c) => {
|
|
132
|
+
try {
|
|
133
|
+
const body = await c.req.json();
|
|
134
|
+
if (typeof body.content !== "string" || !body.revision) throw new FileServiceError(400, "content and revision are required");
|
|
135
|
+
const id = c.req.param("id");
|
|
136
|
+
const resource = await withMutationLock(id, async () => {
|
|
137
|
+
const current = await files.resource(id);
|
|
138
|
+
if (!current.resource.capabilities.includes("edit")) throw new FileServiceError(403, "File is not editable");
|
|
139
|
+
if (body.revision !== current.resource.revision) throw new FileServiceError(409, "File has changed");
|
|
140
|
+
await writeFile(current.absolutePath, body.content, "utf8");
|
|
141
|
+
return fileResourceFromPath(current.space, current.absolutePath);
|
|
142
|
+
});
|
|
143
|
+
return c.json({ resource });
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return errorResponse(c, error);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
authenticated.post("/api/files/spaces/:id/upload", async (c) => {
|
|
149
|
+
try {
|
|
150
|
+
const space = await files.get(c.req.param("id"));
|
|
151
|
+
if (!space.writable) throw new FileServiceError(403, "File space is read-only");
|
|
152
|
+
const form = await c.req.formData();
|
|
153
|
+
const upload = form.get("file");
|
|
154
|
+
const directory = String(form.get("directory") ?? "");
|
|
155
|
+
if (!(upload instanceof File)) throw new FileServiceError(400, "file is required");
|
|
156
|
+
const name = basename(upload.name).trim();
|
|
157
|
+
if (!name || name !== upload.name) throw new FileServiceError(400, "Invalid file name");
|
|
158
|
+
const parent = await resolveFilePath(space.root, directory);
|
|
159
|
+
const target = await resolveFilePath(space.root, join(directory, name), false);
|
|
160
|
+
if (target === parent) throw new FileServiceError(400, "Invalid file name");
|
|
161
|
+
await writeFile(target, new Uint8Array(await upload.arrayBuffer()), { flag: "wx" }).catch((error) => {
|
|
162
|
+
if (error.code === "EEXIST") throw new FileServiceError(409, "File already exists");
|
|
163
|
+
throw error;
|
|
164
|
+
});
|
|
165
|
+
return c.json({ resource: await fileResourceFromPath(space, target) }, 201);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return errorResponse(c, error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
export { registerFilesRoutes };
|
|
@@ -19,6 +19,14 @@ const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
|
|
|
19
19
|
return { register: registerMediaRoutes };
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
id: "files",
|
|
24
|
+
match: (path) => startsWithAny(path, ["/api/files"]),
|
|
25
|
+
load: async () => {
|
|
26
|
+
const { registerFilesRoutes } = await import("./files.js");
|
|
27
|
+
return { register: registerFilesRoutes };
|
|
28
|
+
}
|
|
29
|
+
},
|
|
22
30
|
{
|
|
23
31
|
id: "workspace",
|
|
24
32
|
match: (path) => startsWithAny(path, ["/api/workspace"]),
|