@myagentroam/node 0.9.70 → 0.9.72

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.
@@ -11,7 +11,7 @@ const runtimeCredentialCapability = () => ({
11
11
  httpVersions: ['1.1', '2']
12
12
  });
13
13
  const workspaceDirectTransferCapability = () => ({
14
- apiVersion: 2,
14
+ apiVersion: 3,
15
15
  transport: true,
16
16
  upload: true,
17
17
  download: true,
package/dist/connector.js CHANGED
@@ -1401,7 +1401,6 @@ export class NodeConnector {
1401
1401
  const upload = await this.workspaceUploadService.createDirect(workspace, {
1402
1402
  path: descriptor.resource.path,
1403
1403
  size: descriptor.size,
1404
- sha256: descriptor.sha256,
1405
1404
  overwrite: descriptor.overwrite === true,
1406
1405
  ...(descriptor.composerAttachment === undefined
1407
1406
  ? {}
@@ -1410,7 +1409,7 @@ export class NodeConnector {
1410
1409
  return {
1411
1410
  offset: upload.receivedBytes,
1412
1411
  write: (bytes) => this.workspaceUploadService.writeDirect(upload.uploadId, bytes),
1413
- complete: (size, sha256) => this.workspaceUploadService.completeDirect(upload.uploadId, size, sha256),
1412
+ complete: (size) => this.workspaceUploadService.completeDirect(upload.uploadId, size),
1414
1413
  cancel: () => this.workspaceUploadService.cancel(upload.uploadId)
1415
1414
  };
1416
1415
  },
@@ -275,7 +275,6 @@ export class DirectTransferService {
275
275
  async channelMessage(transfer, data) {
276
276
  const descriptor = transfer.command.transfer;
277
277
  const expectedSize = transferredSize(descriptor);
278
- const expectedSha256 = transferredSha256(descriptor);
279
278
  if (descriptor.direction === 'DOWNLOAD') {
280
279
  if (typeof data !== 'string')
281
280
  throw new Error('DIRECT_FRAME_INVALID');
@@ -285,8 +284,7 @@ export class DirectTransferService {
285
284
  this.emit(transfer.command.directSessionId, {
286
285
  type: 'direct.complete',
287
286
  directSessionId: transfer.command.directSessionId,
288
- size: expectedSize,
289
- sha256: expectedSha256
287
+ size: expectedSize
290
288
  });
291
289
  await this.removeTransfer(transfer);
292
290
  return;
@@ -295,15 +293,14 @@ export class DirectTransferService {
295
293
  const control = directDataControlFrameSchema.parse(JSON.parse(data));
296
294
  if (control.type !== 'COMPLETE' || transfer.upload === undefined)
297
295
  throw new Error('DIRECT_FRAME_INVALID');
298
- if (control.size !== descriptor.size || control.sha256 !== descriptor.sha256)
296
+ if (control.size !== descriptor.size)
299
297
  throw new Error('DIRECT_INTEGRITY_FAILED');
300
- await transfer.upload.complete(control.size, control.sha256);
298
+ await transfer.upload.complete(control.size);
301
299
  transfer.uploadCommitted = true;
302
300
  this.emit(transfer.command.directSessionId, {
303
301
  type: 'direct.complete',
304
302
  directSessionId: transfer.command.directSessionId,
305
- size: control.size,
306
- sha256: control.sha256
303
+ size: control.size
307
304
  });
308
305
  await this.removeTransfer(transfer);
309
306
  return;
@@ -332,7 +329,6 @@ export class DirectTransferService {
332
329
  throw new Error('DIRECT_DOWNLOAD_UNAVAILABLE');
333
330
  const source = await this.options.openDownload(descriptor);
334
331
  const expectedSize = transferredSize(descriptor);
335
- const expectedSha256 = transferredSha256(descriptor);
336
332
  transfer.channel?.send(JSON.stringify({ type: 'ACK', offset: source.offset }));
337
333
  this.emit(transfer.command.directSessionId, {
338
334
  type: 'direct.opened',
@@ -350,7 +346,7 @@ export class DirectTransferService {
350
346
  const end = Math.min(chunk.byteLength, offset + 256 * 1024);
351
347
  channel.send(chunk.buffer.slice(chunk.byteOffset + offset, chunk.byteOffset + end));
352
348
  }
353
- transfer.channel?.send(JSON.stringify({ type: 'COMPLETE', size: expectedSize, sha256: expectedSha256 }));
349
+ transfer.channel?.send(JSON.stringify({ type: 'COMPLETE', size: expectedSize }));
354
350
  }
355
351
  async waitForSendCapacity(transfer, channel) {
356
352
  if (channel.readyState !== 'open')
@@ -433,11 +429,6 @@ function transferredSize(descriptor) {
433
429
  ? descriptor.range.length
434
430
  : descriptor.size;
435
431
  }
436
- function transferredSha256(descriptor) {
437
- return descriptor.direction === 'DOWNLOAD' && descriptor.range !== undefined
438
- ? descriptor.range.sha256
439
- : descriptor.sha256;
440
- }
441
432
  function openFrameSessionId(data) {
442
433
  if (typeof data !== 'string')
443
434
  return undefined;
@@ -1,5 +1,4 @@
1
1
  import { tmpdir } from 'node:os';
2
- import { createHash } from 'node:crypto';
3
2
  import { isAbsolute, win32 } from 'node:path';
4
3
  import { listWorkspaceFiles, mutateWorkspaceFile, normalizeFilePreviewPath, readAllowedFileContentRange, readWorkspaceFileContentRange, readAllowedTextFile, readWorkspaceTextFile, writeWorkspaceTextFile, WorkspaceFileIndex } from '../workspace.js';
5
4
  const INDEX_CACHE_LIMIT = 8;
@@ -62,7 +61,9 @@ export class WorkspaceFileService {
62
61
  readDirect(workspace, input, workspacePath = workspace.path) {
63
62
  const service = this;
64
63
  return (async function* () {
65
- const hash = createHash('sha256');
64
+ const initial = (await service.readContent(workspace, { path: input.path, offset: input.range?.offset ?? 0, limit: 1 }, workspacePath));
65
+ if (initial.size !== input.size || initial.sourceRevision !== input.revision)
66
+ throw new Error('DIRECT_FILE_REVISION_CHANGED');
66
67
  let offset = input.range?.offset ?? 0;
67
68
  const endOffset = input.range === undefined ? input.size : input.range.offset + input.range.length;
68
69
  while (offset < endOffset) {
@@ -74,7 +75,6 @@ export class WorkspaceFileService {
74
75
  if (range.size !== input.size || range.sourceRevision !== input.revision)
75
76
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
76
77
  const bytes = Buffer.from(range.contentBase64, 'base64');
77
- hash.update(bytes);
78
78
  offset += bytes.length;
79
79
  if (bytes.length > 0)
80
80
  yield bytes;
@@ -83,8 +83,7 @@ export class WorkspaceFileService {
83
83
  if (range.nextOffset !== offset)
84
84
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
85
85
  }
86
- const expectedHash = input.range?.sha256 ?? input.sha256;
87
- if (offset !== endOffset || hash.digest('hex') !== expectedHash)
86
+ if (offset !== endOffset)
88
87
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
89
88
  })();
90
89
  }
@@ -92,15 +91,13 @@ export class WorkspaceFileService {
92
91
  if (input.offset !== undefined ||
93
92
  input.length !== undefined ||
94
93
  input.size !== undefined ||
95
- input.sha256 !== undefined) {
94
+ input.revision !== undefined) {
96
95
  if (!Number.isSafeInteger(input.offset) ||
97
96
  input.offset < 0 ||
98
97
  !Number.isSafeInteger(input.length) ||
99
98
  input.length < 1 ||
100
99
  !Number.isSafeInteger(input.size) ||
101
100
  input.size < 0 ||
102
- typeof input.sha256 !== 'string' ||
103
- !/^[a-f0-9]{64}$/u.test(input.sha256) ||
104
101
  typeof input.revision !== 'string' ||
105
102
  !/^[a-f0-9]{64}$/u.test(input.revision))
106
103
  throw new Error('FILE_RANGE_INVALID');
@@ -117,37 +114,12 @@ export class WorkspaceFileService {
117
114
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
118
115
  return {
119
116
  size: expectedSize,
120
- sha256: input.sha256,
121
117
  revision: input.revision,
122
- range: {
123
- offset,
124
- length,
125
- sha256: createHash('sha256').update(bytes).digest('hex')
126
- }
118
+ range: { offset, length }
127
119
  };
128
120
  }
129
- const hash = createHash('sha256');
130
- let offset = 0;
131
- let size;
132
- let revision;
133
- while (true) {
134
- const range = (await this.readContent(workspace, { ...input, offset, limit: 512 * 1024 }, workspacePath));
135
- size ??= range.size;
136
- revision ??= range.sourceRevision;
137
- if (range.size !== size || range.sourceRevision !== revision)
138
- throw new Error('DIRECT_FILE_REVISION_CHANGED');
139
- const bytes = Buffer.from(range.contentBase64, 'base64');
140
- hash.update(bytes);
141
- offset += bytes.length;
142
- if (range.nextOffset === null)
143
- break;
144
- if (range.nextOffset !== offset)
145
- throw new Error('DIRECT_FILE_REVISION_CHANGED');
146
- }
147
- if (size === undefined || revision === undefined || offset !== size)
148
- throw new Error('DIRECT_FILE_REVISION_CHANGED');
149
- const sha256 = hash.digest('hex');
150
- return { size, sha256, revision };
121
+ const metadata = (await this.readContent(workspace, { ...input, offset: 0, limit: 1 }, workspacePath));
122
+ return { size: metadata.size, revision: metadata.sourceRevision };
151
123
  }
152
124
  async write(workspace, input) {
153
125
  if (typeof input.path !== 'string' ||
@@ -1,5 +1,4 @@
1
- import { createHash, randomUUID } from 'node:crypto';
2
- import { createReadStream } from 'node:fs';
1
+ import { randomUUID } from 'node:crypto';
3
2
  import { lstat, mkdir, open, readFile, rename, rm } from 'node:fs/promises';
4
3
  import { dirname, join, posix, resolve } from 'node:path';
5
4
  import { parseComposerAttachmentUpload, validComposerAttachmentName } from '../util/node-operation-parsers.js';
@@ -10,7 +9,6 @@ const MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
10
9
  export class WorkspaceUploadService {
11
10
  state;
12
11
  onCompleted;
13
- directHashes = new Map();
14
12
  constructor(state, onCompleted) {
15
13
  this.state = state;
16
14
  this.onCompleted = onCompleted;
@@ -114,7 +112,6 @@ export class WorkspaceUploadService {
114
112
  mime: input.composerAttachment.mime
115
113
  })
116
114
  });
117
- this.directHashes.set(status.uploadId, input.sha256);
118
115
  return status;
119
116
  }
120
117
  async writeDirect(uploadId, bytes) {
@@ -125,19 +122,11 @@ export class WorkspaceUploadService {
125
122
  });
126
123
  return status.receivedBytes;
127
124
  }
128
- async completeDirect(uploadId, size, sha256) {
125
+ async completeDirect(uploadId, size) {
129
126
  const upload = this.require(uploadId);
130
- const expected = this.directHashes.get(uploadId);
131
- if (size !== upload.size || sha256 !== expected)
132
- throw new Error('UPLOAD_HASH_INVALID');
133
- await upload.handle.sync();
134
- const actual = await fileSha256(upload.temporaryPath);
135
- if (actual !== expected) {
136
- await this.cancel(uploadId);
137
- throw new Error('UPLOAD_HASH_INVALID');
138
- }
127
+ if (size !== upload.size)
128
+ throw new Error('UPLOAD_SIZE_INVALID');
139
129
  await this.complete(uploadId);
140
- this.directHashes.delete(uploadId);
141
130
  }
142
131
  status(uploadId) {
143
132
  const upload = this.require(uploadId);
@@ -203,10 +192,8 @@ export class WorkspaceUploadService {
203
192
  if (typeof uploadId !== 'string')
204
193
  throw new Error('UPLOAD_NOT_FOUND');
205
194
  const active = this.state.uploads.get(uploadId);
206
- if (active !== undefined) {
207
- this.directHashes.delete(active.id);
195
+ if (active !== undefined)
208
196
  return this.remove(active);
209
- }
210
197
  const attachment = this.state.composerAttachments.get(uploadId);
211
198
  if (attachment === undefined)
212
199
  throw new Error('UPLOAD_NOT_FOUND');
@@ -246,7 +233,6 @@ export class WorkspaceUploadService {
246
233
  for (const attachment of this.state.composerAttachments.values())
247
234
  await this.removeCompleted(attachment);
248
235
  this.state.composerAttachments.clear();
249
- this.directHashes.clear();
250
236
  }
251
237
  require(id) {
252
238
  if (typeof id !== 'string')
@@ -258,7 +244,6 @@ export class WorkspaceUploadService {
258
244
  }
259
245
  async remove(upload) {
260
246
  this.state.uploads.delete(upload.id);
261
- this.directHashes.delete(upload.id);
262
247
  clearTimeout(upload.timer);
263
248
  await upload.handle.close().catch(() => undefined);
264
249
  await rm(upload.temporaryPath, { force: true }).catch(() => undefined);
@@ -296,12 +281,6 @@ export class WorkspaceUploadService {
296
281
  };
297
282
  }
298
283
  }
299
- async function fileSha256(path) {
300
- const hash = createHash('sha256');
301
- for await (const chunk of createReadStream(path))
302
- hash.update(chunk);
303
- return hash.digest('hex');
304
- }
305
284
  async function safeDirectory(path) {
306
285
  const value = await lstat(path);
307
286
  if (!value.isDirectory() || value.isSymbolicLink())
@@ -251,7 +251,6 @@ export class WorkspaceWorkbenchService {
251
251
  chunks: this.options.files.readDirect(workspace, {
252
252
  path: descriptor.resource.path,
253
253
  size: descriptor.size,
254
- sha256: descriptor.sha256,
255
254
  revision: descriptor.revision,
256
255
  ...(descriptor.range === undefined ? {} : { range: descriptor.range })
257
256
  }, await this.fileRepository(workspace, input))
@@ -272,7 +271,6 @@ export class WorkspaceWorkbenchService {
272
271
  return {
273
272
  offset: descriptor.range?.offset ?? 0,
274
273
  chunks: (async function* () {
275
- const hash = createHash('sha256');
276
274
  let offset = descriptor.range?.offset ?? 0;
277
275
  const rangeStart = descriptor.range?.offset ?? 0;
278
276
  const rangeEnd = rangeStart + (descriptor.range?.length ?? descriptor.size);
@@ -281,7 +279,6 @@ export class WorkspaceWorkbenchService {
281
279
  if (range.size !== descriptor.size)
282
280
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
283
281
  const bytes = Buffer.from(range.contentBase64, 'base64');
284
- hash.update(bytes);
285
282
  offset += bytes.length;
286
283
  if (bytes.length > 0)
287
284
  yield bytes;
@@ -290,8 +287,7 @@ export class WorkspaceWorkbenchService {
290
287
  if (range.nextOffset !== offset)
291
288
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
292
289
  }
293
- const expectedHash = descriptor.range?.sha256 ?? descriptor.sha256;
294
- if (offset !== rangeEnd || hash.digest('hex') !== expectedHash)
290
+ if (offset !== rangeEnd)
295
291
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
296
292
  })()
297
293
  };
@@ -304,7 +300,6 @@ export class WorkspaceWorkbenchService {
304
300
  if (input.offset !== undefined ||
305
301
  input.length !== undefined ||
306
302
  input.size !== undefined ||
307
- input.sha256 !== undefined ||
308
303
  input.revision !== undefined) {
309
304
  if (!Number.isSafeInteger(input.offset) ||
310
305
  input.offset < 0 ||
@@ -313,8 +308,6 @@ export class WorkspaceWorkbenchService {
313
308
  input.length > 512 * 1024 ||
314
309
  !Number.isSafeInteger(input.size) ||
315
310
  input.size < 0 ||
316
- typeof input.sha256 !== 'string' ||
317
- !/^[a-f0-9]{64}$/u.test(input.sha256) ||
318
311
  input.revision !== revision)
319
312
  throw new Error('FILE_RANGE_INVALID');
320
313
  const offset = input.offset;
@@ -328,25 +321,12 @@ export class WorkspaceWorkbenchService {
328
321
  throw new Error('DIRECT_FILE_REVISION_CHANGED');
329
322
  return {
330
323
  size,
331
- sha256: input.sha256,
332
324
  revision,
333
- range: {
334
- offset,
335
- length,
336
- sha256: createHash('sha256').update(bytes).digest('hex')
337
- }
325
+ range: { offset, length }
338
326
  };
339
327
  }
340
328
  const content = await openGitHistoryFileContent(repository, input.commit, input.path, this.refs(input));
341
- const hash = createHash('sha256');
342
- let offset = 0;
343
- for await (const bytes of content.chunks) {
344
- hash.update(bytes);
345
- offset += bytes.length;
346
- }
347
- if (offset !== content.size)
348
- throw new Error('DIRECT_FILE_REVISION_CHANGED');
349
- return { size: content.size, sha256: hash.digest('hex'), revision };
329
+ return { size: content.size, revision };
350
330
  }
351
331
  async diff(input) {
352
332
  if (typeof input.workspaceId !== 'string' ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.70",
3
+ "version": "0.9.72",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -28,8 +28,8 @@
28
28
  "node-pty": "1.1.0",
29
29
  "ws": "^8.21.3",
30
30
  "zod": "4.4.3",
31
- "@myagentroam/agent": "0.9.70",
32
- "@myagentroam/protocol": "0.9.70"
31
+ "@myagentroam/agent": "0.9.72",
32
+ "@myagentroam/protocol": "0.9.72"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/ws": "^8.18.1"