ostruct-cli 0.8.3__tar.gz → 0.8.7__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 (70) hide show
  1. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/PKG-INFO +33 -3
  2. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/README.md +30 -2
  3. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/pyproject.toml +3 -1
  4. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/cli.py +4 -0
  5. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/click_options.py +2 -8
  6. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/runner.py +15 -15
  7. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/LICENSE +0 -0
  8. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/__init__.py +0 -0
  9. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/__init__.py +0 -0
  10. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/base_errors.py +0 -0
  11. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/cache_manager.py +0 -0
  12. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/code_interpreter.py +0 -0
  13. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/commands/__init__.py +0 -0
  14. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/commands/list_models.py +0 -0
  15. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/commands/quick_ref.py +0 -0
  16. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/commands/run.py +0 -0
  17. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/commands/update_registry.py +0 -0
  18. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/config.py +0 -0
  19. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/cost_estimation.py +0 -0
  20. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/errors.py +0 -0
  21. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/exit_codes.py +0 -0
  22. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/explicit_file_processor.py +0 -0
  23. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/field_utils.py +0 -0
  24. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/file_info.py +0 -0
  25. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/file_list.py +0 -0
  26. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/file_search.py +0 -0
  27. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/file_utils.py +0 -0
  28. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/json_extract.py +0 -0
  29. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/mcp_integration.py +0 -0
  30. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/model_creation.py +0 -0
  31. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/model_validation.py +0 -0
  32. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/path_utils.py +0 -0
  33. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/progress.py +0 -0
  34. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/progress_reporting.py +0 -0
  35. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/registry_updates.py +0 -0
  36. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/schema_utils.py +0 -0
  37. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/schema_validation.py +0 -0
  38. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/__init__.py +0 -0
  39. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/allowed_checker.py +0 -0
  40. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/base.py +0 -0
  41. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/case_manager.py +0 -0
  42. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/errors.py +0 -0
  43. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/normalization.py +0 -0
  44. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/safe_joiner.py +0 -0
  45. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/security_manager.py +0 -0
  46. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/symlink_resolver.py +0 -0
  47. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/types.py +0 -0
  48. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/security/windows_paths.py +0 -0
  49. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/sentinel.py +0 -0
  50. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/serialization.py +0 -0
  51. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/services.py +0 -0
  52. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_debug.py +0 -0
  53. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_debug_help.py +0 -0
  54. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_env.py +0 -0
  55. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_extensions.py +0 -0
  56. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_filters.py +0 -0
  57. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_io.py +0 -0
  58. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_optimizer.py +0 -0
  59. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_processor.py +0 -0
  60. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_rendering.py +0 -0
  61. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_schema.py +0 -0
  62. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_utils.py +0 -0
  63. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/template_validation.py +0 -0
  64. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/token_utils.py +0 -0
  65. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/token_validation.py +0 -0
  66. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/types.py +0 -0
  67. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/unattended_operation.py +0 -0
  68. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/utils.py +0 -0
  69. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/cli/validators.py +0 -0
  70. {ostruct_cli-0.8.3 → ostruct_cli-0.8.7}/src/ostruct/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ostruct-cli
3
- Version: 0.8.3
3
+ Version: 0.8.7
4
4
  Summary: CLI for OpenAI Structured Output with Multi-Tool Integration
5
5
  Author: Yaniv Golan
6
6
  Author-email: yaniv@golan.name
@@ -39,6 +39,7 @@ Requires-Dist: pytest (>=8.3.4,<9.0) ; extra == "dev"
39
39
  Requires-Dist: pytest-asyncio (>=0.25.2,<1.0) ; extra == "dev"
40
40
  Requires-Dist: pytest-mock (>=3.14.0,<4.0) ; extra == "dev"
41
41
  Requires-Dist: pytest-rerunfailures (>=12.0,<13.0) ; extra == "dev"
42
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0)
42
43
  Requires-Dist: python-dotenv (>=1.0.1,<2.0) ; extra == "dev"
43
44
  Requires-Dist: pyyaml (>=6.0.2,<7.0)
