orchestra-cli 0.0.4__tar.gz → 0.1.1__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 (29) hide show
  1. orchestra_cli-0.1.1/PKG-INFO +193 -0
  2. orchestra_cli-0.1.1/README.md +182 -0
  3. orchestra_cli-0.1.1/orchestra_cli/src/cli.py +12 -0
  4. orchestra_cli-0.1.1/orchestra_cli/src/import_pipeline.py +202 -0
  5. orchestra_cli-0.1.1/orchestra_cli/src/run_pipeline.py +182 -0
  6. orchestra_cli-0.0.4/orchestra_cli/src/cli.py → orchestra_cli-0.1.1/orchestra_cli/src/validate_pipeline.py +11 -59
  7. orchestra_cli-0.1.1/orchestra_cli/tests/__init__.py +0 -0
  8. orchestra_cli-0.1.1/orchestra_cli/tests/conftest.py +14 -0
  9. {orchestra_cli-0.0.4 → orchestra_cli-0.1.1}/orchestra_cli/tests/test_cli.py +1 -6
  10. orchestra_cli-0.1.1/orchestra_cli/tests/test_import_pipeline.py +252 -0
  11. orchestra_cli-0.1.1/orchestra_cli/tests/test_run_pipeline.py +260 -0
  12. orchestra_cli-0.1.1/orchestra_cli/tests/test_validate_pipeline.py +11 -0
  13. orchestra_cli-0.1.1/orchestra_cli/utils/constants.py +7 -0
  14. orchestra_cli-0.1.1/orchestra_cli/utils/git.py +50 -0
  15. orchestra_cli-0.1.1/orchestra_cli/utils/styling.py +22 -0
  16. orchestra_cli-0.1.1/orchestra_cli.egg-info/PKG-INFO +193 -0
  17. orchestra_cli-0.1.1/orchestra_cli.egg-info/SOURCES.txt +22 -0
  18. {orchestra_cli-0.0.4 → orchestra_cli-0.1.1}/orchestra_cli.egg-info/entry_points.txt +1 -0
  19. {orchestra_cli-0.0.4 → orchestra_cli-0.1.1}/orchestra_cli.egg-info/requires.txt +0 -3
  20. orchestra_cli-0.1.1/pyproject.toml +53 -0
  21. orchestra_cli-0.0.4/PKG-INFO +0 -32
  22. orchestra_cli-0.0.4/README.md +0 -19
  23. orchestra_cli-0.0.4/orchestra_cli.egg-info/PKG-INFO +0 -32
  24. orchestra_cli-0.0.4/orchestra_cli.egg-info/SOURCES.txt +0 -11
  25. orchestra_cli-0.0.4/pyproject.toml +0 -20
  26. {orchestra_cli-0.0.4 → orchestra_cli-0.1.1}/orchestra_cli/src/__init__.py +0 -0
  27. {orchestra_cli-0.0.4 → orchestra_cli-0.1.1}/orchestra_cli.egg-info/dependency_links.txt +0 -0
  28. {orchestra_cli-0.0.4 → orchestra_cli-0.1.1}/orchestra_cli.egg-info/top_level.txt +0 -0
  29. {orchestra_cli-0.0.4 → orchestra_cli-0.1.1}/setup.cfg +0 -0
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: orchestra-cli
3
+ Version: 0.1.1
4
+ Summary: Orchestra CLI. Perform operations with Orchestra locally.
5
+ Author-email: Orchestra Technologies <support@getorchestra.io>
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: httpx>=0.28.1
9
+ Requires-Dist: pyyaml>=6.0.2
10
+ Requires-Dist: typer>=0.16.0
11
+
12
+ # orchestra-cli
13
+
14
+ Orchestra CLI for working with Orchestra pipelines from your terminal.
15
+
16
+ Two entrypoints are available: `orchestra` and `orchestra-cli` (they are equivalent).
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install orchestra-cli
22
+ ```
23
+
24
+ Or with pipx:
25
+
26
+ ```bash
27
+ pipx install orchestra-cli
28
+ ```
29
+
30
+ ## Environment variables
31
+
32
+ - `ORCHESTRA_API_KEY`: Required for actions that call the API (`import`, `run`).
33
+ - `BASE_URL`: Optional. Override the default API base
34
+ (`https://app.getorchestra.io/api/engine/public/pipelines/{}`) for non‑production/testing.
35
+
36
+ ## Commands overview
37
+
38
+ - `validate`: Validate a pipeline YAML locally against the Orchestra API schema.
39
+ - `import`: Register a pipeline YAML (from a git repo) with Orchestra under an alias.
40
+ - `run`: Start a pipeline run by alias, optionally pinning branch/commit and waiting for completion.
41
+
42
+ Use `orchestra --help` or `orchestra <command> --help` for built-in help.
43
+
44
+ ---
45
+
46
+ ## validate
47
+
48
+ Validate a YAML file against the Orchestra API schema.
49
+
50
+ ```bash
51
+ orchestra validate path/to/pipeline.yaml
52
+ # or
53
+ orchestra-cli validate path/to/pipeline.yaml
54
+ ```
55
+
56
+ Options
57
+
58
+ - `file` (positional): Path to the YAML file to validate.
59
+
60
+ Behavior
61
+
62
+ - Prints a success message on valid input.
63
+ - On validation errors, prints the failing location(s), readable messages, and a YAML snippet when possible.
64
+ - Exit codes: `0` on success, `1` on invalid file/validation failure/HTTP error.
65
+
66
+ Example output (failure)
67
+
68
+ ```text
69
+ ❌ Validation failed with status 422
70
+ Error at: pipeline.tasks[0].type
71
+ Invalid task type "foo"
72
+
73
+ YAML snippet:
74
+ pipeline:
75
+ tasks:
76
+ - type: foo
77
+ ```
78
+
79
+ ---
80
+
81
+ ## import
82
+
83
+ Create (import) a pipeline in Orchestra by referencing a YAML file inside a git repository. The command infers your repository host/provider, default branch, and YAML path relative to the repo root.
84
+
85
+ ```bash
86
+ export ORCHESTRA_API_KEY=... # required
87
+
88
+ orchestra import \
89
+ --alias my-pipeline \
90
+ --path ./pipelines/pipeline.yaml
91
+ # or
92
+ orchestra-cli import -a my-pipeline -p ./pipelines/pipeline.yaml
93
+ ```
94
+
95
+ Options
96
+
97
+ - `-a, --alias` (required): The alias you want to register the pipeline under.
98
+ - `-p, --path` (required): Path to the YAML file. Must be inside a git repository.
99
+
100
+ Notes
101
+
102
+ - The YAML is validated with the API before import; failures are printed clearly.
103
+ - Git details are detected automatically:
104
+ - Supported providers: GitHub, GitLab, Azure DevOps.
105
+ - The default branch is detected from `origin`.
106
+ - The YAML path is computed relative to the repository root.
107
+ - On success, the command prints the created pipeline ID (or a success message).
108
+ - Exit codes: `0` on success, `1` on failure.
109
+
110
+ Common errors
111
+
112
+ - Missing `ORCHESTRA_API_KEY`.
113
+ - Not running inside a git repository, or no `origin` remote configured.
114
+ - Could not detect storage provider or default branch.
115
+
116
+ ---
117
+
118
+ ## run
119
+
120
+ Start a pipeline run by alias. Optionally specify a branch and/or commit. By default, the command waits and polls the run status until completion.
121
+
122
+ ```bash
123
+ export ORCHESTRA_API_KEY=...
124
+
125
+ # Start and wait for completion
126
+ orchestra run --alias my-pipeline
127
+
128
+ # Start without waiting (prints run id and exits)
129
+ orchestra run -a my-pipeline --no-wait
130
+
131
+ # Start for a specific branch/commit
132
+ orchestra run -a my-pipeline -b feature/my-change -c 0123abc
133
+ ```
134
+
135
+ Options
136
+
137
+ - `-a, --alias` (required): Pipeline alias to run.
138
+ - `-b, --branch` (optional): Git branch name to associate with this run.
139
+ - `-c, --commit` (optional): Commit SHA to associate with this run.
140
+ - `--wait/--no-wait` (default: `--wait`): Poll until the run ends.
141
+ - `--force/--no-force` (default: `--no-force`): Skip confirmation if local git warnings are detected.
142
+
143
+ Behavior
144
+
145
+ - Prints the run ID when known and a link to the run lineage page.
146
+ - When waiting, polls status every ~5s until a terminal state:
147
+ - `SUCCEEDED` (exit `0`), `WARNING` (exit `0`), `SKIPPED` (exit `0`)
148
+ - `FAILED` or `CANCELLED` (exit `1`)
149
+ - When not waiting, exits after start and prints the run ID.
150
+
151
+ Non-interactive usage
152
+
153
+ - If your repo has warnings (e.g., uncommitted changes), the CLI prompts for confirmation unless `--force` is provided. For CI or scripts, pass `--force` or ensure a clean repo.
154
+
155
+ ---
156
+
157
+ ## Examples
158
+
159
+ ```bash
160
+ # Validate a pipeline file
161
+ orchestra validate ./examples/etl.yaml
162
+
163
+ # Import a pipeline and capture the created ID
164
+ PIPELINE_ID=$(orchestra import -a finance-etl -p ./pipelines/etl.yaml)
165
+
166
+ # Start a run and wait for completion
167
+ orchestra run -a finance-etl
168
+
169
+ # Start a run and exit immediately
170
+ orchestra run -a finance-etl --no-wait
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Development
176
+
177
+ - Make sure [uv](https://github.com/astral-sh/uv) is installed
178
+ - Use `uv pip install -e ".[dev]"` to install the CLI in editable mode for development
179
+ - For local development, run `uv run orchestra` to start the CLI
180
+ - you can use `uv run --env-file .env ...` to run the CLI with env vars
181
+ - For testing, run `uv run pytest`
182
+ - For linting, run `uv run ruff check .`
183
+ - For formatting, run `uv run black --check .`
184
+ - For type checking, run `uv run pyright`
185
+
186
+ ## Building and Releasing
187
+
188
+ - Bump the version in `pyproject.toml` or by running `uv version --bump <major/minor/patch>`
189
+ - Run `uv sync` to install the dependencies
190
+ - Run `uv build` to build the CLI
191
+ - Run `uv publish` to publish the CLI (you will need to pass the `--token` flag)
192
+
193
+ **Note: Failure to bump the version will result in a failed release.**
@@ -0,0 +1,182 @@
1
+ # orchestra-cli
2
+
3
+ Orchestra CLI for working with Orchestra pipelines from your terminal.
4
+
5
+ Two entrypoints are available: `orchestra` and `orchestra-cli` (they are equivalent).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install orchestra-cli
11
+ ```
12
+
13
+ Or with pipx:
14
+
15
+ ```bash
16
+ pipx install orchestra-cli
17
+ ```
18
+
19
+ ## Environment variables
20
+
21
+ - `ORCHESTRA_API_KEY`: Required for actions that call the API (`import`, `run`).
22
+ - `BASE_URL`: Optional. Override the default API base
23
+ (`https://app.getorchestra.io/api/engine/public/pipelines/{}`) for non‑production/testing.
24
+
25
+ ## Commands overview
26
+
27
+ - `validate`: Validate a pipeline YAML locally against the Orchestra API schema.
28
+ - `import`: Register a pipeline YAML (from a git repo) with Orchestra under an alias.
29
+ - `run`: Start a pipeline run by alias, optionally pinning branch/commit and waiting for completion.
30
+
31
+ Use `orchestra --help` or `orchestra <command> --help` for built-in help.
32
+
33
+ ---
34
+
35
+ ## validate
36
+
37
+ Validate a YAML file against the Orchestra API schema.
38
+
39
+ ```bash
40
+ orchestra validate path/to/pipeline.yaml
41
+ # or
42
+ orchestra-cli validate path/to/pipeline.yaml
43
+ ```
44
+
45
+ Options
46
+
47
+ - `file` (positional): Path to the YAML file to validate.
48
+
49
+ Behavior
50
+
51
+ - Prints a success message on valid input.
52
+ - On validation errors, prints the failing location(s), readable messages, and a YAML snippet when possible.
53
+ - Exit codes: `0` on success, `1` on invalid file/validation failure/HTTP error.
54
+
55
+ Example output (failure)
56
+
57
+ ```text
58
+ ❌ Validation failed with status 422
59
+ Error at: pipeline.tasks[0].type
60
+ Invalid task type "foo"
61
+
62
+ YAML snippet:
63
+ pipeline:
64
+ tasks:
65
+ - type: foo
66
+ ```
67
+
68
+ ---
69
+
70
+ ## import
71
+
72
+ Create (import) a pipeline in Orchestra by referencing a YAML file inside a git repository. The command infers your repository host/provider, default branch, and YAML path relative to the repo root.
73
+
74
+ ```bash
75
+ export ORCHESTRA_API_KEY=... # required
76
+
77
+ orchestra import \
78
+ --alias my-pipeline \
79
+ --path ./pipelines/pipeline.yaml
80
+ # or
81
+ orchestra-cli import -a my-pipeline -p ./pipelines/pipeline.yaml
82
+ ```
83
+
84
+ Options
85
+
86
+ - `-a, --alias` (required): The alias you want to register the pipeline under.
87
+ - `-p, --path` (required): Path to the YAML file. Must be inside a git repository.
88
+
89
+ Notes
90
+
91
+ - The YAML is validated with the API before import; failures are printed clearly.
92
+ - Git details are detected automatically:
93
+ - Supported providers: GitHub, GitLab, Azure DevOps.
94
+ - The default branch is detected from `origin`.
95
+ - The YAML path is computed relative to the repository root.
96
+ - On success, the command prints the created pipeline ID (or a success message).
97
+ - Exit codes: `0` on success, `1` on failure.
98
+
99
+ Common errors
100
+
101
+ - Missing `ORCHESTRA_API_KEY`.
102
+ - Not running inside a git repository, or no `origin` remote configured.
103
+ - Could not detect storage provider or default branch.
104
+
105
+ ---
106
+
107
+ ## run
108
+
109
+ Start a pipeline run by alias. Optionally specify a branch and/or commit. By default, the command waits and polls the run status until completion.
110
+
111
+ ```bash
112
+ export ORCHESTRA_API_KEY=...
113
+
114
+ # Start and wait for completion
115
+ orchestra run --alias my-pipeline
116
+
117
+ # Start without waiting (prints run id and exits)
118
+ orchestra run -a my-pipeline --no-wait
119
+
120
+ # Start for a specific branch/commit
121
+ orchestra run -a my-pipeline -b feature/my-change -c 0123abc
122
+ ```
123
+
124
+ Options
125
+
126
+ - `-a, --alias` (required): Pipeline alias to run.
127
+ - `-b, --branch` (optional): Git branch name to associate with this run.
128
+ - `-c, --commit` (optional): Commit SHA to associate with this run.
129
+ - `--wait/--no-wait` (default: `--wait`): Poll until the run ends.
130
+ - `--force/--no-force` (default: `--no-force`): Skip confirmation if local git warnings are detected.
131
+
132
+ Behavior
133
+
134
+ - Prints the run ID when known and a link to the run lineage page.
135
+ - When waiting, polls status every ~5s until a terminal state:
136
+ - `SUCCEEDED` (exit `0`), `WARNING` (exit `0`), `SKIPPED` (exit `0`)
137
+ - `FAILED` or `CANCELLED` (exit `1`)
138
+ - When not waiting, exits after start and prints the run ID.
139
+
140
+ Non-interactive usage
141
+
142
+ - If your repo has warnings (e.g., uncommitted changes), the CLI prompts for confirmation unless `--force` is provided. For CI or scripts, pass `--force` or ensure a clean repo.
143
+
144
+ ---
145
+
146
+ ## Examples
147
+
148
+ ```bash
149
+ # Validate a pipeline file
150
+ orchestra validate ./examples/etl.yaml
151
+
152
+ # Import a pipeline and capture the created ID
153
+ PIPELINE_ID=$(orchestra import -a finance-etl -p ./pipelines/etl.yaml)
154
+
155
+ # Start a run and wait for completion
156
+ orchestra run -a finance-etl
157
+
158
+ # Start a run and exit immediately
159
+ orchestra run -a finance-etl --no-wait
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Development
165
+
166
+ - Make sure [uv](https://github.com/astral-sh/uv) is installed
167
+ - Use `uv pip install -e ".[dev]"` to install the CLI in editable mode for development
168
+ - For local development, run `uv run orchestra` to start the CLI
169
+ - you can use `uv run --env-file .env ...` to run the CLI with env vars
170
+ - For testing, run `uv run pytest`
171
+ - For linting, run `uv run ruff check .`
172
+ - For formatting, run `uv run black --check .`
173
+ - For type checking, run `uv run pyright`
174
+
175
+ ## Building and Releasing
176
+
177
+ - Bump the version in `pyproject.toml` or by running `uv version --bump <major/minor/patch>`
178
+ - Run `uv sync` to install the dependencies
179
+ - Run `uv build` to build the CLI
180
+ - Run `uv publish` to publish the CLI (you will need to pass the `--token` flag)
181
+
182
+ **Note: Failure to bump the version will result in a failed release.**
@@ -0,0 +1,12 @@
1
+ import typer
2
+
3
+ from .import_pipeline import import_pipeline
4
+ from .run_pipeline import run_pipeline
5
+ from .validate_pipeline import validate
6
+
7
+ app = typer.Typer(help="Orchestra CLI – perform operations with Orchestra locally.")
8
+
9
+
10
+ app.command(name="validate")(validate)
11
+ app.command(name="import")(import_pipeline)
12
+ app.command(name="run")(run_pipeline)
@@ -0,0 +1,202 @@
1
+ import json
2
+ import os
3
+ import re
4
+ from pathlib import Path
5
+
6
+ import httpx
7
+ import typer
8
+ import yaml
9
+
10
+ from ..utils.constants import get_api_url
11
+ from ..utils.git import detect_repo_root, git_warnings, run_git_command
12
+ from ..utils.styling import bold, green, indent_message, red, yellow
13
+
14
+
15
+ def _detect_repository_url(repo_root: Path) -> str | None:
16
+ ok, remote = run_git_command(["remote", "get-url", "origin"], repo_root)
17
+ if not ok or not remote:
18
+ return None
19
+
20
+ # First, handle special cases like Azure URLs by removing segments like /_git/
21
+ cleaned_remote = re.sub(r"/(?:_git|scm|v3)(?=/)", "", remote.strip())
22
+
23
+ # A single regex can then capture the owner/repo from the cleaned URL
24
+ pattern = r".*[:/]([^/]+)/([^/]+?)(?:\.git)?/?$"
25
+
26
+ if match := re.search(pattern, cleaned_remote):
27
+ return f"{match.group(1)}/{match.group(2)}"
28
+
29
+ return None
30
+
31
+
32
+ def _get_remote_url(repo_root: Path) -> str | None:
33
+ """Return the raw git remote URL for origin without transformation."""
34
+ ok, remote = run_git_command(["remote", "get-url", "origin"], repo_root)
35
+ if not ok:
36
+ return None
37
+ return remote
38
+
39
+
40
+ def _detect_default_branch(repo_root: Path) -> str | None:
41
+ # Try symbolic-ref of remote HEAD first
42
+ ok, out = run_git_command(["symbolic-ref", "refs/remotes/origin/HEAD"], repo_root)
43
+ if ok and out:
44
+ # refs/remotes/origin/main -> main
45
+ return out.split("/")[-1]
46
+ # Fallback to parsing `git remote show origin`
47
+ ok, out = run_git_command(["remote", "show", "origin"], repo_root)
48
+ if ok and out:
49
+ m = re.search(r"HEAD branch:\s*(\S+)", out)
50
+ if m:
51
+ return m.group(1)
52
+ return None
53
+
54
+
55
+ def _detect_storage_provider(repository_url: str | None) -> str:
56
+ if not repository_url:
57
+ typer.echo(red("Could not detect storage provider - no repository URL"))
58
+ raise typer.Exit(code=1)
59
+ url = repository_url.lower()
60
+ if "github.com" in url:
61
+ return "GITHUB"
62
+ if "gitlab.com" in url:
63
+ return "GITLAB"
64
+ if any(host in url for host in ["dev.azure.com", "azure.com", "visualstudio.com"]):
65
+ return "AZURE_DEVOPS"
66
+ typer.echo(red("Could not detect storage provider - no matching host"))
67
+ raise typer.Exit(code=1)
68
+
69
+
70
+ def _load_yaml(file: Path) -> tuple[dict | None, str | None]:
71
+ try:
72
+ with file.open("r") as f:
73
+ data = yaml.safe_load(f)
74
+ return data, None
75
+ except Exception as e:
76
+ return None, str(e)
77
+
78
+
79
+ def _validate_yaml_with_api(data: dict) -> tuple[bool, str | None]:
80
+ try:
81
+ response = httpx.post(get_api_url("schema"), json=data, timeout=15)
82
+ except Exception as e:
83
+ return False, f"HTTP request failed: {e}"
84
+
85
+ if response.status_code == 200:
86
+ return True, None
87
+ try:
88
+ errors = response.json()
89
+ return False, json.dumps(errors, indent=2)
90
+ except Exception:
91
+ return False, response.text
92
+
93
+
94
+ def import_pipeline(
95
+ alias: str = typer.Option(..., "--alias", "-a", help="Pipeline alias"),
96
+ path: Path = typer.Option(
97
+ ...,
98
+ "--path",
99
+ "-p",
100
+ exists=True,
101
+ file_okay=True,
102
+ dir_okay=False,
103
+ readable=True,
104
+ resolve_path=True,
105
+ help="Path to pipeline YAML inside a git repository",
106
+ ),
107
+ ):
108
+ """
109
+ Create a pipeline in Orchestra by referencing a YAML file in your git repository.
110
+ """
111
+
112
+ if not path.exists():
113
+ typer.echo(red(f"File not found: {path}"))
114
+ raise typer.Exit(code=1)
115
+
116
+ api_key = os.getenv("ORCHESTRA_API_KEY")
117
+ if not api_key:
118
+ typer.echo(red("ORCHESTRA_API_KEY is not set"))
119
+ raise typer.Exit(code=1)
120
+
121
+ # Load and validate YAML with API first
122
+ data, err = _load_yaml(path)
123
+ if err is not None:
124
+ typer.echo(red(f"Invalid YAML: {err}"))
125
+ raise typer.Exit(code=1)
126
+
127
+ ok, err_msg = _validate_yaml_with_api(data or {})
128
+ if not ok:
129
+ typer.echo(red("❌ Validation failed"))
130
+ if err_msg:
131
+ typer.echo(yellow(indent_message(err_msg)))
132
+ raise typer.Exit(code=1)
133
+
134
+ # Detect git repository info
135
+ repo_root = detect_repo_root(path.parent)
136
+ if repo_root is None:
137
+ typer.echo(red("Not a git repository (could not detect repository root)"))
138
+ raise typer.Exit(code=1)
139
+
140
+ repository_slug = _detect_repository_url(repo_root)
141
+ if not repository_slug:
142
+ typer.echo(red("Could not detect repository URL from git"))
143
+ raise typer.Exit(code=1)
144
+ default_branch = _detect_default_branch(repo_root)
145
+ if not default_branch:
146
+ typer.echo(red("Could not detect default branch from git"))
147
+ raise typer.Exit(code=1)
148
+
149
+ # Compute YAML path relative to repo root
150
+ try:
151
+ yaml_path = str(path.resolve().relative_to(repo_root.resolve()))
152
+ except Exception:
153
+ typer.echo(red("YAML file must be inside the git repository"))
154
+ raise typer.Exit(code=1)
155
+
156
+ # Show warnings
157
+ for w in git_warnings(repo_root):
158
+ typer.echo(yellow(f"⚠ {w}"))
159
+
160
+ payload = {
161
+ "storage_provider": _detect_storage_provider(_get_remote_url(repo_root)),
162
+ "repository": repository_slug,
163
+ "default_branch": default_branch,
164
+ "yaml_path": yaml_path,
165
+ "alias": alias,
166
+ }
167
+
168
+ try:
169
+ response = httpx.post(
170
+ get_api_url("import"),
171
+ json=payload,
172
+ timeout=30,
173
+ headers={"Authorization": f"Bearer {api_key}"},
174
+ )
175
+ except Exception as e:
176
+ typer.echo(red(f"HTTP request failed: {e}"))
177
+ raise typer.Exit(code=1)
178
+
179
+ if response.status_code == 201:
180
+ try:
181
+ body = response.json()
182
+ except Exception:
183
+ body = {}
184
+ pipeline_id = body.get("id")
185
+ if pipeline_id:
186
+ # Print only the pipeline id as requested
187
+ typer.echo(f"Pipeline with alias '{alias}' imported successfully: {pipeline_id}")
188
+ raise typer.Exit(code=0)
189
+ else:
190
+ # Fallback if server does not return a field we expect
191
+ typer.echo(green("✅ Pipeline imported successfully"))
192
+ if body:
193
+ typer.echo(bold(json.dumps(body)))
194
+ raise typer.Exit(code=0)
195
+
196
+ # Error handling for non-201 responses
197
+ typer.echo(red(f"❌ Import failed with status {response.status_code}"))
198
+ try:
199
+ typer.echo(yellow(indent_message(json.dumps(response.json(), indent=2))))
200
+ except Exception:
201
+ typer.echo(yellow(indent_message(response.text)))
202
+ raise typer.Exit(code=1)