@delta-agentcloud/cli 0.0.0-stage → 1.1.22

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 (77) hide show
  1. package/README.md +498 -2
  2. package/checksums.txt +5 -0
  3. package/package.json +47 -4
  4. package/scripts/install-wizard.js +683 -0
  5. package/scripts/install.js +435 -0
  6. package/scripts/run.js +65 -0
  7. package/skills/delta-protein/AGENTS.md +73 -0
  8. package/skills/delta-protein/SKILL.md +389 -0
  9. package/skills/delta-protein/agents/openai.yaml +4 -0
  10. package/skills/delta-protein/references/bfitter.md +64 -0
  11. package/skills/delta-protein/references/catapro_predict.md +63 -0
  12. package/skills/delta-protein/references/catpred_predict.md +65 -0
  13. package/skills/delta-protein/references/caver_tunnel_analyze.md +63 -0
  14. package/skills/delta-protein/references/consensus_mutation.md +61 -0
  15. package/skills/delta-protein/references/consurf_predict.md +67 -0
  16. package/skills/delta-protein/references/crop_sequence.md +69 -0
  17. package/skills/delta-protein/references/dock.md +82 -0
  18. package/skills/delta-protein/references/dock_score.md +70 -0
  19. package/skills/delta-protein/references/easifa_predict_active_site.md +70 -0
  20. package/skills/delta-protein/references/ephod_predict_mutation.md +66 -0
  21. package/skills/delta-protein/references/ephod_predict_protein.md +61 -0
  22. package/skills/delta-protein/references/esmfold2_predict.md +67 -0
  23. package/skills/delta-protein/references/evcouplings_mutation.md +64 -0
  24. package/skills/delta-protein/references/fasta_to_sequence.md +59 -0
  25. package/skills/delta-protein/references/foldseek.md +61 -0
  26. package/skills/delta-protein/references/generate_msa.md +61 -0
  27. package/skills/delta-protein/references/interpro_analyze.md +63 -0
  28. package/skills/delta-protein/references/mpnn_design_sequence.md +71 -0
  29. package/skills/delta-protein/references/multievolve.md +73 -0
  30. package/skills/delta-protein/references/pisa_query.md +70 -0
  31. package/skills/delta-protein/references/plip_analyze.md +65 -0
  32. package/skills/delta-protein/references/pocket_prediction.md +63 -0
  33. package/skills/delta-protein/references/protein_design.md +65 -0
  34. package/skills/delta-protein/references/protein_evolution.md +77 -0
  35. package/skills/delta-protein/references/protein_sol_predict_mutation.md +66 -0
  36. package/skills/delta-protein/references/protein_sol_predict_protein.md +61 -0
  37. package/skills/delta-protein/references/protenix.md +66 -0
  38. package/skills/delta-protein/references/protrek_search.md +90 -0
  39. package/skills/delta-protein/references/pubchem_search.md +68 -0
  40. package/skills/delta-protein/references/pubmed_search.md +79 -0
  41. package/skills/delta-protein/references/reap_design_primer.md +69 -0
  42. package/skills/delta-protein/references/rfd3_design.md +65 -0
  43. package/skills/delta-protein/references/structure_to_sequence.md +61 -0
  44. package/skills/delta-protein/references/summarize_result.md +68 -0
  45. package/skills/delta-protein/references/surface_charge.md +84 -0
  46. package/skills/delta-protein/references/uniprot_batch_query.md +63 -0
  47. package/skills/delta-protein/references/uniprot_query.md +64 -0
  48. package/skills/delta-protein/references/zero_shot_mutant_candidates.md +76 -0
  49. package/skills/delta-protein/scripts/gen_references.py +167 -0
  50. package/skills/delta-protein/scripts/output_metadata.json +275 -0
  51. package/skills/delta-sandbox/SKILL.md +388 -0
  52. package/skills/delta-sandbox/agents/openai.yaml +4 -0
  53. package/skills/delta-sandbox/references/commands.md +58 -0
  54. package/skills/delta-sandbox/references/lifecycle.md +100 -0
  55. package/skills/delta-sandbox/references/recipes.md +138 -0
  56. package/skills/delta-science/AGENTS.md +47 -0
  57. package/skills/delta-science/SKILL.md +227 -0
  58. package/skills/delta-science/agents/openai.yaml +4 -0
  59. package/skills/delta-science/references/antbo.md +105 -0
  60. package/skills/delta-science/references/delta-bo.md +92 -0
  61. package/skills/delta-science/references/gsasii.md +118 -0
  62. package/skills/delta-science/references/lammps.md +68 -0
  63. package/skills/delta-science/references/ldm-bo.md +132 -0
  64. package/skills/delta-science/references/materials-design.md +130 -0
  65. package/skills/delta-science/references/pubchem.md +128 -0
  66. package/skills/delta-science/references/pymatgen.md +69 -0
  67. package/skills/delta-science/references/qe.md +134 -0
  68. package/skills/delta-science/references/rdkit.md +141 -0
  69. package/skills/delta-science/references/strbo.md +95 -0
  70. package/skills/delta-science/references/synbo.md +201 -0
  71. package/skills/delta-science/references/xrd.md +688 -0
  72. package/skills/delta-science/scripts/catalog.py +92 -0
  73. package/skills/delta-science/scripts/generated_catalog.py +69 -0
  74. package/skills/delta-science/scripts/invoke.py +346 -0
  75. package/skills/delta-shared/SKILL.md +113 -0
  76. package/skills/delta-shared/agents/openai.yaml +4 -0
  77. package/skills/manifest.json +20 -0
