envloop-cli 0.2.0__tar.gz → 0.3.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. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/PKG-INFO +32 -11
  2. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/README.pypi.md +28 -8
  3. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/agent_runs.py +8 -8
  4. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/auth_cli.py +4 -1
  5. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/cli.py +188 -88
  6. envloop_cli-0.3.0/envloop/cli_errors.py +248 -0
  7. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/cli_output.py +53 -20
  8. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/client.py +119 -8
  9. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/http.py +67 -13
  10. envloop_cli-0.3.0/envloop/job_logs.py +16 -0
  11. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/navigation_resources.py +105 -18
  12. envloop_cli-0.3.0/envloop/profile_secrets.py +27 -0
  13. envloop_cli-0.3.0/envloop/profile_updates.py +49 -0
  14. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/resource_cli.py +40 -38
  15. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/resources.py +4 -2
  16. envloop_cli-0.3.0/envloop/sandbox.py +27 -0
  17. envloop_cli-0.3.0/envloop/train.py +68 -0
  18. envloop_cli-0.3.0/envloop/train_log_cli.py +97 -0
  19. envloop_cli-0.3.0/envloop/training/__init__.py +121 -0
  20. envloop_cli-0.3.0/envloop/training/service_client.py +108 -0
  21. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/pyproject.toml +4 -5
  22. envloop_cli-0.2.0/envloop/train.py +0 -51
  23. envloop_cli-0.2.0/envloop/training/__init__.py +0 -128
  24. envloop_cli-0.2.0/envloop/training/service_client.py +0 -40
  25. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/.gitignore +0 -0
  26. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/__init__.py +0 -0
  27. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/auth_login.py +0 -0
  28. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/auth_workspace.py +0 -0
  29. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/config.py +0 -0
  30. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/lifecycle_cli.py +0 -0
  31. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/training/credentials.py +0 -0
  32. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/utils/__init__.py +0 -0
  33. {envloop_cli-0.2.0 → envloop_cli-0.3.0}/envloop/utils/logging.py +0 -0
@@ -1,16 +1,17 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: envloop-cli
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: EnvLoop CLI and Python SDK for jobs, training, checkpoints and inference
5
5
  Project-URL: Homepage, https://envloop.ai
6
6
  Project-URL: Documentation, https://docs.envloop.ai
7
+ Project-URL: Repository, https://github.com/EnvLoop/envloop-cli
8
+ Project-URL: Issues, https://github.com/EnvLoop/envloop-cli/issues
7
9
  Project-URL: Agent instructions, https://docs.envloop.ai/skill.md
8
10
  Requires-Python: >=3.11
9
- Provides-Extra: train
10
- Requires-Dist: tinker==0.30.2; extra == 'train'
11
+ Requires-Dist: tinker==0.30.2
11
12
  Description-Content-Type: text/markdown
12
13
 
13
- # EnvLoop CLI and Python SDK
14
+ # envloop-cli: EnvLoop CLI and Python SDK
14
15
 
15
16
  Install the public client to run agent Jobs, inspect Trials, manage training and
16
17
  checkpoints, and deploy or call inference endpoints. The `el` and `envloop`
@@ -21,19 +22,17 @@ commands are equivalent. The Python import package is `envloop`.
21
22
  Use a Python virtual environment:
22
23
 
23
24
  ```bash
24
- python -m pip install envloop-cli
25
+ python -m pip install --upgrade envloop-cli
25
26
  el --help
26
27
  ```
27
28
 
28
- For Python training workflows, install the optional training dependency:
29
+ The standard installation includes training support:
29
30
 
30
31
  ```bash
31
- python -m pip install 'envloop-cli[train]'
32
32
  python -c 'from envloop import Client, training; print("EnvLoop SDK ready")'
33
33
  ```
34
34
 
35
- The base client uses Python's standard library. The training extra installs the
36
- training protocol client. You do not need a source checkout, GitHub account,
35
+ The package installs the training protocol client by default. You do not need a source checkout, GitHub account,
37
36
  local execution backend, Docker, or a GPU to install the client.
38
37
 
39
38
  ## Connect
@@ -60,9 +59,11 @@ your secret manager. Public API access is scoped to the authorized project.
60
59
 
61
60
  ```bash
62
61
  el job list --limit 5
63
- el train list --limit 5
62
+ el job list --type train --limit 5
63
+ el trail list --limit 5
64
+ el trail list --type train --job-id JOB_ID
64
65
  el checkpoint list --limit 5
65
- el inference endpoint list
66
+ el job list --type inference
66
67
  ```
67
68
 
68
69
  Query a returned Job ID with `el job get JOB_ID` and `el job result JOB_ID`.
@@ -76,12 +77,32 @@ from envloop import Client
76
77
 
77
78
  with Client.remote() as client:
78
79
  print(client.jobs.list(limit=5))
80
+ print(client.trials.list(type="train", limit=5))
79
81
  ```
80
82
 
81
83
  Submitting work, training a model or starting inference can allocate billable
82
84
  resources. Follow the task-specific guide and retain the outputs you need before
83
85
  cleaning up temporary resources.
84
86
 
87
+ ## Sandbox jobs
88
+
89
+ With a matching platform and a published `envhub/base` ANS Runner, submit a
90
+ Sandbox Job without a Dataset:
91
+
92
+ ```bash
93
+ el job submit --type sandbox --runner base --command 'python --version'
94
+ el job submit --type sandbox --runner base --image python:3.11 --wait
95
+ el job list --type sandbox
96
+ el job log JOB_ID --follow
97
+ ```
98
+
99
+ The platform resolves `base` to the registered `envhub/base@main` source ref and
100
+ freezes its immutable Revision. The Sandbox image defaults to `python:3.11`;
101
+ `--image` accepts an image name, tag or digest. The execution provider is configured
102
+ by the server. Job logs retain the actual captured output and collection metadata.
103
+ Failures include a code, readable explanation, HTTP status when available, and
104
+ recovery guidance without echoing arbitrary response bodies or credentials.
105
+
85
106
  ## Existing installations
86
107
 
87
108
  This distribution provides both the CLI and the `envloop` import package. Use a
@@ -1,4 +1,4 @@
1
- # EnvLoop CLI and Python SDK
1
+ # envloop-cli: EnvLoop CLI and Python SDK
2
2
 
3
3
  Install the public client to run agent Jobs, inspect Trials, manage training and
4
4
  checkpoints, and deploy or call inference endpoints. The `el` and `envloop`
@@ -9,19 +9,17 @@ commands are equivalent. The Python import package is `envloop`.
9
9
  Use a Python virtual environment:
10
10
 
11
11
  ```bash
12
- python -m pip install envloop-cli
12
+ python -m pip install --upgrade envloop-cli
13
13
  el --help
14
14
  ```
15
15
 
16
- For Python training workflows, install the optional training dependency:
16
+ The standard installation includes training support:
17
17
 
18
18
  ```bash
19
- python -m pip install 'envloop-cli[train]'
20
19
  python -c 'from envloop import Client, training; print("EnvLoop SDK ready")'
21
20
  ```
22
21
 
23
- The base client uses Python's standard library. The training extra installs the
24
- training protocol client. You do not need a source checkout, GitHub account,
22
+ The package installs the training protocol client by default. You do not need a source checkout, GitHub account,
25
23
  local execution backend, Docker, or a GPU to install the client.
26
24
 
27
25
  ## Connect
@@ -48,9 +46,11 @@ your secret manager. Public API access is scoped to the authorized project.
48
46
 
49
47
  ```bash
50
48
  el job list --limit 5
51
- el train list --limit 5
49
+ el job list --type train --limit 5
50
+ el trail list --limit 5
51
+ el trail list --type train --job-id JOB_ID
52
52
  el checkpoint list --limit 5
53
- el inference endpoint list
53
+ el job list --type inference
54
54
  ```
55
55
 
56
56
  Query a returned Job ID with `el job get JOB_ID` and `el job result JOB_ID`.
@@ -64,12 +64,32 @@ from envloop import Client
64
64
 
65
65
  with Client.remote() as client:
66
66
  print(client.jobs.list(limit=5))
67
+ print(client.trials.list(type="train", limit=5))
67
68
  ```
68
69
 
69
70
  Submitting work, training a model or starting inference can allocate billable
70
71
  resources. Follow the task-specific guide and retain the outputs you need before
71
72
  cleaning up temporary resources.
72
73
 
74
+ ## Sandbox jobs
75
+
76
+ With a matching platform and a published `envhub/base` ANS Runner, submit a
77
+ Sandbox Job without a Dataset:
78
+
79
+ ```bash
80
+ el job submit --type sandbox --runner base --command 'python --version'
81
+ el job submit --type sandbox --runner base --image python:3.11 --wait
82
+ el job list --type sandbox
83
+ el job log JOB_ID --follow
84
+ ```
85
+
86
+ The platform resolves `base` to the registered `envhub/base@main` source ref and
87
+ freezes its immutable Revision. The Sandbox image defaults to `python:3.11`;
88
+ `--image` accepts an image name, tag or digest. The execution provider is configured
89
+ by the server. Job logs retain the actual captured output and collection metadata.
90
+ Failures include a code, readable explanation, HTTP status when available, and
91
+ recovery guidance without echoing arbitrary response bodies or credentials.
92
+
73
93
  ## Existing installations
74
94
 
75
95
  This distribution provides both the CLI and the `envloop` import package. Use a
@@ -2,7 +2,7 @@
2
2
 
