ostruct-cli 0.3.0__py3-none-any.whl → 0.5.0__py3-none-any.whl

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 (35) hide show
  1. ostruct/cli/base_errors.py +183 -0
  2. ostruct/cli/cli.py +830 -585
  3. ostruct/cli/click_options.py +338 -211
  4. ostruct/cli/errors.py +214 -227
  5. ostruct/cli/exit_codes.py +18 -0
  6. ostruct/cli/file_info.py +126 -69
  7. ostruct/cli/file_list.py +191 -72
  8. ostruct/cli/file_utils.py +132 -97
  9. ostruct/cli/path_utils.py +86 -77
  10. ostruct/cli/security/__init__.py +32 -0
  11. ostruct/cli/security/allowed_checker.py +55 -0
  12. ostruct/cli/security/base.py +46 -0
  13. ostruct/cli/security/case_manager.py +75 -0
  14. ostruct/cli/security/errors.py +164 -0
  15. ostruct/cli/security/normalization.py +161 -0
  16. ostruct/cli/security/safe_joiner.py +211 -0
  17. ostruct/cli/security/security_manager.py +366 -0
  18. ostruct/cli/security/symlink_resolver.py +483 -0
  19. ostruct/cli/security/types.py +108 -0
  20. ostruct/cli/security/windows_paths.py +404 -0
  21. ostruct/cli/serialization.py +25 -0
  22. ostruct/cli/template_filters.py +13 -8
  23. ostruct/cli/template_rendering.py +46 -22
  24. ostruct/cli/template_utils.py +12 -4
  25. ostruct/cli/template_validation.py +26 -8
  26. ostruct/cli/token_utils.py +43 -0
  27. ostruct/cli/validators.py +109 -0
  28. {ostruct_cli-0.3.0.dist-info → ostruct_cli-0.5.0.dist-info}/METADATA +64 -24
  29. ostruct_cli-0.5.0.dist-info/RECORD +42 -0
  30. {ostruct_cli-0.3.0.dist-info → ostruct_cli-0.5.0.dist-info}/WHEEL +1 -1
  31. ostruct/cli/security.py +0 -964
  32. ostruct/cli/security_types.py +0 -46
  33. ostruct_cli-0.3.0.dist-info/RECORD +0 -28
  34. {ostruct_cli-0.3.0.dist-info → ostruct_cli-0.5.0.dist-info}/LICENSE +0 -0
  35. {ostruct_cli-0.3.0.dist-info → ostruct_cli-0.5.0.dist-info}/entry_points.txt +0 -0
@@ -67,7 +67,7 @@ from jinja2 import meta
67
67
  from jinja2.nodes import For, Name, Node
68
68
 
69
69
  from . import template_filters
70
- from .errors import TemplateValidationError
70
+ from .errors import TaskTemplateVariableError, TemplateValidationError
71
71
  from .template_env import create_jinja_env
