@xpert-ai/plugin-spaces 0.0.0-stage → 0.3.0
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/README.md +25 -2
- package/dist/access.d.ts +38 -0
- package/dist/access.js +96 -0
- package/dist/ai.d.ts +53 -0
- package/dist/ai.js +143 -0
- package/dist/artifacts.d.ts +9 -0
- package/dist/artifacts.js +40 -0
- package/dist/capability.d.ts +22 -0
- package/dist/capability.js +63 -0
- package/dist/catalog.d.ts +196 -0
- package/dist/catalog.js +140 -0
- package/dist/content-backup.d.ts +7 -0
- package/dist/content-backup.js +35 -0
- package/dist/content-coverage.d.ts +9 -0
- package/dist/content-coverage.js +12 -0
- package/dist/content-knowledge.d.ts +40 -0
- package/dist/content-knowledge.js +168 -0
- package/dist/content.d.ts +19 -0
- package/dist/content.js +47 -0
- package/dist/documents.d.ts +38 -0
- package/dist/documents.js +177 -0
- package/dist/domain.d.ts +95 -0
- package/dist/domain.js +23 -0
- package/dist/editors.d.ts +16 -0
- package/dist/editors.js +63 -0
- package/dist/entities.d.ts +91 -0
- package/dist/entities.js +318 -0
- package/dist/files.d.ts +79 -0
- package/dist/files.js +228 -0
- package/dist/imports.d.ts +83 -0
- package/dist/imports.js +139 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +51 -0
- package/dist/item-catalog.d.ts +13 -0
- package/dist/item-catalog.js +71 -0
- package/dist/knowledge.d.ts +57 -0
- package/dist/knowledge.js +172 -0
- package/dist/middleware.d.ts +17 -0
- package/dist/middleware.js +82 -0
- package/dist/pages.d.ts +38 -0
- package/dist/pages.js +41 -0
- package/dist/proposal-id.d.ts +3 -0
- package/dist/proposal-id.js +11 -0
- package/dist/references.d.ts +22 -0
- package/dist/references.js +119 -0
- package/dist/remote/app.css +4 -0
- package/dist/remote/app.js +167 -0
- package/dist/resource-folders.d.ts +7 -0
- package/dist/resource-folders.js +21 -0
- package/dist/resource-provider.d.ts +39 -0
- package/dist/resource-provider.js +160 -0
- package/dist/spaces.d.ts +95 -0
- package/dist/spaces.js +233 -0
- package/dist/subpages.d.ts +24 -0
- package/dist/subpages.js +118 -0
- package/dist/view.d.ts +99 -0
- package/dist/view.js +259 -0
- package/docs/.mintignore +7 -0
- package/docs/AGENTS.md +3 -0
- package/docs/LICENSE +21 -0
- package/docs/README.md +3 -0
- package/docs/architecture.mdx +49 -0
- package/docs/collaboration.mdx +50 -0
- package/docs/docs.json +39 -0
- package/docs/feishu-document-import.mdx +118 -0
- package/docs/index.mdx +12 -0
- package/docs/insert-menu-plan.mdx +74 -0
- package/docs/math-subpages.mdx +62 -0
- package/docs/page-resources-and-connectors.mdx +103 -0
- package/docs/quickstart.mdx +17 -0
- package/migrations/20261023-document-import-origin.sql +4 -0
- package/migrations/20261023-subpages.sql +6 -0
- package/package.json +79 -4
package/dist/entities.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
|
11
|
+
export class ScopedEntity {
|
|
12
|
+
id;
|
|
13
|
+
tenantId;
|
|
14
|
+
organizationId;
|
|
15
|
+
createdAt;
|
|
16
|
+
updatedAt;
|
|
17
|
+
}
|
|
18
|
+
__decorate([
|
|
19
|
+
PrimaryGeneratedColumn('uuid'),
|
|
20
|
+
__metadata("design:type", String)
|
|
21
|
+
], ScopedEntity.prototype, "id", void 0);
|
|
22
|
+
__decorate([
|
|
23
|
+
Column({ type: 'uuid' }),
|
|
24
|
+
__metadata("design:type", String)
|
|
25
|
+
], ScopedEntity.prototype, "tenantId", void 0);
|
|
26
|
+
__decorate([
|
|
27
|
+
Column({ type: 'uuid' }),
|
|
28
|
+
__metadata("design:type", String)
|
|
29
|
+
], ScopedEntity.prototype, "organizationId", void 0);
|
|
30
|
+
__decorate([
|
|
31
|
+
CreateDateColumn(),
|
|
32
|
+
__metadata("design:type", Date)
|
|
33
|
+
], ScopedEntity.prototype, "createdAt", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
UpdateDateColumn(),
|
|
36
|
+
__metadata("design:type", Date)
|
|
37
|
+
], ScopedEntity.prototype, "updatedAt", void 0);
|
|
38
|
+
let Space = class Space extends ScopedEntity {
|
|
39
|
+
/** Storage and membership belong to this Project; Space only adds content metadata. */
|
|
40
|
+
projectId;
|
|
41
|
+
knowledgebaseId;
|
|
42
|
+
title;
|
|
43
|
+
ownerId;
|
|
44
|
+
visibility;
|
|
45
|
+
version;
|
|
46
|
+
deleted;
|
|
47
|
+
};
|
|
48
|
+
__decorate([
|
|
49
|
+
Column({ type: 'uuid' }),
|
|
50
|
+
__metadata("design:type", String)
|
|
51
|
+
], Space.prototype, "projectId", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
Column({ type: 'uuid', nullable: true }),
|
|
54
|
+
__metadata("design:type", Object)
|
|
55
|
+
], Space.prototype, "knowledgebaseId", void 0);
|
|
56
|
+
__decorate([
|
|
57
|
+
Column({ type: 'varchar', length: 200 }),
|
|
58
|
+
__metadata("design:type", String)
|
|
59
|
+
], Space.prototype, "title", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
Column({ type: 'uuid' }),
|
|
62
|
+
__metadata("design:type", String)
|
|
63
|
+
], Space.prototype, "ownerId", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
Column({ type: 'varchar', length: 20 }),
|
|
66
|
+
__metadata("design:type", String)
|
|
67
|
+
], Space.prototype, "visibility", void 0);
|
|
68
|
+
__decorate([
|
|
69
|
+
Column({ type: 'int', default: 1 }),
|
|
70
|
+
__metadata("design:type", Number)
|
|
71
|
+
], Space.prototype, "version", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
Column({ type: 'boolean', default: false }),
|
|
74
|
+
__metadata("design:type", Boolean)
|
|
75
|
+
], Space.prototype, "deleted", void 0);
|
|
76
|
+
Space = __decorate([
|
|
77
|
+
Entity('plugin_spaces_space'),
|
|
78
|
+
Index(['tenantId', 'organizationId']),
|
|
79
|
+
Index('IDX_space_project', ['tenantId', 'organizationId', 'projectId'], { unique: true })
|
|
80
|
+
], Space);
|
|
81
|
+
export { Space };
|
|
82
|
+
let SpaceItem = class SpaceItem extends ScopedEntity {
|
|
83
|
+
/** Immutable import receipt; content stays in the project file. A copy does not inherit remote ACLs. */
|
|
84
|
+
importOrigin;
|
|
85
|
+
/** Logical page parent; parentId continues to mean the physical file folder. */
|
|
86
|
+
parentPageId;
|
|
87
|
+
/** Original request title makes a retried create idempotent even after a later rename. */
|
|
88
|
+
childRequestTitle;
|
|
89
|
+
filePath;
|
|
90
|
+
fileHash;
|
|
91
|
+
syncStatus;
|
|
92
|
+
syncError;
|
|
93
|
+
knowledgeDocumentId;
|
|
94
|
+
indexedHash;
|
|
95
|
+
indexStatus;
|
|
96
|
+
indexError;
|
|
97
|
+
spaceId;
|
|
98
|
+
parentId;
|
|
99
|
+
createdById;
|
|
100
|
+
title;
|
|
101
|
+
kind;
|
|
102
|
+
version;
|
|
103
|
+
deleted;
|
|
104
|
+
projectedSequence;
|
|
105
|
+
markdown;
|
|
106
|
+
editorKey;
|
|
107
|
+
currentFileVersionId;
|
|
108
|
+
file;
|
|
109
|
+
artifactId;
|
|
110
|
+
artifactVersionId;
|
|
111
|
+
};
|
|
112
|
+
__decorate([
|
|
113
|
+
Column({ type: 'jsonb', nullable: true }),
|
|
114
|
+
__metadata("design:type", Object)
|
|
115
|
+
], SpaceItem.prototype, "importOrigin", void 0);
|
|
116
|
+
__decorate([
|
|
117
|
+
Column({ type: 'uuid', nullable: true }),
|
|
118
|
+
__metadata("design:type", Object)
|
|
119
|
+
], SpaceItem.prototype, "parentPageId", void 0);
|
|
120
|
+
__decorate([
|
|
121
|
+
Column({ type: 'varchar', length: 200, nullable: true }),
|
|
122
|
+
__metadata("design:type", Object)
|
|
123
|
+
], SpaceItem.prototype, "childRequestTitle", void 0);
|
|
124
|
+
__decorate([
|
|
125
|
+
Column({ type: 'text', nullable: true }),
|
|
126
|
+
__metadata("design:type", Object)
|
|
127
|
+
], SpaceItem.prototype, "filePath", void 0);
|
|
128
|
+
__decorate([
|
|
129
|
+
Column({ type: 'varchar', length: 64, nullable: true }),
|
|
130
|
+
__metadata("design:type", Object)
|
|
131
|
+
], SpaceItem.prototype, "fileHash", void 0);
|
|
132
|
+
__decorate([
|
|
133
|
+
Column({ type: 'varchar', length: 24, default: 'pending' }),
|
|
134
|
+
__metadata("design:type", String)
|
|
135
|
+
], SpaceItem.prototype, "syncStatus", void 0);
|
|
136
|
+
__decorate([
|
|
137
|
+
Column({ type: 'text', nullable: true }),
|
|
138
|
+
__metadata("design:type", Object)
|
|
139
|
+
], SpaceItem.prototype, "syncError", void 0);
|
|
140
|
+
__decorate([
|
|
141
|
+
Column({ type: 'uuid', nullable: true }),
|
|
142
|
+
__metadata("design:type", Object)
|
|
143
|
+
], SpaceItem.prototype, "knowledgeDocumentId", void 0);
|
|
144
|
+
__decorate([
|
|
145
|
+
Column({ type: 'varchar', length: 64, nullable: true }),
|
|
146
|
+
__metadata("design:type", Object)
|
|
147
|
+
], SpaceItem.prototype, "indexedHash", void 0);
|
|
148
|
+
__decorate([
|
|
149
|
+
Column({ type: 'varchar', length: 24, default: 'pending' }),
|
|
150
|
+
__metadata("design:type", String)
|
|
151
|
+
], SpaceItem.prototype, "indexStatus", void 0);
|
|
152
|
+
__decorate([
|
|
153
|
+
Column({ type: 'text', nullable: true }),
|
|
154
|
+
__metadata("design:type", Object)
|
|
155
|
+
], SpaceItem.prototype, "indexError", void 0);
|
|
156
|
+
__decorate([
|
|
157
|
+
Column({ type: 'uuid' }),
|
|
158
|
+
__metadata("design:type", String)
|
|
159
|
+
], SpaceItem.prototype, "spaceId", void 0);
|
|
160
|
+
__decorate([
|
|
161
|
+
Column({ type: 'uuid', nullable: true }),
|
|
162
|
+
__metadata("design:type", Object)
|
|
163
|
+
], SpaceItem.prototype, "parentId", void 0);
|
|
164
|
+
__decorate([
|
|
165
|
+
Column({ type: 'uuid' }),
|
|
166
|
+
__metadata("design:type", String)
|
|
167
|
+
], SpaceItem.prototype, "createdById", void 0);
|
|
168
|
+
__decorate([
|
|
169
|
+
Column({ type: 'varchar', length: 200 }),
|
|
170
|
+
__metadata("design:type", String)
|
|
171
|
+
], SpaceItem.prototype, "title", void 0);
|
|
172
|
+
__decorate([
|
|
173
|
+
Column({ type: 'varchar', length: 20 }),
|
|
174
|
+
__metadata("design:type", String)
|
|
175
|
+
], SpaceItem.prototype, "kind", void 0);
|
|
176
|
+
__decorate([
|
|
177
|
+
Column({ type: 'int', default: 1 }),
|
|
178
|
+
__metadata("design:type", Number)
|
|
179
|
+
], SpaceItem.prototype, "version", void 0);
|
|
180
|
+
__decorate([
|
|
181
|
+
Column({ type: 'boolean', default: false }),
|
|
182
|
+
__metadata("design:type", Boolean)
|
|
183
|
+
], SpaceItem.prototype, "deleted", void 0);
|
|
184
|
+
__decorate([
|
|
185
|
+
Column({ type: 'int', default: -1 }),
|
|
186
|
+
__metadata("design:type", Number)
|
|
187
|
+
], SpaceItem.prototype, "projectedSequence", void 0);
|
|
188
|
+
__decorate([
|
|
189
|
+
Column({ type: 'text', default: '' }),
|
|
190
|
+
__metadata("design:type", String)
|
|
191
|
+
], SpaceItem.prototype, "markdown", void 0);
|
|
192
|
+
__decorate([
|
|
193
|
+
Column({ type: 'varchar', length: 100, nullable: true }),
|
|
194
|
+
__metadata("design:type", Object)
|
|
195
|
+
], SpaceItem.prototype, "editorKey", void 0);
|
|
196
|
+
__decorate([
|
|
197
|
+
Column({ type: 'uuid', nullable: true }),
|
|
198
|
+
__metadata("design:type", Object)
|
|
199
|
+
], SpaceItem.prototype, "currentFileVersionId", void 0);
|
|
200
|
+
__decorate([
|
|
201
|
+
Column({ type: 'jsonb', nullable: true }),
|
|
202
|
+
__metadata("design:type", Object)
|
|
203
|
+
], SpaceItem.prototype, "file", void 0);
|
|
204
|
+
__decorate([
|
|
205
|
+
Column({ type: 'uuid', nullable: true }),
|
|
206
|
+
__metadata("design:type", Object)
|
|
207
|
+
], SpaceItem.prototype, "artifactId", void 0);
|
|
208
|
+
__decorate([
|
|
209
|
+
Column({ type: 'uuid', nullable: true }),
|
|
210
|
+
__metadata("design:type", Object)
|
|
211
|
+
], SpaceItem.prototype, "artifactVersionId", void 0);
|
|
212
|
+
SpaceItem = __decorate([
|
|
213
|
+
Entity('plugin_spaces_item'),
|
|
214
|
+
Index(['tenantId', 'organizationId', 'spaceId']),
|
|
215
|
+
Index('IDX_space_page_parent', ['tenantId', 'organizationId', 'spaceId', 'parentPageId']),
|
|
216
|
+
Index('IDX_space_item_active_path', ['spaceId', 'filePath'], { unique: true, where: '"deleted" = false AND "filePath" IS NOT NULL' })
|
|
217
|
+
], SpaceItem);
|
|
218
|
+
export { SpaceItem };
|
|
219
|
+
let SpaceFileVersion = class SpaceFileVersion extends ScopedEntity {
|
|
220
|
+
itemId;
|
|
221
|
+
createdById;
|
|
222
|
+
reference;
|
|
223
|
+
revisionId;
|
|
224
|
+
sha256;
|
|
225
|
+
};
|
|
226
|
+
__decorate([
|
|
227
|
+
Column({ type: 'uuid' }),
|
|
228
|
+
__metadata("design:type", String)
|
|
229
|
+
], SpaceFileVersion.prototype, "itemId", void 0);
|
|
230
|
+
__decorate([
|
|
231
|
+
Column({ type: 'uuid' }),
|
|
232
|
+
__metadata("design:type", String)
|
|
233
|
+
], SpaceFileVersion.prototype, "createdById", void 0);
|
|
234
|
+
__decorate([
|
|
235
|
+
Column({ type: 'jsonb' }),
|
|
236
|
+
__metadata("design:type", Object)
|
|
237
|
+
], SpaceFileVersion.prototype, "reference", void 0);
|
|
238
|
+
__decorate([
|
|
239
|
+
Column({ type: 'uuid', nullable: true }),
|
|
240
|
+
__metadata("design:type", Object)
|
|
241
|
+
], SpaceFileVersion.prototype, "revisionId", void 0);
|
|
242
|
+
__decorate([
|
|
243
|
+
Column({ type: 'varchar', length: 64 }),
|
|
244
|
+
__metadata("design:type", String)
|
|
245
|
+
], SpaceFileVersion.prototype, "sha256", void 0);
|
|
246
|
+
SpaceFileVersion = __decorate([
|
|
247
|
+
Entity('plugin_spaces_file_version'),
|
|
248
|
+
Index(['tenantId', 'organizationId', 'itemId'])
|
|
249
|
+
], SpaceFileVersion);
|
|
250
|
+
export { SpaceFileVersion };
|
|
251
|
+
/** Organizational placement only. Native resources keep one authoritative provider and
|
|
252
|
+
* stable project bundle; moving a library entry never duplicates business content. */
|
|
253
|
+
let SpaceResourcePlacement = class SpaceResourcePlacement extends ScopedEntity {
|
|
254
|
+
spaceId;
|
|
255
|
+
contentId;
|
|
256
|
+
parentId;
|
|
257
|
+
version;
|
|
258
|
+
};
|
|
259
|
+
__decorate([
|
|
260
|
+
Column({ type: 'uuid' }),
|
|
261
|
+
__metadata("design:type", String)
|
|
262
|
+
], SpaceResourcePlacement.prototype, "spaceId", void 0);
|
|
263
|
+
__decorate([
|
|
264
|
+
Column({ type: 'uuid' }),
|
|
265
|
+
__metadata("design:type", String)
|
|
266
|
+
], SpaceResourcePlacement.prototype, "contentId", void 0);
|
|
267
|
+
__decorate([
|
|
268
|
+
Column({ type: 'uuid', nullable: true }),
|
|
269
|
+
__metadata("design:type", Object)
|
|
270
|
+
], SpaceResourcePlacement.prototype, "parentId", void 0);
|
|
271
|
+
__decorate([
|
|
272
|
+
Column({ type: 'int', default: 1 }),
|
|
273
|
+
__metadata("design:type", Number)
|
|
274
|
+
], SpaceResourcePlacement.prototype, "version", void 0);
|
|
275
|
+
SpaceResourcePlacement = __decorate([
|
|
276
|
+
Entity('plugin_spaces_resource_placement'),
|
|
277
|
+
Index('IDX_space_resource_placement', ['tenantId', 'organizationId', 'spaceId', 'contentId'], { unique: true })
|
|
278
|
+
], SpaceResourcePlacement);
|
|
279
|
+
export { SpaceResourcePlacement };
|
|
280
|
+
/** Disposable revision-bound knowledge state, never a source body or permission cache. */
|
|
281
|
+
let SpaceContentIndex = class SpaceContentIndex extends ScopedEntity {
|
|
282
|
+
spaceId;
|
|
283
|
+
contentId;
|
|
284
|
+
revision;
|
|
285
|
+
bindingRevision;
|
|
286
|
+
status;
|
|
287
|
+
documents;
|
|
288
|
+
};
|
|
289
|
+
__decorate([
|
|
290
|
+
Column({ type: 'uuid' }),
|
|
291
|
+
__metadata("design:type", String)
|
|
292
|
+
], SpaceContentIndex.prototype, "spaceId", void 0);
|
|
293
|
+
__decorate([
|
|
294
|
+
Column({ type: 'uuid' }),
|
|
295
|
+
__metadata("design:type", String)
|
|
296
|
+
], SpaceContentIndex.prototype, "contentId", void 0);
|
|
297
|
+
__decorate([
|
|
298
|
+
Column({ type: 'varchar', length: 256, nullable: true }),
|
|
299
|
+
__metadata("design:type", Object)
|
|
300
|
+
], SpaceContentIndex.prototype, "revision", void 0);
|
|
301
|
+
__decorate([
|
|
302
|
+
Column({ type: 'int', nullable: true }),
|
|
303
|
+
__metadata("design:type", Object)
|
|
304
|
+
], SpaceContentIndex.prototype, "bindingRevision", void 0);
|
|
305
|
+
__decorate([
|
|
306
|
+
Column({ type: 'varchar', length: 24, default: 'pending' }),
|
|
307
|
+
__metadata("design:type", String)
|
|
308
|
+
], SpaceContentIndex.prototype, "status", void 0);
|
|
309
|
+
__decorate([
|
|
310
|
+
Column({ type: 'jsonb', default: () => "'[]'::jsonb" }),
|
|
311
|
+
__metadata("design:type", Array)
|
|
312
|
+
], SpaceContentIndex.prototype, "documents", void 0);
|
|
313
|
+
SpaceContentIndex = __decorate([
|
|
314
|
+
Entity('plugin_spaces_content_index'),
|
|
315
|
+
Index('IDX_space_content_index', ['tenantId', 'organizationId', 'spaceId', 'contentId'], { unique: true })
|
|
316
|
+
], SpaceContentIndex);
|
|
317
|
+
export { SpaceContentIndex };
|
|
318
|
+
export const entities = [Space, SpaceItem, SpaceFileVersion, SpaceResourcePlacement, SpaceContentIndex];
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { SpaceActor } from './domain.js';
|
|
2
|
+
import { type AgentMiddlewareRuntimeServiceApi } from '@xpert-ai/plugin-sdk';
|
|
3
|
+
import { Repository } from 'typeorm';
|
|
4
|
+
import { SpaceAccess } from './access.js';
|
|
5
|
+
import { SpaceItem, SpaceFileVersion } from './entities.js';
|
|
6
|
+
import { SpacePages } from './pages.js';
|
|
7
|
+
import { SpaceDocuments } from './documents.js';
|
|
8
|
+
import { SpaceKnowledge } from './knowledge.js';
|
|
9
|
+
import { SpacesService } from './spaces.js';
|
|
10
|
+
export declare class SpaceFiles {
|
|
11
|
+
private readonly access;
|
|
12
|
+
private readonly pages;
|
|
13
|
+
private readonly spaces;
|
|
14
|
+
private readonly documents;
|
|
15
|
+
private readonly knowledge;
|
|
16
|
+
private readonly versions;
|
|
17
|
+
private readonly runtime;
|
|
18
|
+
constructor(access: SpaceAccess, pages: SpacePages, spaces: SpacesService, documents: SpaceDocuments, knowledge: SpaceKnowledge, versions: Repository<SpaceFileVersion>, runtime: AgentMiddlewareRuntimeServiceApi);
|
|
19
|
+
put(actor: SpaceActor, itemId: string, buffer: Buffer, fileName: string, mimeType: string, revisionId?: string | null, updateCurrent?: boolean): Promise<SpaceFileVersion>;
|
|
20
|
+
/** Opaque file protocol. Editor plugins own parsing; this resource owns snapshots and CAS. */
|
|
21
|
+
editorRead(actor: SpaceActor, itemId: string): Promise<{
|
|
22
|
+
buffer: Buffer<ArrayBufferLike>;
|
|
23
|
+
revision: string;
|
|
24
|
+
}>;
|
|
25
|
+
private editorRevision;
|
|
26
|
+
editorWrite(actor: SpaceActor, itemId: string, expectedRevision: string, buffer: Buffer): Promise<{
|
|
27
|
+
revision: string;
|
|
28
|
+
}>;
|
|
29
|
+
readBytes(actor: SpaceActor, versionId: string): Promise<Buffer<ArrayBufferLike>>;
|
|
30
|
+
upload(actor: SpaceActor, spaceId: string, parentId: string | null, fileName: string, mimeType: string, buffer: Buffer): Promise<{
|
|
31
|
+
item: SpaceItem;
|
|
32
|
+
version: SpaceFileVersion;
|
|
33
|
+
}>;
|
|
34
|
+
get(actor: SpaceActor, versionId: string): Promise<SpaceFileVersion>;
|
|
35
|
+
list(actor: SpaceActor, itemId: string): Promise<SpaceFileVersion[]>;
|
|
36
|
+
exportPage(actor: SpaceActor, itemId: string): Promise<{
|
|
37
|
+
fileVersionId: string;
|
|
38
|
+
revisionId: string;
|
|
39
|
+
sequence: number;
|
|
40
|
+
artifactId: string;
|
|
41
|
+
versionId: string;
|
|
42
|
+
url: string;
|
|
43
|
+
}>;
|
|
44
|
+
collect(actor: SpaceActor, spaceId: string, parentId: string | null, artifactId: string, versionId: string): Promise<SpaceItem>;
|
|
45
|
+
artifactBytes(actor: SpaceActor, artifactId: string, versionId: string): Promise<{
|
|
46
|
+
artifact: import("@xpert-ai/plugin-sdk").ArtifactRecord;
|
|
47
|
+
version: import("@xpert-ai/plugin-sdk").ArtifactVersionRecord;
|
|
48
|
+
buffer: Buffer<ArrayBufferLike>;
|
|
49
|
+
}>;
|
|
50
|
+
available(actor: SpaceActor, page: number): Promise<{
|
|
51
|
+
items: {
|
|
52
|
+
id: string;
|
|
53
|
+
title: string | null | undefined;
|
|
54
|
+
versions: import("@xpert-ai/plugin-sdk").ArtifactVersionRecord[];
|
|
55
|
+
}[];
|
|
56
|
+
total: number;
|
|
57
|
+
page: number;
|
|
58
|
+
pageSize: number;
|
|
59
|
+
}>;
|
|
60
|
+
current(actor: SpaceActor, itemId: string): Promise<{
|
|
61
|
+
reference: {
|
|
62
|
+
userId: string;
|
|
63
|
+
projectId: string;
|
|
64
|
+
catalog: "projects";
|
|
65
|
+
source: "platform.workspace.files";
|
|
66
|
+
filePath: string;
|
|
67
|
+
scopeId: string;
|
|
68
|
+
workspacePath: string;
|
|
69
|
+
tenantId: string;
|
|
70
|
+
organizationId: string;
|
|
71
|
+
};
|
|
72
|
+
fileName: string;
|
|
73
|
+
mimeType: string;
|
|
74
|
+
}>;
|
|
75
|
+
openArtifact(actor: SpaceActor, itemId: string): Promise<{
|
|
76
|
+
artifact: import("@xpert-ai/plugin-sdk").ArtifactRecord;
|
|
77
|
+
version: import("@xpert-ai/plugin-sdk").ArtifactVersionRecord;
|
|
78
|
+
}>;
|
|
79
|
+
}
|
package/dist/files.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { BadRequestException, ConflictException, Inject, Injectable } from '@nestjs/common';
|
|
14
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
15
|
+
import { ArtifactsRuntimeCapability, WorkspaceFilesRuntimeCapability, XPERT_AGENT_MIDDLEWARE_RUNTIME_TOKEN } from '@xpert-ai/plugin-sdk';
|
|
16
|
+
import { Repository } from 'typeorm';
|
|
17
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
18
|
+
import { SpaceAccess } from './access.js';
|
|
19
|
+
import { Space, SpaceItem, SpaceFileVersion } from './entities.js';
|
|
20
|
+
import { SpacePages } from './pages.js';
|
|
21
|
+
import { SpaceDocuments, contentHash } from './documents.js';
|
|
22
|
+
import { SpaceKnowledge } from './knowledge.js';
|
|
23
|
+
import { SpacesService } from './spaces.js';
|
|
24
|
+
import { denied, PLUGIN, scoped } from './domain.js';
|
|
25
|
+
let SpaceFiles = class SpaceFiles {
|
|
26
|
+
access;
|
|
27
|
+
pages;
|
|
28
|
+
spaces;
|
|
29
|
+
documents;
|
|
30
|
+
knowledge;
|
|
31
|
+
versions;
|
|
32
|
+
runtime;
|
|
33
|
+
constructor(access, pages, spaces, documents, knowledge, versions, runtime) {
|
|
34
|
+
this.access = access;
|
|
35
|
+
this.pages = pages;
|
|
36
|
+
this.spaces = spaces;
|
|
37
|
+
this.documents = documents;
|
|
38
|
+
this.knowledge = knowledge;
|
|
39
|
+
this.versions = versions;
|
|
40
|
+
this.runtime = runtime;
|
|
41
|
+
}
|
|
42
|
+
async put(actor, itemId, buffer, fileName, mimeType, revisionId = null, updateCurrent = true) {
|
|
43
|
+
const access = await this.access.resource(actor, itemId, 'write');
|
|
44
|
+
if (!access.space.projectId || !access.item || buffer.byteLength > 25 * 1024 * 1024)
|
|
45
|
+
throw denied();
|
|
46
|
+
const safeName = fileName.replace(/[\\/\u0000-\u001f]/gu, '_').replace(/^\.+/u, '_').slice(0, 180) || 'file';
|
|
47
|
+
const versionId = randomUUID();
|
|
48
|
+
// Space is a Project extension; current files and snapshots share its volume and ACL.
|
|
49
|
+
const folder = `.space-history/${itemId}/${versionId}`;
|
|
50
|
+
const scope = { ...scoped(actor), userId: actor.userId, catalog: 'projects',
|
|
51
|
+
projectId: access.space.projectId, scopeId: access.space.projectId };
|
|
52
|
+
const files = this.runtime.createScopedApi(scope).capabilities?.get(WorkspaceFilesRuntimeCapability);
|
|
53
|
+
if (!files)
|
|
54
|
+
throw new Error('spaces.files_unavailable');
|
|
55
|
+
const uploaded = await files.uploadBuffer({ ...scope, buffer, originalName: safeName, fileName: safeName, folder, mimeType });
|
|
56
|
+
const reference = { ...scope, source: 'platform.workspace.files',
|
|
57
|
+
filePath: uploaded.filePath, workspacePath: uploaded.filePath, originalName: safeName, mimeType, size: buffer.byteLength };
|
|
58
|
+
const version = await this.versions.save(this.versions.create({ ...scoped(actor), id: versionId, itemId,
|
|
59
|
+
createdById: actor.userId, reference, revisionId, sha256: createHash('sha256').update(buffer).digest('hex') }));
|
|
60
|
+
if (updateCurrent) {
|
|
61
|
+
await this.documents.write(actor, itemId, buffer);
|
|
62
|
+
await this.access.items.update({ ...scoped(actor), id: itemId }, { file: reference, currentFileVersionId: version.id });
|
|
63
|
+
await this.knowledge.sync(actor, access.space, access.item);
|
|
64
|
+
}
|
|
65
|
+
return version;
|
|
66
|
+
}
|
|
67
|
+
/** Opaque file protocol. Editor plugins own parsing; this resource owns snapshots and CAS. */
|
|
68
|
+
async editorRead(actor, itemId) {
|
|
69
|
+
const { item, space } = await this.access.resource(actor, itemId, 'read');
|
|
70
|
+
if (!item?.editorKey || !item.currentFileVersionId)
|
|
71
|
+
throw denied();
|
|
72
|
+
const disk = await this.documents.read(actor, space, item);
|
|
73
|
+
if (!disk || disk.sha256 !== item.fileHash)
|
|
74
|
+
throw new ConflictException('spaces.version_conflict');
|
|
75
|
+
return { buffer: disk.buffer, revision: this.editorRevision(item) };
|
|
76
|
+
}
|
|
77
|
+
editorRevision(item) {
|
|
78
|
+
// Metadata changes (move/trash/restore) invalidate a previously opened file as well.
|
|
79
|
+
return `${item.version}:${item.currentFileVersionId}:${item.fileHash}`;
|
|
80
|
+
}
|
|
81
|
+
async editorWrite(actor, itemId, expectedRevision, buffer) {
|
|
82
|
+
const access = await this.access.resource(actor, itemId, 'write');
|
|
83
|
+
const result = await this.access.items.manager.transaction(async (manager) => {
|
|
84
|
+
await manager.getRepository(Space).findOneOrFail({ where: { ...scoped(actor), id: access.space.id }, lock: { mode: 'pessimistic_write' } });
|
|
85
|
+
const repo = manager.getRepository(SpaceItem);
|
|
86
|
+
const item = await repo.findOneOrFail({ where: { ...scoped(actor), id: itemId }, lock: { mode: 'pessimistic_write' } });
|
|
87
|
+
await this.access.resource(actor, itemId, 'write');
|
|
88
|
+
if (!item.editorKey || !item.currentFileVersionId || !item.filePath || item.deleted)
|
|
89
|
+
throw denied();
|
|
90
|
+
if (this.editorRevision(item) !== expectedRevision)
|
|
91
|
+
throw new ConflictException('spaces.version_conflict');
|
|
92
|
+
const disk = await this.documents.read(actor, access.space, item);
|
|
93
|
+
if (!disk || disk.sha256 !== item.fileHash)
|
|
94
|
+
throw new ConflictException('spaces.version_conflict');
|
|
95
|
+
const version = await this.put(actor, itemId, buffer, item.filePath.split('/').pop() ?? item.title, item.file?.mimeType ?? 'application/octet-stream', null, false);
|
|
96
|
+
// ProjectFiles performs a second atomic hash check against edits made outside this resource.
|
|
97
|
+
await this.documents.api().write({ ...this.documents.scope(actor, access.space), path: item.filePath,
|
|
98
|
+
buffer, expectedHash: item.fileHash });
|
|
99
|
+
const updated = { ...item, currentFileVersionId: version.id, file: version.reference,
|
|
100
|
+
fileHash: contentHash(buffer), version: item.version + 1, syncStatus: 'ready', syncError: null };
|
|
101
|
+
await repo.update(item.id, { currentFileVersionId: updated.currentFileVersionId, file: updated.file,
|
|
102
|
+
fileHash: updated.fileHash, version: updated.version, syncStatus: updated.syncStatus, syncError: null });
|
|
103
|
+
return { revision: this.editorRevision(updated) };
|
|
104
|
+
});
|
|
105
|
+
await this.knowledge.sync(actor, access.space, await this.access.items.findOneByOrFail({ ...scoped(actor), id: itemId }));
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
async readBytes(actor, versionId) {
|
|
109
|
+
const version = await this.get(actor, versionId);
|
|
110
|
+
const files = this.runtime.createScopedApi(actor).capabilities?.get(WorkspaceFilesRuntimeCapability);
|
|
111
|
+
if (!files)
|
|
112
|
+
throw new Error('spaces.files_unavailable');
|
|
113
|
+
return (await files.readBuffer(version.reference)).buffer;
|
|
114
|
+
}
|
|
115
|
+
async upload(actor, spaceId, parentId, fileName, mimeType, buffer) {
|
|
116
|
+
await this.access.resource(actor, parentId ?? spaceId, 'write');
|
|
117
|
+
if (buffer.byteLength > 25 * 1024 * 1024)
|
|
118
|
+
throw new Error('spaces.file_too_large');
|
|
119
|
+
const item = await this.spaces.createItem(actor, { spaceId, parentId, title: fileName.slice(0, 200), kind: 'file' });
|
|
120
|
+
try {
|
|
121
|
+
return { item, version: await this.put(actor, item.id, buffer, fileName, mimeType) };
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
await this.access.items.update({ ...scoped(actor), id: item.id }, { deleted: true });
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async get(actor, versionId) {
|
|
129
|
+
const version = await this.versions.findOneByOrFail({ ...scoped(actor), id: versionId });
|
|
130
|
+
await this.access.resource(actor, version.itemId, 'read');
|
|
131
|
+
return version;
|
|
132
|
+
}
|
|
133
|
+
async list(actor, itemId) {
|
|
134
|
+
await this.access.resource(actor, itemId, 'read');
|
|
135
|
+
return this.versions.find({ where: { ...scoped(actor), itemId }, order: { createdAt: 'DESC' } });
|
|
136
|
+
}
|
|
137
|
+
async exportPage(actor, itemId) {
|
|
138
|
+
const revision = await this.pages.checkpoint(actor, itemId, 'Export');
|
|
139
|
+
const access = await this.access.resource(actor, itemId, 'write');
|
|
140
|
+
const file = await this.put(actor, itemId, Buffer.from(revision.markdown || '\n', 'utf8'), `${access.item?.title ?? 'Page'}.md`, 'text/markdown', revision.id);
|
|
141
|
+
const artifacts = this.runtime.createScopedApi(actor).capabilities?.get(ArtifactsRuntimeCapability);
|
|
142
|
+
if (!artifacts)
|
|
143
|
+
throw new Error('spaces.artifacts_unavailable');
|
|
144
|
+
const artifact = await artifacts.createArtifact({ source: { pluginName: PLUGIN, resourceType: 'space-item', resourceId: itemId },
|
|
145
|
+
authorizationProviderKey: 'spaces', scope: { projectId: access.space.projectId, userId: null }, kind: 'markdown', title: access.item?.title });
|
|
146
|
+
const version = await artifacts.ensureArtifactVersion({ artifactId: artifact.id, idempotencyKey: `revision:${revision.id}`,
|
|
147
|
+
workspaceFileRef: file.reference, mimeType: 'text/markdown', fileName: file.reference.originalName, sha256: file.sha256,
|
|
148
|
+
sourceVersionId: revision.id });
|
|
149
|
+
const link = await artifacts.createArtifactLink({ artifactId: artifact.id, artifactVersionId: version.version.id,
|
|
150
|
+
versionMode: 'version', access: { mode: 'organization_all' }, presentation: { disposition: 'attachment', allowDownload: true } });
|
|
151
|
+
return { fileVersionId: file.id, revisionId: revision.id, sequence: revision.sequence, artifactId: artifact.id, versionId: version.version.id, url: link.publicUrl };
|
|
152
|
+
}
|
|
153
|
+
async collect(actor, spaceId, parentId, artifactId, versionId) {
|
|
154
|
+
await this.access.resource(actor, parentId ?? spaceId, 'write');
|
|
155
|
+
const { artifact, version, buffer } = await this.artifactBytes(actor, artifactId, versionId);
|
|
156
|
+
const item = await this.spaces.createItem(actor, { spaceId, parentId, title: version.fileName ?? artifact.title ?? 'Artifact', kind: 'artifact', artifactId, artifactVersionId: versionId });
|
|
157
|
+
try {
|
|
158
|
+
await this.put(actor, item.id, buffer, version.fileName ?? item.title, version.mimeType ?? 'application/octet-stream');
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
await this.access.items.update(item.id, { deleted: true });
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
return item;
|
|
165
|
+
}
|
|
166
|
+
async artifactBytes(actor, artifactId, versionId) {
|
|
167
|
+
const artifacts = this.runtime.createScopedApi(actor).capabilities?.get(ArtifactsRuntimeCapability);
|
|
168
|
+
if (!artifacts)
|
|
169
|
+
throw new Error('spaces.artifacts_unavailable');
|
|
170
|
+
const artifact = await artifacts.getArtifact(artifactId);
|
|
171
|
+
const version = (await artifacts.listArtifactVersions({ artifactId })).find((version) => version.id === versionId);
|
|
172
|
+
if (!version)
|
|
173
|
+
throw denied();
|
|
174
|
+
if (!version.workspaceFileRef)
|
|
175
|
+
throw new BadRequestException('spaces.artifact_file_unavailable');
|
|
176
|
+
// Collection can read another authorized project; its own ACL is checked by both
|
|
177
|
+
// Artifacts and WorkspaceFiles. The destination Project must not replace that scope.
|
|
178
|
+
const files = this.runtime.createScopedApi({ ...scoped(actor), userId: actor.userId }).capabilities?.get(WorkspaceFilesRuntimeCapability);
|
|
179
|
+
if (!files)
|
|
180
|
+
throw new Error('spaces.files_unavailable');
|
|
181
|
+
const bytes = await files.readBuffer(version.workspaceFileRef);
|
|
182
|
+
return { artifact, version, buffer: bytes.buffer };
|
|
183
|
+
}
|
|
184
|
+
async available(actor, page) {
|
|
185
|
+
await this.access.project(actor);
|
|
186
|
+
const api = this.runtime.createScopedApi(actor).capabilities?.require(ArtifactsRuntimeCapability);
|
|
187
|
+
if (!api)
|
|
188
|
+
throw new Error('spaces.artifacts_unavailable');
|
|
189
|
+
const result = await api.listArtifacts({ page, pageSize: 40 });
|
|
190
|
+
return { ...result, items: await Promise.all(result.items.map(async (artifact) => ({
|
|
191
|
+
id: artifact.id, title: artifact.title, versions: (await api.listArtifactVersions({ artifactId: artifact.id })).filter((version) => version.workspaceFileRef)
|
|
192
|
+
}))) };
|
|
193
|
+
}
|
|
194
|
+
async current(actor, itemId) {
|
|
195
|
+
const { item, space } = await this.access.resource(actor, itemId, 'read');
|
|
196
|
+
if (!space.projectId || !item?.filePath || item.kind === 'folder')
|
|
197
|
+
throw denied();
|
|
198
|
+
return { reference: { ...scoped(actor), userId: actor.userId, projectId: space.projectId,
|
|
199
|
+
catalog: 'projects', source: 'platform.workspace.files', filePath: item.filePath,
|
|
200
|
+
scopeId: space.projectId, workspacePath: item.filePath }, fileName: item.filePath.split('/').pop() ?? item.title,
|
|
201
|
+
mimeType: item.kind === 'page' ? 'text/markdown' : item.file?.mimeType ?? 'application/octet-stream' };
|
|
202
|
+
}
|
|
203
|
+
async openArtifact(actor, itemId) {
|
|
204
|
+
const access = await this.access.resource(actor, itemId, 'read');
|
|
205
|
+
if (!access.item?.artifactId)
|
|
206
|
+
throw denied();
|
|
207
|
+
// A collection is a reference. It never expands access to the original Artifact.
|
|
208
|
+
const artifacts = this.runtime.createScopedApi(actor).capabilities?.get(ArtifactsRuntimeCapability);
|
|
209
|
+
if (!artifacts)
|
|
210
|
+
throw new Error('spaces.artifacts_unavailable');
|
|
211
|
+
const artifact = await artifacts.getArtifact(access.item.artifactId);
|
|
212
|
+
const versions = await artifacts.listArtifactVersions({ artifactId: artifact.id });
|
|
213
|
+
const version = versions.find((entry) => entry.id === access.item?.artifactVersionId);
|
|
214
|
+
if (!version)
|
|
215
|
+
throw denied();
|
|
216
|
+
return { artifact, version };
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
SpaceFiles = __decorate([
|
|
220
|
+
Injectable(),
|
|
221
|
+
__param(5, InjectRepository(SpaceFileVersion)),
|
|
222
|
+
__param(6, Inject(XPERT_AGENT_MIDDLEWARE_RUNTIME_TOKEN)),
|
|
223
|
+
__metadata("design:paramtypes", [SpaceAccess, SpacePages, SpacesService,
|
|
224
|
+
SpaceDocuments,
|
|
225
|
+
SpaceKnowledge,
|
|
226
|
+
Repository, Object])
|
|
227
|
+
], SpaceFiles);
|
|
228
|
+
export { SpaceFiles };
|