dsh-a2a-server 0.3.0 → 0.4.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +39 -28
  2. package/CHANGELOG.zh-CN.md +39 -28
  3. package/README.md +36 -16
  4. package/README.zh-CN.md +36 -13
  5. package/docker/Dockerfile +5 -0
  6. package/docs/visitor-isolation.en.md +51 -0
  7. package/docs/visitor-isolation.md +75 -0
  8. package/lib/config.d.ts +8 -4
  9. package/lib/config.d.ts.map +1 -1
  10. package/lib/config.js +4 -0
  11. package/lib/config.js.map +1 -1
  12. package/lib/docker-executor.d.ts +11 -0
  13. package/lib/docker-executor.d.ts.map +1 -0
  14. package/lib/docker-executor.js +115 -0
  15. package/lib/docker-executor.js.map +1 -0
  16. package/lib/index.d.ts.map +1 -1
  17. package/lib/index.js +55 -12
  18. package/lib/index.js.map +1 -1
  19. package/lib/isolation.d.ts +21 -0
  20. package/lib/isolation.d.ts.map +1 -0
  21. package/lib/isolation.js +108 -0
  22. package/lib/isolation.js.map +1 -0
  23. package/lib/management.d.ts.map +1 -1
  24. package/lib/management.js +2 -1
  25. package/lib/management.js.map +1 -1
  26. package/lib/microsandbox-executor.d.ts +39 -0
  27. package/lib/microsandbox-executor.d.ts.map +1 -0
  28. package/lib/microsandbox-executor.js +316 -0
  29. package/lib/microsandbox-executor.js.map +1 -0
  30. package/lib/microsandbox-runtime.d.ts +28 -0
  31. package/lib/microsandbox-runtime.d.ts.map +1 -0
  32. package/lib/microsandbox-runtime.js +164 -0
  33. package/lib/microsandbox-runtime.js.map +1 -0
  34. package/lib/router.d.ts +2 -0
  35. package/lib/router.d.ts.map +1 -1
  36. package/lib/router.js +13 -2
  37. package/lib/router.js.map +1 -1
  38. package/lib/sandbox-files.d.ts +16 -0
  39. package/lib/sandbox-files.d.ts.map +1 -0
  40. package/lib/sandbox-files.js +168 -0
  41. package/lib/sandbox-files.js.map +1 -0
  42. package/lib/sandbox-lifecycle.d.ts +48 -0
  43. package/lib/sandbox-lifecycle.d.ts.map +1 -0
  44. package/lib/sandbox-lifecycle.js +125 -0
  45. package/lib/sandbox-lifecycle.js.map +1 -0
  46. package/lib/version.d.ts +1 -1
  47. package/lib/version.js +1 -1
  48. package/lib/visitor-read.d.ts +4 -0
  49. package/lib/visitor-read.d.ts.map +1 -0
  50. package/lib/visitor-read.js +20 -0
  51. package/lib/visitor-read.js.map +1 -0
  52. package/lib/visitor-worker.d.ts +2 -0
  53. package/lib/visitor-worker.d.ts.map +1 -0
  54. package/lib/visitor-worker.js +18 -0
  55. package/lib/visitor-worker.js.map +1 -0
  56. package/lib/visitor-worker.mjs +14686 -0
  57. package/lib/visitor-worker.py +78 -0
  58. package/package.json +12 -4