3
3
  The platform stores no run object. Agents tag every Train Job they create with
4
4
  ``user_metadata`` (the RSI loop: ``rsi_run``, ``rsi_round``, ``rsi_checkpoint``...);
5
- this module pages through ``GET /v1/train/jobs`` and derives runs from those tags,
5
+ this module pages through ``GET /v1/job`` and derives runs from those tags,
6
6
  using the same rules as the Console's agent runs page.
7
7
  """
8
8
 
@@ -19,8 +19,8 @@ KEYS = {
19
19
  "checkpoint": ("agent_checkpoint", "rsi_checkpoint"),
20
20
  }
21
21
  RUN_KEYS = KEYS["run"] # first non-empty string wins
22
- ACTIVE_STATUSES = frozenset({"LAUNCHING", "STARTING", "RUNNING", "RECOVERING"})
23
- TERMINAL_STATUSES = frozenset({"SUCCEEDED", "FAILED", "CANCELLED"})
22
+ ACTIVE_STATUSES = frozenset({"LAUNCHING", "STARTING", "RUNNING", "RECOVERING", "queued", "pending", "starting", "running", "recovering", "cancelling"})
23
+ TERMINAL_STATUSES = frozenset({"SUCCEEDED", "FAILED", "CANCELLED", "completed", "failed", "cancelled"})
24
24
  PAGE_LIMIT = 100 # the platform's maximum page size
25
25
 
26
26
 
@@ -70,7 +70,7 @@ def _round(job) -> int | None:
70
70
 
71
71
  def _order(job):
72
72
  # Creation order, as the platform pages jobs (newest first, reversed here).
73
- return (str(job.get("created_at") or ""), str(job.get("train_job_id") or ""))
73
+ return (str(job.get("created_at") or ""), str(job.get("job_id") or job.get("train_job_id") or ""))
74
74
 
75
75
 
76
76
  def _duration_sec(job) -> int | None:
@@ -97,7 +97,7 @@ def _checkpoint(job) -> dict | None:
97
97
  def _job_view(job) -> dict:
98
98
  metadata = _metadata(job)
99
99
  return {
100
- "train_job_id": job.get("train_job_id"),
100
+ "train_job_id": job.get("job_id") or job.get("train_job_id"),
101
101
  "status": job.get("status"),
102
102
  "round": _round(job),
103
103
  "purpose": _field(job, "purpose"),
@@ -121,11 +121,11 @@ def _summary(run: str, jobs: list) -> dict:
121
121
  status = "running"
122
122
  else:
123
123
  status = str(latest.get("status") or "unknown").lower()
124
- succeeded = [job for job in jobs if job.get("status") == "SUCCEEDED"]
124
+ succeeded = [job for job in jobs if job.get("status") in {"SUCCEEDED", "completed"}]
125
125
  latest_saved = next((job for job in reversed(jobs) if _checkpoint(job) is not None), None)
126
126
  latest_checkpoint = _checkpoint(latest_saved) if latest_saved is not None else None
127
127
  if latest_checkpoint is not None:
128
- latest_checkpoint["train_job_id"] = latest_saved.get("train_job_id")
128
+ latest_checkpoint["train_job_id"] = latest_saved.get("job_id") or latest_saved.get("train_job_id")
129
129
  rounds = [number for number in map(_round, jobs) if number is not None]
130
130
  activity = [stamp for job in jobs if isinstance(stamp := job.get("updated_at") or job.get("created_at"), str)]
131
131
  return {
@@ -172,7 +172,7 @@ class AgentRuns:
172
172
  if observed_at is None:
173
173
  observed_at = page.get("observed_at")
174
174
  for job in page.get("items") or []:
175
- job_id = job.get("train_job_id") if isinstance(job, dict) else None
175
+ job_id = job.get("job_id") or job.get("train_job_id") if isinstance(job, dict) else None
176
176
  if not isinstance(job, dict) or job_id in ids:
177
177
  continue
178
178
  if job_id is not None:
@@ -277,9 +277,12 @@ def run_auth(args, parser):
277
277
  config=False, environment=False).list_jobs(limit=1)
278
278
  report["check"] = "access_confirmed" if connection.api_key else "reachable_anonymous"
279
279
  except (ConfigError, RemoteError) as error:
280
+ from .cli_errors import format_error, safe_error_code
281
+
280
282
  report["check"] = {401: "authentication_rejected", 403: "authorization_denied"}.get(
281
- getattr(error, "status_code", None), error.code)
283
+ getattr(error, "status_code", None), safe_error_code(error))
282
284
  _print(report, args.json)
285
+ print(format_error(error, args=args, program=parser.prog), file=sys.stderr)
283
286
  return 1
284
287
  _print(report, getattr(args, "json", False))
285
288
  return 0