dsh-a2a-server 0.3.0 → 0.4.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +45 -28
  2. package/CHANGELOG.zh-CN.md +45 -28
  3. package/README.md +42 -16
  4. package/README.zh-CN.md +42 -13
  5. package/docker/Dockerfile +5 -0
  6. package/docs/public-visitors.en.md +39 -0
  7. package/docs/public-visitors.md +86 -0
  8. package/docs/visitor-isolation.en.md +50 -0
  9. package/docs/visitor-isolation.md +74 -0
  10. package/lib/config.d.ts +12 -4
  11. package/lib/config.d.ts.map +1 -1
  12. package/lib/config.js +9 -0
  13. package/lib/config.js.map +1 -1
  14. package/lib/docker-executor.d.ts +11 -0
  15. package/lib/docker-executor.d.ts.map +1 -0
  16. package/lib/docker-executor.js +115 -0
  17. package/lib/docker-executor.js.map +1 -0
  18. package/lib/index.d.ts.map +1 -1
  19. package/lib/index.js +92 -12
  20. package/lib/index.js.map +1 -1
  21. package/lib/isolation.d.ts +21 -0
  22. package/lib/isolation.d.ts.map +1 -0
  23. package/lib/isolation.js +108 -0
  24. package/lib/isolation.js.map +1 -0
  25. package/lib/live-visitors.d.ts +21 -0
  26. package/lib/live-visitors.d.ts.map +1 -0
  27. package/lib/live-visitors.js +66 -0
  28. package/lib/live-visitors.js.map +1 -0
  29. package/lib/management.d.ts.map +1 -1
  30. package/lib/management.js +3 -1
  31. package/lib/management.js.map +1 -1
  32. package/lib/microsandbox-executor.d.ts +39 -0
  33. package/lib/microsandbox-executor.d.ts.map +1 -0
  34. package/lib/microsandbox-executor.js +316 -0
  35. package/lib/microsandbox-executor.js.map +1 -0
  36. package/lib/microsandbox-runtime.d.ts +28 -0
  37. package/lib/microsandbox-runtime.d.ts.map +1 -0
  38. package/lib/microsandbox-runtime.js +164 -0
  39. package/lib/microsandbox-runtime.js.map +1 -0
  40. package/lib/protocol/card.d.ts +1 -0
  41. package/lib/protocol/card.d.ts.map +1 -1
  42. package/lib/protocol/card.js +3 -0
  43. package/lib/protocol/card.js.map +1 -1
  44. package/lib/public-visitors.d.ts +38 -0
  45. package/lib/public-visitors.d.ts.map +1 -0
  46. package/lib/public-visitors.js +107 -0
  47. package/lib/public-visitors.js.map +1 -0
  48. package/lib/router.d.ts +15 -0
  49. package/lib/router.d.ts.map +1 -1
  50. package/lib/router.js +122 -9
  51. package/lib/router.js.map +1 -1
  52. package/lib/sandbox-files.d.ts +16 -0
  53. package/lib/sandbox-files.d.ts.map +1 -0
  54. package/lib/sandbox-files.js +168 -0
  55. package/lib/sandbox-files.js.map +1 -0
  56. package/lib/sandbox-lifecycle.d.ts +48 -0
  57. package/lib/sandbox-lifecycle.d.ts.map +1 -0
  58. package/lib/sandbox-lifecycle.js +125 -0
  59. package/lib/sandbox-lifecycle.js.map +1 -0
  60. package/lib/version.d.ts +1 -1
  61. package/lib/version.js +1 -1
  62. package/lib/visitor-read.d.ts +4 -0
  63. package/lib/visitor-read.d.ts.map +1 -0
  64. package/lib/visitor-read.js +20 -0
  65. package/lib/visitor-read.js.map +1 -0
  66. package/lib/visitor-worker.d.ts +2 -0
  67. package/lib/visitor-worker.d.ts.map +1 -0
  68. package/lib/visitor-worker.js +18 -0
  69. package/lib/visitor-worker.js.map +1 -0
  70. package/lib/visitor-worker.mjs +14686 -0
  71. package/lib/visitor-worker.py +78 -0
  72. package/package.json +14 -4
