super-dsh 0.3.0-f → 0.3.0-g

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/cordis.patch.yml CHANGED
@@ -51,6 +51,9 @@
51
51
  # get their own injection in P5, spec §10). Dormant without tools.
52
52
  # Off: restate with `disabled: true` or drop the row — nothing else
53
53
  # registers the tool.
54
+ # Independent build of Better's canonical remote source; default-on per Agent.
55
+ - id: superd-remote
56
+ name: 'super-dsh/remote'
54
57
  - id: superd-a2a
55
58
  name: 'super-dsh/a2a'
56
59
  - id: agent-omp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-dsh",
3
- "version": "0.3.0-f",
3
+ "version": "0.3.0-g",
4
4
  "description": "Super D for the DeepSeek Harness (DSH) — one dsh plugin that mounts the superd hub (runtime selector, roster, per-runtime agents) and joins local foreign-agent runtimes (OMP, Codex, Claude, Pi, Hermes, Antigravity) into the DSH Web UI. Each runtime keeps its native home and session store; the hub only bridges.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,7 +27,11 @@
27
27
  "./package.json": "./package.json",
28
28
  "./fusion-transport": "./superd-hub/dist/fusion-transport.js",
29
29
  "./a2a": "./superd-hub/dist/a2a-row.js",
30
- "./setup": "./superd-hub/dist/bundle-setup.js"
30
+ "./setup": "./superd-hub/dist/bundle-setup.js",
31
+ "./remote": {
32
+ "types": "./remote/plugin.d.ts",
33
+ "default": "./remote/plugin.js"
34
+ }
31
35
  },
32
36
  "files": [
33
37
  "cordis.patch.yml",
@@ -68,7 +72,8 @@
68
72
  "agent-agy-sdk/dist",
69
73
  "agent-agy-sdk/package.json",
70
74
  "agent-agy-sdk/cordis.patch.yml",
71
- "agent-agy-sdk/bridge"
75
+ "agent-agy-sdk/bridge",
76
+ "remote"
72
77
  ],
