paddleocr-skills 1.0.0 → 1.1.0
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.
- package/README.md +220 -220
- package/bin/paddleocr-skills.js +33 -20
- package/lib/copy.js +39 -39
- package/lib/installer.js +76 -70
- package/lib/prompts.js +67 -67
- package/lib/python.js +75 -75
- package/lib/verify.js +121 -121
- package/package.json +42 -42
- package/templates/.env.example +12 -12
- package/templates/{paddleocr-vl/references/paddleocr-vl → paddleocr-vl-1.5/references/paddleocr-vl-1.5}/layout_schema.md +64 -64
- package/templates/{paddleocr-vl/references/paddleocr-vl → paddleocr-vl-1.5/references/paddleocr-vl-1.5}/output_format.md +154 -154
- package/templates/{paddleocr-vl/references/paddleocr-vl → paddleocr-vl-1.5/references/paddleocr-vl-1.5}/vl_model_spec.md +157 -157
- package/templates/{paddleocr-vl/scripts/paddleocr-vl → paddleocr-vl-1.5/scripts/paddleocr-vl-1.5}/_lib.py +780 -780
- package/templates/{paddleocr-vl/scripts/paddleocr-vl → paddleocr-vl-1.5/scripts/paddleocr-vl-1.5}/configure.py +270 -270
- package/templates/{paddleocr-vl/scripts/paddleocr-vl → paddleocr-vl-1.5/scripts/paddleocr-vl-1.5}/optimize_file.py +226 -226
- package/templates/{paddleocr-vl/scripts/paddleocr-vl → paddleocr-vl-1.5/scripts/paddleocr-vl-1.5}/requirements-optimize.txt +8 -8
- package/templates/{paddleocr-vl/scripts/paddleocr-vl → paddleocr-vl-1.5/scripts/paddleocr-vl-1.5}/requirements.txt +7 -7
- package/templates/{paddleocr-vl/scripts/paddleocr-vl → paddleocr-vl-1.5/scripts/paddleocr-vl-1.5}/smoke_test.py +199 -199
- package/templates/{paddleocr-vl/scripts/paddleocr-vl → paddleocr-vl-1.5/scripts/paddleocr-vl-1.5}/vl_caller.py +232 -232
- package/templates/{paddleocr-vl/skills/paddleocr-vl → paddleocr-vl-1.5/skills/paddleocr-vl-1.5}/SKILL.md +481 -481
- package/templates/ppocrv5/references/ppocrv5/agent_policy.md +258 -258
- package/templates/ppocrv5/references/ppocrv5/normalized_schema.md +257 -257
- package/templates/ppocrv5/references/ppocrv5/provider_api.md +140 -140
- package/templates/ppocrv5/scripts/ppocrv5/_lib.py +635 -635
- package/templates/ppocrv5/scripts/ppocrv5/configure.py +346 -346
- package/templates/ppocrv5/scripts/ppocrv5/ocr_caller.py +684 -684
- package/templates/ppocrv5/scripts/ppocrv5/requirements.txt +4 -4
- package/templates/ppocrv5/scripts/ppocrv5/smoke_test.py +139 -139
- package/templates/ppocrv5/skills/ppocrv5/SKILL.md +272 -272
|
@@ -1,232 +1,232 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
PaddleOCR-VL Document Parser
|
|
4
|
-
|
|
5
|
-
High-quality document parsing with layout analysis.
|
|
6
|
-
Returns complete API response without filtering.
|
|
7
|
-
|
|
8
|
-
Usage:
|
|
9
|
-
python scripts/paddleocr-vl/vl_caller.py --file-url "URL"
|
|
10
|
-
python scripts/paddleocr-vl/vl_caller.py --file-path "document.pdf"
|
|
11
|
-
python scripts/paddleocr-vl/vl_caller.py --file-path "doc.pdf" --pretty --show-quality
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import argparse
|
|
15
|
-
import json
|
|
16
|
-
import sys
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
|
|
19
|
-
# Add current directory to Python path for imports
|
|
20
|
-
script_dir = Path(__file__).parent
|
|
21
|
-
sys.path.insert(0, str(script_dir))
|
|
22
|
-
|
|
23
|
-
from _lib import (
|
|
24
|
-
Config,
|
|
25
|
-
make_api_request,
|
|
26
|
-
format_error_output,
|
|
27
|
-
wrap_success_output,
|
|
28
|
-
QualityEvaluator,
|
|
29
|
-
setup_logging,
|
|
30
|
-
ERROR_CONFIG,
|
|
31
|
-
ERROR_AUTH,
|
|
32
|
-
ERROR_QUOTA,
|
|
33
|
-
ERROR_TIMEOUT,
|
|
34
|
-
ERROR_OVERLOADED,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def main():
|
|
39
|
-
parser = argparse.ArgumentParser(
|
|
40
|
-
description='PaddleOCR-VL - High-quality document parsing with layout analysis',
|
|
41
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
42
|
-
epilog="""
|
|
43
|
-
Examples:
|
|
44
|
-
# Parse document from URL
|
|
45
|
-
python scripts/paddleocr-vl/vl_caller.py --file-url "https://example.com/document.pdf"
|
|
46
|
-
|
|
47
|
-
# Parse local file
|
|
48
|
-
python scripts/paddleocr-vl/vl_caller.py --file-path "./invoice.pdf"
|
|
49
|
-
|
|
50
|
-
# Pretty print JSON output with quality metrics
|
|
51
|
-
python scripts/paddleocr-vl/vl_caller.py --file-path "doc.pdf" --pretty --show-quality
|
|
52
|
-
|
|
53
|
-
# Disable cache for fresh processing
|
|
54
|
-
python scripts/paddleocr-vl/vl_caller.py --file-url "URL" --no-cache
|
|
55
|
-
|
|
56
|
-
Notes:
|
|
57
|
-
- This script returns COMPLETE API response (all content)
|
|
58
|
-
- Claude will extract what the user needs from the full data
|
|
59
|
-
- No content is filtered or removed at script level
|
|
60
|
-
- Results are cached for 10 minutes by default
|
|
61
|
-
"""
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# Input file (mutually exclusive)
|
|
65
|
-
input_group = parser.add_mutually_exclusive_group(required=True)
|
|
66
|
-
input_group.add_argument(
|
|
67
|
-
'--file-url',
|
|
68
|
-
help='URL to document (PDF, PNG, JPG, etc.)'
|
|
69
|
-
)
|
|
70
|
-
input_group.add_argument(
|
|
71
|
-
'--file-path',
|
|
72
|
-
help='Local file path'
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
# Output options
|
|
76
|
-
parser.add_argument(
|
|
77
|
-
'--pretty',
|
|
78
|
-
action='store_true',
|
|
79
|
-
help='Pretty print JSON output'
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
parser.add_argument(
|
|
83
|
-
'--output', '-o',
|
|
84
|
-
metavar='FILE',
|
|
85
|
-
help='Save result to JSON file (absolute or relative path)'
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
parser.add_argument(
|
|
89
|
-
'--show-quality',
|
|
90
|
-
action='store_true',
|
|
91
|
-
help='Show quality assessment and confidence scores'
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
parser.add_argument(
|
|
95
|
-
'--no-cache',
|
|
96
|
-
action='store_true',
|
|
97
|
-
help='Disable cache, force fresh API call'
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
# Advanced options
|
|
101
|
-
parser.add_argument(
|
|
102
|
-
'--timeout',
|
|
103
|
-
type=int,
|
|
104
|
-
default=30000,
|
|
105
|
-
help='Request timeout in milliseconds (default: 30000)'
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
parser.add_argument(
|
|
109
|
-
'--log-level',
|
|
110
|
-
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
|
|
111
|
-
default='INFO',
|
|
112
|
-
help='Set logging level (default: INFO)'
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
args = parser.parse_args()
|
|
116
|
-
|
|
117
|
-
# Setup logging
|
|
118
|
-
setup_logging(args.log_level)
|
|
119
|
-
|
|
120
|
-
# Load config from .env file
|
|
121
|
-
try:
|
|
122
|
-
api_url = Config.get_vl_api_url()
|
|
123
|
-
token = Config.get_vl_token()
|
|
124
|
-
timeout_ms = Config.get_timeout_ms()
|
|
125
|
-
max_retry = Config.get_max_retry()
|
|
126
|
-
cache_ttl_sec = Config.get_cache_ttl_sec()
|
|
127
|
-
except ValueError as e:
|
|
128
|
-
print(f"\nConfiguration error: {e}", file=sys.stderr)
|
|
129
|
-
sys.exit(2)
|
|
130
|
-
|
|
131
|
-
# Call API
|
|
132
|
-
try:
|
|
133
|
-
result = make_api_request(
|
|
134
|
-
file_path=args.file_path,
|
|
135
|
-
file_url=args.file_url,
|
|
136
|
-
timeout_ms=args.timeout,
|
|
137
|
-
use_cache=not args.no_cache
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
# Show quality assessment if requested
|
|
141
|
-
if args.show_quality and result.get("ok", False):
|
|
142
|
-
quality = QualityEvaluator.evaluate(result)
|
|
143
|
-
|
|
144
|
-
print("=" * 60)
|
|
145
|
-
print("QUALITY ASSESSMENT")
|
|
146
|
-
print("=" * 60)
|
|
147
|
-
print(f"Overall Confidence: {quality['overall_confidence']:.2f} / 1.00")
|
|
148
|
-
print(f"Quality Level: {quality['quality_level']}")
|
|
149
|
-
|
|
150
|
-
if quality.get('region_stats'):
|
|
151
|
-
stats = quality['region_stats']
|
|
152
|
-
print(f"\nRegions Detected: {stats.get('total_regions', 0)}")
|
|
153
|
-
if stats.get('by_type'):
|
|
154
|
-
print("Region Types:")
|
|
155
|
-
for region_type, count in stats['by_type'].items():
|
|
156
|
-
print(f" - {region_type}: {count}")
|
|
157
|
-
|
|
158
|
-
if stats.get('low_confidence_count', 0) > 0:
|
|
159
|
-
print(f"\nWarning: {stats['low_confidence_count']} regions have low confidence")
|
|
160
|
-
|
|
161
|
-
if quality.get('warnings'):
|
|
162
|
-
print("\nWarnings:")
|
|
163
|
-
for warning in quality['warnings']:
|
|
164
|
-
print(f" ⚠ {warning}")
|
|
165
|
-
|
|
166
|
-
print("=" * 60)
|
|
167
|
-
print()
|
|
168
|
-
|
|
169
|
-
# Ensure result is wrapped in standard format
|
|
170
|
-
output = wrap_success_output(result)
|
|
171
|
-
|
|
172
|
-
# Prepare JSON output
|
|
173
|
-
indent = 2 if args.pretty else None
|
|
174
|
-
json_output = json.dumps(output, indent=indent, ensure_ascii=False)
|
|
175
|
-
|
|
176
|
-
# Save to file if --output specified
|
|
177
|
-
if args.output:
|
|
178
|
-
try:
|
|
179
|
-
output_path = Path(args.output).resolve()
|
|
180
|
-
|
|
181
|
-
# Create directory if not exists
|
|
182
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
-
|
|
184
|
-
# Write file
|
|
185
|
-
with open(output_path, 'w', encoding='utf-8') as f:
|
|
186
|
-
f.write(json_output)
|
|
187
|
-
|
|
188
|
-
# Print success message to stderr (so it doesn't mix with JSON output)
|
|
189
|
-
print(f"Result saved to: {output_path}", file=sys.stderr)
|
|
190
|
-
|
|
191
|
-
except PermissionError:
|
|
192
|
-
print(f"Error: Permission denied to write to {output_path}", file=sys.stderr)
|
|
193
|
-
sys.exit(5)
|
|
194
|
-
except OSError as e:
|
|
195
|
-
print(f"Error: Cannot write to {output_path}: {e}", file=sys.stderr)
|
|
196
|
-
sys.exit(5)
|
|
197
|
-
else:
|
|
198
|
-
# No --output: print to stdout (original behavior)
|
|
199
|
-
print(json_output)
|
|
200
|
-
|
|
201
|
-
# Determine exit code based on result
|
|
202
|
-
if not result.get("ok", False):
|
|
203
|
-
error_code = result.get("error", {}).get("code", "UNKNOWN")
|
|
204
|
-
|
|
205
|
-
# Map error codes to exit codes (aligned with ppocrv5)
|
|
206
|
-
if error_code == ERROR_CONFIG:
|
|
207
|
-
sys.exit(1) # Configuration error
|
|
208
|
-
elif error_code in [ERROR_AUTH, ERROR_QUOTA]:
|
|
209
|
-
sys.exit(2) # Authentication or quota error
|
|
210
|
-
elif error_code in [ERROR_TIMEOUT, ERROR_OVERLOADED]:
|
|
211
|
-
sys.exit(3) # Timeout or service overload
|
|
212
|
-
else:
|
|
213
|
-
sys.exit(4) # Other errors
|
|
214
|
-
|
|
215
|
-
# Success
|
|
216
|
-
sys.exit(0)
|
|
217
|
-
|
|
218
|
-
except ValueError as e:
|
|
219
|
-
# Configuration errors
|
|
220
|
-
output = format_error_output(e, ERROR_CONFIG)
|
|
221
|
-
print(json.dumps(output, indent=2, ensure_ascii=False), file=sys.stderr)
|
|
222
|
-
sys.exit(1)
|
|
223
|
-
|
|
224
|
-
except Exception as e:
|
|
225
|
-
# Unexpected errors
|
|
226
|
-
output = format_error_output(e)
|
|
227
|
-
print(json.dumps(output, indent=2, ensure_ascii=False), file=sys.stderr)
|
|
228
|
-
sys.exit(4)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if __name__ == '__main__':
|
|
232
|
-
main()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PaddleOCR-VL 1.5 Document Parser
|
|
4
|
+
|
|
5
|
+
High-quality document parsing with layout analysis.
|
|
6
|
+
Returns complete API response without filtering.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python scripts/paddleocr-vl-1.5/vl_caller.py --file-url "URL"
|
|
10
|
+
python scripts/paddleocr-vl-1.5/vl_caller.py --file-path "document.pdf"
|
|
11
|
+
python scripts/paddleocr-vl-1.5/vl_caller.py --file-path "doc.pdf" --pretty --show-quality
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
# Add current directory to Python path for imports
|
|
20
|
+
script_dir = Path(__file__).parent
|
|
21
|
+
sys.path.insert(0, str(script_dir))
|
|
22
|
+
|
|
23
|
+
from _lib import (
|
|
24
|
+
Config,
|
|
25
|
+
make_api_request,
|
|
26
|
+
format_error_output,
|
|
27
|
+
wrap_success_output,
|
|
28
|
+
QualityEvaluator,
|
|
29
|
+
setup_logging,
|
|
30
|
+
ERROR_CONFIG,
|
|
31
|
+
ERROR_AUTH,
|
|
32
|
+
ERROR_QUOTA,
|
|
33
|
+
ERROR_TIMEOUT,
|
|
34
|
+
ERROR_OVERLOADED,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main():
|
|
39
|
+
parser = argparse.ArgumentParser(
|
|
40
|
+
description='PaddleOCR-VL 1.5 - High-quality document parsing with layout analysis',
|
|
41
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
42
|
+
epilog="""
|
|
43
|
+
Examples:
|
|
44
|
+
# Parse document from URL
|
|
45
|
+
python scripts/paddleocr-vl-1.5/vl_caller.py --file-url "https://example.com/document.pdf"
|
|
46
|
+
|
|
47
|
+
# Parse local file
|
|
48
|
+
python scripts/paddleocr-vl-1.5/vl_caller.py --file-path "./invoice.pdf"
|
|
49
|
+
|
|
50
|
+
# Pretty print JSON output with quality metrics
|
|
51
|
+
python scripts/paddleocr-vl-1.5/vl_caller.py --file-path "doc.pdf" --pretty --show-quality
|
|
52
|
+
|
|
53
|
+
# Disable cache for fresh processing
|
|
54
|
+
python scripts/paddleocr-vl-1.5/vl_caller.py --file-url "URL" --no-cache
|
|
55
|
+
|
|
56
|
+
Notes:
|
|
57
|
+
- This script returns COMPLETE API response (all content)
|
|
58
|
+
- Claude will extract what the user needs from the full data
|
|
59
|
+
- No content is filtered or removed at script level
|
|
60
|
+
- Results are cached for 10 minutes by default
|
|
61
|
+
"""
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Input file (mutually exclusive)
|
|
65
|
+
input_group = parser.add_mutually_exclusive_group(required=True)
|
|
66
|
+
input_group.add_argument(
|
|
67
|
+
'--file-url',
|
|
68
|
+
help='URL to document (PDF, PNG, JPG, etc.)'
|
|
69
|
+
)
|
|
70
|
+
input_group.add_argument(
|
|
71
|
+
'--file-path',
|
|
72
|
+
help='Local file path'
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Output options
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
'--pretty',
|
|
78
|
+
action='store_true',
|
|
79
|
+
help='Pretty print JSON output'
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
parser.add_argument(
|
|
83
|
+
'--output', '-o',
|
|
84
|
+
metavar='FILE',
|
|
85
|
+
help='Save result to JSON file (absolute or relative path)'
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
parser.add_argument(
|
|
89
|
+
'--show-quality',
|
|
90
|
+
action='store_true',
|
|
91
|
+
help='Show quality assessment and confidence scores'
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
'--no-cache',
|
|
96
|
+
action='store_true',
|
|
97
|
+
help='Disable cache, force fresh API call'
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Advanced options
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
'--timeout',
|
|
103
|
+
type=int,
|
|
104
|
+
default=30000,
|
|
105
|
+
help='Request timeout in milliseconds (default: 30000)'
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
'--log-level',
|
|
110
|
+
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
|
|
111
|
+
default='INFO',
|
|
112
|
+
help='Set logging level (default: INFO)'
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
args = parser.parse_args()
|
|
116
|
+
|
|
117
|
+
# Setup logging
|
|
118
|
+
setup_logging(args.log_level)
|
|
119
|
+
|
|
120
|
+
# Load config from .env file
|
|
121
|
+
try:
|
|
122
|
+
api_url = Config.get_vl_api_url()
|
|
123
|
+
token = Config.get_vl_token()
|
|
124
|
+
timeout_ms = Config.get_timeout_ms()
|
|
125
|
+
max_retry = Config.get_max_retry()
|
|
126
|
+
cache_ttl_sec = Config.get_cache_ttl_sec()
|
|
127
|
+
except ValueError as e:
|
|
128
|
+
print(f"\nConfiguration error: {e}", file=sys.stderr)
|
|
129
|
+
sys.exit(2)
|
|
130
|
+
|
|
131
|
+
# Call API
|
|
132
|
+
try:
|
|
133
|
+
result = make_api_request(
|
|
134
|
+
file_path=args.file_path,
|
|
135
|
+
file_url=args.file_url,
|
|
136
|
+
timeout_ms=args.timeout,
|
|
137
|
+
use_cache=not args.no_cache
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Show quality assessment if requested
|
|
141
|
+
if args.show_quality and result.get("ok", False):
|
|
142
|
+
quality = QualityEvaluator.evaluate(result)
|
|
143
|
+
|
|
144
|
+
print("=" * 60)
|
|
145
|
+
print("QUALITY ASSESSMENT")
|
|
146
|
+
print("=" * 60)
|
|
147
|
+
print(f"Overall Confidence: {quality['overall_confidence']:.2f} / 1.00")
|
|
148
|
+
print(f"Quality Level: {quality['quality_level']}")
|
|
149
|
+
|
|
150
|
+
if quality.get('region_stats'):
|
|
151
|
+
stats = quality['region_stats']
|
|
152
|
+
print(f"\nRegions Detected: {stats.get('total_regions', 0)}")
|
|
153
|
+
if stats.get('by_type'):
|
|
154
|
+
print("Region Types:")
|
|
155
|
+
for region_type, count in stats['by_type'].items():
|
|
156
|
+
print(f" - {region_type}: {count}")
|
|
157
|
+
|
|
158
|
+
if stats.get('low_confidence_count', 0) > 0:
|
|
159
|
+
print(f"\nWarning: {stats['low_confidence_count']} regions have low confidence")
|
|
160
|
+
|
|
161
|
+
if quality.get('warnings'):
|
|
162
|
+
print("\nWarnings:")
|
|
163
|
+
for warning in quality['warnings']:
|
|
164
|
+
print(f" ⚠ {warning}")
|
|
165
|
+
|
|
166
|
+
print("=" * 60)
|
|
167
|
+
print()
|
|
168
|
+
|
|
169
|
+
# Ensure result is wrapped in standard format
|
|
170
|
+
output = wrap_success_output(result)
|
|
171
|
+
|
|
172
|
+
# Prepare JSON output
|
|
173
|
+
indent = 2 if args.pretty else None
|
|
174
|
+
json_output = json.dumps(output, indent=indent, ensure_ascii=False)
|
|
175
|
+
|
|
176
|
+
# Save to file if --output specified
|
|
177
|
+
if args.output:
|
|
178
|
+
try:
|
|
179
|
+
output_path = Path(args.output).resolve()
|
|
180
|
+
|
|
181
|
+
# Create directory if not exists
|
|
182
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
|
|
184
|
+
# Write file
|
|
185
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
186
|
+
f.write(json_output)
|
|
187
|
+
|
|
188
|
+
# Print success message to stderr (so it doesn't mix with JSON output)
|
|
189
|
+
print(f"Result saved to: {output_path}", file=sys.stderr)
|
|
190
|
+
|
|
191
|
+
except PermissionError:
|
|
192
|
+
print(f"Error: Permission denied to write to {output_path}", file=sys.stderr)
|
|
193
|
+
sys.exit(5)
|
|
194
|
+
except OSError as e:
|
|
195
|
+
print(f"Error: Cannot write to {output_path}: {e}", file=sys.stderr)
|
|
196
|
+
sys.exit(5)
|
|
197
|
+
else:
|
|
198
|
+
# No --output: print to stdout (original behavior)
|
|
199
|
+
print(json_output)
|
|
200
|
+
|
|
201
|
+
# Determine exit code based on result
|
|
202
|
+
if not result.get("ok", False):
|
|
203
|
+
error_code = result.get("error", {}).get("code", "UNKNOWN")
|
|
204
|
+
|
|
205
|
+
# Map error codes to exit codes (aligned with ppocrv5)
|
|
206
|
+
if error_code == ERROR_CONFIG:
|
|
207
|
+
sys.exit(1) # Configuration error
|
|
208
|
+
elif error_code in [ERROR_AUTH, ERROR_QUOTA]:
|
|
209
|
+
sys.exit(2) # Authentication or quota error
|
|
210
|
+
elif error_code in [ERROR_TIMEOUT, ERROR_OVERLOADED]:
|
|
211
|
+
sys.exit(3) # Timeout or service overload
|
|
212
|
+
else:
|
|
213
|
+
sys.exit(4) # Other errors
|
|
214
|
+
|
|
215
|
+
# Success
|
|
216
|
+
sys.exit(0)
|
|
217
|
+
|
|
218
|
+
except ValueError as e:
|
|
219
|
+
# Configuration errors
|
|
220
|
+
output = format_error_output(e, ERROR_CONFIG)
|
|
221
|
+
print(json.dumps(output, indent=2, ensure_ascii=False), file=sys.stderr)
|
|
222
|
+
sys.exit(1)
|
|
223
|
+
|
|
224
|
+
except Exception as e:
|
|
225
|
+
# Unexpected errors
|
|
226
|
+
output = format_error_output(e)
|
|
227
|
+
print(json.dumps(output, indent=2, ensure_ascii=False), file=sys.stderr)
|
|
228
|
+
sys.exit(4)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if __name__ == '__main__':
|
|
232
|
+
main()
|