@@ -0,0 +1,168 @@
1
+ import { constants } from 'node:fs';
2
+ import { lstat, mkdir, open, opendir, realpath, rename, rm, readFile, writeFile } from 'node:fs/promises';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
+ import { dirname, join, resolve, relative } from 'node:path';
5
+ import { contains } from "./isolation.js";
6
+ export async function atomicJson(file, value) {
7
+ await mkdir(dirname(file), { recursive: true, mode: 0o700 });
8
+ const temporary = `${file}.${randomUUID()}.tmp`;
9
+ await writeFile(temporary, JSON.stringify(value), { mode: 0o600 });
10
+ try {
11
+ await rename(temporary, file);
12
+ }
13
+ finally {
14
+ await rm(temporary, { force: true });
15
+ }
16
+ }
17
+ /** Never recursively remove a path outside an explicitly owned parent. */
18
+ export async function removeOwned(parent, target) {
19
+ const base = await realpath(parent), absolute = resolve(target);
20
+ if (absolute === base || !contains(base, absolute))
21
+ throw new Error('Unsafe sandbox cleanup path');
22
+ const stat = await lstat(absolute).catch(error => { if (error.code === 'ENOENT')
23
+ return undefined; throw error; });
24
+ if (!stat)
25
+ return;
26
+ if (stat.isSymbolicLink() || !contains(base, await realpath(absolute)))
27
+ throw new Error('Sandbox cleanup path was redirected');
28
+ await rm(absolute, { recursive: true, force: true });
29
+ }
30
+ /** Inter-process ownership; stale locks are reclaimed only after their owner exits. */
31
+ export class LeaseBusyError extends Error {
32
+ }
33
+ export async function acquireLease(file) {
34
+ await mkdir(dirname(file), { recursive: true, mode: 0o700 });
35
+ const token = randomUUID();
36
+ for (let attempt = 0; attempt < 2; attempt++) {
37
+ try {
38
+ const handle = await open(file, 'wx', 0o600);
39
+ try {
40
+ await handle.writeFile(JSON.stringify({ pid: process.pid, token }));
41
+ }
42
+ finally {
43
+ await handle.close();
44
+ }
45
+ return async () => {
46
+ const value = JSON.parse(await readFile(file, 'utf8').catch(() => '{}'));
47
+ if (value.token === token)
48
+ await rm(file, { force: true });
49
+ };
50
+ }
51
+ catch (error) {
52
+ if (error.code !== 'EEXIST')
53
+ throw error;
54
+ let owner;
55
+ try {
56
+ owner = JSON.parse(await readFile(file, 'utf8'));
57
+ }
58
+ catch {
59
+ throw new LeaseBusyError('Sandbox preparation is already in progress; retry shortly');
60
+ }
61
+ if (!Number.isSafeInteger(owner.pid) || owner.pid <= 0)
62
+ throw new Error('Invalid sandbox ownership lock');
63
+ try {
64
+ process.kill(owner.pid, 0);
65
+ throw new LeaseBusyError('Sandbox is already managed by another process; stop it before restarting');
66
+ }
67
+ catch (probe) {
68
+ if (probe.code !== 'ESRCH')
69
+ throw probe;
70
+ }
71
+ await rm(file, { force: true });
72
+ }
73
+ }
74
+ throw new Error('Could not acquire sandbox ownership');
75
+ }
76
+ /** Different agents may prepare the shared runtime concurrently on first launch. */
77
+ export async function waitForLease(file, timeoutMs = 1_200_000) {
78
+ const deadline = Date.now() + timeoutMs;
79
+ for (;;) {
80
+ try {
81
+ return await acquireLease(file);
82
+ }
83
+ catch (error) {
84
+ if (!(error instanceof LeaseBusyError) || Date.now() >= deadline)
85
+ throw error;
86
+ await new Promise(resolve => setTimeout(resolve, Math.min(200, Math.max(1, deadline - Date.now()))));
87
+ }
88
+ }
89
+ }
90
+ /** Copy bytes, never host links. A private projection is the only public bind. */
91
+ export async function copySafeTree(source, destination, options = {}) {
92
+ const excluded = new Set(options.excludeNames?.map(name => name.toLowerCase()));
93
+ const root = await realpath(source);
94
+ if ((await lstat(source)).isSymbolicLink())
95
+ throw new Error('Data root must not be a link');
96
+ const device = (await lstat(root)).dev;
97
+ let bytes = 0, entries = 0;
98
+ const digest = createHash('sha256');
99
+ async function verify(path) {
100
+ const stat = await lstat(path);
101
+ if (stat.isSymbolicLink() || stat.dev !== device || stat.isFile() && stat.nlink !== 1 || !contains(root, await realpath(path)))
102
+ throw new Error('Links and nested mounts are not allowed in sandbox data');
103
+ return stat;
104
+ }
105
+ async function visit(from, to) {
106
+ if (++entries > (options.maxEntries ?? 100_000))
107
+ throw new Error('Sandbox data exceeds 100000 entries');
108
+ const before = await verify(from);
109
+ digest.update(JSON.stringify([relative(root, from), before.isDirectory() ? 'dir' : before.size]));
110
+ if (before.isDirectory()) {
111
+ await mkdir(to, { recursive: true, mode: 0o700 });
112
+ const names = [];
113
+ for await (const entry of await opendir(from)) {
114
+ names.push(entry.name);
115
+ if (names.length > (options.maxEntries ?? 100_000))
116
+ throw new Error('Too many sandbox data entries');
117
+ }
118
+ for (const name of names.sort()) {
119
+ if (!excluded.has(name.toLowerCase()))
120
+ await visit(join(from, name), join(to, name));
121
+ }
122
+ }
123
+ else if (before.isFile()) {
124
+ bytes += before.size;
125
+ if (bytes > (options.maxBytes ?? 512 * 1024 * 1024))
126
+ throw new Error('Sandbox shared data exceeds 512 MiB; choose a smaller directory');
127
+ const input = await open(from, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
128
+ try {
129
+ const opened = await input.stat();
130
+ if (opened.ino !== before.ino || opened.dev !== before.dev || opened.nlink !== 1 || !opened.isFile())
131
+ throw new Error('Data changed during open');
132
+ await verify(from);
133
+ const output = await open(to, 'wx', 0o600);
134
+ try {
135
+ const buffer = Buffer.alloc(64 * 1024);
136
+ let offset = 0;
137
+ while (offset < before.size) {
138
+ const { bytesRead } = await input.read(buffer, 0, Math.min(buffer.length, before.size - offset), offset);
139
+ if (!bytesRead)
140
+ throw new Error('Data changed during copy');
141
+ digest.update(buffer.subarray(0, bytesRead));
142
+ await output.writeFile(buffer.subarray(0, bytesRead));
143
+ offset += bytesRead;
144
+ }
145
+ }
146
+ finally {
147
+ await output.close();
148
+ }
149
+ const after = await verify(from), final = await input.stat();
150
+ if (after.ino !== opened.ino || after.dev !== opened.dev || after.size !== before.size || after.mtimeMs !== before.mtimeMs || final.nlink !== 1)
151
+ throw new Error('Data changed during copy; retry');
152
+ }
153
+ finally {
154
+ await input.close();
155
+ }
156
+ }
157
+ else
158
+ throw new Error('Special files are not allowed in sandbox data');
159
+ const after = await verify(from);
160
+ if (after.ino !== before.ino || after.dev !== before.dev)
161
+ throw new Error('Data directory changed during copy');
162
+ }
163
+ await visit(root, destination);
164
+ if (await realpath(source) !== root)
165
+ throw new Error('Data root changed during copy');
166
+ return digest.digest('hex');
167
+ }
168
+ //# sourceMappingURL=sandbox-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-files.js","sourceRoot":"","sources":["../src/sandbox-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ export declare const SANDBOX_PAUSE_MS = 30000;
2
+ export declare const SANDBOX_STOP_MS = 600000;
3
+ export declare const MAX_RESIDENT_SANDBOXES = 4;
4
+ export interface SandboxVM {
5
+ pause(): Promise<void>;
6
+ resume(): Promise<void>;
7
+ stop(): Promise<void>;
8
+ }
9
+ export interface SandboxDriver<VM extends SandboxVM> {
10
+ /** A validated new public projection, committed only after the old VM stops. */
11
+ stage(peer: string): Promise<undefined | {
12
+ commit(): Promise<void>;
13
+ discard(): Promise<void>;
14
+ }>;
15
+ start(peer: string): Promise<VM>;
16
+ close?(): Promise<void>;
17
+ }
18
+ /** One lifecycle per authenticated peer, shared by all of its conversations. */
19
+ export declare class VisitorSandboxes<VM extends SandboxVM> {
20
+ private readonly driver;
21
+ private readonly options;
22
+ private readonly entries;
23
+ private capacity;
24
+ private closed;
25
+ private closing?;
26
+ private timer;
27
+ constructor(driver: SandboxDriver<VM>, options?: {
28
+ now?: () => number;
29
+ pauseMs?: number;
30
+ stopMs?: number;
31
+ maxResident?: number;
32
+ autoTick?: boolean;
33
+ warn?: (message: string) => void;
34
+ });
35
+ private now;
36
+ private locked;
37
+ private stop;
38
+ run<T>(peer: string, operation: (vm: VM) => Promise<T>, signal: AbortSignal): Promise<T>;
39
+ tick(): Promise<void>;
40
+ snapshot(): Array<{
41
+ peer: string;
42
+ state: 'running' | 'paused' | 'stopped';
43
+ active: number;
44
+ lastUsed: number;
45
+ }>;
46
+ close(): Promise<void>;
47
+ }
48
+ //# sourceMappingURL=sandbox-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-lifecycle.d.ts","sourceRoot":"","sources":["../src/sandbox-lifecycle.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,QAAS,CAAA;AACtC,eAAO,MAAM,eAAe,SAAU,CAAA;AACtC,eAAO,MAAM,sBAAsB,IAAI,CAAA;AACvC,MAAM,WAAW,SAAS;IAAG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE;AACrG,MAAM,WAAW,aAAa,CAAC,EAAE,SAAS,SAAS;IACjD,gFAAgF;IAChF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG;QAAE,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC,CAAA;IAC/F,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;IAChC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACxB;AAGD,gFAAgF;AAChF,qBAAa,gBAAgB,CAAC,EAAE,SAAS,SAAS;IAMpC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAqB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALhF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,KAAK,CAA4B;gBACZ,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC,EAAmB,OAAO,GAAE;QAChF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;KAC7H;IAON,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;YAKA,IAAI;IAMlB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAmClF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,QAAQ,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAG9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAYvB"}
@@ -0,0 +1,125 @@
1
+ export const SANDBOX_PAUSE_MS = 30_000;
2
+ export const SANDBOX_STOP_MS = 600_000;
3
+ export const MAX_RESIDENT_SANDBOXES = 4;
4
+ /** One lifecycle per authenticated peer, shared by all of its conversations. */
5
+ export class VisitorSandboxes {
6
+ driver;
7
+ options;
8
+ entries = new Map();
9
+ capacity = Promise.resolve();
10
+ closed = false;
11
+ closing;
12
+ timer;
13
+ constructor(driver, options = {}) {
14
+ this.driver = driver;
15
+ this.options = options;
16
+ if ((options.pauseMs ?? SANDBOX_PAUSE_MS) <= 0 || (options.stopMs ?? SANDBOX_STOP_MS) <= (options.pauseMs ?? SANDBOX_PAUSE_MS))
17
+ throw new Error('Invalid sandbox idle policy');
18
+ if (options.autoTick !== false) {
19
+ this.timer = setInterval(() => { void this.tick().catch(error => options.warn?.(`Sandbox idle transition failed: ${String(error)}`)); }, 1000);
20
+ this.timer.unref();
21
+ }
22
+ }
23
+ now() { return this.options.now?.() ?? Date.now(); }
24
+ locked(fn) {
25
+ const pending = this.capacity.then(fn, fn);
26
+ this.capacity = pending.catch(() => { });
27
+ return pending;
28
+ }
29
+ async stop(entry) {
30
+ if (!entry.vm)
31
+ return;
32
+ // driver.stop resumes a paused VM before graceful shutdown and keeps all disks.
33
+ await entry.vm.stop();
34
+ delete entry.vm;
35
+ entry.paused = false;
36
+ }
37
+ run(peer, operation, signal) {
38
+ if (this.closed)
39
+ return Promise.reject(new Error('Visitor sandbox manager is closed'));
40
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(peer) || peer === 'owner')
41
+ return Promise.reject(new Error('Invalid visitor identity'));
42
+ let entry = this.entries.get(peer);
43
+ if (!entry) {
44
+ entry = { peer, paused: false, active: 0, lastUsed: this.now(), chain: Promise.resolve() };
45
+ this.entries.set(peer, entry);
46
+ }
47
+ const target = entry;
48
+ target.active++; // queued operations also prevent idle transitions and eviction
49
+ const execute = async () => {
50
+ signal.throwIfAborted();
51
+ if (this.closed)
52
+ throw new Error('Visitor sandbox manager is closed');
53
+ const projection = await this.driver.stage(peer);
54
+ try {
55
+ await this.locked(async () => {
56
+ signal.throwIfAborted();
57
+ if (this.closed)
58
+ throw new Error('Visitor sandbox manager is closed');
59
+ if (projection) {
60
+ await this.stop(target);
61
+ await projection.commit();
62
+ }
63
+ if (!target.vm) {
64
+ const resident = [...this.entries.values()].filter(e => e.vm);
65
+ if (resident.length >= (this.options.maxResident ?? MAX_RESIDENT_SANDBOXES)) {
66
+ const oldest = resident.filter(e => !e.active).sort((a, b) => a.lastUsed - b.lastUsed)[0];
67
+ if (!oldest)
68
+ throw new Error('Visitor tools are busy; try again later');
69
+ await this.stop(oldest);
70
+ }
71
+ target.vm = await this.driver.start(peer);
72
+ }
73
+ if (target.paused) {
74
+ await target.vm.resume();
75
+ target.paused = false;
76
+ }
77
+ });
78
+ }
79
+ finally {
80
+ await projection?.discard();
81
+ }
82
+ signal.throwIfAborted();
83
+ return await operation(target.vm);
84
+ };
85
+ const result = target.chain.then(execute, execute).finally(() => { target.active--; target.lastUsed = this.now(); });
86
+ target.chain = result.catch(() => { });
87
+ return result;
88
+ }
89
+ async tick() {
90
+ await this.locked(async () => {
91
+ if (this.closed)
92
+ return;
93
+ for (const entry of this.entries.values()) {
94
+ if (!entry.vm || entry.active)
95
+ continue;
96
+ const idle = this.now() - entry.lastUsed;
97
+ if (idle >= (this.options.stopMs ?? SANDBOX_STOP_MS))
98
+ await this.stop(entry);
99
+ else if (!entry.paused && idle >= (this.options.pauseMs ?? SANDBOX_PAUSE_MS)) {
100
+ await entry.vm.pause();
101
+ entry.paused = true;
102
+ }
103
+ }
104
+ });
105
+ }
106
+ snapshot() {
107
+ return [...this.entries.values()].map(e => ({ peer: e.peer, state: !e.vm ? 'stopped' : e.paused ? 'paused' : 'running', active: e.active, lastUsed: e.lastUsed }));
108
+ }
109
+ close() {
110
+ if (this.closing)
111
+ return this.closing;
112
+ this.closed = true;
113
+ clearInterval(this.timer);
114
+ this.closing = (async () => {
115
+ await Promise.allSettled([...this.entries.values()].map(e => e.chain));
116
+ const results = await this.locked(() => Promise.allSettled([...this.entries.values()].map(e => this.stop(e))));
117
+ await this.driver.close?.();
118
+ const failed = results.filter(r => r.status === 'rejected');
119
+ if (failed.length)
120
+ throw new AggregateError(failed.map(r => r.reason), 'Could not stop visitor sandboxes');
121
+ })();
122
+ return this.closing;
123
+ }
124
+ }
125
+ //# sourceMappingURL=sandbox-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-lifecycle.js","sourceRoot":"","sources":["../src/sandbox-lifecycle.ts"],"names":[],"mappings":"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"}
package/lib/version.d.ts CHANGED
@@ -16,5 +16,5 @@
16
16
  * @module dsh-a2a/version