44
45
  Requires-Dist: sphinx (>=7.0,<8.0) ; extra == "dev"
@@ -48,6 +49,7 @@ Requires-Dist: sphinx-rtd-theme (>=1.0,<2.0) ; extra == "docs"
48
49
  Requires-Dist: tenacity (>=8.2.3,<9.0) ; extra == "examples"
49
50
  Requires-Dist: tiktoken (==0.9.0)
50
51
  Requires-Dist: tomli (>=2.0.1,<3.0) ; (python_version < "3.11") and (extra == "docs")
52
+ Requires-Dist: tomli (>=2.0.1,<3.0) ; extra == "dev"
51
53
  Requires-Dist: tomli (>=2.0.1,<3.0) ; python_version < "3.11"
52
54
  Requires-Dist: twine (>=6.0.1,<7.0) ; extra == "dev"
53
55
  Requires-Dist: types-cachetools (>=5.5.0.20240820) ; extra == "dev"
@@ -214,7 +216,27 @@ Break down words into their components, showing their origins, meanings, and hie
214
216
 
215
217
  ## Installation
216
218
 
217
- ### For Users
219
+ ### Quick Install (macOS)
220
+
221
+ For macOS users, we provide a one-line installer that handles everything automatically:
222
+
223
+ ```bash
224
+ curl -sSL https://raw.githubusercontent.com/yaniv-golan/ostruct/main/scripts/install-macos.sh | bash
225
+ ```
226
+
227
+ This script will:
228
+
229
+ - Install Python 3.10+ if needed (via Homebrew or python.org)
230
+ - Install Homebrew if needed
231
+ - Install ostruct-cli via pip
232
+ - Configure your shell PATH automatically
233
+ - Verify the installation works
234
+
235
+ After installation, restart your terminal and test with `ostruct --version`.
236
+
237
+ ### Manual Installation
238
+
239
+ #### For Users
218
240
 
219
241
  To install the latest stable version from PyPI:
220
242
 
@@ -222,7 +244,9 @@ To install the latest stable version from PyPI:
222
244
  pip install ostruct-cli
223
245
  ```
224
246
 
225
- ### For Developers
247
+ **Note**: If the `ostruct` command isn't found after installation, you may need to add Python's user bin directory to your PATH. See the [troubleshooting guide](scripts/README.md#troubleshooting) for details.
248
+
249
+ #### For Developers
226
250
 
227
251
  If you plan to contribute to the project, see the [Development Setup](#development-setup) section below for instructions on setting up the development environment with Poetry.
228
252
 
@@ -237,6 +261,8 @@ ostruct-cli respects the following environment variables:
237
261
  - `OSTRUCT_DISABLE_REGISTRY_UPDATE_CHECKS`: Set to "1", "true", or "yes" to disable automatic registry update checks
238
262
  - `MCP_<NAME>_URL`: Custom MCP server URLs (e.g., `MCP_STRIPE_URL=https://mcp.stripe.com`)
239
263
 
264
+ **💡 Tip**: ostruct automatically loads `.env` files from the current directory. Environment variables take precedence over `.env` file values.
265
+
240
266
  <details>
241
267
  <summary><strong>Shell Completion Setup</strong> (Click to expand)</summary>
242
268
 
@@ -427,7 +453,11 @@ tools:
427
453
  1. Set your OpenAI API key:
428
454
 
429
455
  ```bash
456
+ # Environment variable
430
457
  export OPENAI_API_KEY=your-api-key
458
+
459
+ # Or create a .env file
460
+ echo 'OPENAI_API_KEY=your-api-key' > .env
431
461
  ```
432
462
 
433
463
  ### Example 1: Basic Text Extraction (Simplest)
@@ -151,7 +151,27 @@ Break down words into their components, showing their origins, meanings, and hie
151
151
 
152
152
  ## Installation
153
153
 