@@ -0,0 +1,435 @@
1
+ // Copyright (c) 2026 Delta Agent Cloud Authors
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { execFileSync } = require("child_process");
7
+ const os = require("os");
8
+ const crypto = require("crypto");
9
+
10
+ const PKG = "@delta-agentcloud/cli";
11
+
12
+ // Resolve the version to download:
13
+ // env var > npm registry > local package.json (fallback)
14
+ function resolveVersion() {
15
+ if (process.env.DELTA_CLI_VERSION) return process.env.DELTA_CLI_VERSION.replace(/^v/, "");
16
+ try {
17
+ const out = require("child_process").execFileSync("npm", ["view", PKG, "version"], {
18
+ stdio: ["ignore", "pipe", "pipe"],
19
+ timeout: 15000,
20
+ encoding: "utf8",
21
+ });
22
+ const ver = out.trim();
23
+ if (/^\d+\.\d+\.\d+/.test(ver)) return ver;
24
+ } catch {}
25
+ return require("../package.json").version.replace(/-.*$/, "");
26
+ }
27
+
28
+ const VERSION = resolveVersion();
29
+ const NAME = "delta-cli";
30
+ const REPO = "delta-agentcloud/delta-cli";
31
+ const DEFAULT_ALLOWED_HOSTS = new Set([
32
+ "github.com",
33
+ "objects.githubusercontent.com",
34
+ "release-assets.githubusercontent.com",
35
+ "github-releases.githubusercontent.com",
36
+ "raw.githubusercontent.com",
37
+ "registry.npmjs.org",
38
+ ]);
39
+ const DEFAULT_ALLOWED_HOST_PREFIXES = [
40
+ "github-production-release-asset-",
41
+ ];
42
+
43
+ const PLATFORM_MAP = {
44
+ darwin: "darwin",
45
+ linux: "linux",
46
+ win32: "windows",
47
+ };
48
+
49
+ const ARCH_MAP = {
50
+ x64: "amd64",
51
+ arm64: "arm64",
52
+ };
53
+
54
+ const platform = PLATFORM_MAP[process.platform];
55
+ const arch = ARCH_MAP[process.arch];
56
+
57
+ const isWindows = process.platform === "win32";
58
+ const ext = isWindows ? ".zip" : ".tar.gz";
59
+ const archiveName = `${NAME}-${platform}-${arch}${ext}`;
60
+ const GITHUB_URL = `https://github.com/${REPO}/releases/download/v${VERSION}/${archiveName}`;
61
+
62
+ const binDir = path.join(__dirname, "..", "bin");
63
+ const dest = path.join(binDir, NAME + (isWindows ? ".exe" : ""));
64
+
65
+ // ── Mirror URL resolution ──────────────────────────────────────────────────
66
+
67
+ var _curlTimeoutMs = null;
68
+
69
+ function _getCurlTimeout() {
70
+ if (_curlTimeoutMs !== null) return _curlTimeoutMs;
71
+ var env = process.env.DELTA_CLI_DOWNLOAD_TIMEOUT;
72
+ var t = env ? parseInt(env, 10) : 0;
73
+ _curlTimeoutMs = t > 0 ? t : 60000;
74
+ return _curlTimeoutMs;
75
+ }
76
+
77
+ // GitHub release download mirrors.
78
+ // Format: [hostname, urlPrefix] — urlPrefix is prepended to the GitHub URL.
79
+ // The overseas build downloads directly from GitHub; no proxy mirrors.
80
+ var _MIRROR_LIST = [];
81
+
82
+ // A safe "discard" output path for curl. /dev/null does not exist on Windows
83
+ // and makes curl fail with "client returned ERROR on write" (exit 23), which
84
+ // silently broke mirror probing on Windows. Use a throwaway temp file instead.
85
+ function discardOutputPath() {
86
+ if (process.platform !== "win32") return "/dev/null";
87
+ try {
88
+ return path.join(os.tmpdir(), "delta-cli-probe-" + process.pid + ".out");
89
+ } catch {
90
+ return "delta-cli-probe-" + process.pid + ".out";
91
+ }
92
+ }
93
+
94
+ function removeDiscardOutput(p) {
95
+ if (process.platform === "win32" && p) {
96
+ try { fs.unlinkSync(p); } catch {}
97
+ }
98
+ }
99
+
100
+ // Quick connectivity check: fetch a small file (checksums.txt) from each
101
+ // mirror and return only the ones that respond within the probe timeout.
102
+ function probeMirrors(version, cb) {
103
+ console.error("Probing " + _MIRROR_LIST.length + " download mirrors...");
104
+ var probeUrl = "/https://github.com/" + REPO + "/releases/download/v" + version + "/checksums.txt";
105
+ var results = [];
106
+ var pending = _MIRROR_LIST.length;
107
+ var done = false;
108
+
109
+ function finish() {
110
+ if (done) return;
111
+ done = true;
112
+ if (results.length > 0) {
113
+ console.error("Found " + results.length + " available mirror(s)");
114
+ } else {
115
+ console.error("No mirror responded, falling back to GitHub source");
116
+ }
117
+ cb(results);
118
+ }
119
+
120
+ if (pending === 0) return finish();
121
+
122
+ for (var i = 0; i < _MIRROR_LIST.length; i++) {
123
+ (function(idx) {
124
+ var entry = _MIRROR_LIST[idx];
125
+ var host = entry[0];
126
+ var prefix = entry[1];
127
+ var testUrl = prefix + probeUrl;
128
+ var discardPath = discardOutputPath();
129
+ var args = [
130
+ "--fail", "--location", "--silent", "--show-error",
131
+ "--connect-timeout", "3", "--max-time", "5",
132
+ "--output", discardPath, testUrl,
133
+ ];
134
+ var child;
135
+ try {
136
+ child = require("child_process").spawn("curl", args, { stdio: ["ignore", "pipe", "pipe"], timeout: 5000 });
137
+ var stdout = "";
138
+ var stderr = "";
139
+ child.stdout.on("data", function(d) { stdout += d; });
140
+ child.stderr.on("data", function(d) { stderr += d; });
141
+ child.on("close", function(code) {
142
+ removeDiscardOutput(discardPath);
143
+ if (code === 0) {
144
+ results.push(prefix + "/https://github.com/" + REPO + "/releases/download/v" + version + "/" + archiveName);
145
+ }
146
+ pending--;
147
+ if (pending === 0) finish();
148
+ });
149
+ child.on("error", function() {
150
+ removeDiscardOutput(discardPath);
151
+ pending--;
152
+ if (pending === 0) finish();
153
+ });
154
+ } catch (e) {
155
+ removeDiscardOutput(discardPath);
156
+ pending--;
157
+ if (pending === 0) finish();
158
+ }
159
+ })(i);
160
+ }
161
+ // Safety timeout: if probes hang, continue without mirrors after 10s
162
+ setTimeout(finish, 5000);
163
+ }
164
+
165
+ function releaseAssetUrls(version, assetName, cb) {
166
+ var urls = [];
167
+ var mirror = process.env.DELTA_CLI_MIRROR || "";
168
+ if (mirror) {
169
+ // The mirror proxies the full GitHub release URL by prefixing it (same
170
+ // scheme the probed mirrors use, e.g. https://<mirror>/https://github.com/...).
171
+ urls.push("" + mirror.replace(/\/$/, "") + "/https://github.com/" + REPO + "/releases/download/v" + version + "/" + assetName);
172
+ }
173
+ // Probe mirrors in parallel first, then fallback
174
+ probeMirrors(version, function(mirrorUrls) {
175
+ for (var i = 0; i < mirrorUrls.length; i++) {
176
+ urls.push(mirrorUrls[i]);
177
+ }
178
+ // GitHub source (last resort)
179
+ urls.push(GITHUB_URL);
180
+ cb(urls);
181
+ });
182
+ }
183
+
184
+ function assertAllowedHost(url) {
185
+ const { hostname } = new URL(url);
186
+ const allowlist = new Set(DEFAULT_ALLOWED_HOSTS);
187
+ (process.env.DELTA_CLI_MIRROR_ALLOWLIST || "")
188
+ .split(",")
189
+ .map((h) => h.trim().toLowerCase())
190
+ .filter(Boolean)
191
+ .forEach((h) => allowlist.add(h));
192
+ for (const allowed of allowlist) {
193
+ if (hostname === allowed || hostname.endsWith("." + allowed)) return;
194
+ }
195
+ for (const prefix of DEFAULT_ALLOWED_HOST_PREFIXES) {
196
+ if (hostname.startsWith(prefix)) return;
197
+ }
198
+ throw new Error(`Download host not allowed: ${hostname}`);
199
+ }
200
+
201
+ // ── curl version detection (for --ssl-revoke-best-effort) ──────────────────
202
+
203
+ function isCurlVersionSupported(versionOutput) {
204
+ const match = String(versionOutput).match(/^\s*curl\s+(\d+)\.(\d+)\.(\d+)/i);
205
+ if (!match) return false;
206
+ const major = parseInt(match[1], 10);
207
+ const minor = parseInt(match[2], 10);
208
+ return major > 7 || (major === 7 && minor >= 70);
209
+ }
210
+
211
+ let _curlSupportsSslRevokeBestEffort;
212
+
213
+ function curlSupportsSslRevokeBestEffort() {
214
+ if (_curlSupportsSslRevokeBestEffort !== undefined) {
215
+ return _curlSupportsSslRevokeBestEffort;
216
+ }
217
+ try {
218
+ const output = execFileSync("curl", ["--version"], {
219
+ stdio: ["ignore", "pipe", "ignore"],
220
+ encoding: "utf8",
221
+ timeout: 5000,
222
+ });
223
+ _curlSupportsSslRevokeBestEffort = isCurlVersionSupported(output);
224
+ } catch {
225
+ _curlSupportsSslRevokeBestEffort = false;
226
+ }
227
+ return _curlSupportsSslRevokeBestEffort;
228
+ }
229
+
230
+ // ── Download ───────────────────────────────────────────────────────────────
231
+
232
+ function download(url, destPath) {
233
+ assertAllowedHost(url);
234
+ const args = [
235
+ "--fail", "--location", "--silent", "--show-error",
236
+ "--connect-timeout", "10",
237
+ "--max-time", "45",
238
+ "--speed-limit", "50000", "--speed-time", "10",
239
+ "--max-redirs", "3",
240
+ "--output", destPath,
241
+ ];
242
+ if (isWindows && curlSupportsSslRevokeBestEffort()) {
243
+ args.unshift("--ssl-revoke-best-effort");
244
+ }
245
+ args.push(url);
246
+ execFileSync("curl", args, { stdio: ["ignore", "ignore", "pipe"] });
247
+ }
248
+
249
+ // ── Extraction ─────────────────────────────────────────────────────────────
250
+
251
+ function extractZipWindows(archivePath, destDir) {
252
+ const psOpts = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"];
253
+ const psEnv = {
254
+ DELTA_CLI_ARCHIVE: archivePath,
255
+ DELTA_CLI_DEST: destDir,
256
+ };
257
+ try {
258
+ const dotnet =
259
+ "$ErrorActionPreference='Stop';" +
260
+ "Add-Type -AssemblyName System.IO.Compression.FileSystem;" +
261
+ "[System.IO.Compression.ZipFile]::ExtractToDirectory($env:DELTA_CLI_ARCHIVE,$env:DELTA_CLI_DEST)";
262
+ execFileSync("powershell.exe", [...psOpts, dotnet], { stdio: ["ignore", "inherit", "inherit"], env: psEnv });
263
+ } catch (primaryErr) {
264
+ try {
265
+ const cmdlet =
266
+ "$ErrorActionPreference='Stop';" +
267
+ "Expand-Archive -LiteralPath $env:DELTA_CLI_ARCHIVE -DestinationPath $env:DELTA_CLI_DEST -Force";
268
+ execFileSync("powershell.exe", [...psOpts, cmdlet], { stdio: ["ignore", "inherit", "inherit"], env: psEnv });
269
+ } catch (secondErr) {
270
+ try {
271
+ execFileSync("tar", ["-xf", archivePath, "-C", destDir], { stdio: "ignore" });
272
+ } catch (fallbackErr) {
273
+ throw new Error(
274
+ `Failed to extract ${archivePath}. ` +
275
+ `.NET ZipFile: ${primaryErr.message}. ` +
276
+ `Expand-Archive: ${secondErr.message}. ` +
277
+ `tar: ${fallbackErr.message}`
278
+ );
279
+ }
280
+ }
281
+ }
282
+ }
283
+
284
+ // ── Checksum ───────────────────────────────────────────────────────────────
285
+
286
+ function getExpectedChecksum(archiveName, checksumsDir) {
287
+ const dir = checksumsDir || path.join(__dirname, "..");
288
+ const checksumsPath = path.join(dir, "checksums.txt");
289
+ if (!fs.existsSync(checksumsPath)) {
290
+ console.error("[WARN] checksums.txt not found, skipping checksum verification");
291
+ return null;
292
+ }
293
+ const content = fs.readFileSync(checksumsPath, "utf8");
294
+ for (const line of content.split("\n")) {
295
+ const trimmed = line.trim();
296
+ if (!trimmed) continue;
297
+ const match = trimmed.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
298
+ if (!match) continue;
299
+ const hash = match[1];
300
+ const name = match[2];
301
+ if (name === archiveName) return hash;
302
+ }
303
+ throw new Error(`Checksum entry not found for ${archiveName}`);
304
+ }
305
+
306
+ function verifyChecksum(archivePath, expectedHash) {
307
+ if (expectedHash === null) return;
308
+ const hash = crypto.createHash("sha256");
309
+ const fd = fs.openSync(archivePath, "r");
310
+ try {
311
+ const buf = Buffer.alloc(64 * 1024);
312
+ let bytesRead;
313
+ while ((bytesRead = fs.readSync(fd, buf, 0, buf.length, null)) > 0) {
314
+ hash.update(buf.subarray(0, bytesRead));
315
+ }
316
+ } finally {
317
+ fs.closeSync(fd);
318
+ }
319
+ const actual = hash.digest("hex");
320
+ if (actual.toLowerCase() !== expectedHash.toLowerCase()) {
321
+ throw new Error(
322
+ `[SECURITY] Checksum mismatch for ${path.basename(archivePath)}: expected ${expectedHash} but got ${actual}`
323
+ );
324
+ }
325
+ }
326
+
327
+ // ── Main install ───────────────────────────────────────────────────────────
328
+
329
+ function install() {
330
+ fs.mkdirSync(binDir, { recursive: true });
331
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "delta-cli-"));
332
+ var archivePath = path.join(tmpDir, archiveName);
333
+
334
+ releaseAssetUrls(VERSION, archiveName, function(downloadUrls) {
335
+ try {
336
+ var lastErr;
337
+ var downloaded = false;
338
+ for (var i = 0; i < downloadUrls.length; i++) {
339
+ try {
340
+ console.error("Downloading from " + downloadUrls[i]);
341
+ download(downloadUrls[i], archivePath);
342
+ console.error("Download complete, verifying checksum...");
343
+ const expectedHash = getExpectedChecksum(archiveName);
344
+ verifyChecksum(archivePath, expectedHash);
345
+ console.error("Checksum verified");
346
+ downloaded = true;
347
+ break;
348
+ } catch (e) {
349
+ console.error("Failed: " + e.message + " (" + downloadUrls[i] + ")");
350
+ lastErr = e;
351
+ }
352
+ }
353
+ if (!downloaded) throw lastErr;
354
+
355
+ if (isWindows) {
356
+ console.error("Extracting archive...");
357
+ extractZipWindows(archivePath, tmpDir);
358
+ } else {
359
+ console.error("Extracting archive...");
360
+ execFileSync("tar", ["-xzf", archivePath, "-C", tmpDir], { stdio: "ignore" });
361
+ }
362
+
363
+ const binaryName = NAME + (isWindows ? ".exe" : "");
364
+ const extractedBinary = path.join(tmpDir, NAME + "-" + platform + "-" + arch);
365
+ if (fs.existsSync(extractedBinary)) {
366
+ fs.copyFileSync(extractedBinary, dest);
367
+ } else {
368
+ // Fallback: try the plain name (older release format)
369
+ const plainBinary = path.join(tmpDir, binaryName);
370
+ if (fs.existsSync(plainBinary)) {
371
+ fs.copyFileSync(plainBinary, dest);
372
+ } else {
373
+ // Search in tmpDir
374
+ const files = fs.readdirSync(tmpDir).filter(function(f) { return f.indexOf(NAME) !== -1 || f.indexOf(binaryName) !== -1; });
375
+ if (files.length > 0) {
376
+ fs.copyFileSync(path.join(tmpDir, files[0]), dest);
377
+ } else {
378
+ throw new Error("Binary not found in extracted archive. Files: " + fs.readdirSync(tmpDir).join(", "));
379
+ }
380
+ }
381
+ }
382
+ fs.chmodSync(dest, 0o755);
383
+ ensureExecutable(path.join(__dirname, "run.js"));
384
+ console.error("Installed to " + dest);
385
+ } finally {
386
+ fs.rmSync(tmpDir, { recursive: true, force: true });
387
+ }
388
+ });
389
+ }
390
+
391
+ function ensureExecutable(filePath) {
392
+ try {
393
+ if (fs.existsSync(filePath)) fs.chmodSync(filePath, 0o755);
394
+ } catch {}
395
+ }
396
+
397
+ // ── Entry ──────────────────────────────────────────────────────────────────
398
+
399
+ if (require.main === module) {
400
+ if (!platform || !arch) {
401
+ console.error(`Unsupported platform: ${process.platform}-${process.arch}`);
402
+ process.exit(1);
403
+ }
404
+
405
+ // npx postinstall guard: skip binary download in ephemeral npx context
406
+ const isNpxPostinstall =
407
+ process.env.npm_command === "exec" && !process.env.DELTA_CLI_RUN;
408
+
409
+ if (isNpxPostinstall) {
410
+ process.exit(0);
411
+ }
412
+
413
+ try {
414
+ install();
415
+ console.log(`${NAME} v${VERSION} installed successfully`);
416
+ } catch (err) {
417
+ console.error(`Failed to install ${NAME}:`, err.message);
418
+ console.error(
419
+ `\nIf you are behind a firewall or in a restricted network, try one of:\n` +
420
+ ` # 1. Use a proxy:\n` +
421
+ ` export https_proxy=http://your-proxy:port\n` +
422
+ ` npm install -g ${PKG}\n\n` +
423
+ ` # 2. Point to a corporate npm mirror:\n` +
424
+ ` npm install -g ${PKG} --registry=https://your-corp-mirror/\n` +
425
+ `\nOr manually download from:\n` +
426
+ ` https://github.com/${REPO}/releases/tag/v${VERSION}`
427
+ );
428
+ process.exit(1);
429
+ }
430
+ }
431
+
432
+ module.exports = {
433
+ getExpectedChecksum, verifyChecksum, assertAllowedHost,
434
+ isCurlVersionSupported, curlSupportsSslRevokeBestEffort,
435
+ };
package/scripts/run.js ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ // Copyright (c) 2026 Delta Agent Cloud Authors
3
+ // SPDX-License-Identifier: MIT
4
+
5
+ const { execFileSync } = require("child_process");
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+
9
+ const ext = process.platform === "win32" ? ".exe" : "";
10
+ const bin = path.join(__dirname, "..", "bin", "delta-cli" + ext);
11
+
12
+ // On Windows, a crashed self-update may have left the binary renamed to .old.
13
+ const oldBin = bin + ".old";
14
+ function restoreOldBinary() {
15
+ try {
16
+ if (fs.existsSync(bin)) {
17
+ fs.rmSync(bin, { force: true });
18
+ }
19
+ fs.renameSync(oldBin, bin);
20
+ return true;
21
+ } catch { return false; }
22
+ }
23
+
24
+ if (process.platform === "win32" && fs.existsSync(oldBin)) {
25
+ if (!fs.existsSync(bin)) {
26
+ restoreOldBinary();
27
+ } else {
28
+ try {
29
+ execFileSync(bin, ["--version"], { stdio: "ignore", timeout: 10000 });
30
+ try { fs.rmSync(oldBin, { force: true }); } catch {}
31
+ } catch {
32
+ restoreOldBinary();
33
+ }
34
+ }
35
+ }
36
+
37
+ const args = process.argv.slice(2);
38
+
39
+ // Intercept install/uninstall — run wizard, bypass native binary
40
+ if (args[0] === "install" || args[0] === "uninstall") {
41
+ require("./install-wizard.js");
42
+ } else {
43
+ // Auto-download binary if missing (e.g. npx skipped postinstall)
44
+ if (!fs.existsSync(bin)) {
45
+ try {
46
+ execFileSync(process.execPath, [path.join(__dirname, "install.js")], {
47
+ stdio: "inherit",
48
+ env: { ...process.env, DELTA_CLI_RUN: "true", DELTA_CLI_VERSION: process.env.DELTA_CLI_VERSION || "" },
49
+ });
50
+ } catch {
51
+ console.error(
52
+ `\nFailed to auto-install delta-cli binary.\n` +
53
+ `To fix, run the install script manually:\n` +
54
+ ` node "${path.join(__dirname, "install.js")}"\n`
55
+ );
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ try {
61
+ execFileSync(bin, args, { stdio: "inherit" });
62
+ } catch (e) {
63
+ process.exit(e.status || 1);
64
+ }
65
+ }
@@ -0,0 +1,73 @@
1
+ # skills/delta-protein — Protein Analysis Skill Package
2
+
3
+ **Parent:** `../AGENTS.md`
4
+
5
+ ## OVERVIEW
6
+
7
+ Single public `delta-protein` skill: the unified entry point for Huada protein and
8
+ bioinformatics online analysis tools (mutation prediction, structure prediction, MSA,
9
+ protein design, docking, pocket/active-site analysis, conservation/InterPro,
10
+ enzyme kinetics, solubility/pH prediction, and PubMed/PubChem/UniProt/PISA/PLIP
11
+ lookup). It is host-neutral and routed exclusively through the Delta CLI
12
+ (`science invoke`) to the Science Server's `huada_protein_tools` tool. Per-endpoint
13
+ operation contracts live in `references/` and are loaded on demand; there are no
14
+ separate planner-visible per-tool skills and no workflow endpoints.
15
+
16
+ ## STRUCTURE
17
+
18
+ ```
19
+ delta-protein/
20
+ ├── SKILL.md # single entry point: routing, CLI-only execution, safety red lines
21
+ ├── agents/
22
+ │ └── openai.yaml # host agent registration
23
+ └── references/
24
+ └── <endpoint>.md # one registered endpoint's operation contract
25
+ # (39 endpoints under tool `huada_protein_tools`)
26
+ ```
27
+
28
+ ## DATA SOURCE
29
+
30
+ - Tool/endpoint identity and request fields come from the live Catalog
31
+ (`delta-cli science list`, `delta-cli science endpoints list <tool>`) plus the
32
+ authoritative parameter registry in
33
+ `agent/protein_common/tool_registry/definitions.py` of the `delta-search-backend`
34
+ project (per-tool `params`, `required_groups`, `default_params`, `timeout`).
35
+ - Response envelope is the Huada contract `{"status","message","tool_type",
36
+ "resultjson","download_url"}`; per-tool `resultjson` keys are derived from the
37
+ tool implementations, not invented.
38
+ - Each registered endpoint is a single tool call. Multi-step card workflows
39
+ (cards/*.json) are NOT registered as endpoints and are not documented here; an
40
+ agent may chain single endpoints in sequence when the task requires it.
41
+
42
+ ## CONVENTIONS
43
+
44
+ - JSON/query operations go through `delta-cli science invoke --tool huada_protein_tools --endpoint <name>`; binary artifacts from `download_url` go through `delta-cli science file download`
45
+ - Endpoint names are exact server contracts, read from the Catalog — never invent fields/enums from memory
46
+ - Success is reported only when the subprocess exit is 0 AND top-level JSON `ok` is `true`; `data` holds the service response
47
+ - `references/` contains only registered real endpoints; routing and tool selection belong in `SKILL.md`
48
+ - Host-neutral: `delta-cli` is the sole service boundary; no host-specific tool/env dependencies
49
+ - Discover newly-deployed endpoints via `delta-cli science endpoints list huada_protein_tools`
50
+
51
+ ## MAINTENANCE
52
+
53
+ - `scripts/gen_references.py` regenerates the `## Output Projection` and
54
+ `## Output Template` sections in every reference from
55
+ `scripts/output_metadata.json` (single source of truth for field projection /
56
+ presentation form / downstream reuse / provenance marker). Stdlib only, idempotent.
57
+ - Workflow: update `output_metadata.json` → run
58
+ `python scripts/gen_references.py` → run `python scripts/gen_references.py --check`.
59
+ `--check` fails loudly on missing sections, drifting titles, broken SKILL links or
60
+ references not covered by metadata.
61
+ - `output_metadata.json` `provenance` is `tested` (verified by a real `delta-cli`
62
+ invocation) or `derived` (from ToolSpec descriptions / agent templates, marked
63
+ "pending live calibration"). Newly verified endpoints should be flipped to `tested`.
64
+ - After changing references/SKILL, resync the opencode install dir:
65
+ `python scripts/gen_references.py --sync "%USERPROFILE%\.config\opencode\skills\delta-protein"`.
66
+
67
+ ## ANTI-PATTERNS
68
+
69
+ - Do not call `curl`/`requests`/`httpx`/browser/PowerShell web directly against any Huada or Gateway URL
70
+ - Do not bypass delta-cli or attach local MCP tool servers directly
71
+ - Do not fabricate protein sequences, structures, activity data, or algorithm results, and do not pass local-library results off as Science results
72
+ - Do not create extra JSON/CSV/report/image artifacts unless the user explicitly asks
73
+ - Do not break relative links between SKILL.md and `references/`