17
17
  */
18
18
  /** Must equal `version` in package.json. */
19
- export declare const SERVER_VERSION = "0.3.0";
19
+ export declare const SERVER_VERSION = "0.4.1";
20
20
  //# sourceMappingURL=version.d.ts.map
package/lib/version.js CHANGED
@@ -16,5 +16,5 @@
16
16
  * @module dsh-a2a/version
17
17
  */
18
18
  /** Must equal `version` in package.json. */
19
- export const SERVER_VERSION = '0.3.0';
19
+ export const SERVER_VERSION = '0.4.1';
20
20
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,4 @@
1
+ import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
2
+ /** Both roots are explicit; a visitor can never select another visitor's root. */
3
+ export declare function visitorReadTool(dataRoot: string, workRoot: string, privatePaths: string[]): Promise<ToolDefinition>;
4
+ //# sourceMappingURL=visitor-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visitor-read.d.ts","sourceRoot":"","sources":["../src/visitor-read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAG5D,kFAAkF;AAClF,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAczH"}
@@ -0,0 +1,20 @@
1
+ import { workspaceReadTool } from "./workspace-read.js";
2
+ /** Both roots are explicit; a visitor can never select another visitor's root. */
3
+ export async function visitorReadTool(dataRoot, workRoot, privatePaths) {
4
+ const data = await workspaceReadTool(dataRoot, privatePaths);
5
+ const work = await workspaceReadTool(workRoot, []);
6
+ return {
7
+ ...data,
8
+ description: 'Read UTF-8 text or list directories. root="public" (default) reads shared public data; root="workspace" reads only your private workspace. path "." lists that root. Other visitors, host files and conversation logs are unavailable. Maximum file size 2 MiB; limit <= 500.',
9
+ parameters: { ...data.parameters, properties: { ...data.parameters.properties, root: { type: 'string', enum: ['public', 'workspace'] } } },
10
+ execute: async (raw, context) => {
11
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
12
+ throw new Error('Invalid read request');
13
+ const { root = 'public', ...args } = raw;
14
+ if (root !== 'public' && root !== 'workspace')
15
+ throw new Error('Unknown data root');
16
+ return await (root === 'public' ? data : work).execute(args, context);
17
+ },
18
+ };
19
+ }
20
+ //# sourceMappingURL=visitor-read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visitor-read.js","sourceRoot":"","sources":["../src/visitor-read.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEvD,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,QAAgB,EAAE,YAAsB;IAC9F,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IAC5D,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;IAClD,OAAO;QACL,GAAG,IAAI;QACP,WAAW,EAAE,+QAA+Q;QAC5R,UAAU,EAAE,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,GAAI,IAAI,CAAC,UAAU,CAAC,UAAsC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE;QACvK,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;YAC9B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAA;YAClG,MAAM,EAAE,IAAI,GAAG,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,GAA8B,CAAA;YACnE,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,WAAW;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;YACnF,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACvE,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=visitor-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visitor-worker.d.ts","sourceRoot":"","sources":["../src/visitor-worker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ // Bundled into one dependency-free executable; receives no host credentials.
2
+ import { visitorReadTool } from "./visitor-read.js";
3
+ let input = '';
4
+ for await (const bytes of process.stdin) {
5
+ input += String(bytes);
6
+ if (Buffer.byteLength(input) > 8192)
7
+ throw new Error('Read request too large');
8
+ }
9
+ try {
10
+ const tool = await visitorReadTool('/source', '/work', []);
11
+ const result = await tool.execute(JSON.parse(input), { signal: new AbortController().signal });
12
+ process.stdout.write(JSON.stringify({ result }));
13
+ }
14
+ catch (error) {
15
+ process.stdout.write(JSON.stringify({ error: error instanceof Error ? error.message : 'Read failed' }));
16
+ process.exitCode = 1;
17
+ }
18
+ //# sourceMappingURL=visitor-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visitor-worker.js","sourceRoot":"","sources":["../src/visitor-worker.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAGnD,IAAI,KAAK,GAAG,EAAE,CAAA;AACd,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IACxC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;AAChF,CAAC;AACD,IAAI,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,CAAA;IAC1D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC,MAAM,EAAoB,CAAC,CAAA;IAChH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;AAClD,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAA;IACvG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACtB,CAAC"}