154
- ### For Users
154
+ ### Quick Install (macOS)
155
+
156
+ For macOS users, we provide a one-line installer that handles everything automatically:
157
+
158
+ ```bash
159
+ curl -sSL https://raw.githubusercontent.com/yaniv-golan/ostruct/main/scripts/install-macos.sh | bash
160
+ ```
161
+
162
+ This script will:
163
+
164
+ - Install Python 3.10+ if needed (via Homebrew or python.org)
165
+ - Install Homebrew if needed
166
+ - Install ostruct-cli via pip
167
+ - Configure your shell PATH automatically
168
+ - Verify the installation works
169
+
170
+ After installation, restart your terminal and test with `ostruct --version`.
171
+
172
+ ### Manual Installation
173
+
174
+ #### For Users
155
175
 
156
176
  To install the latest stable version from PyPI:
157
177
 
@@ -159,7 +179,9 @@ To install the latest stable version from PyPI:
159
179
  pip install ostruct-cli
160
180
  ```
161
181
 
162
- ### For Developers
182
+ **Note**: If the `ostruct` command isn't found after installation, you may need to add Python's user bin directory to your PATH. See the [troubleshooting guide](scripts/README.md#troubleshooting) for details.
183
+
184
+ #### For Developers
163
185
 
164
186
  If you plan to contribute to the project, see the [Development Setup](#development-setup) section below for instructions on setting up the development environment with Poetry.
165
187
 
@@ -174,6 +196,8 @@ ostruct-cli respects the following environment variables:
174
196
  - `OSTRUCT_DISABLE_REGISTRY_UPDATE_CHECKS`: Set to "1", "true", or "yes" to disable automatic registry update checks
175
197
  - `MCP_<NAME>_URL`: Custom MCP server URLs (e.g., `MCP_STRIPE_URL=https://mcp.stripe.com`)
176
198
 
199
+ **💡 Tip**: ostruct automatically loads `.env` files from the current directory. Environment variables take precedence over `.env` file values.
200
+
177
201
  <details>
178
202
  <summary><strong>Shell Completion Setup</strong> (Click to expand)</summary>
179
203
 
@@ -364,7 +388,11 @@ tools:
364
388
  1. Set your OpenAI API key:
365
389
 
366
390
  ```bash
391
+ # Environment variable
367
392
  export OPENAI_API_KEY=your-api-key
393
+
394
+ # Or create a .env file
395
+ echo 'OPENAI_API_KEY=your-api-key' > .env
368
396
  ```
369
397
 
370
398
  ### Example 1: Basic Text Extraction (Simplest)
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [project]
6
6
  name = "ostruct-cli"
7
- version = "0.8.3"
7
+ version = "0.8.7"
8
8
  description = "CLI for OpenAI Structured Output with Multi-Tool Integration"
9
9
  authors = [{name = "Yaniv Golan", email = "yaniv@golan.name"}]
10
10
  readme = "README.md"
@@ -25,6 +25,7 @@ dependencies = [
25
25
  "pygments>=2.15.0,<3.0",
26
26
  "jinja2>=3.1.2,<4.0",
27
27
  "openai-model-registry>=0.7.0,<1.0",
28
+ "python-dotenv>=1.0.1,<2.0",
28
29
  ]
29
30
 
30
31
  [project.scripts]
@@ -56,6 +57,7 @@ dev = [
56
57
  "pre-commit>=4.1.0,<5.0",
57
58
  "psutil>=7.0.0,<8.0",
58
59
  "hypothesis>=6.0.0,<7.0",
60
+ "tomli>=2.0.1,<3.0",
59
61
  ]
