@deepseek-ai/dsh-api-workspace-files 0.1.6-alpha.2 → 0.1.7-alpha.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.
@@ -140,8 +140,6 @@ let WorkspaceFiles = (() => {
140
140
  let _instanceExtraInitializers = [];
141
141
  let _read_decorators;
142
142
  let _readBytes_decorators;
143
- let _readAll_decorators;
144
- let _readRelated_decorators;
145
143
  let _stat_decorators;
146
144
  let _list_decorators;
147
145
  let _changes_decorators;
@@ -150,15 +148,11 @@ let WorkspaceFiles = (() => {
150
148
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
151
149
  _read_decorators = [Remote];
152
150
  _readBytes_decorators = [Remote];
153
- _readAll_decorators = [Remote];
154
- _readRelated_decorators = [Remote];
155
151
  _stat_decorators = [Remote];
156
152
  _list_decorators = [Remote];
157
153
  _changes_decorators = [Remote({ mode: 'stream' })];
158
154
  __esDecorate(this, null, _read_decorators, { kind: "method", name: "read", static: false, private: false, access: { has: obj => "read" in obj, get: obj => obj.read }, metadata: _metadata }, null, _instanceExtraInitializers);
159
155
  __esDecorate(this, null, _readBytes_decorators, { kind: "method", name: "readBytes", static: false, private: false, access: { has: obj => "readBytes" in obj, get: obj => obj.readBytes }, metadata: _metadata }, null, _instanceExtraInitializers);
160
- __esDecorate(this, null, _readAll_decorators, { kind: "method", name: "readAll", static: false, private: false, access: { has: obj => "readAll" in obj, get: obj => obj.readAll }, metadata: _metadata }, null, _instanceExtraInitializers);
161
- __esDecorate(this, null, _readRelated_decorators, { kind: "method", name: "readRelated", static: false, private: false, access: { has: obj => "readRelated" in obj, get: obj => obj.readRelated }, metadata: _metadata }, null, _instanceExtraInitializers);
162
156
  __esDecorate(this, null, _stat_decorators, { kind: "method", name: "stat", static: false, private: false, access: { has: obj => "stat" in obj, get: obj => obj.stat }, metadata: _metadata }, null, _instanceExtraInitializers);
163
157
  __esDecorate(this, null, _list_decorators, { kind: "method", name: "list", static: false, private: false, access: { has: obj => "list" in obj, get: obj => obj.list }, metadata: _metadata }, null, _instanceExtraInitializers);
164
158
  __esDecorate(this, null, _changes_decorators, { kind: "method", name: "changes", static: false, private: false, access: { has: obj => "changes" in obj, get: obj => obj.changes }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -221,30 +215,23 @@ let WorkspaceFiles = (() => {
221
215
  return { ...this.statOf(target, info), offset, text: page.text, lines: page.lines, eof: page.eof };
222
216
  }
223
217
  /**
224
- * Read one byte window of a regular file readable by the filesystem backend: raw
225
- * bytes, no text decoding and no binary rejection.
218
+ * Read a complete regular file or one byte range without text decoding.
226
219
  * @param workspaceFileScope - header-derived workspace root for the Session identity on the wire.
227
- * @param path - absolute path or path relative to the workspace root; files outside it are allowed.
228
- * @param range - the byte window; omitted fields take the window defaults.
229
- * @param signal - caller cancellation.
230
- * @returns the window in base64, the file's version and size at the stat before it, and whether it reaches the last byte.
231
- */
232
- async readBytes(workspaceFileScope, path, range, signal) {
233
- const { offset, length } = this.resolveWindow(range, path);
234
- const { target, info } = await this.locateFile(workspaceFileScope, path, signal);
235
- const data = await this.ctx.fs.readByteRange(target, { offset, length }, signal);
236
- const eof = info.size === undefined ? data.length < length : offset + data.length >= info.size;
237
- return { ...this.statOf(target, info), offset, data: Buffer.from(data).toString('base64'), eof };
238
- }
239
- /**
240
- * Read a complete regular file as bytes, subject to the configured full-file cap.
241
- * @param workspaceFileScope - header-derived workspace root for the Session identity on the wire.
242
- * @param path - absolute or workspace-relative file path.
220
+ * @param path - target path, absolute or workspace-relative; relative to the base file's directory when provided.
221
+ * @param options - optional base file and range; without a range the complete-file cap applies.
243
222
  * @param signal - caller cancellation.
244
- * @returns one complete base64 window with offset zero and eof true; oversized files fail with too-large.
223
+ * @returns native bytes with the file's version and size at the preceding stat, byte offset, and EOF marker.
245
224
  */
246
- async readAll(workspaceFileScope, path, signal) {
247
- const { target, info } = await this.locateFile(workspaceFileScope, path, signal);
225
+ async readBytes(workspaceFileScope, path, options, signal) {
226
+ const window = options.range === undefined ? undefined : this.resolveWindow(options.range, path);
227
+ const resolved = options.baseFile === undefined ? path : await this.relativePath(workspaceFileScope, options.baseFile, path, signal);
228
+ const { target, info } = await this.locateFile(workspaceFileScope, resolved, signal);
229
+ if (window !== undefined) {
230
+ const { offset, length } = window;
231
+ const data = await this.ctx.fs.readByteRange(target, window, signal);
232
+ const eof = info.size === undefined ? data.length < length : offset + data.length >= info.size;
233
+ return { ...this.statOf(target, info), offset, data, eof };
234
+ }
248
235
  const limit = this.config.maxFileBytes;
249
236
  const data = await this.ctx.fs.readBytes(target, signal, limit).catch((cause) => {
250
237
  if (typeof cause === 'object' && cause !== null && 'code' in cause && cause.code === 'FS_TOO_LARGE') {
@@ -252,25 +239,7 @@ let WorkspaceFiles = (() => {
252
239
  }
253
240
  throw cause;
254
241
  });
255
- return { ...this.statOf(target, info), offset: 0, data: Buffer.from(data).toString('base64'), eof: true };
256
- }
257
- /**
258
- * Read a complete file relative to another file's directory, including outside the workspace.
259
- * @param workspaceFileScope - header-derived workspace root for the Session identity on the wire.
260
- * @param path - base file, absolute or workspace-relative.
261
- * @param relativePath - relative filesystem path, not a URL or absolute path.
262
- * @param signal - caller cancellation.
263
- * @returns the complete related file using the ordinary file-size and access checks.
264
- */
265
- async readRelated(workspaceFileScope, path, relativePath, signal) {
266
- const relative = relativePath.replace(/\\/g, '/');
267
- if (relative.length === 0 || relative.startsWith('/') || /^[a-z][a-z\d+.-]*:/iu.test(relative) || relative.includes(NUL)) {
268
- throw new RemoteError('gateway/bad-request', 'relativePath must be a relative filesystem path', {});
269
- }
270
- const { target } = await this.locateFile(workspaceFileScope, path, signal);
271
- const absolute = this.ctx.fs.processPath(target);
272
- const paths = absolute.startsWith('/') ? posix : win32;
273
- return this.readAll(workspaceFileScope, paths.resolve(paths.dirname(absolute), relative), signal);
242
+ return { ...this.statOf(target, info), offset: 0, data, eof: true };
274
243
  }
275
244
  /**
276
245
  * Report one regular file's identity, version, and size without its content.
@@ -304,16 +273,26 @@ let WorkspaceFiles = (() => {
304
273
  };
305
274
  }
306
275
  /**
307
- * Stream every `fs/observed` observation of a file inside the Session's
308
- * workspace. Only instrumented filesystem operations report here; the OS is
309
- * not watched.
276
+ * Watch one file or a directory's direct entries in the Session's filesystem.
277
+ * Files use the backend's read authority; directories remain workspace-scoped.
310
278
  * @param workspaceFileScope - header-derived workspace root for the Session identity on the wire.
279
+ * @param path - target path; the Host determines its type and confines directories to the workspace.
311
280
  * @param signal - generation cancellation.
312
- * @returns `ready` once the Host observation queue is active and the workspace
313
- * root is resolved, then queued and live observations in emission order.
281
+ * @returns `ready` once the target watch is active, then current metadata for queued and live invalidations.
282
+ * @throws RemoteError when watching is unavailable or a directory is outside the workspace.
314
283
  */
315
- changes(workspaceFileScope, signal) {
316
- return this.feed.follow(workspaceFileScope.workspaceRoot, signal);
284
+ changes(workspaceFileScope, path, signal) {
285
+ return this.feed.follow(workspaceFileScope.workspaceRoot, path, signal);
286
+ }
287
+ async relativePath(scope, baseFile, path, signal) {
288
+ const relative = path.replace(/\\/g, '/');
289
+ if (relative.length === 0 || relative.startsWith('/') || /^[a-z][a-z\d+.-]*:/iu.test(relative) || relative.includes(NUL)) {
290
+ throw new RemoteError('gateway/bad-request', 'path must be relative when baseFile is provided', {});
291
+ }
292
+ const { target } = await this.locateFile(scope, baseFile, signal);
293
+ const absolute = this.ctx.fs.processPath(target);
294
+ const paths = absolute.startsWith('/') ? posix : win32;
295
+ return paths.resolve(paths.dirname(absolute), relative);
317
296
  }
318
297
  /** Apply the page defaults and caps here, so the request never carries them implicitly. */
319
298
  resolvePage(range) {
@@ -60,15 +60,22 @@ export interface WorkspaceByteRange {
60
60
  /** Largest number of bytes in the window. Defaults to, and may not exceed, the configured `maxBytes`. */
61
61
  readonly length?: number;
62
62
  }
63
+ /** Target resolution and optional byte range for one binary file read. */
64
+ export interface WorkspaceByteReadOptions {
65
+ /** Byte window; omit to read the complete file under the configured `maxFileBytes` cap. */
66
+ readonly range?: WorkspaceByteRange;
67
+ /** Base file, absolute or workspace-relative; resolve the relative target from its directory. */
68
+ readonly baseFile?: string;
69
+ }
63
70
  /**
64
71
  * One byte window of a workspace file as a Client reads it: raw bytes, no text
65
72
  * decoding and no binary rejection. `bytes` is the complete file's size.
66
73
  */
67
- export interface WorkspaceFileBytes extends WorkspaceFileStat {
74
+ export interface WorkspaceFileBytes<Data extends Uint8Array = Uint8Array> extends WorkspaceFileStat {
68
75
  /** First byte of the window, as requested. */
69
76
  readonly offset: number;
70
- /** The window's bytes in base64; empty when `offset` lies at or past the file's end. */
71
- readonly data: string;
77
+ /** Native file bytes; empty at or past EOF. */
78
+ readonly data: Data;
72
79
  /** Whether the window includes the file's last byte. */
73
80
  readonly eof: boolean;
74
81
  }
@@ -103,25 +110,25 @@ export interface WorkspaceDirectoryListing {
103
110
  readonly truncated: boolean;
104
111
  }
105
112
  /**
106
- * One observation of a workspace file made by an instrumented filesystem
107
- * operation. Frames report observations, not deltas: a consumer already
108
- * holding `version` learns nothing new from the frame and can ignore it.
113
+ * Current target metadata after a filesystem invalidation. File consumers may
114
+ * ignore an already known version; directory consumers relist on every frame
115
+ * because a child's metadata can change without changing the directory version.
109
116
  */
110
117
  export type WorkspaceFileChange = {
111
- /** Absolute path of the observed file, in the same form as {@link WorkspaceFileStat.absolutePath}. */
118
+ /** Absolute target path, in the same form as {@link WorkspaceFileStat.absolutePath}. */
112
119
  readonly absolutePath: string;
113
- /** Opaque freshness token after the observed operation; never parsed. */
120
+ /** Opaque freshness token from the current stat; never parsed. */
114
121
  readonly version: string;
115
122
  } | {
116
- /** Absolute path of the observed file, in the same form as {@link WorkspaceFileStat.absolutePath}. */
123
+ /** Absolute target path, in the same form as {@link WorkspaceFileStat.absolutePath}. */
117
124
  readonly absolutePath: string;
118
- /** The file was observed to be gone. */
125
+ /** The target was observed to be gone. */
119
126
  readonly absent: true;
120
127
  };
121
128
  /**
122
129
  * One frame of a workspace file watch generation. `ready` confirms that the
123
- * Host is observing filesystem operations and has resolved the workspace
124
- * root; observations queued during that resolution follow as `change` frames.
130
+ * Host has initialized the target watcher; queued and live invalidations
131
+ * follow as `change` frames with current target metadata.
125
132
  */
126
133
  export type WorkspaceFileWatchFrame = {
127
134
  readonly kind: 'ready';
@@ -131,11 +138,15 @@ export type WorkspaceFileWatchFrame = {
131
138
  };
132
139
  declare module '@deepseek-ai/dsh-typert-protocol' {
133
140
  interface RemoteErrorDetailsMap {
141
+ /** The filesystem provider cannot initialize a watch for this target. */
142
+ 'workspace-file/watch-unsupported': {
143
+ readonly path: string;
144
+ };
134
145
  /** No entry exists at that path inside the workspace. */
135
146
  'workspace-file/not-found': {
136
147
  readonly path: string;
137
148
  };
138
- /** The directory listing path resolves outside the session's workspace root. */
149
+ /** The directory listing or watch path resolves outside the Session's workspace root. */
139
150
  'workspace-file/outside-workspace': {
140
151
  readonly path: string;
141
152
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-api-workspace-files",
3
3
  "description": "Workspace file service and Client resource provider: bounded reads, directory listing, and live metadata over the workspaceFiles Remote namespace",
4
- "version": "0.1.6-alpha.2",
4
+ "version": "0.1.7-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -49,23 +49,25 @@
49
49
  "license": "MIT",
50
50
  "dependencies": {
51
51
  "zod": "^4.4.3",
52
- "@deepseek-ai/dsh-deque": "^0.1.6-alpha.2",
53
- "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.2",
54
- "@deepseek-ai/schemastery": "^3.18.2"
52
+ "@deepseek-ai/dsh-deque": "^0.1.7-alpha.1",
53
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1",
54
+ "@deepseek-ai/schemastery": "^3.18.3"
55
55
  },
56
56
  "peerDependencies": {
57
- "@deepseek-ai/cordis": "^4.0.2"
57
+ "@deepseek-ai/cordis": "^4.0.3"
58
58
  },
59
59
  "devDependencies": {
60
- "@deepseek-ai/cordis": "^4.0.2",
61
- "@deepseek-ai/dsh-api-gateway": "^0.1.6-alpha.2",
62
- "@deepseek-ai/dsh-client-resources": "^0.1.6-alpha.2",
63
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
64
- "@deepseek-ai/dsh-fs": "^0.1.6-alpha.2",
65
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
66
- "@deepseek-ai/dsh-sandbox-policy": "^0.1.6-alpha.2",
67
- "@deepseek-ai/dsh-session-persistence": "^0.1.6-alpha.2",
68
- "@deepseek-ai/dsh-util-workspace-path": "^0.1.6-alpha.2"
60
+ "@deepseek-ai/cordis": "^4.0.3",
61
+ "@deepseek-ai/dsh-api-gateway": "^0.1.7-alpha.1",
62
+ "@deepseek-ai/dsh-client-connection": "^0.1.7-alpha.1",
63
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
64
+ "@deepseek-ai/dsh-client-resources": "^0.1.7-alpha.1",
65
+ "@deepseek-ai/dsh-fs": "^0.1.7-alpha.1",
66
+ "@deepseek-ai/dsh-remote-mock": "^0.1.7-alpha.1",
67
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.7-alpha.1",
68
+ "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
69
+ "@deepseek-ai/dsh-util-workspace-path": "^0.1.7-alpha.1",
70
+ "@deepseek-ai/dsh-session-persistence": "^0.1.7-alpha.1"
69
71
  },
70
72
  "files": [
71
73
  "lib/index.js",