@delta-infra/cli 1.0.76 → 1.0.77

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 (50) hide show
  1. package/README.md +405 -405
  2. package/checksums.txt +5 -5
  3. package/package.json +1 -1
  4. package/scripts/install-wizard.js +596 -596
  5. package/scripts/install.js +400 -400
  6. package/scripts/run.js +65 -65
  7. package/skills/antbo-ldm-guard/SKILL.md +51 -51
  8. package/skills/antbo-ldm-guard/agents/openai.yaml +4 -4
  9. package/skills/antbo-service/SKILL.md +71 -71
  10. package/skills/antbo-service/agents/openai.yaml +4 -4
  11. package/skills/antbo-service/references/operations.md +45 -45
  12. package/skills/delta-bo/SKILL.md +28 -28
  13. package/skills/delta-bo/agents/openai.yaml +4 -4
  14. package/skills/delta-bo/references/endpoints.md +263 -263
  15. package/skills/delta-sandbox/SKILL.md +356 -356
  16. package/skills/delta-sandbox/agents/openai.yaml +4 -4
  17. package/skills/delta-sandbox/references/commands.md +56 -56
  18. package/skills/delta-sandbox/references/lifecycle.md +92 -92
  19. package/skills/delta-sandbox/references/recipes.md +133 -133
  20. package/skills/delta-science/SKILL.md +163 -163
  21. package/skills/delta-science/agents/openai.yaml +4 -4
  22. package/skills/delta-science/references/cli-contract.md +84 -84
  23. package/skills/delta-science/references/services.md +112 -112
  24. package/skills/delta-science/references/workflows.md +44 -44
  25. package/skills/delta-science/scripts/invoke.py +257 -257
  26. package/skills/delta-shared/SKILL.md +95 -95
  27. package/skills/delta-shared/agents/openai.yaml +4 -4
  28. package/skills/gsasii/SKILL.md +34 -34
  29. package/skills/gsasii/agents/openai.yaml +4 -4
  30. package/skills/gsasii/references/endpoints.md +130 -130
  31. package/skills/lammps/SKILL.md +31 -31
  32. package/skills/lammps/agents/openai.yaml +4 -4
  33. package/skills/lammps/references/endpoints.md +61 -61
  34. package/skills/ldm-bo/SKILL.md +27 -27
  35. package/skills/ldm-bo/agents/openai.yaml +4 -4
  36. package/skills/ldm-bo/references/endpoints.md +127 -127
  37. package/skills/pubchem/SKILL.md +85 -85
  38. package/skills/pubchem/agents/openai.yaml +4 -4
  39. package/skills/pubchem/references/endpoints.md +122 -122
  40. package/skills/pubchem/references/operations.md +41 -41
  41. package/skills/pymatgen/SKILL.md +46 -46
  42. package/skills/pymatgen/agents/openai.yaml +4 -4
  43. package/skills/pymatgen/references/endpoints.md +66 -66
  44. package/skills/rdkit/SKILL.md +104 -104
  45. package/skills/rdkit/agents/openai.yaml +4 -4
  46. package/skills/rdkit/references/endpoints.md +133 -133
  47. package/skills/rdkit/references/operations.md +64 -64
  48. package/skills/synbo-service/SKILL.md +28 -28
  49. package/skills/synbo-service/agents/openai.yaml +4 -4
  50. package/skills/synbo-service/references/endpoints.md +157 -157
