ophiolite-sdk 0.1.0__tar.gz

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 (33) hide show
  1. ophiolite_sdk-0.1.0/PKG-INFO +67 -0
  2. ophiolite_sdk-0.1.0/README.md +60 -0
  3. ophiolite_sdk-0.1.0/pyproject.toml +24 -0
  4. ophiolite_sdk-0.1.0/setup.cfg +4 -0
  5. ophiolite_sdk-0.1.0/src/ophiolite_automation/__init__.py +3 -0
  6. ophiolite_sdk-0.1.0/src/ophiolite_automation/bin/ophiolite-cli +0 -0
  7. ophiolite_sdk-0.1.0/src/ophiolite_automation/catalog.py +13 -0
  8. ophiolite_sdk-0.1.0/src/ophiolite_automation/cli.py +468 -0
  9. ophiolite_sdk-0.1.0/src/ophiolite_automation/client.py +368 -0
  10. ophiolite_sdk-0.1.0/src/ophiolite_automation/conformance.py +182 -0
  11. ophiolite_sdk-0.1.0/src/ophiolite_automation/examples/sample.las +46 -0
  12. ophiolite_sdk-0.1.0/src/ophiolite_sdk/__init__.py +108 -0
  13. ophiolite_sdk-0.1.0/src/ophiolite_sdk/analysis.py +48 -0
  14. ophiolite_sdk-0.1.0/src/ophiolite_sdk/avo.py +287 -0
  15. ophiolite_sdk-0.1.0/src/ophiolite_sdk/external.py +116 -0
  16. ophiolite_sdk-0.1.0/src/ophiolite_sdk/external_runner.py +65 -0
  17. ophiolite_sdk-0.1.0/src/ophiolite_sdk/interop.py +63 -0
  18. ophiolite_sdk-0.1.0/src/ophiolite_sdk/logs.py +2037 -0
  19. ophiolite_sdk-0.1.0/src/ophiolite_sdk/models.py +846 -0
  20. ophiolite_sdk-0.1.0/src/ophiolite_sdk/operators.py +3 -0
  21. ophiolite_sdk-0.1.0/src/ophiolite_sdk/platform.py +11 -0
  22. ophiolite_sdk-0.1.0/src/ophiolite_sdk/project.py +369 -0
  23. ophiolite_sdk-0.1.0/src/ophiolite_sdk/seismic.py +1752 -0
  24. ophiolite_sdk-0.1.0/src/ophiolite_sdk/surveys.py +243 -0
  25. ophiolite_sdk-0.1.0/src/ophiolite_sdk/views.py +93 -0
  26. ophiolite_sdk-0.1.0/src/ophiolite_sdk/wells.py +258 -0
  27. ophiolite_sdk-0.1.0/src/ophiolite_sdk.egg-info/PKG-INFO +67 -0
  28. ophiolite_sdk-0.1.0/src/ophiolite_sdk.egg-info/SOURCES.txt +31 -0
  29. ophiolite_sdk-0.1.0/src/ophiolite_sdk.egg-info/dependency_links.txt +1 -0
  30. ophiolite_sdk-0.1.0/src/ophiolite_sdk.egg-info/entry_points.txt +3 -0
  31. ophiolite_sdk-0.1.0/src/ophiolite_sdk.egg-info/top_level.txt +2 -0
  32. ophiolite_sdk-0.1.0/tests/test_log_surfaces.py +65 -0
  33. ophiolite_sdk-0.1.0/tests/test_seismic_surfaces.py +518 -0
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: ophiolite-sdk
3
+ Version: 0.1.0
4
+ Summary: Local-first Python automation and SDK surfaces for Ophiolite
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+
8
+ # ophiolite-sdk
9
+
10
+ `ophiolite-sdk` provides the `ophiolite_sdk` Python package and the local `ophiolite` command surface.
11
+
12
+ It does not introduce a second backend. Automation commands shell out to the existing JSON-producing CLI so scripts, notebooks, and local automation can reuse the same platform-owned operation surface.
13
+
14
+ ## Scope
15
+
16
+ Current wrapped platform operations:
17
+
18
+ - project lifecycle and inventory
19
+ - well, wellbore, and survey discovery
20
+ - well-panel, survey-map, trajectory, and section-overlay resolution
21
+ - operator lock inspection and operator package installation
22
+ - compute catalog lookup and compute execution
23
+ - install doctor and local license certificate inspection
24
+ - import preview, vendor bridge, and fixture-generation utilities
25
+
26
+ The Python wrapper stays checked against the platform operation catalog, so the wrapped CLI surface does not drift silently.
27
+
28
+ ## Usage
29
+
30
+ From this directory:
31
+
32
+ ```bash
33
+ python -m pip install -e .
34
+ ophiolite doctor
35
+ ophiolite auth status
36
+ ophiolite-automation operation-catalog
37
+ ```
38
+
39
+ `ophiolite` is the preferred command name for user-facing install checks. `ophiolite-automation` remains available as the explicit automation wrapper name.
40
+
41
+ Build the preview SDK package with:
42
+
43
+ ```bash
44
+ python -m pip install --upgrade build
45
+ python -m build
46
+ ```
47
+
48
+ Or from Python:
49
+
50
+ ```python
51
+ from ophiolite_automation import OphioliteApp
52
+
53
+ app = OphioliteApp()
54
+ catalog = app.operation_catalog()
55
+ print(catalog["catalog_name"])
56
+ ```
57
+
58
+ ## Surface Conformance
59
+
60
+ Ophiolite keeps a checked-in platform operation catalog at
61
+ `crates/ophiolite-cli/operations/catalog.json`.
62
+
63
+ Run the Python-side conformance check with:
64
+
65
+ ```bash
66
+ ophiolite-automation verify-surface-contracts
67
+ ```
@@ -0,0 +1,60 @@
1
+ # ophiolite-sdk
2
+
3
+ `ophiolite-sdk` provides the `ophiolite_sdk` Python package and the local `ophiolite` command surface.
4
+
5
+ It does not introduce a second backend. Automation commands shell out to the existing JSON-producing CLI so scripts, notebooks, and local automation can reuse the same platform-owned operation surface.
6
+
7
+ ## Scope
8
+
9
+ Current wrapped platform operations:
10
+
11
+ - project lifecycle and inventory
12
+ - well, wellbore, and survey discovery
13
+ - well-panel, survey-map, trajectory, and section-overlay resolution
14
+ - operator lock inspection and operator package installation
15
+ - compute catalog lookup and compute execution
16
+ - install doctor and local license certificate inspection
17
+ - import preview, vendor bridge, and fixture-generation utilities
18
+
19
+ The Python wrapper stays checked against the platform operation catalog, so the wrapped CLI surface does not drift silently.
20
+
21
+ ## Usage
22
+
23
+ From this directory:
24
+
25
+ ```bash
26
+ python -m pip install -e .
27
+ ophiolite doctor
28
+ ophiolite auth status
29
+ ophiolite-automation operation-catalog
30
+ ```
31
+
32
+ `ophiolite` is the preferred command name for user-facing install checks. `ophiolite-automation` remains available as the explicit automation wrapper name.
33
+
34
+ Build the preview SDK package with:
35
+
36
+ ```bash
37
+ python -m pip install --upgrade build
38
+ python -m build
39
+ ```
40
+
41
+ Or from Python:
42
+
43
+ ```python
44
+ from ophiolite_automation import OphioliteApp
45
+
46
+ app = OphioliteApp()
47
+ catalog = app.operation_catalog()
48
+ print(catalog["catalog_name"])
49
+ ```
50
+
51
+ ## Surface Conformance
52
+
53
+ Ophiolite keeps a checked-in platform operation catalog at
54
+ `crates/ophiolite-cli/operations/catalog.json`.
55
+
56
+ Run the Python-side conformance check with:
57
+
58
+ ```bash
59
+ ophiolite-automation verify-surface-contracts
60
+ ```
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ophiolite-sdk"
7
+ version = "0.1.0"
8
+ description = "Local-first Python automation and SDK surfaces for Ophiolite"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = []
12
+
13
+ [project.scripts]
14
+ ophiolite = "ophiolite_automation.cli:main"
15
+ ophiolite-automation = "ophiolite_automation.cli:main"
16
+
17
+ [tool.setuptools]
18
+ package-dir = {"" = "src"}
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
22
+
23
+ [tool.setuptools.package-data]
24
+ ophiolite_automation = ["bin/ophiolite-cli", "bin/ophiolite-cli.exe", "examples/sample.las"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .client import OphioliteApp, OphioliteCommandError
2
+
3
+ __all__ = ["OphioliteApp", "OphioliteCommandError"]
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+
8
+ REPO_ROOT = Path(__file__).resolve().parents[3]
9
+ DEFAULT_OPERATION_CATALOG_PATH = REPO_ROOT / "crates" / "ophiolite-cli" / "operations" / "catalog.json"
10
+
11
+
12
+ def load_operation_catalog(path: Path = DEFAULT_OPERATION_CATALOG_PATH) -> dict[str, Any]:
13
+ return json.loads(path.read_text())
@@ -0,0 +1,468 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Sequence
8
+
9
+ from .client import DEFAULT_MANIFEST_PATH, REPO_ROOT, OphioliteApp, OphioliteCommandError
10
+
11
+
12
+ def build_parser() -> argparse.ArgumentParser:
13
+ parser = argparse.ArgumentParser(description="Python automation wrapper around ophiolite-cli")
14
+ parser.add_argument("--repo-root", default=str(REPO_ROOT))
15
+ parser.add_argument("--manifest-path", default=str(DEFAULT_MANIFEST_PATH))
16
+ parser.add_argument("--binary", default=None)
17
+
18
+ subparsers = parser.add_subparsers(dest="command", required=True)
19
+
20
+ subparsers.add_parser("doctor")
21
+
22
+ auth = subparsers.add_parser("auth")
23
+ auth_subparsers = auth.add_subparsers(dest="auth_command", required=True)
24
+ auth_subparsers.add_parser("status")
25
+ auth_import = auth_subparsers.add_parser("import")
26
+ auth_import.add_argument("license_json_path")
27
+ auth_subparsers.add_parser("login")
28
+
29
+ subparsers.add_parser("operation-catalog")
30
+
31
+ create_project = subparsers.add_parser("create-project")
32
+ create_project.add_argument("project_root")
33
+
34
+ open_project = subparsers.add_parser("open-project")
35
+ open_project.add_argument("project_root")
36
+
37
+ project_summary = subparsers.add_parser("project-summary")
38
+ project_summary.add_argument("project_root")
39
+
40
+ import_project_las = subparsers.add_parser("import-las-asset")
41
+ import_project_las.add_argument("project_root")
42
+ import_project_las.add_argument("las_path")
43
+ import_project_las.add_argument("collection_name", nargs="?")
44
+
45
+ import_project_las_with_binding = subparsers.add_parser("import-las-asset-with-binding")
46
+ import_project_las_with_binding.add_argument("project_root")
47
+ import_project_las_with_binding.add_argument("las_path")
48
+ import_project_las_with_binding.add_argument("binding_json")
49
+ import_project_las_with_binding.add_argument("collection_name", nargs="?")
50
+
51
+ import_project_tops_source_with_binding = subparsers.add_parser(
52
+ "import-tops-source-with-binding"
53
+ )
54
+ import_project_tops_source_with_binding.add_argument("project_root")
55
+ import_project_tops_source_with_binding.add_argument("source_path")
56
+ import_project_tops_source_with_binding.add_argument("binding_json")
57
+ import_project_tops_source_with_binding.add_argument("collection_name", nargs="?")
58
+ import_project_tops_source_with_binding.add_argument("depth_reference", nargs="?")
59
+
60
+ list_project_wells = subparsers.add_parser("list-project-wells")
61
+ list_project_wells.add_argument("project_root")
62
+
63
+ list_project_wellbores = subparsers.add_parser("list-project-wellbores")
64
+ list_project_wellbores.add_argument("project_root")
65
+ list_project_wellbores.add_argument("well_id")
66
+
67
+ list_project_surveys = subparsers.add_parser("list-project-surveys")
68
+ list_project_surveys.add_argument("project_root")
69
+
70
+ resolve_well_panel_source = subparsers.add_parser("resolve-well-panel-source")
71
+ resolve_well_panel_source.add_argument("project_root")
72
+ resolve_well_panel_source.add_argument("request_json")
73
+
74
+ resolve_survey_map_source = subparsers.add_parser("resolve-survey-map-source")
75
+ resolve_survey_map_source.add_argument("project_root")
76
+ resolve_survey_map_source.add_argument("request_json")
77
+
78
+ resolve_wellbore_trajectory = subparsers.add_parser("resolve-wellbore-trajectory")
79
+ resolve_wellbore_trajectory.add_argument("project_root")
80
+ resolve_wellbore_trajectory.add_argument("wellbore_id")
81
+
82
+ resolve_section_well_overlays = subparsers.add_parser("resolve-section-well-overlays")
83
+ resolve_section_well_overlays.add_argument("project_root")
84
+ resolve_section_well_overlays.add_argument("request_json")
85
+
86
+ project_operator_lock = subparsers.add_parser("project-operator-lock")
87
+ project_operator_lock.add_argument("project_root")
88
+
89
+ install_operator_package = subparsers.add_parser("install-operator-package")
90
+ install_operator_package.add_argument("project_root")
91
+ install_operator_package.add_argument("manifest_path")
92
+
93
+ list_project_compute_catalog = subparsers.add_parser("list-project-compute-catalog")
94
+ list_project_compute_catalog.add_argument("project_root")
95
+ list_project_compute_catalog.add_argument("asset_id")
96
+
97
+ list_project_operator_catalog = subparsers.add_parser("list-project-operator-catalog")
98
+ list_project_operator_catalog.add_argument("project_root")
99
+ list_project_operator_catalog.add_argument("asset_id")
100
+
101
+ run_project_compute = subparsers.add_parser("run-project-compute")
102
+ run_project_compute.add_argument("project_root")
103
+ run_project_compute.add_argument("request_json")
104
+
105
+ preview_project_trace_local_processing = subparsers.add_parser(
106
+ "preview-project-trace-local-processing"
107
+ )
108
+ preview_project_trace_local_processing.add_argument("project_root")
109
+ preview_project_trace_local_processing.add_argument("request_json")
110
+
111
+ run_project_trace_local_processing = subparsers.add_parser(
112
+ "run-project-trace-local-processing"
113
+ )
114
+ run_project_trace_local_processing.add_argument("project_root")
115
+ run_project_trace_local_processing.add_argument("request_json")
116
+
117
+ preview_project_subvolume_processing = subparsers.add_parser(
118
+ "preview-project-subvolume-processing"
119
+ )
120
+ preview_project_subvolume_processing.add_argument("project_root")
121
+ preview_project_subvolume_processing.add_argument("request_json")
122
+
123
+ run_project_subvolume_processing = subparsers.add_parser(
124
+ "run-project-subvolume-processing"
125
+ )
126
+ run_project_subvolume_processing.add_argument("project_root")
127
+ run_project_subvolume_processing.add_argument("request_json")
128
+
129
+ preview_project_post_stack_neighborhood_processing = subparsers.add_parser(
130
+ "preview-project-post-stack-neighborhood-processing"
131
+ )
132
+ preview_project_post_stack_neighborhood_processing.add_argument("project_root")
133
+ preview_project_post_stack_neighborhood_processing.add_argument("request_json")
134
+
135
+ run_project_post_stack_neighborhood_processing = subparsers.add_parser(
136
+ "run-project-post-stack-neighborhood-processing"
137
+ )
138
+ run_project_post_stack_neighborhood_processing.add_argument("project_root")
139
+ run_project_post_stack_neighborhood_processing.add_argument("request_json")
140
+
141
+ preview_project_gather_processing = subparsers.add_parser(
142
+ "preview-project-gather-processing"
143
+ )
144
+ preview_project_gather_processing.add_argument("project_root")
145
+ preview_project_gather_processing.add_argument("request_json")
146
+
147
+ run_project_gather_processing = subparsers.add_parser("run-project-gather-processing")
148
+ run_project_gather_processing.add_argument("project_root")
149
+ run_project_gather_processing.add_argument("request_json")
150
+
151
+ run_project_velocity_scan = subparsers.add_parser("run-project-velocity-scan")
152
+ run_project_velocity_scan.add_argument("project_root")
153
+ run_project_velocity_scan.add_argument("request_json")
154
+
155
+ run_avo_reflectivity = subparsers.add_parser("run-avo-reflectivity")
156
+ run_avo_reflectivity.add_argument("request_json")
157
+
158
+ run_rock_physics_attribute = subparsers.add_parser("run-rock-physics-attribute")
159
+ run_rock_physics_attribute.add_argument("request_json")
160
+
161
+ run_avo_intercept_gradient_attribute = subparsers.add_parser(
162
+ "run-avo-intercept-gradient-attribute"
163
+ )
164
+ run_avo_intercept_gradient_attribute.add_argument("request_json")
165
+
166
+ preview_well_folder_import = subparsers.add_parser("preview-well-folder-import")
167
+ preview_well_folder_import.add_argument("folder_path")
168
+
169
+ preview_well_source_import = subparsers.add_parser("preview-well-source-import")
170
+ preview_well_source_import.add_argument("source_root")
171
+ preview_well_source_import.add_argument("source_paths", nargs="+")
172
+
173
+ vendor_scan = subparsers.add_parser("vendor-scan")
174
+ vendor_scan.add_argument("vendor")
175
+ vendor_scan.add_argument("project_root")
176
+
177
+ vendor_plan = subparsers.add_parser("vendor-plan")
178
+ vendor_plan.add_argument("request_json")
179
+
180
+ vendor_commit = subparsers.add_parser("vendor-commit")
181
+ vendor_commit.add_argument("request_json")
182
+
183
+ vendor_runtime_probe = subparsers.add_parser("vendor-runtime-probe")
184
+ vendor_runtime_probe.add_argument("request_json")
185
+
186
+ vendor_connector_contract = subparsers.add_parser("vendor-connector-contract")
187
+ vendor_connector_contract.add_argument("vendor")
188
+
189
+ vendor_bridge_capabilities = subparsers.add_parser("vendor-bridge-capabilities")
190
+ vendor_bridge_capabilities.add_argument("vendor")
191
+
192
+ vendor_bridge_run = subparsers.add_parser("vendor-bridge-run")
193
+ vendor_bridge_run.add_argument("request_json")
194
+
195
+ vendor_bridge_commit = subparsers.add_parser("vendor-bridge-commit")
196
+ vendor_bridge_commit.add_argument("request_json")
197
+
198
+ inspect_horizon_xyz = subparsers.add_parser("inspect-horizon-xyz")
199
+ inspect_horizon_xyz.add_argument("input_paths", nargs="+")
200
+
201
+ preview_horizon_source_import = subparsers.add_parser("preview-horizon-source-import")
202
+ preview_horizon_source_import.add_argument("store_path")
203
+ preview_horizon_source_import.add_argument("input_paths", nargs="+")
204
+
205
+ import_bundle = subparsers.add_parser("import")
206
+ import_bundle.add_argument("input")
207
+ import_bundle.add_argument("bundle_dir")
208
+
209
+ inspect_file = subparsers.add_parser("inspect-file")
210
+ inspect_file.add_argument("input")
211
+
212
+ summary = subparsers.add_parser("summary")
213
+ summary.add_argument("bundle_dir")
214
+
215
+ list_curves = subparsers.add_parser("list-curves")
216
+ list_curves.add_argument("bundle_dir")
217
+
218
+ subparsers.add_parser("examples")
219
+
220
+ generate_fixtures = subparsers.add_parser("generate-fixture-packages")
221
+ generate_fixtures.add_argument("input_root")
222
+ generate_fixtures.add_argument("output_root")
223
+
224
+ subparsers.add_parser("verify-surface-contracts")
225
+
226
+ return parser
227
+
228
+
229
+ def app_from_args(args: argparse.Namespace) -> OphioliteApp:
230
+ return OphioliteApp(
231
+ repo_root=Path(args.repo_root),
232
+ manifest_path=Path(args.manifest_path),
233
+ binary=args.binary,
234
+ )
235
+
236
+
237
+ def main(argv: Sequence[str] | None = None) -> int:
238
+ parser = build_parser()
239
+ args = parser.parse_args(list(argv) if argv is not None else None)
240
+ app = app_from_args(args)
241
+
242
+ try:
243
+ if args.command == "doctor":
244
+ result = app.doctor()
245
+ elif args.command == "auth":
246
+ if args.auth_command == "import":
247
+ result = app.auth("import", args.license_json_path)
248
+ else:
249
+ result = app.auth(args.auth_command)
250
+ elif args.command == "operation-catalog":
251
+ result = app.operation_catalog()
252
+ elif args.command == "create-project":
253
+ result = app.create_project(args.project_root)
254
+ elif args.command == "open-project":
255
+ result = app.open_project(args.project_root)
256
+ elif args.command == "project-summary":
257
+ result = app.project_summary(args.project_root)
258
+ elif args.command == "import-las-asset":
259
+ result = app.import_project_las(args.project_root, args.las_path, args.collection_name)
260
+ elif args.command == "import-las-asset-with-binding":
261
+ if args.binding_json == "-":
262
+ payload = json.load(sys.stdin)
263
+ else:
264
+ with open(args.binding_json, encoding="utf-8") as handle:
265
+ payload = json.load(handle)
266
+ result = app.import_project_las_with_binding(
267
+ args.project_root,
268
+ args.las_path,
269
+ payload,
270
+ args.collection_name,
271
+ )
272
+ elif args.command == "import-tops-source-with-binding":
273
+ if args.binding_json == "-":
274
+ payload = json.load(sys.stdin)
275
+ else:
276
+ with open(args.binding_json, encoding="utf-8") as handle:
277
+ payload = json.load(handle)
278
+ result = app.import_project_tops_source_with_binding(
279
+ args.project_root,
280
+ args.source_path,
281
+ payload,
282
+ None if args.collection_name == "" else args.collection_name,
283
+ args.depth_reference,
284
+ )
285
+ elif args.command == "list-project-wells":
286
+ result = app.list_project_wells(args.project_root)
287
+ elif args.command == "list-project-wellbores":
288
+ result = app.list_project_wellbores(args.project_root, args.well_id)
289
+ elif args.command == "list-project-surveys":
290
+ result = app.list_project_surveys(args.project_root)
291
+ elif args.command == "resolve-well-panel-source":
292
+ if args.request_json == "-":
293
+ payload = json.load(sys.stdin)
294
+ else:
295
+ with open(args.request_json, encoding="utf-8") as handle:
296
+ payload = json.load(handle)
297
+ result = app.resolve_well_panel_source(args.project_root, payload)
298
+ elif args.command == "resolve-survey-map-source":
299
+ if args.request_json == "-":
300
+ payload = json.load(sys.stdin)
301
+ else:
302
+ with open(args.request_json, encoding="utf-8") as handle:
303
+ payload = json.load(handle)
304
+ result = app.resolve_survey_map_source(args.project_root, payload)
305
+ elif args.command == "resolve-wellbore-trajectory":
306
+ result = app.resolve_wellbore_trajectory(args.project_root, args.wellbore_id)
307
+ elif args.command == "resolve-section-well-overlays":
308
+ if args.request_json == "-":
309
+ payload = json.load(sys.stdin)
310
+ else:
311
+ with open(args.request_json, encoding="utf-8") as handle:
312
+ payload = json.load(handle)
313
+ result = app.resolve_section_well_overlays(args.project_root, payload)
314
+ elif args.command == "project-operator-lock":
315
+ result = app.project_operator_lock(args.project_root)
316
+ elif args.command == "install-operator-package":
317
+ result = app.install_operator_package(args.project_root, args.manifest_path)
318
+ elif args.command == "list-project-compute-catalog":
319
+ result = app.list_project_compute_catalog(args.project_root, args.asset_id)
320
+ elif args.command == "list-project-operator-catalog":
321
+ result = app.list_project_operator_catalog(args.project_root, args.asset_id)
322
+ elif args.command == "run-project-compute":
323
+ if args.request_json == "-":
324
+ payload = json.load(sys.stdin)
325
+ else:
326
+ with open(args.request_json, encoding="utf-8") as handle:
327
+ payload = json.load(handle)
328
+ result = app.run_project_compute(args.project_root, payload)
329
+ elif args.command == "preview-project-trace-local-processing":
330
+ payload = _read_json_argument(args.request_json)
331
+ result = app.preview_project_trace_local_processing(args.project_root, payload)
332
+ elif args.command == "run-project-trace-local-processing":
333
+ payload = _read_json_argument(args.request_json)
334
+ result = app.run_project_trace_local_processing(args.project_root, payload)
335
+ elif args.command == "preview-project-subvolume-processing":
336
+ payload = _read_json_argument(args.request_json)
337
+ result = app.preview_project_subvolume_processing(args.project_root, payload)
338
+ elif args.command == "run-project-subvolume-processing":
339
+ payload = _read_json_argument(args.request_json)
340
+ result = app.run_project_subvolume_processing(args.project_root, payload)
341
+ elif args.command == "preview-project-post-stack-neighborhood-processing":
342
+ payload = _read_json_argument(args.request_json)
343
+ result = app.preview_project_post_stack_neighborhood_processing(
344
+ args.project_root, payload
345
+ )
346
+ elif args.command == "run-project-post-stack-neighborhood-processing":
347
+ payload = _read_json_argument(args.request_json)
348
+ result = app.run_project_post_stack_neighborhood_processing(
349
+ args.project_root, payload
350
+ )
351
+ elif args.command == "preview-project-gather-processing":
352
+ payload = _read_json_argument(args.request_json)
353
+ result = app.preview_project_gather_processing(args.project_root, payload)
354
+ elif args.command == "run-project-gather-processing":
355
+ payload = _read_json_argument(args.request_json)
356
+ result = app.run_project_gather_processing(args.project_root, payload)
357
+ elif args.command == "run-project-velocity-scan":
358
+ payload = _read_json_argument(args.request_json)
359
+ result = app.run_project_velocity_scan(args.project_root, payload)
360
+ elif args.command == "run-avo-reflectivity":
361
+ if args.request_json == "-":
362
+ payload = json.load(sys.stdin)
363
+ else:
364
+ with open(args.request_json, encoding="utf-8") as handle:
365
+ payload = json.load(handle)
366
+ result = app.run_avo_reflectivity(payload)
367
+ elif args.command == "run-rock-physics-attribute":
368
+ if args.request_json == "-":
369
+ payload = json.load(sys.stdin)
370
+ else:
371
+ with open(args.request_json, encoding="utf-8") as handle:
372
+ payload = json.load(handle)
373
+ result = app.run_rock_physics_attribute(payload)
374
+ elif args.command == "run-avo-intercept-gradient-attribute":
375
+ if args.request_json == "-":
376
+ payload = json.load(sys.stdin)
377
+ else:
378
+ with open(args.request_json, encoding="utf-8") as handle:
379
+ payload = json.load(handle)
380
+ result = app.run_avo_intercept_gradient_attribute(payload)
381
+ elif args.command == "preview-well-folder-import":
382
+ result = app.preview_well_folder_import(args.folder_path)
383
+ elif args.command == "preview-well-source-import":
384
+ result = app.preview_well_source_import(args.source_root, args.source_paths)
385
+ elif args.command == "vendor-scan":
386
+ result = app.vendor_scan(args.vendor, args.project_root)
387
+ elif args.command == "vendor-plan":
388
+ if args.request_json == "-":
389
+ payload = json.load(sys.stdin)
390
+ else:
391
+ with open(args.request_json, encoding="utf-8") as handle:
392
+ payload = json.load(handle)
393
+ result = app.vendor_plan(payload)
394
+ elif args.command == "vendor-commit":
395
+ if args.request_json == "-":
396
+ payload = json.load(sys.stdin)
397
+ else:
398
+ with open(args.request_json, encoding="utf-8") as handle:
399
+ payload = json.load(handle)
400
+ result = app.vendor_commit(payload)
401
+ elif args.command == "vendor-runtime-probe":
402
+ if args.request_json == "-":
403
+ payload = json.load(sys.stdin)
404
+ else:
405
+ with open(args.request_json, encoding="utf-8") as handle:
406
+ payload = json.load(handle)
407
+ result = app.vendor_runtime_probe(payload)
408
+ elif args.command == "vendor-connector-contract":
409
+ result = app.vendor_connector_contract(args.vendor)
410
+ elif args.command == "vendor-bridge-capabilities":
411
+ result = app.vendor_bridge_capabilities(args.vendor)
412
+ elif args.command == "vendor-bridge-run":
413
+ if args.request_json == "-":
414
+ payload = json.load(sys.stdin)
415
+ else:
416
+ with open(args.request_json, encoding="utf-8") as handle:
417
+ payload = json.load(handle)
418
+ result = app.vendor_bridge_run(payload)
419
+ elif args.command == "vendor-bridge-commit":
420
+ if args.request_json == "-":
421
+ payload = json.load(sys.stdin)
422
+ else:
423
+ with open(args.request_json, encoding="utf-8") as handle:
424
+ payload = json.load(handle)
425
+ result = app.vendor_bridge_commit(payload)
426
+ elif args.command == "inspect-horizon-xyz":
427
+ result = app.inspect_horizon_xyz(args.input_paths)
428
+ elif args.command == "preview-horizon-source-import":
429
+ result = app.preview_horizon_source_import(args.store_path, args.input_paths)
430
+ elif args.command == "import":
431
+ result = app.import_las_bundle(args.input, args.bundle_dir)
432
+ elif args.command == "inspect-file":
433
+ result = app.inspect_las_file(args.input)
434
+ elif args.command == "summary":
435
+ result = app.open_bundle_summary(args.bundle_dir)
436
+ elif args.command == "list-curves":
437
+ result = app.list_bundle_curves(args.bundle_dir)
438
+ elif args.command == "examples":
439
+ result = app.examples_root()
440
+ elif args.command == "generate-fixture-packages":
441
+ result = app.generate_fixture_packages(args.input_root, args.output_root)
442
+ elif args.command == "verify-surface-contracts":
443
+ from .conformance import verify_surface_contracts
444
+
445
+ result = verify_surface_contracts()
446
+ print(json.dumps(result, indent=2))
447
+ return 0 if result["ok"] else 1
448
+ else:
449
+ parser.error(f"unsupported command: {args.command}")
450
+ except OphioliteCommandError as exc:
451
+ if exc.stderr:
452
+ print(exc.stderr, file=sys.stderr)
453
+ print(str(exc), file=sys.stderr)
454
+ return 1
455
+
456
+ print(json.dumps(result, indent=2))
457
+ return 0
458
+
459
+
460
+ def _read_json_argument(argument: str) -> object:
461
+ if argument == "-":
462
+ return json.load(sys.stdin)
463
+ with open(argument, encoding="utf-8") as handle:
464
+ return json.load(handle)
465
+
466
+
467
+ if __name__ == "__main__":
468
+ raise SystemExit(main())