73
78
  "dsh": {
74
79
  "bundle": {
@@ -106,7 +111,8 @@
106
111
  "@deepseek-ai/dsh-app-boot": ">=0.1.5-0 <0.3.0-0",
107
112
  "@deepseek-ai/dsh-atomic-write": ">=0.1.5-0 <0.3.0-0",
108
113
  "@deepseek-ai/dsh-cmdline": ">=0.1.5-0 <0.3.0-0",
109
- "@deepseek-ai/dsh-plugin-manager": ">=0.1.5-0 <0.3.0-0"
114
+ "@deepseek-ai/dsh-plugin-manager": ">=0.1.5-0 <0.3.0-0",
115
+ "@deepseek-ai/dsh-jobs": ">=0.1.5-0 <0.3.0-0"
110
116
  },
111
117
  "peerDependenciesMeta": {
112
118
  "@deepseek-ai/cordis": {
@@ -168,6 +174,9 @@
168
174
  },
169
175
  "@deepseek-ai/dsh-plugin-manager": {
170
176
  "optional": true
177
+ },
178
+ "@deepseek-ai/dsh-jobs": {
179
+ "optional": true
171
180
  }
172
181
  },
173
182
  "publishConfig": {
@@ -0,0 +1,341 @@
1
+ import { constants, createReadStream, createWriteStream, mkdirSync } from "node:fs";
2
+ import { pipeline } from "node:stream/promises";
3
+ import { lstat, mkdir, mkdtemp, open, readdir, readlink, rename, rm, symlink } from "node:fs/promises";
4
+ import { Readable } from "node:stream";
5
+ import { createHash, randomUUID } from "node:crypto";
6
+ import { tmpdir } from "node:os";
7
+ import { dirname, join, posix, resolve } from "node:path";
8
+
9
+ //#region src/remote/archive.ts
10
+ const FILE_FRAME_BYTES = 64 * 1024;
11
+ const FILE_MAX_ENTRIES = 1e4;
12
+ const FILE_MAX_METADATA = 8 * 1024 * 1024;
13
+ const order = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b));
14
+ function checkRelative(p) {
15
+ if (typeof p !== "string" || Buffer.byteLength(p) > 4096 || p.includes("\0") || p !== "" && (p.startsWith("/") || p.split("/").some((s) => !s || s === "." || s === ".."))) throw new Error("unsafe archive relative path");
16
+ }
17
+ function checkAbsolute(p) {
18
+ if (typeof p !== "string" || !p.startsWith("/") || p.includes("\0") || p.split("/").includes("..")) throw new Error("file endpoint requires an absolute path without traversal or NUL");
19
+ }
20
+ function checkSelection(selection) {
21
+ if (selection.length > FILE_MAX_ENTRIES || Buffer.byteLength(JSON.stringify(selection)) > FILE_MAX_METADATA) throw new Error("file selection metadata limit exceeded");
22
+ const seen = /* @__PURE__ */ new Set();
23
+ for (const p of selection) {
24
+ checkRelative(p);
25
+ if (seen.has(p)) throw new Error("duplicate selection path");
26
+ seen.add(p);
27
+ }
28
+ }
29
+ function checkLink(e) {
30
+ const link = e.link;
31
+ if (e.p === "" || typeof link !== "string" || !link || link.includes("\0") || link.startsWith("/") || Buffer.byteLength(link) > 4096) throw new Error("unsafe archive symlink");
32
+ const target = posix.normalize(posix.join(posix.dirname(e.p), link));
33
+ if (target === ".." || target.startsWith("../")) throw new Error("unsafe archive symlink escape");
34
+ }
35
+ /** Conservative effective resolution: links may traverse only members of this incoming tree.
36
+ * Preserved destination extras cannot supply a symlink target or any of its ancestors.
37
+ */
38
+ function checkTreeLinks(entries) {
39
+ const incoming = new Map(entries.map((e) => [e.p, e]));
40
+ for (const link of entries.filter((e) => e.t === "l")) {
41
+ const stack = posix.dirname(link.p) === "." ? [] : posix.dirname(link.p).split("/");
42
+ let pending = link.link.split("/"), hops = 0;
43
+ while (pending.length) {
44
+ const part = pending.shift();
45
+ if (!part || part === ".") continue;
46
+ if (part === "..") {
47
+ if (!stack.length) throw new Error("unsafe symlink chain escape");
48
+ stack.pop();
49
+ continue;
50
+ }
51
+ const entry = incoming.get([...stack, part].join("/"));
52
+ if (!entry) throw new Error("unsafe symlink target outside incoming tree (destination aliases are not followed)");
53
+ if (entry.t === "l") {
54
+ if (++hops > 40) throw new Error("unsafe symlink cycle or chain limit");
55
+ pending = [...entry.link.split("/"), ...pending];
56
+ } else {
57
+ if (pending.length && entry.t !== "d") throw new Error("unsafe symlink non-directory target ancestor");
58
+ stack.push(part);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ function header(e) {
64
+ const b = Buffer.from(JSON.stringify(e)), n = Buffer.alloc(4);
65
+ n.writeUInt32BE(b.length);
66
+ return Buffer.concat([n, b]);
67
+ }
68
+ function manifestDigest(entries) {
69
+ const hash = createHash("sha256");
70
+ let bytes = 0;
71
+ for (const e of [...entries].sort((a, b) => order(a.p, b.p))) {
72
+ hash.update(header([
73
+ e.p,
74
+ e.t,
75
+ e.n,
76
+ e.t === "f" ? e.hash : e.t === "l" ? e.link : ""
77
+ ]));
78
+ if (e.t === "f") bytes += e.n;
79
+ }
80
+ return {
81
+ sha256: hash.digest("hex"),
82
+ bytes,
83
+ entries: entries.length
84
+ };
85
+ }
86
+ /** Reject even existing parent symlinks; leaf may be an explicitly copied symlink. */
87
+ async function ancestors(path, includeLeaf = false) {
88
+ checkAbsolute(path);
89
+ const parts = resolve(path).split("/").filter(Boolean);
90
+ for (let i = 1; i <= parts.length - (includeLeaf ? 0 : 1); i++) try {
91
+ const st = await lstat("/" + parts.slice(0, i).join("/"));
92
+ if (st.isSymbolicLink() || !st.isDirectory()) throw new Error("unsafe destination/source ancestor: symlink or non-directory");
93
+ } catch (e) {
94
+ if (e.code !== "ENOENT") throw e;
95
+ }
96
+ }
97
+ async function safeRead(path, signal) {
98
+ return (await open(path, constants.O_RDONLY | constants.O_NOFOLLOW)).createReadStream({
99
+ autoClose: true,
100
+ highWaterMark: FILE_FRAME_BYTES,
101
+ signal
102
+ });
103
+ }
104
+ async function entryAt(root, p, signal) {
105
+ signal.throwIfAborted();
106
+ const path = p ? join(root, p) : root;
107
+ await ancestors(path);
108
+ const st = await lstat(path);
109
+ if (st.isDirectory()) return {
110
+ p,
111
+ t: "d",
112
+ n: 0
113
+ };
114
+ if (st.isSymbolicLink()) {
115
+ const e = {
116
+ p,
117
+ t: "l",
118
+ n: 0,
119
+ link: await readlink(path)
120
+ };
121
+ checkLink(e);
122
+ return e;
123
+ }
124
+ if (!st.isFile()) throw new Error("unsupported file type (only files, directories and safe relative symlinks)");
125
+ const hash = createHash("sha256");
126
+ for await (const chunk of await safeRead(path, signal)) hash.update(chunk);
127
+ return {
128
+ p,
129
+ t: "f",
130
+ n: st.size,
131
+ hash: hash.digest("hex")
132
+ };
133
+ }
134
+ async function tree(root, signal, selection) {
135
+ checkAbsolute(root);
136
+ const entries = [];
137
+ const visit = async (p) => {
138
+ const e = await entryAt(root, p, signal);
139
+ entries.push(e);
140
+ if (entries.length > FILE_MAX_ENTRIES) throw new Error("archive entry limit exceeded");
141
+ if (e.t === "d") for (const name of (await readdir(p ? join(root, p) : root)).sort(order)) await visit(p ? `${p}/${name}` : name);
142
+ };
143
+ if (selection) {
144
+ checkSelection(selection);
145
+ for (const p of selection) entries.push(await entryAt(root, p, signal));
146
+ } else await visit("");
147
+ if (Buffer.byteLength(JSON.stringify(entries)) > FILE_MAX_METADATA) throw new Error("archive metadata limit exceeded");
148
+ return entries;
149
+ }
150
+ async function inspectArchive(path, signal) {
151
+ const file = await open(path, "r");
152
+ let offset = 0, metadata = 0;
153
+ const entries = [];
154
+ const seen = /* @__PURE__ */ new Map();
155
+ const exact = async (n) => {
156
+ const b = Buffer.alloc(n);
157
+ let at = 0;
158
+ while (at < n) {
159
+ signal.throwIfAborted();
160
+ const r = await file.read(b, at, n - at, offset);
161
+ if (!r.bytesRead) throw new Error("truncated binary archive");
162
+ at += r.bytesRead;
163
+ offset += r.bytesRead;
164
+ }
165
+ return b;
166
+ };
167
+ try {
168
+ while (true) {
169
+ const size = (await exact(4)).readUInt32BE();
170
+ if (!size || size > FILE_FRAME_BYTES) throw new Error("archive header limit exceeded");
171
+ metadata += size;
172
+ if (metadata > FILE_MAX_METADATA) throw new Error("archive metadata limit exceeded");
173
+ const e = JSON.parse((await exact(size)).toString("utf8"));
174
+ if (e.end === true) {
175
+ if ((await file.stat()).size !== offset) throw new Error("trailing archive bytes");
176
+ break;
177
+ }
178
+ checkRelative(e.p);
179
+ if (![
180
+ "f",
181
+ "d",
182
+ "l"
183
+ ].includes(e.t) || !Number.isSafeInteger(e.n) || e.n < 0 || e.t !== "f" && e.n !== 0) throw new Error("unsafe archive type/size");
184
+ if (entries.length >= FILE_MAX_ENTRIES || seen.has(e.p)) throw new Error("archive entry limit or duplicate path");
185
+ if (e.p !== "" && seen.get(posix.dirname(e.p) === "." ? "" : posix.dirname(e.p))?.t !== "d") throw new Error("unsafe archive parent ordering/type");
186
+ if (!entries.length && e.p !== "") throw new Error("archive root missing");
187
+ if (e.t === "l") checkLink(e);
188
+ e.offset = offset;
189
+ if (e.t === "f") {
190
+ const hash = createHash("sha256");
191
+ let left = e.n;
192
+ while (left) {
193
+ const chunk = await exact(Math.min(FILE_FRAME_BYTES, left));
194
+ hash.update(chunk);
195
+ left -= chunk.length;
196
+ }
197
+ e.hash = hash.digest("hex");
198
+ }
199
+ entries.push(e);
200
+ seen.set(e.p, e);
201
+ }
202
+ if (!entries.length) throw new Error("archive root missing");
203
+ return entries;
204
+ } finally {
205
+ await file.close();
206
+ }
207
+ }
208
+ async function importArchive(destination, payload, mkdirParents, signal, resources) {
209
+ checkAbsolute(destination);
210
+ const entries = await inspectArchive(payload, signal);
211
+ checkTreeLinks(entries);
212
+ for (const e of entries) {
213
+ const path = e.p ? join(destination, e.p) : destination;
214
+ await ancestors(path);
215
+ try {
216
+ const st = await lstat(path);
217
+ const type = st.isSymbolicLink() ? "l" : st.isDirectory() ? "d" : st.isFile() ? "f" : "?";
218
+ if (type !== e.t || type === "l") throw new Error("destination path/type conflict or existing symlink");
219
+ } catch (error) {
220
+ if (error.code !== "ENOENT") throw error;
221
+ }
222
+ }
223
+ await ancestors(destination);
224
+ if (mkdirParents) await mkdir(dirname(destination), { recursive: true });
225
+ for (const e of entries) {
226
+ signal.throwIfAborted();
227
+ const path = e.p ? join(destination, e.p) : destination;
228
+ await ancestors(path);
229
+ if (e.t === "d") {
230
+ await mkdir(path, { recursive: false }).catch((error) => {
231
+ if (error.code !== "EEXIST") throw error;
232
+ });
233
+ continue;
234
+ }
235
+ if (e.t === "l") {
236
+ await symlink(e.link, path);
237
+ continue;
238
+ }
239
+ const temporary = join(dirname(path), `.remote-copy-${randomUUID()}`);
240
+ await resources.temporary?.(temporary, true);
241
+ try {
242
+ const output = createWriteStream(temporary, {
243
+ flags: "wx",
244
+ mode: 384
245
+ });
246
+ await pipeline(e.n ? createReadStream(payload, {
247
+ start: e.offset,
248
+ end: e.offset + e.n - 1
249
+ }) : Readable.from([]), output, { signal });
250
+ await ancestors(path);
251
+ signal.throwIfAborted();
252
+ await rename(temporary, path);
253
+ } finally {
254
+ await rm(temporary, { force: true });
255
+ await resources.temporary?.(temporary, false);
256
+ }
257
+ }
258
+ }
259
+ function localFileEndpoint(resources = {}) {
260
+ return {
261
+ async exportTree(path, signal) {
262
+ const entries = await tree(path, signal);
263
+ return Readable.from((async function* () {
264
+ for (const e of entries) {
265
+ signal.throwIfAborted();
266
+ yield header({
267
+ p: e.p,
268
+ t: e.t,
269
+ n: e.n,
270
+ ...e.link === void 0 ? {} : { link: e.link }
271
+ });
272
+ if (e.t === "f" && e.n) for await (const chunk of await safeRead(e.p ? join(path, e.p) : path, signal)) yield chunk;
273
+ }
274
+ yield header({ end: true });
275
+ })());
276
+ },
277
+ async importTree(path, source, { mkdir: parents, signal }) {
278
+ checkAbsolute(path);
279
+ const scratch = resources.receiveRoot ?? await mkdtemp(join(tmpdir(), "remote-file-receive-"));
280
+ try {
281
+ const payload = join(scratch, "payload");
282
+ await pipeline(source, createWriteStream(payload, {
283
+ flags: "wx",
284
+ mode: 384
285
+ }), { signal });
286
+ await importArchive(path, payload, parents, signal, resources);
287
+ } finally {
288
+ source.destroy?.();
289
+ await rm(scratch, {
290
+ recursive: true,
291
+ force: true
292
+ });
293
+ }
294
+ },
295
+ async digest(path, signal, selection) {
296
+ return manifestDigest(await tree(path, signal, selection));
297
+ }
298
+ };
299
+ }
300
+
301
+ //#endregion
302
+ //#region src/remote/file-worker.ts
303
+ async function runFileWorker(request) {
304
+ const abort = new AbortController(), cancel = () => abort.abort(/* @__PURE__ */ new Error("binary file worker cancelled"));
305
+ process.on("SIGTERM", cancel);
306
+ process.on("SIGHUP", cancel);
307
+ const report = (value) => new Promise((resolve$1, reject) => process.stderr.write(JSON.stringify(value) + "\n", (error) => error ? reject(error) : resolve$1()));
308
+ try {
309
+ mkdirSync(request.root, { mode: 448 });
310
+ const endpoint = localFileEndpoint({
311
+ receiveRoot: request.root,
312
+ temporary: (path, active) => report({
313
+ temporary: path,
314
+ active
315
+ })
316
+ });
317
+ if (request.action === "export") await pipeline(await endpoint.exportTree(request.path, abort.signal), process.stdout, { signal: abort.signal });
318
+ else if (request.action === "import") await endpoint.importTree(request.path, process.stdin, {
319
+ mkdir: request.mkdir,
320
+ signal: abort.signal
321
+ });
322
+ else {
323
+ let metadata = "";
324
+ process.stdin.setEncoding("utf8");
325
+ for await (const chunk of process.stdin) {
326
+ metadata += String(chunk);
327
+ if (Buffer.byteLength(metadata) > FILE_MAX_METADATA) throw new Error("file selection metadata limit exceeded");
328
+ }
329
+ process.stdout.write(JSON.stringify(await endpoint.digest(request.path, abort.signal, request.selected ? JSON.parse(metadata) : void 0)));
330
+ }
331
+ } catch (error) {
332
+ await report({ error: error instanceof Error ? error.message : String(error) }).catch(() => {});
333
+ process.exitCode = 1;
334
+ } finally {
335
+ process.removeListener("SIGTERM", cancel);
336
+ process.removeListener("SIGHUP", cancel);
337
+ }
338
+ }
339
+
340
+ //#endregion
341
+ export { runFileWorker };