@@ -0,0 +1,78 @@
1
+ """Trusted bounded reader. Public data and a visitor's disk are the only roots."""
2
+ import json
3
+ import os
4
+ import pathlib
5
+ import stat
6
+ import sys
7
+
8
+ def read_request(args):
9
+ if not isinstance(args, dict) or set(args) - {'root', 'path', 'offset', 'limit'}:
10
+ raise ValueError('Invalid read request')
11
+ root_name = args.get('root', 'public')
12
+ if root_name not in ('public', 'workspace'):
13
+ raise ValueError('Unknown data root')
14
+ root = pathlib.Path('/source' if root_name == 'public' else '/work')
15
+ requested = args.get('path')
16
+ offset, limit = args.get('offset', 1), args.get('limit', 200)
17
+ if not isinstance(requested, str) or not 1 <= len(requested) <= 4096 or any(c in requested for c in ('\0', '\\', ':')):
18
+ raise ValueError('Invalid file path')
19
+ if type(offset) is not int or not 1 <= offset <= 10000000 or type(limit) is not int or not 1 <= limit <= 500:
20
+ raise ValueError('Invalid read pagination')
21
+ target = pathlib.Path(os.path.abspath(root / requested))
22
+ try:
23
+ parts = target.relative_to(root).parts
24
+ except ValueError:
25
+ raise ValueError('Path is outside the authorized data directory')
26
+ current = root
27
+ for component in ('',) + parts:
28
+ current = current / component
29
+ if stat.S_ISLNK(current.lstat().st_mode):
30
+ raise ValueError('Symbolic links are not available')
31
+ before = target.lstat()
32
+ if stat.S_ISDIR(before.st_mode):
33
+ entries, truncated = [], False
34
+ with os.scandir(target) as iterator:
35
+ for index, entry in enumerate(iterator):
36
+ if index >= 1000:
37
+ truncated = True
38
+ break
39
+ if entry.is_symlink():
40
+ continue
41
+ if entry.is_file(follow_symlinks=False) or entry.is_dir(follow_symlinks=False):
42
+ entries.append(entry.name + ('/' if entry.is_dir(follow_symlinks=False) else ''))
43
+ entries.sort()
44
+ text = '\n'.join(entries[offset - 1:offset - 1 + limit])
45
+ truncated = truncated or len(entries) > offset - 1 + limit
46
+ kind = 'directory'
47
+ else:
48
+ if not stat.S_ISREG(before.st_mode) or before.st_nlink != 1:
49
+ raise ValueError('Only regular files without hard links can be read')
50
+ if before.st_size > 2 * 1024 * 1024:
51
+ raise ValueError('File exceeds the 2 MiB read limit')
52
+ fd = os.open(target, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
53
+ with os.fdopen(fd, 'rb') as stream:
54
+ opened = os.fstat(stream.fileno())
55
+ if (opened.st_dev, opened.st_ino, opened.st_nlink) != (before.st_dev, before.st_ino, 1):
56
+ raise ValueError('File changed during open')
57
+ data = stream.read(2 * 1024 * 1024 + 1)
58
+ if len(data) > 2 * 1024 * 1024:
59
+ raise ValueError('File exceeds the 2 MiB read limit')
60
+ text = data.decode('utf-8', errors='strict')
61
+ if '\0' in text:
62
+ raise ValueError('Binary files are not supported by read_file')
63
+ lines = text.replace('\r\n', '\n').split('\n')
64
+ text = '\n'.join(lines[offset - 1:offset - 1 + limit])
65
+ truncated = len(lines) > offset - 1 + limit
66
+ kind = 'file'
67
+ after = target.lstat()
68
+ if (before.st_dev, before.st_ino) != (after.st_dev, after.st_ino):
69
+ raise ValueError('Path changed during read')
70
+ return {'kind': kind, 'path': str(target.relative_to(root)), 'content': text[:32000], 'truncated': truncated or len(text) > 32000}
71
+
72
+ try:
73
+ if len(sys.argv[1].encode('utf-8')) > 8192:
74
+ raise ValueError('Read request too large')
75
+ print(json.dumps({'result': read_request(json.loads(sys.argv[1]))}, ensure_ascii=False))
76
+ except Exception as error:
77
+ print(json.dumps({'error': str(error)}, ensure_ascii=False))
78
+ sys.exit(1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-a2a-server",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Inbound A2A (Agent2Agent) protocol server for DeepSeek Harness: publish an Agent Card and let any A2A-compliant peer submit tasks to a harness agent",
5
5
  "keywords": [
6
6
  "dsh",
@@ -27,7 +27,11 @@
27
27
  },
28
28
  "files": [
29
29
  "lib",
30
+ "docker",
30
31
  "cordis.patch.yml",
32
+ "README.zh-CN.md",
33
+ "docs/visitor-isolation.md",
34
+ "docs/visitor-isolation.en.md",
31
35
  "CHANGELOG.md",
32
36
  "CHANGELOG.zh-CN.md"
33
37
  ],
@@ -40,10 +44,12 @@
40
44
  "dsh": ">=0.2.1-rc.2 <0.3.0"
41
45
  },
42
46
  "scripts": {
43
- "build": "tsc -p tsconfig.build.json",
47
+ "build": "tsc -p tsconfig.build.json && node scripts/build-worker.mjs",
44
48
  "typecheck": "tsc -p tsconfig.json --noEmit",
45
49
  "test": "vitest run",
46
50
  "test:watch": "vitest",
51
+ "test:docker": "pnpm build && node scripts/test-docker.mjs",
52
+ "test:microsandbox": "pnpm build && node scripts/test-microsandbox.mjs",
47
53
  "test:package": "pnpm build && node scripts/test-package.mjs",
48
54
  "serve": "tsx example/serve.ts",
49
55
  "probe": "node example/probe.mjs",
@@ -57,8 +63,8 @@
57
63
  "@deepseek-ai/dsh-llm": ">=0.2.1-rc.2 <0.3.0",
58
64
  "@deepseek-ai/dsh-session": ">=0.2.1-rc.2 <0.3.0",
59
65
  "@deepseek-ai/dsh-session-projection": ">=0.2.1-rc.2 <0.3.0",
60
- "@deepseek-ai/dsh-tools": ">=0.2.1-rc.2 <0.3.0",
61
- "@deepseek-ai/dsh-system-prompt": ">=0.2.1-rc.2 <0.3.0"
66
+ "@deepseek-ai/dsh-system-prompt": ">=0.2.1-rc.2 <0.3.0",
67
+ "@deepseek-ai/dsh-tools": ">=0.2.1-rc.2 <0.3.0"
62
68
  },
63
69
  "dependencies": {
64
70
  "@deepseek-ai/schemastery": "^3.18.5",
@@ -84,6 +90,8 @@
84
90
  "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
85
91
  "@deepseek-ai/dsh-tools": "0.2.1-rc.2",
86
92
  "@types/node": "^22.10.0",
93
+ "esbuild": "0.28.2",
94
+ "microsandbox": "0.7.11",
87
95
  "tsx": "^4.19.2",
88
96
  "typescript": "^5.7.2",
89
97
  "vitest": "^2.1.8"