@eum-agent/file-explorer 0.0.0-stage → 1.0.1
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/index.js +1387 -0
- package/dist/schema.js +215 -0
- package/package.json +51 -3
- package/ui/ui.js +8563 -0
- package/README.md +0 -3
package/dist/schema.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/schema.ts
|
|
3
|
+
/** The provider's sole wire declarations; browser and server infer types from these schemas. */
|
|
4
|
+
/** A literal safe host filename or explicit identity segment. */
|
|
5
|
+
const SegmentSchema = z.string().min(1).max(1024).refine((value) => value !== "." && value !== ".." && [...value].every((character) => character !== "/" && character !== "\\" && character.charCodeAt(0) > 31 && character.charCodeAt(0) !== 127), "Invalid segment");
|
|
6
|
+
/** Existing workspace/session identifiers are nonempty independent segments. */
|
|
7
|
+
const IdentitySchema = SegmentSchema;
|
|
8
|
+
/** Canonical lower-case Gateway UUID. */
|
|
9
|
+
const GatewayIdSchema = z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/);
|
|
10
|
+
/** Stable root/file IDs retain the full lowercase SHA-256 digest. */
|
|
11
|
+
const DigestSchema = z.string().regex(/^[0-9a-f]{64}$/);
|
|
12
|
+
/** A literal relative path; the root has an empty path. */
|
|
13
|
+
const PathSchema = z.string().max(4096).refine((value) => value === "" || value.split("/").every((segment) => SegmentSchema.safeParse(segment).success), "Invalid relative path");
|
|
14
|
+
/** Explicit workspace root selection; never defaults to a current chat or home. */
|
|
15
|
+
const FileTargetSchema = z.object({
|
|
16
|
+
workspaceId: IdentitySchema,
|
|
17
|
+
rootId: DigestSchema,
|
|
18
|
+
path: PathSchema
|
|
19
|
+
}).strict();
|
|
20
|
+
/** Root discovery always names an existing workspace. */
|
|
21
|
+
const RootsParamsSchema = z.object({ workspaceId: IdentitySchema }).strict();
|
|
22
|
+
/** Stat always names one explicit current root-relative selection. */
|
|
23
|
+
const StatParamsSchema = z.object({ target: FileTargetSchema }).strict();
|
|
24
|
+
/** Display metadata for one current filesystem object. */
|
|
25
|
+
const FileEntrySchema = z.object({
|
|
26
|
+
name: z.string(),
|
|
27
|
+
path: PathSchema,
|
|
28
|
+
kind: z.enum([
|
|
29
|
+
"file",
|
|
30
|
+
"directory",
|
|
31
|
+
"symlink",
|
|
32
|
+
"other"
|
|
33
|
+
]),
|
|
34
|
+
size: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
35
|
+
modifiedAt: z.number().finite(),
|
|
36
|
+
fingerprint: DigestSchema,
|
|
37
|
+
uri: z.string(),
|
|
38
|
+
referenceKind: z.literal("current"),
|
|
39
|
+
mutable: z.boolean()
|
|
40
|
+
}).strict();
|
|
41
|
+
/** Validated bounded business context; no arbitrary error object crosses the wire. */
|
|
42
|
+
const ErrorDetailsSchema = z.object({
|
|
43
|
+
existing: FileEntrySchema.optional(),
|
|
44
|
+
destinationCommitted: z.boolean().optional(),
|
|
45
|
+
sourceRetained: z.boolean().optional(),
|
|
46
|
+
retainedPaths: z.array(PathSchema).max(1e4).optional(),
|
|
47
|
+
contentLength: z.number().int().nonnegative().optional(),
|
|
48
|
+
operationId: z.uuid().optional()
|
|
49
|
+
}).strict();
|
|
50
|
+
/** Safe business error result. */
|
|
51
|
+
const AccessErrorSchema = z.object({
|
|
52
|
+
code: z.string().max(64),
|
|
53
|
+
message: z.string().max(256),
|
|
54
|
+
details: ErrorDetailsSchema.optional()
|
|
55
|
+
}).strict();
|
|
56
|
+
/** A retained execution fact or an explicit unknown query result. */
|
|
57
|
+
const OperationResultSchema = z.object({
|
|
58
|
+
operationId: z.uuid(),
|
|
59
|
+
instanceId: GatewayIdSchema,
|
|
60
|
+
status: z.enum([
|
|
61
|
+
"pending",
|
|
62
|
+
"succeeded",
|
|
63
|
+
"failed",
|
|
64
|
+
"unknown"
|
|
65
|
+
]),
|
|
66
|
+
entry: FileEntrySchema.optional(),
|
|
67
|
+
error: AccessErrorSchema.optional()
|
|
68
|
+
}).strict();
|
|
69
|
+
/** Untrusted upload source metadata, not an authorization or server fingerprint. */
|
|
70
|
+
const FileSourceObservationSchema = z.object({
|
|
71
|
+
clientName: SegmentSchema,
|
|
72
|
+
size: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
73
|
+
modifiedAt: z.number().min(-864e13).max(864e13)
|
|
74
|
+
}).strict();
|
|
75
|
+
/** File resource business parameters; PUT additionally requires operationId and source. */
|
|
76
|
+
const FileBytesParamsSchema = z.object({
|
|
77
|
+
target: FileTargetSchema,
|
|
78
|
+
expectedFingerprint: DigestSchema.optional(),
|
|
79
|
+
operationId: z.uuid().optional(),
|
|
80
|
+
source: FileSourceObservationSchema.optional(),
|
|
81
|
+
replace: z.boolean().optional(),
|
|
82
|
+
targetFingerprint: DigestSchema.optional()
|
|
83
|
+
}).strict();
|
|
84
|
+
/** List RPC selection and opaque next-page cursor. */
|
|
85
|
+
const ListParamsSchema = z.object({
|
|
86
|
+
target: FileTargetSchema,
|
|
87
|
+
cursor: z.string().max(128).optional()
|
|
88
|
+
}).strict();
|
|
89
|
+
/** One immutable listing generation, with current external-change diagnostics. */
|
|
90
|
+
const ListResultSchema = z.object({
|
|
91
|
+
entries: z.array(FileEntrySchema).max(200),
|
|
92
|
+
nextCursor: z.string().optional(),
|
|
93
|
+
fingerprint: DigestSchema,
|
|
94
|
+
listingId: z.uuid(),
|
|
95
|
+
snapshotAt: z.number(),
|
|
96
|
+
stale: z.boolean()
|
|
97
|
+
}).strict();
|
|
98
|
+
/** Public root display projection. */
|
|
99
|
+
const RootsResultSchema = z.object({ roots: z.array(z.object({
|
|
100
|
+
id: DigestSchema,
|
|
101
|
+
kind: z.enum(["own", "directory"]),
|
|
102
|
+
name: z.string(),
|
|
103
|
+
path: z.string()
|
|
104
|
+
}).strict()) }).strict();
|
|
105
|
+
/** A complete current-file observation and confirmed host display path. */
|
|
106
|
+
const StatResultSchema = z.object({
|
|
107
|
+
entry: FileEntrySchema,
|
|
108
|
+
realPath: z.string()
|
|
109
|
+
}).strict();
|
|
110
|
+
/** Explicit exclusive creation. */
|
|
111
|
+
const MkdirParamsSchema = z.object({
|
|
112
|
+
target: FileTargetSchema,
|
|
113
|
+
operationId: z.uuid()
|
|
114
|
+
}).strict();
|
|
115
|
+
/** Same-root rename with optional observed replacement. */
|
|
116
|
+
const RenameParamsSchema = z.object({
|
|
117
|
+
source: FileTargetSchema,
|
|
118
|
+
name: SegmentSchema,
|
|
119
|
+
sourceFingerprint: DigestSchema,
|
|
120
|
+
operationId: z.uuid(),
|
|
121
|
+
replace: z.boolean().optional(),
|
|
122
|
+
targetFingerprint: DigestSchema.optional()
|
|
123
|
+
}).strict();
|
|
124
|
+
/** Same-workspace move to an explicit final target path. */
|
|
125
|
+
const MoveParamsSchema = z.object({
|
|
126
|
+
source: FileTargetSchema,
|
|
127
|
+
destination: FileTargetSchema,
|
|
128
|
+
sourceFingerprint: DigestSchema,
|
|
129
|
+
operationId: z.uuid(),
|
|
130
|
+
replace: z.boolean().optional(),
|
|
131
|
+
targetFingerprint: DigestSchema.optional()
|
|
132
|
+
}).strict();
|
|
133
|
+
/** Removal requires the selected revision and an explicit recursive choice. */
|
|
134
|
+
const RemoveParamsSchema = z.object({
|
|
135
|
+
target: FileTargetSchema,
|
|
136
|
+
fingerprint: DigestSchema,
|
|
137
|
+
recursive: z.boolean(),
|
|
138
|
+
operationId: z.uuid()
|
|
139
|
+
}).strict();
|
|
140
|
+
/** Query retained facts without executing an operation. */
|
|
141
|
+
const OperationStatusParamsSchema = z.object({
|
|
142
|
+
instanceId: GatewayIdSchema,
|
|
143
|
+
operationId: z.uuid()
|
|
144
|
+
}).strict();
|
|
145
|
+
const encoded = (value) => new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
146
|
+
const budget = {
|
|
147
|
+
maxItemBytes: z.number().int().min(1).max(8388608).default(262144),
|
|
148
|
+
maxTotalBytes: z.number().int().min(1).max(8388608).default(8388608)
|
|
149
|
+
};
|
|
150
|
+
/** Rejects input and declared normalized response costs before any filesystem work. */
|
|
151
|
+
function admit(input, targets, memberBytes, totalBytes, ctx) {
|
|
152
|
+
if (encoded(input) * 2 > 8388608 || targets.length * memberBytes + 8192 > totalBytes || targets.some((target) => encoded(target) + 4096 > memberBytes)) ctx.addIssue({
|
|
153
|
+
code: "custom",
|
|
154
|
+
message: "File collection exceeds its byte budget"
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** Complete file observations with independent encoded response budgets. */
|
|
158
|
+
const StatsParamsSchema = z.object({
|
|
159
|
+
targets: z.array(FileTargetSchema).min(1).max(64),
|
|
160
|
+
...budget
|
|
161
|
+
}).strict().superRefine((p, ctx) => admit(p, p.targets, p.maxItemBytes, p.maxTotalBytes, ctx));
|
|
162
|
+
/** Retained operation queries never execute work, including unknown identities. */
|
|
163
|
+
const OperationStatusesParamsSchema = z.object({
|
|
164
|
+
instanceId: GatewayIdSchema,
|
|
165
|
+
operationIds: z.array(z.uuid()).min(1).max(64),
|
|
166
|
+
...budget
|
|
167
|
+
}).strict().superRefine((p, ctx) => admit(p, p.operationIds, p.maxItemBytes, p.maxTotalBytes, ctx));
|
|
168
|
+
/** Validates complete mutation members and refuses conflicting reuse before any member starts. */
|
|
169
|
+
function mutations(schema) {
|
|
170
|
+
return z.object({ items: z.array(schema).min(1).max(64) }).strict().superRefine((p, ctx) => {
|
|
171
|
+
if (encoded(p) * 2 > 8388608) ctx.addIssue({
|
|
172
|
+
code: "custom",
|
|
173
|
+
message: "File collection exceeds its byte budget"
|
|
174
|
+
});
|
|
175
|
+
const identities = /* @__PURE__ */ new Map();
|
|
176
|
+
for (const member of p.items) {
|
|
177
|
+
const input = JSON.stringify(member);
|
|
178
|
+
if (identities.has(member.operationId) && identities.get(member.operationId) !== input) ctx.addIssue({
|
|
179
|
+
code: "custom",
|
|
180
|
+
message: "Operation identity has conflicting members"
|
|
181
|
+
});
|
|
182
|
+
identities.set(member.operationId, input);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/** Each move retains its own complete operation identity and physical outcome. */
|
|
187
|
+
const MoveFilesParamsSchema = mutations(MoveParamsSchema);
|
|
188
|
+
/** Each removal retains its own selected fingerprint and recursive decision. */
|
|
189
|
+
const RemoveFilesParamsSchema = mutations(RemoveParamsSchema);
|
|
190
|
+
/** One notification lists all workspaces physically changed by this collection. */
|
|
191
|
+
const FilesChangedSchema = z.object({ workspaces: z.array(IdentitySchema).min(1).max(64) }).strict();
|
|
192
|
+
/** Complete successful members and bounded safe refusals retain their original order. */
|
|
193
|
+
function collectionResult(target, value) {
|
|
194
|
+
return z.object({ results: z.array(z.discriminatedUnion("ok", [z.object({
|
|
195
|
+
index: z.number().int().min(0).max(63),
|
|
196
|
+
target,
|
|
197
|
+
ok: z.literal(true),
|
|
198
|
+
value
|
|
199
|
+
}).strict(), z.object({
|
|
200
|
+
index: z.number().int().min(0).max(63),
|
|
201
|
+
target,
|
|
202
|
+
ok: z.literal(false),
|
|
203
|
+
error: AccessErrorSchema
|
|
204
|
+
}).strict()])).min(1).max(64) }).strict();
|
|
205
|
+
}
|
|
206
|
+
/** Ordered explicit file observations. */
|
|
207
|
+
const StatsResultSchema = collectionResult(FileTargetSchema, StatResultSchema);
|
|
208
|
+
/** Ordered retained operation facts. */
|
|
209
|
+
const OperationStatusesResultSchema = collectionResult(z.uuid(), OperationResultSchema);
|
|
210
|
+
/** Ordered move results keep complete immutable operation members as their safe target. */
|
|
211
|
+
const MoveFilesResultSchema = collectionResult(MoveParamsSchema, OperationResultSchema);
|
|
212
|
+
/** Ordered removal results keep complete immutable operation members as their safe target. */
|
|
213
|
+
const RemoveFilesResultSchema = collectionResult(RemoveParamsSchema, OperationResultSchema);
|
|
214
|
+
//#endregion
|
|
215
|
+
export { AccessErrorSchema, DigestSchema, ErrorDetailsSchema, FileBytesParamsSchema, FileEntrySchema, FileSourceObservationSchema, FileTargetSchema, FilesChangedSchema, GatewayIdSchema, IdentitySchema, ListParamsSchema, ListResultSchema, MkdirParamsSchema, MoveFilesParamsSchema, MoveFilesResultSchema, MoveParamsSchema, OperationResultSchema, OperationStatusParamsSchema, OperationStatusesParamsSchema, OperationStatusesResultSchema, PathSchema, RemoveFilesParamsSchema, RemoveFilesResultSchema, RemoveParamsSchema, RenameParamsSchema, RootsParamsSchema, RootsResultSchema, SegmentSchema, StatParamsSchema, StatResultSchema, StatsParamsSchema, StatsResultSchema, collectionResult };
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eum-agent/file-explorer",
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
"displayName": {
|
|
4
|
+
"en-US": "Workspace Files",
|
|
5
|
+
"zh-CN": "工作空间文件"
|
|
6
|
+
},
|
|
7
|
+
"version": "1.0.1",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"description": "Sessionless workspace file browsing, transfers and private references",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./schema": {
|
|
16
|
+
"default": "./dist/schema.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"ui"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"zod": "^4.4.3"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@eum-agent/plugin": "1.0.1",
|
|
28
|
+
"@eum-agent/protocol": "1.0.0",
|
|
29
|
+
"react": "^19.2.0",
|
|
30
|
+
"react-dom": "^19.2.0",
|
|
31
|
+
"@types/react": "^19.2.18",
|
|
32
|
+
"@types/react-dom": "^19.2.5",
|
|
33
|
+
"vite": "^8.2.2",
|
|
34
|
+
"@eum-agent/infra-local": "1.0.1"
|
|
35
|
+
},
|
|
36
|
+
"yaoguang": {
|
|
37
|
+
"description": {
|
|
38
|
+
"en-US": "Sessionless workspace file browsing, transfers and private references",
|
|
39
|
+
"zh-CN": "独立的工作区文件浏览、传输与私有引用"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@eum-agent/file-references": "1.0.1"
|
|
43
|
+
},
|
|
44
|
+
"kind": "extension",
|
|
45
|
+
"foundationDependencies": {
|
|
46
|
+
"@eum-agent/infra-local": "1.0.1"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsdown && vite build",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.ui.json"
|
|
53
|
+
}
|
|
6
54
|
}
|