@@ -1,263 +1,263 @@
1
- #!/usr/bin/env python3
2
- """Invoke one Delta Science operation strictly through delta-cli."""
3
-
4
- from __future__ import annotations
5
-
6
- import argparse
7
- import json
8
- import os
9
- from pathlib import Path
10
- import shutil
11
- import subprocess
12
- import sys
13
- import time
14
- from typing import Any
1
+ #!/usr/bin/env python3
2
+ """Invoke one Delta Science operation strictly through delta-cli."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+ import shutil
11
+ import subprocess
12
+ import sys
13
+ import time
14
+ from typing import Any
15
15
  from urllib.parse import urlsplit
16
16
 
17
17
  from generated_catalog import LEGACY_ENDPOINTS, LEGACY_TOOL_ALIASES
18
18
 
19
19
 
20
20
  SKILL_DIR = Path(__file__).resolve().parents[1]
21
-
22
-
23
- def _json_object(raw: str | None, flag: str) -> dict[str, Any] | None:
24
- if raw is None:
25
- return None
26
- try:
27
- value = json.loads(raw)
28
- except json.JSONDecodeError as exc:
29
- raise ValueError(f"{flag} is not valid JSON: {exc}") from exc
30
- if not isinstance(value, dict):
31
- raise ValueError(f"{flag} must decode to a JSON object")
32
- return value
33
-
34
-
35
- def _runtime_config() -> dict[str, Any]:
36
- path = SKILL_DIR / "runtime.local.json"
37
- if not path.is_file():
38
- return {}
39
- try:
40
- value = json.loads(path.read_text(encoding="utf-8"))
41
- except (OSError, json.JSONDecodeError):
42
- return {}
43
- return value if isinstance(value, dict) else {}
44
-
45
-
46
- def _resolve_cli(explicit: str | None, runtime: dict[str, Any]) -> str:
47
- candidates: list[str | None] = [
48
- explicit,
49
- os.environ.get("DELTA_CLI_PATH"),
50
- runtime.get("cli_path"),
51
- shutil.which("delta-cli"),
52
- shutil.which("delta-cli.exe"),
53
- ]
54
- package_root = SKILL_DIR.parents[1]
55
- exe_name = "delta-cli.exe" if os.name == "nt" else "delta-cli"
56
- candidates.append(str(package_root / "bin" / exe_name))
57
- for candidate in candidates:
58
- if candidate and Path(candidate).is_file():
59
- return str(Path(candidate).resolve())
60
- raise FileNotFoundError(
61
- "delta-cli not found; install @delta-infra/cli, set DELTA_CLI_PATH, "
62
- "or add cli_path to delta-science/runtime.local.json"
63
- )
64
-
65
-
66
- def _cli_science_base_url(cli: str, env: dict[str, str], timeout: float) -> str:
67
- explicit = env.get("DELTA_INFRA_SCIENCE_BASE_URL", "").strip()
68
- if explicit:
69
- return explicit
70
- completed = subprocess.run(
71
- [cli, "config", "show"],
72
- capture_output=True,
73
- text=True,
74
- encoding="utf-8",
75
- errors="replace",
76
- env=env,
77
- timeout=min(timeout, 15.0),
78
- check=False,
79
- )
80
- if completed.returncode != 0:
81
- return ""
82
- try:
83
- root = json.loads(completed.stdout)
84
- value = root.get("data", {}).get("science_base_url", "")
85
- except (AttributeError, json.JSONDecodeError):
86
- return ""
87
- return str(value).strip()
88
-
89
-
90
- def _catalog_profile(base_url: str, requested: str) -> str:
91
- if requested != "auto":
92
- return requested
93
- try:
94
- path = urlsplit(base_url).path.rstrip("/").lower()
95
- except ValueError:
96
- path = ""
97
- return "legacy" if path.endswith("/science_tool") else "canonical"
98
-
99
-
100
- def _resolve_catalog_names(tool: str, endpoint: str, profile: str) -> tuple[str, str]:
101
- if profile == "canonical":
102
- return tool, endpoint
103
- canonical_tool = tool.removesuffix("-service") if tool in {"antbo-service", "synbo-service"} else tool
104
- resolved_tool = LEGACY_TOOL_ALIASES.get(canonical_tool, canonical_tool)
105
- if endpoint.startswith(("chem_", "biology_")):
106
- return resolved_tool, endpoint
107
- resolved_endpoint = LEGACY_ENDPOINTS.get(canonical_tool, {}).get(endpoint)
108
- if not resolved_endpoint:
109
- raise ValueError(
110
- f"no legacy science_tool mapping for {canonical_tool}/{endpoint}"
111
- )
112
- return resolved_tool, resolved_endpoint
113
-
114
-
115
- def _unwrap(stdout: str) -> tuple[Any, int]:
116
- try:
117
- root = json.loads(stdout)
118
- except json.JSONDecodeError as exc:
119
- raise RuntimeError(f"CLI stdout is not JSON: {exc}") from exc
120
- if not isinstance(root, dict):
121
- raise RuntimeError("CLI stdout root is not an object")
122
- if root.get("ok") is not True:
123
- raise RuntimeError(json.dumps(root.get("error", root), ensure_ascii=False))
124
- if "data" not in root:
125
- raise RuntimeError("CLI success envelope is missing data")
126
-
127
- value: Any = root["data"]
128
- depth = 0
129
- while isinstance(value, dict):
130
- if "status_code" in value and "data" in value:
131
- status_code = value.get("status_code")
132
- if not isinstance(status_code, int) or not 200 <= status_code < 300:
133
- raise RuntimeError(json.dumps(value, ensure_ascii=False))
134
- value = value["data"]
135
- elif (
136
- "code" in value
137
- and "data" in value
138
- and set(value).intersection({"message", "msg"})
139
- ):
140
- code = value.get("code")
141
- if code not in (0, "0", None):
142
- raise RuntimeError(json.dumps(value, ensure_ascii=False))
143
- value = value["data"]
144
- else:
145
- break
146
- depth += 1
147
- if depth > 4:
148
- raise RuntimeError("too many nested service envelopes")
149
- if isinstance(value, dict) and value.get("valid") is False:
150
- raise RuntimeError(json.dumps({
151
- "valid": False,
152
- "error": value.get("error"),
153
- "warnings": value.get("warnings"),
154
- }, ensure_ascii=False))
155
- return value, depth
156
-
157
-
158
- def _parser() -> argparse.ArgumentParser:
159
- parser = argparse.ArgumentParser(description=__doc__)
160
- parser.add_argument("--tool", required=True)
161
- parser.add_argument("--endpoint", required=True)
162
- body = parser.add_mutually_exclusive_group()
163
- body.add_argument("--data-json")
164
- body.add_argument("--params-json")
165
- parser.add_argument("--cli")
166
- parser.add_argument("--science-base-url")
167
- parser.add_argument(
168
- "--catalog-profile",
169
- choices=("auto", "canonical", "legacy"),
170
- default="auto",
171
- )
172
- parser.add_argument("--timeout", type=float, default=120.0)
173
- return parser
174
-
175
-
176
- def main() -> int:
177
- args = _parser().parse_args()
178
- started = time.perf_counter()
179
- stage = "arguments"
180
- profile = "unknown"
181
- resolved_tool = args.tool
182
- resolved_endpoint = args.endpoint
183
- try:
184
- data = _json_object(args.data_json, "--data-json")
185
- params = _json_object(args.params_json, "--params-json")
186
- runtime = _runtime_config()
187
- stage = "cli-resolution"
188
- cli = _resolve_cli(args.cli, runtime)
189
-
190
- env = os.environ.copy()
191
- base_url = args.science_base_url or runtime.get("science_base_url")
192
- if base_url:
193
- env["DELTA_INFRA_SCIENCE_BASE_URL"] = str(base_url)
194
- configured_base_url = _cli_science_base_url(cli, env, args.timeout)
195
- requested_profile = str(runtime.get("catalog_profile", args.catalog_profile))
196
- if requested_profile not in {"auto", "canonical", "legacy"}:
197
- raise ValueError("catalog_profile must be auto, canonical, or legacy")
198
- profile = _catalog_profile(configured_base_url, requested_profile)
199
- resolved_tool, resolved_endpoint = _resolve_catalog_names(
200
- args.tool, args.endpoint, profile
201
- )
202
-
203
- command = [
204
- cli, "science", "invoke",
205
- "--tool", resolved_tool,
206
- "--endpoint", resolved_endpoint,
207
- ]
208
- if data is not None:
209
- command.extend(["--data", json.dumps(data, ensure_ascii=False, separators=(",", ":"))])
210
- if params is not None:
211
- command.extend(["--params", json.dumps(params, ensure_ascii=False, separators=(",", ":"))])
212
-
213
- stage = "delta-cli"
214
- completed = subprocess.run(
215
- command,
216
- capture_output=True,
217
- text=True,
218
- encoding="utf-8",
219
- errors="replace",
220
- env=env,
221
- timeout=args.timeout,
222
- check=False,
223
- )
224
- if completed.returncode != 0:
225
- detail = completed.stderr.strip() or completed.stdout.strip()
226
- raise RuntimeError(f"exit_code={completed.returncode}: {detail}")
227
- stage = "response-validation"
228
- native, depth = _unwrap(completed.stdout)
229
- result = {
230
- "ok": True,
231
- "transport": "delta-cli",
232
- "tool": args.tool,
233
- "endpoint": args.endpoint,
234
- "resolved_tool": resolved_tool,
235
- "resolved_endpoint": resolved_endpoint,
236
- "catalog_profile": profile,
237
- "elapsed_seconds": round(time.perf_counter() - started, 3),
238
- "envelope_depth": depth,
239
- "native": native,
240
- }
241
- print(json.dumps(result, ensure_ascii=False))
242
- return 0
243
- except subprocess.TimeoutExpired:
244
- error = f"timeout after {args.timeout}s"
245
- except (OSError, ValueError, RuntimeError) as exc:
246
- error = str(exc)
247
- print(json.dumps({
248
- "ok": False,
249
- "transport": "delta-cli",
250
- "tool": args.tool,
251
- "endpoint": args.endpoint,
252
- "resolved_tool": resolved_tool,
253
- "resolved_endpoint": resolved_endpoint,
254
- "catalog_profile": profile,
255
- "stage": stage,
256
- "elapsed_seconds": round(time.perf_counter() - started, 3),
257
- "error": error[:2000],
258
- }, ensure_ascii=False))
259
- return 1
260
-
261
-
262
- if __name__ == "__main__":
263
- sys.exit(main())
21
+
22
+
23
+ def _json_object(raw: str | None, flag: str) -> dict[str, Any] | None:
24
+ if raw is None:
25
+ return None
26
+ try:
27
+ value = json.loads(raw)
28
+ except json.JSONDecodeError as exc:
29
+ raise ValueError(f"{flag} is not valid JSON: {exc}") from exc
30
+ if not isinstance(value, dict):
31
+ raise ValueError(f"{flag} must decode to a JSON object")
32
+ return value
33
+
34
+
35
+ def _runtime_config() -> dict[str, Any]:
36
+ path = SKILL_DIR / "runtime.local.json"
37
+ if not path.is_file():
38
+ return {}
39
+ try:
40
+ value = json.loads(path.read_text(encoding="utf-8"))
41
+ except (OSError, json.JSONDecodeError):
42
+ return {}
43
+ return value if isinstance(value, dict) else {}
44
+
45
+
46
+ def _resolve_cli(explicit: str | None, runtime: dict[str, Any]) -> str:
47
+ candidates: list[str | None] = [
48
+ explicit,
49
+ os.environ.get("DELTA_CLI_PATH"),
50
+ runtime.get("cli_path"),
51
+ shutil.which("delta-cli"),
52
+ shutil.which("delta-cli.exe"),
53
+ ]
54
+ package_root = SKILL_DIR.parents[1]
55
+ exe_name = "delta-cli.exe" if os.name == "nt" else "delta-cli"
56
+ candidates.append(str(package_root / "bin" / exe_name))
57
+ for candidate in candidates:
58
+ if candidate and Path(candidate).is_file():
59
+ return str(Path(candidate).resolve())
60
+ raise FileNotFoundError(
61
+ "delta-cli not found; install @delta-infra/cli, set DELTA_CLI_PATH, "
62
+ "or add cli_path to delta-science/runtime.local.json"
63
+ )
64
+
65
+
66
+ def _cli_science_base_url(cli: str, env: dict[str, str], timeout: float) -> str:
67
+ explicit = env.get("DELTA_INFRA_SCIENCE_BASE_URL", "").strip()
68
+ if explicit:
69
+ return explicit
70
+ completed = subprocess.run(
71
+ [cli, "config", "show"],
72
+ capture_output=True,
73
+ text=True,
74
+ encoding="utf-8",
75
+ errors="replace",
76
+ env=env,
77
+ timeout=min(timeout, 15.0),
78
+ check=False,
79
+ )
80
+ if completed.returncode != 0:
81
+ return ""
82
+ try:
83
+ root = json.loads(completed.stdout)
84
+ value = root.get("data", {}).get("science_base_url", "")
85
+ except (AttributeError, json.JSONDecodeError):
86
+ return ""
87
+ return str(value).strip()
88
+
89
+
90
+ def _catalog_profile(base_url: str, requested: str) -> str:
91
+ if requested != "auto":
92
+ return requested
93
+ try:
94
+ path = urlsplit(base_url).path.rstrip("/").lower()
95
+ except ValueError:
96
+ path = ""
97
+ return "legacy" if path.endswith("/science_tool") else "canonical"
98
+
99
+
100
+ def _resolve_catalog_names(tool: str, endpoint: str, profile: str) -> tuple[str, str]:
101
+ if profile == "canonical":
102
+ return tool, endpoint
103
+ canonical_tool = tool.removesuffix("-service") if tool in {"antbo-service", "synbo-service"} else tool
104
+ resolved_tool = LEGACY_TOOL_ALIASES.get(canonical_tool, canonical_tool)
105
+ if endpoint.startswith(("chem_", "biology_")):
106
+ return resolved_tool, endpoint
107
+ resolved_endpoint = LEGACY_ENDPOINTS.get(canonical_tool, {}).get(endpoint)
108
+ if not resolved_endpoint:
109
+ raise ValueError(
110
+ f"no legacy science_tool mapping for {canonical_tool}/{endpoint}"
111
+ )
112
+ return resolved_tool, resolved_endpoint
113
+
114
+
115
+ def _unwrap(stdout: str) -> tuple[Any, int]:
116
+ try:
117
+ root = json.loads(stdout)
118
+ except json.JSONDecodeError as exc:
119
+ raise RuntimeError(f"CLI stdout is not JSON: {exc}") from exc
120
+ if not isinstance(root, dict):
121
+ raise RuntimeError("CLI stdout root is not an object")
122
+ if root.get("ok") is not True:
123
+ raise RuntimeError(json.dumps(root.get("error", root), ensure_ascii=False))
124
+ if "data" not in root:
125
+ raise RuntimeError("CLI success envelope is missing data")
126
+
127
+ value: Any = root["data"]
128
+ depth = 0
129
+ while isinstance(value, dict):
130
+ if "status_code" in value and "data" in value:
131
+ status_code = value.get("status_code")
132
+ if not isinstance(status_code, int) or not 200 <= status_code < 300:
133
+ raise RuntimeError(json.dumps(value, ensure_ascii=False))
134
+ value = value["data"]
135
+ elif (
136
+ "code" in value
137
+ and "data" in value
138
+ and set(value).intersection({"message", "msg"})
139
+ ):
140
+ code = value.get("code")
141
+ if code not in (0, "0", None):
142
+ raise RuntimeError(json.dumps(value, ensure_ascii=False))
143
+ value = value["data"]
144
+ else:
145
+ break
146
+ depth += 1
147
+ if depth > 4:
148
+ raise RuntimeError("too many nested service envelopes")
149
+ if isinstance(value, dict) and value.get("valid") is False:
150
+ raise RuntimeError(json.dumps({
151
+ "valid": False,
152
+ "error": value.get("error"),
153
+ "warnings": value.get("warnings"),
154
+ }, ensure_ascii=False))
155
+ return value, depth
156
+
157
+
158
+ def _parser() -> argparse.ArgumentParser:
159
+ parser = argparse.ArgumentParser(description=__doc__)
160
+ parser.add_argument("--tool", required=True)
161
+ parser.add_argument("--endpoint", required=True)
162
+ body = parser.add_mutually_exclusive_group()
163
+ body.add_argument("--data-json")
164
+ body.add_argument("--params-json")
165
+ parser.add_argument("--cli")
166
+ parser.add_argument("--science-base-url")
167
+ parser.add_argument(
168
+ "--catalog-profile",
169
+ choices=("auto", "canonical", "legacy"),
170
+ default="auto",
171
+ )
172
+ parser.add_argument("--timeout", type=float, default=120.0)
173
+ return parser
174
+
175
+
176
+ def main() -> int:
177
+ args = _parser().parse_args()
178
+ started = time.perf_counter()
179
+ stage = "arguments"
180
+ profile = "unknown"
181
+ resolved_tool = args.tool
182
+ resolved_endpoint = args.endpoint
183
+ try:
184
+ data = _json_object(args.data_json, "--data-json")
185
+ params = _json_object(args.params_json, "--params-json")
186
+ runtime = _runtime_config()
187
+ stage = "cli-resolution"
188
+ cli = _resolve_cli(args.cli, runtime)
189
+
190
+ env = os.environ.copy()
191
+ base_url = args.science_base_url or runtime.get("science_base_url")
192
+ if base_url:
193
+ env["DELTA_INFRA_SCIENCE_BASE_URL"] = str(base_url)
194
+ configured_base_url = _cli_science_base_url(cli, env, args.timeout)
195
+ requested_profile = str(runtime.get("catalog_profile", args.catalog_profile))
196
+ if requested_profile not in {"auto", "canonical", "legacy"}:
197
+ raise ValueError("catalog_profile must be auto, canonical, or legacy")
198
+ profile = _catalog_profile(configured_base_url, requested_profile)
199
+ resolved_tool, resolved_endpoint = _resolve_catalog_names(
200
+ args.tool, args.endpoint, profile
201
+ )
202
+
203
+ command = [
204
+ cli, "science", "invoke",
205
+ "--tool", resolved_tool,
206
+ "--endpoint", resolved_endpoint,
207
+ ]
208
+ if data is not None:
209
+ command.extend(["--data", json.dumps(data, ensure_ascii=False, separators=(",", ":"))])
210
+ if params is not None:
211
+ command.extend(["--params", json.dumps(params, ensure_ascii=False, separators=(",", ":"))])
212
+
213
+ stage = "delta-cli"
214
+ completed = subprocess.run(
215
+ command,
216
+ capture_output=True,
217
+ text=True,
218
+ encoding="utf-8",
219
+ errors="replace",
220
+ env=env,
221
+ timeout=args.timeout,
222
+ check=False,
223
+ )
224
+ if completed.returncode != 0:
225
+ detail = completed.stderr.strip() or completed.stdout.strip()
226
+ raise RuntimeError(f"exit_code={completed.returncode}: {detail}")
227
+ stage = "response-validation"
228
+ native, depth = _unwrap(completed.stdout)
229
+ result = {
230
+ "ok": True,
231
+ "transport": "delta-cli",
232
+ "tool": args.tool,
233
+ "endpoint": args.endpoint,
234
+ "resolved_tool": resolved_tool,
235
+ "resolved_endpoint": resolved_endpoint,
236
+ "catalog_profile": profile,
237
+ "elapsed_seconds": round(time.perf_counter() - started, 3),
238
+ "envelope_depth": depth,
239
+ "native": native,
240
+ }
241
+ print(json.dumps(result, ensure_ascii=False))
242
+ return 0
243
+ except subprocess.TimeoutExpired:
244
+ error = f"timeout after {args.timeout}s"
245
+ except (OSError, ValueError, RuntimeError) as exc:
246
+ error = str(exc)
247
+ print(json.dumps({
248
+ "ok": False,
249
+ "transport": "delta-cli",
250
+ "tool": args.tool,
251
+ "endpoint": args.endpoint,
252
+ "resolved_tool": resolved_tool,
253
+ "resolved_endpoint": resolved_endpoint,
254
+ "catalog_profile": profile,
255
+ "stage": stage,
256
+ "elapsed_seconds": round(time.perf_counter() - started, 3),
257
+ "error": error[:2000],
258
+ }, ensure_ascii=False))
259
+ return 1
260
+
261
+
262
+ if __name__ == "__main__":
263
+ sys.exit(main())