72
72
  from .template_schema import (
73
73
  DictProxy,
@@ -160,7 +160,8 @@ def validate_template_placeholders(
160
160
  template_context: Optional context to validate against
161
161
 
162
162
  Raises:
163
- TemplateValidationError: If any placeholders are invalid
163
+ TaskTemplateVariableError: If any variables are undefined
164
+ TemplateValidationError: If template validation fails for other reasons
164
165
  """
165
166
  logger = logging.getLogger(__name__)
166
167
 
@@ -309,10 +310,15 @@ def validate_template_placeholders(
309
310
  }
310
311
 
311
312
  if missing:
312
- raise TemplateValidationError(
313
- f"Task template uses undefined variables: {', '.join(sorted(missing))}. "
314
- f"Available variables: {', '.join(sorted(available_vars))}"
313
+ # Create a more user-friendly error message
314
+ error_msg = (
315
+ f"Missing required template variable(s): {', '.join(sorted(missing))}\n"
316
+ f"Available variables: {', '.join(sorted(available_vars))}\n"
317
+ "To fix this, please provide the missing variable(s) using:\n"
315
318
  )
319
+ for var in sorted(missing):
320
+ error_msg += f" -V {var}='value'\n"
321
+ raise TaskTemplateVariableError(error_msg)
316
322
 
317
323
  logger.debug(
318
324
  "Before create_validation_context - available_vars type: %s, value: %s",
@@ -336,8 +342,17 @@ def validate_template_placeholders(
336
342
  try:
337
343
  env.from_string(template).render(validation_context)
338
344
  except jinja2.UndefinedError as e:
339
- # Convert Jinja2 undefined errors to our own ValueError
340
- raise TemplateValidationError(str(e))
345
+ # Convert Jinja2 undefined errors to TaskTemplateVariableError with helpful message
346
+ var_name = str(e).split("'")[
347
+ 1
348
+ ] # Extract variable name from error message
349
+ error_msg = (
350
+ f"Missing required template variable: {var_name}\n"
351
+ f"Available variables: {', '.join(sorted(available_vars))}\n"
352
+ "To fix this, please provide the variable using:\n"
353
+ f" -V {var_name}='value'"
354
+ )
355
+ raise TaskTemplateVariableError(error_msg) from e
341
356
  except ValueError as e:
342
357
  # Convert validation errors from template_schema to TemplateValidationError
343
358
  raise TemplateValidationError(str(e))
@@ -348,10 +363,13 @@ def validate_template_placeholders(
348
363
  raise
349
364
 
350
365
  except jinja2.TemplateSyntaxError as e:
351
- # Convert Jinja2 syntax errors to our own ValueError
366
+ # Convert Jinja2 syntax errors to TemplateValidationError
352
367
  raise TemplateValidationError(
353
368
  f"Invalid task template syntax: {str(e)}"
354
369
  )
370
+ except (TaskTemplateVariableError, TemplateValidationError):
371
+ # Re-raise these without wrapping
372
+ raise
355
373
  except Exception as e:
356
374
  logger.error("Unexpected error during template validation: %s", str(e))
357
375
  raise
@@ -0,0 +1,43 @@
1
+ """Token estimation utilities."""
2
+
3
+ from typing import Any, Dict, List, Union
4
+
5
+ import tiktoken
6
+
7
+
8
+ def estimate_tokens_with_encoding(
9
+ messages: Union[str, Dict[str, str], List[Dict[str, str]]],
10
+ model: str,
11
+ encoder: Any = None,
12
+ ) -> int:
13
+ """Estimate the number of tokens in a chat completion.
14
+
15
+ Args:
16
+ messages: Message content - can be string, single message dict, or list of messages
17
+ model: Model name
18
+ encoder: Optional tiktoken encoder for testing
19
+
20
+ Returns:
21
+ int: Estimated token count
22
+ """
23
+ if encoder is None:
24
+ # Use o200k_base for gpt-4o and o1 models
25
+ if model.startswith(("gpt-4o", "o1", "o3")):
26
+ encoder = tiktoken.get_encoding("o200k_base")
27
+ else:
28
+ encoder = tiktoken.get_encoding("cl100k_base")
29
+
30
+ if isinstance(messages, str):
31
+ return len(encoder.encode(messages))
32
+ elif isinstance(messages, dict):
33
+ return len(encoder.encode(str(messages.get("content", ""))))
34
+ else:
35
+ num_tokens = 0
36
+ for message in messages:
37
+ num_tokens += 4 # message overhead
38
+ for key, value in message.items():
39
+ num_tokens += len(encoder.encode(str(value)))
40
+ if key == "name":
41
+ num_tokens -= 1 # role is omitted
42
+ num_tokens += 2 # reply priming
43
+ return num_tokens
@@ -0,0 +1,109 @@
1
+ """Validators for CLI options and arguments."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any, List, Optional, Tuple, Union
6
+
7
+ import click
8
+
9
+ from .errors import InvalidJSONError, VariableNameError
10
+
11
+
12
+ def validate_name_path_pair(
13
+ ctx: click.Context,
14
+ param: click.Parameter,
15
+ value: List[Tuple[str, Union[str, Path]]],
16
+ ) -> List[Tuple[str, Union[str, Path]]]:
17
+ """Validate name/path pairs for files and directories.
18
+
19
+ Args:
20
+ ctx: Click context
21
+ param: Click parameter
22
+ value: List of (name, path) tuples
23
+
24
+ Returns:
25
+ List of validated (name, Path) tuples
26
+
27
+ Raises:
28
+ click.BadParameter: If validation fails
29
+ """
30
+ if not value:
31
+ return value
32
+
33
+ result: List[Tuple[str, Union[str, Path]]] = []
34
+ for name, path in value:
35
+ if not name.isidentifier():
36
+ raise click.BadParameter(f"Invalid variable name: {name}")
37
+ result.append((name, Path(path)))
38
+ return result
39
+
40
+
41
+ def validate_variable(
42
+ ctx: click.Context, param: click.Parameter, value: Optional[List[str]]
43
+ ) -> Optional[List[Tuple[str, str]]]:
44
+ """Validate name=value format for simple variables.
45
+
46
+ Args:
47
+ ctx: Click context
48
+ param: Click parameter
49
+ value: List of "name=value" strings
50
+
51
+ Returns:
52
+ List of validated (name, value) tuples
53
+
54
+ Raises:
55
+ click.BadParameter: If validation fails
56
+ """
57
+ if not value:
58
+ return None
59
+
60
+ result = []
61
+ for var in value:
62
+ if "=" not in var:
63
+ raise click.BadParameter(
64
+ f"Variable must be in format name=value: {var}"
65
+ )
66
+ name, val = var.split("=", 1)
67
+ if not name.isidentifier():
68
+ raise click.BadParameter(f"Invalid variable name: {name}")
69
+ result.append((name, val))
70
+ return result
71
+
72
+
73
+ def validate_json_variable(
74
+ ctx: click.Context, param: click.Parameter, value: Optional[List[str]]
75
+ ) -> Optional[List[Tuple[str, Any]]]:
76
+ """Validate JSON variable format.
77
+
78
+ Args:
79
+ ctx: Click context
80
+ param: Click parameter
81
+ value: List of "name=json_string" values
82
+
83
+ Returns:
84
+ List of validated (name, parsed_json) tuples
85
+
86
+ Raises:
87
+ click.BadParameter: If validation fails
88
+ """
89
+ if not value:
90
+ return None
91
+
92
+ result = []
93
+ for var in value:
94
+ if "=" not in var:
95
+ raise InvalidJSONError(
96
+ f'JSON variable must be in format name=\'{"json":"value"}\': {var}'
97
+ )
98
+ name, json_str = var.split("=", 1)
99
+ if not name.isidentifier():
100
+ raise VariableNameError(f"Invalid variable name: {name}")
101
+ try:
102
+ json_value = json.loads(json_str)
103
+ result.append((name, json_value))
104
+ except json.JSONDecodeError as e:
105
+ raise InvalidJSONError(
106
+ f"Invalid JSON value for variable {name!r}: {json_str!r}",
107
+ context={"variable_name": name},
108
+ ) from e
109
+ return result
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ostruct-cli
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: CLI for OpenAI Structured Output
5
5
  Author: Yaniv Golan
6
6
  Author-email: yaniv@golan.name
7
- Requires-Python: >=3.9,<4.0
7
+ Requires-Python: >=3.10,<4.0
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.9
10
9
  Classifier: Programming Language :: Python :: 3.10
11
10
  Classifier: Programming Language :: Python :: 3.11
12
11
  Classifier: Programming Language :: Python :: 3.12
@@ -16,10 +15,11 @@ Requires-Dist: chardet (>=5.0.0,<6.0.0)
16
15
  Requires-Dist: click (>=8.1.7,<9.0.0)
17
16
  Requires-Dist: ijson (>=3.2.3,<4.0.0)
18
17
  Requires-Dist: jsonschema (>=4.23.0,<5.0.0)
19
- Requires-Dist: openai-structured (>=1.0.0,<2.0.0)
18
+ Requires-Dist: openai (>=1.0.0,<2.0.0)
19
+ Requires-Dist: openai-structured (>=2.0.0,<3.0.0)
20
20
  Requires-Dist: pydantic (>=2.6.3,<3.0.0)
21
21
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
22
- Requires-Dist: tiktoken (>=0.8.0,<0.9.0)
22
+ Requires-Dist: tiktoken (>=0.9.0,<0.10.0)
23
23
  Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
24
24
  Requires-Dist: typing-extensions (>=4.9.0,<5.0.0)
25
25
  Requires-Dist: werkzeug (>=3.1.3,<4.0.0)
@@ -28,8 +28,8 @@ Description-Content-Type: text/markdown
28
28
  # ostruct-cli
29
29
 
30
30
  [![PyPI version](https://badge.fury.io/py/ostruct-cli.svg)](https://badge.fury.io/py/ostruct-cli)
31
- [![Python Versions](https://img.shields.io/pypi/pyversions/ostruct-cli.svg)](https://pypi.org/project/ostruct-cli/)
32
- [![Documentation Status](https://readthedocs.org/projects/ostruct-cli/badge/?version=latest)](https://ostruct-cli.readthedocs.io/en/latest/?badge=latest)
31
+ [![Python Versions](https://img.shields.io/pypi/pyversions/ostruct-cli.svg)](https://pypi.org/project/ostruct-cli)
32
+ [![Documentation Status](https://readthedocs.org/projects/ostruct/badge/?version=latest)](https://ostruct.readthedocs.io/en/latest/?badge=latest)
33
33
  [![CI](https://github.com/yaniv-golan/ostruct/actions/workflows/ci.yml/badge.svg)](https://github.com/yaniv-golan/ostruct/actions/workflows/ci.yml)
34
34
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
35
35
 
@@ -90,7 +90,16 @@ Extract information about the person: {{ stdin }}
90
90
  4. Run the CLI:
91
91
 
92
92
  ```bash
93
- echo "John Smith is a 35 year old software engineer" | ostruct --task @task.j2 --schema schema.json
93
+ ostruct run task.j2 schema.json
94
+ ```
95
+
96
+ Or with more options:
97
+
98
+ ```bash
99
+ ostruct run task.j2 schema.json \
100
+ -f content input.txt \
101
+ -m gpt-4o \
102
+ --sys-prompt "You are an expert content analyzer"
94
103
  ```
95
104
 
96
105
  Output:
@@ -111,22 +120,63 @@ Template files use the `.j2` extension to indicate they contain Jinja2 template
111
120
  - Makes it clear the file contains template logic
112
121
  - Follows industry standards for Jinja2 templates
113
122
 
114
- While the CLI accepts templates with any extension (when prefixed with `@`), we recommend using `.j2` for better tooling support and clarity.
123
+ ## CLI Options
124
+
125
+ The CLI revolves around a single subcommand called `run`. Basic usage:
126
+
127
+ ```bash
128
+ ostruct run <TASK_TEMPLATE> <SCHEMA_FILE> [OPTIONS]
129
+ ```
130
+
131
+ Common options include:
132
+
133
+ - File & Directory Inputs:
134
+ - `-f <NAME> <PATH>`: Map a single file to a variable name
135
+ - `-d <NAME> <DIR>`: Map a directory to a variable name
136
+ - `-p <NAME> <PATTERN>`: Map files matching a glob pattern to a variable name
137
+ - `-R, --recursive`: Enable recursive directory/pattern scanning
138
+
139
+ - Variables:
140
+ - `-V name=value`: Define a simple string variable
141
+ - `-J name='{"key":"value"}'`: Define a JSON variable
142
+
143
+ - Model Parameters:
144
+ - `-m, --model MODEL`: Select the OpenAI model (supported: gpt-4o, o1, o3-mini)
145
+ - `--temperature FLOAT`: Set sampling temperature (0.0-2.0)
146
+ - `--max-output-tokens INT`: Set maximum output tokens
147
+ - `--top-p FLOAT`: Set top-p sampling parameter (0.0-1.0)
148
+ - `--frequency-penalty FLOAT`: Adjust frequency penalty (-2.0-2.0)
149
+ - `--presence-penalty FLOAT`: Adjust presence penalty (-2.0-2.0)
150
+ - `--reasoning-effort [low|medium|high]`: Control model reasoning effort
151
+
152
+ - System Prompt:
153
+ - `--sys-prompt TEXT`: Provide system prompt directly
154
+ - `--sys-file FILE`: Load system prompt from file
155
+ - `--ignore-task-sysprompt`: Ignore system prompt in template frontmatter
156
+
157
+ - API Configuration:
158
+ - `--api-key KEY`: OpenAI API key (defaults to OPENAI_API_KEY env var)
159
+ - `--timeout FLOAT`: API timeout in seconds (default: 60.0)
115
160
 
116
161
  ## Debug Options
117
162
 
118
- - `--show-model-schema`: Display the generated Pydantic model schema
119
163
  - `--debug-validation`: Show detailed schema validation debugging
120
- - `--verbose-schema`: Enable verbose schema debugging output
121
- - `--debug-openai-stream`: Enable low-level debug output for OpenAI streaming (very verbose)
122
- - `--progress-level {none,basic,detailed}`: Set progress reporting level (default: basic)
164
+ - `--debug-openai-stream`: Enable low-level debug output for OpenAI streaming
165
+ - `--progress-level {none,basic,detailed}`: Set progress reporting level
166
+ - `none`: No progress indicators
167
+ - `basic`: Show key operation steps (default)
168
+ - `detailed`: Show all steps with additional info
169
+ - `--show-model-schema`: Display the generated Pydantic model schema
170
+ - `--verbose`: Enable verbose logging
171
+ - `--dry-run`: Validate and render template without making API calls
172
+ - `--no-progress`: Disable all progress indicators
123
173
 
124
174
  All debug and error logs are written to:
125
175
 
126
176
  - `~/.ostruct/logs/ostruct.log`: General application logs
127
177
  - `~/.ostruct/logs/openai_stream.log`: OpenAI streaming operations logs
128
178
 
129
- For more detailed documentation and examples, visit our [documentation](https://ostruct-cli.readthedocs.io/).
179
+ For more detailed documentation and examples, visit our [documentation](https://ostruct.readthedocs.io/).
130
180
 
131
181
  ## Development
132
182
 
@@ -173,13 +223,3 @@ Contributions are welcome! Please feel free to submit a Pull Request.
173
223
 
174
224
  This project is licensed under the MIT License - see the LICENSE file for details.
175
225
 
176
- ## Migration from openai-structured
177
-
178
- If you were previously using the CLI bundled with openai-structured (pre-1.0.0), this is its new home. The migration is straightforward:
179
-
180
- 1. Update openai-structured to version 1.0.0 or later
181
- 2. Install ostruct-cli
182
- 3. Replace any `openai-structured` CLI commands with `ostruct`
183
-
184
- The functionality remains the same, just moved to a dedicated package for better maintenance and focus.
185
-
@@ -0,0 +1,42 @@
1
+ ostruct/__init__.py,sha256=X6zo6V7ZNMv731Wi388aTVQngD1410ExGwGx4J6lpyo,187
2
+ ostruct/cli/__init__.py,sha256=sYHKT6o1kFy1acbXejzAvVm8Cy8U91Yf1l4DlzquHKg,409
3
+ ostruct/cli/base_errors.py,sha256=S1cQxoiALbXKPxzgLo6XdSWpzPRb7RKz0QARmu9Zt4g,5987
4
+ ostruct/cli/cache_manager.py,sha256=ej3KrRfkKKZ_lEp2JswjbJ5bW2ncsvna9NeJu81cqqs,5192
5
+ ostruct/cli/cli.py,sha256=R9k4eHpREmvQJb-JLY1VRiWZJO8fJcer1QgnaDX0RrY,74011
6
+ ostruct/cli/click_options.py,sha256=WbRJdB9sO63ChN3fnCP7XWs73DHKl0C1ervfwL11am0,11371
7
+ ostruct/cli/errors.py,sha256=Muc4PygxON7M4bdZJ7-apztK9MrF252PXLPVNEogUv0,13322
8
+ ostruct/cli/exit_codes.py,sha256=uNjvQeUGwU1mlUJYIDrExAn7YlwOXZo603yLAwpqIwk,338
9
+ ostruct/cli/file_info.py,sha256=ilpT8IuckfhadLF1QQAPLXJp7p8kVpffDEEJ2erHPZU,14485
10
+ ostruct/cli/file_list.py,sha256=jLuCd1ardoAXX8FNwPgIqEM-ixzr1xP5ZSqXo2lmrj0,11270
11
+ ostruct/cli/file_utils.py,sha256=J3-6fbEGQ7KD_bU81pAxueHLv9XV0X7f8FSMt_0AJGQ,22537
12
+ ostruct/cli/path_utils.py,sha256=j44q1OoLkqMErgK-qEuhuIZ1VyzqRIvNgxR1et9PoXA,4813
13
+ ostruct/cli/progress.py,sha256=rj9nVEco5UeZORMbzd7mFJpFGJjbH9KbBFh5oTE5Anw,3415
14
+ ostruct/cli/security/__init__.py,sha256=CQpkCgTFYlA1p6atpQeNgIKtE4LZGUKt4EbytbGKpCs,846
15
+ ostruct/cli/security/allowed_checker.py,sha256=N5UXlpjdj5zAbKk-lRDlHiHV3KtQHtJNhtZI_qGB4zw,1638
16
+ ostruct/cli/security/base.py,sha256=q9YUdHEj2eg5w8GEw5403E9OQKIjZbEiaWsvYFnCGLw,1359
17
+ ostruct/cli/security/case_manager.py,sha256=I_ZJSyntLuGx5qVzze559CI-OxsaNPSibkAN8zZ7PvE,2345
18
+ ostruct/cli/security/errors.py,sha256=VZDOGGD-jYLf6E5gCkKxrE34RJXJP_CPWGOF5jV_r4I,5230
19
+ ostruct/cli/security/normalization.py,sha256=qevvxW3hHDtD1cVvDym8LJEQD1AKenVB-0ZvjCYjn5E,5242
20
+ ostruct/cli/security/safe_joiner.py,sha256=PHowCeBAkfHfPqRwuO5Com0OemGuq3cHkdu2p9IYNT0,7107
21
+ ostruct/cli/security/security_manager.py,sha256=R54CgE7eG_0VybvjXj4fNn1jB-RHMUlnJ6Yw8BOtKKc,13512
22
+ ostruct/cli/security/symlink_resolver.py,sha256=wtZdJ_T_0FOy6B1P5ty1odEXQk9vr8BzlWeAFD4huJE,16744
23
+ ostruct/cli/security/types.py,sha256=15yuG_T4CXyAFFFdSWLjVS7ACmDGIPXhQpZ8awcDwCQ,2991
24
+ ostruct/cli/security/windows_paths.py,sha256=qxC2H2kLwtmQ7YePYde3UrmOJcGnsLEebDLh242sUaI,13453
25
+ ostruct/cli/serialization.py,sha256=ec0UswDE2onwtZVUoZaMCsGv6zW_tSKdBng2qVo6Ucs,704
26
+ ostruct/cli/template_env.py,sha256=S2ZvxuMQMicodSVqUhrw0kOzbNmlpQjSHtWlOwjXCms,1538
27
+ ostruct/cli/template_extensions.py,sha256=tJN3HGAS2yzGI8Up6STPday8NVL0VV6UCClBrtDKYr0,1623
28
+ ostruct/cli/template_filters.py,sha256=SjuQxlM5S283TS2El_AbrzETGnYoQeTpmA9sv5et3QI,19222
29
+ ostruct/cli/template_io.py,sha256=yUWO-8rZnSdX97DTMSEX8fG9CP1ISsOhm2NZN3Fab9A,8821
30
+ ostruct/cli/template_rendering.py,sha256=vp_4gvrYLd_kbQi3TYrYNniXLTeLmTaitGVBQManXvo,13342
31
+ ostruct/cli/template_schema.py,sha256=ckH4rUZnEgfm_BHS9LnMGr8LtDxRmZ0C6UBVrSp8KTc,19604
32
+ ostruct/cli/template_utils.py,sha256=Lf1TvonlRA835nxyevEBSPTEbKiz_momvQYM0ZoZDZU,8034
33
+ ostruct/cli/template_validation.py,sha256=AXa2zmsws1j-0CTFlp7fMiZR43iNLnj4h467up2JdgU,12693
34
+ ostruct/cli/token_utils.py,sha256=r4KPEO3Sec18Q6mU0aClK6XGShvusgUggXEQgEPPlaA,1369
35
+ ostruct/cli/utils.py,sha256=1UCl4rHjBWKR5EKugvlVGHiHjO3XXmqvkgeAUSyIPDU,831
36
+ ostruct/cli/validators.py,sha256=BYFZeebCPZObTUjO1TaAMpsD6h7ROkYAFn9C7uf1Q68,2992
37
+ ostruct/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ ostruct_cli-0.5.0.dist-info/LICENSE,sha256=QUOY6QCYVxAiH8vdrUTDqe3i9hQ5bcNczppDSVpLTjk,1068
39
+ ostruct_cli-0.5.0.dist-info/METADATA,sha256=1SaOOVJvTKEqaheLw2MZzdwtinTgw2x_ms3xerwuCKA,6533
40
+ ostruct_cli-0.5.0.dist-info/WHEEL,sha256=7dDg4QLnNKTvwIDR9Ac8jJaAmBC_owJrckbC0jjThyA,88
41
+ ostruct_cli-0.5.0.dist-info/entry_points.txt,sha256=NFq9IuqHVTem0j9zKjV8C1si_zGcP1RL6Wbvt9fUDXw,48
42
+ ostruct_cli-0.5.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.0.1
2
+ Generator: poetry-core 2.1.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any