60
62
  docs = [
61
63
  "sphinx>=7.0,<8.0",
@@ -4,6 +4,7 @@ import sys
4
4
  from typing import Optional
5
5
 
6
6
  import click
7
+ from dotenv import load_dotenv
7
8
 
8
9
  from .. import __version__
9
10
  from .commands import create_command_group
@@ -107,6 +108,9 @@ def create_cli() -> click.Command:
107
108
 
108
109
  def main() -> None:
109
110
  """Main entry point for the CLI."""
111
+ # Load environment variables from .env file
112
+ load_dotenv()
113
+
110
114
  try:
111
115
  cli(standalone_mode=False)
112
116
  except (
@@ -492,12 +492,13 @@ def api_options(f: Union[Command, Callable[..., Any]]) -> Command:
492
492
  environment variable.""",
493
493
  )(cmd)
494
494
 
495
+ # API timeout for OpenAI calls
495
496
  cmd = click.option(
496
497
  "--timeout",
497
498
  type=click.FloatRange(1.0, None),
498
499
  default=60.0,
499
500
  show_default=True,
500
- help="API timeout in seconds.",
501
+ help="Timeout in seconds for OpenAI API calls.",
501
502
  )(cmd)
502
503
 
503
504
  return cast(Command, cmd)
@@ -854,13 +855,6 @@ def debug_progress_options(f: Union[Command, Callable[..., Any]]) -> Command:
854
855
  "--verbose", is_flag=True, help="Enable verbose logging"
855
856
  )(cmd)
856
857
 
857
- cmd = click.option(
858
- "--timeout",
859
- type=int,
860
- default=3600,
861
- help="Operation timeout in seconds (default: 3600 = 1 hour)",
862
- )(cmd)
863
-
864
858
  return cast(Command, cmd)
865
859
 
866
860
 
@@ -33,9 +33,6 @@ from .sentinel import extract_json_block
33
33
  from .serialization import LogSerializer
34
34
  from .services import ServiceContainer
35
35
  from .types import CLIParams
36
- from .unattended_operation import (
37
- UnattendedOperationManager,
38
- )
39
36
 
40
37
 
41
38
  # Error classes for API operations
@@ -855,10 +852,6 @@ async def execute_model(
855
852
  """Execute the model with the given parameters."""
856
853
  logger.debug("=== Execution Phase ===")
857
854
 
858
- # Initialize unattended operation manager
859
- timeout_seconds = int(args.get("timeout", 3600))
860
- operation_manager = UnattendedOperationManager(timeout_seconds)
861
-
862
855
  # Pre-validate unattended compatibility
863
856
  # Note: MCP validation is handled during MCP configuration processing
864
857
  # mcp_servers = args.get("mcp_servers", [])
@@ -874,12 +867,22 @@ async def execute_model(
874
867
 
875
868
  api_key = args.get("api_key") or os.getenv("OPENAI_API_KEY")
876
869
  if not api_key:
877
- msg = "No API key provided. Set OPENAI_API_KEY environment variable or use --api-key"
870
+ msg = (
871
+ "No OpenAI API key found. Please:\n"
872
+ " • Set OPENAI_API_KEY environment variable, or\n"
873
+ " • Create a .env file with OPENAI_API_KEY=your-key-here, or\n"
874
+ " • Use --api-key option (not recommended for production)\n"
875
+ "\n"
876
+ "Get your API key from: https://platform.openai.com/api-keys"
877
+ )
878
878
  logger.error(msg)
879
879
  raise CLIError(msg, exit_code=ExitCode.API_ERROR)
880
880
 
881
+ # Get API timeout
882
+ api_timeout = args.get("timeout", 60.0)
881
883
  client = AsyncOpenAI(
882
- api_key=api_key, timeout=min(args.get("timeout", 60.0), 300.0)
884
+ api_key=api_key,
885
+ timeout=min(float(api_timeout), 300.0),
883
886
  ) # Cap at 5 min for client timeout
884
887
 
885
888
  # Create service container for dependency management
@@ -1352,13 +1355,10 @@ async def execute_model(
1352
1355
 
1353
1356
  return ExitCode.SUCCESS
1354
1357
 
1355
- # Execute main operation with timeout safeguards
1358
+ # Execute main operation
1356
1359
  try:
1357
- result = await operation_manager.execute_with_safeguards(
1358
- execute_main_operation, "model execution"
1359
- )
1360
- # The result should be an ExitCode from execute_main_operation
1361
- return result # type: ignore[no-any-return]
1360
+ result = await execute_main_operation()
1361
+ return result
1362
1362
  except (
1363
1363
  APIResponseError,
1364
1364
  EmptyResponseError,
File without changes