licos-dev-cli 0.2.16__tar.gz → 0.2.17__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.
- {licos_dev_cli-0.2.16 → licos_dev_cli-0.2.17}/PKG-INFO +2 -2
- {licos_dev_cli-0.2.16 → licos_dev_cli-0.2.17}/pyproject.toml +19 -19
- {licos_dev_cli-0.2.16 → licos_dev_cli-0.2.17}/src/licos_dev_cli/main.py +549 -549
- {licos_dev_cli-0.2.16 → licos_dev_cli-0.2.17}/.gitignore +0 -0
- {licos_dev_cli-0.2.16 → licos_dev_cli-0.2.17}/src/licos_dev_cli/__init__.py +0 -0
- {licos_dev_cli-0.2.16 → licos_dev_cli-0.2.17}/tests/test_cli_output_paths.py +0 -0
- {licos_dev_cli-0.2.16 → licos_dev_cli-0.2.17}/tests/test_credit_check.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: licos-dev-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.17
|
|
4
4
|
Summary: LICOS Dev CLI - generate files and call model capabilities
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: click>=8.1
|
|
7
|
-
Requires-Dist: licos-dev-sdk>=0.2.
|
|
7
|
+
Requires-Dist: licos-dev-sdk>=0.2.18
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
3
|
-
build-backend = "hatchling.build"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "licos-dev-cli"
|
|
7
|
-
version = "0.2.
|
|
8
|
-
description = "LICOS Dev CLI - generate files and call model capabilities"
|
|
9
|
-
requires-python = ">=3.10"
|
|
10
|
-
dependencies = [
|
|
11
|
-
"licos-dev-sdk>=0.2.
|
|
12
|
-
"click>=8.1",
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
[project.scripts]
|
|
16
|
-
licos-dev = "licos_dev_cli.main:cli"
|
|
17
|
-
|
|
18
|
-
[tool.hatch.build.targets.wheel]
|
|
19
|
-
packages = ["src/licos_dev_cli"]
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "licos-dev-cli"
|
|
7
|
+
version = "0.2.17"
|
|
8
|
+
description = "LICOS Dev CLI - generate files and call model capabilities"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"licos-dev-sdk>=0.2.18",
|
|
12
|
+
"click>=8.1",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
licos-dev = "licos_dev_cli.main:cli"
|
|
17
|
+
|
|
18
|
+
[tool.hatch.build.targets.wheel]
|
|
19
|
+
packages = ["src/licos_dev_cli"]
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
"""CLI entry point - wraps licos-dev-sdk functions as click commands."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
1
|
+
"""CLI entry point - wraps licos-dev-sdk functions as click commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
5
|
import sys
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
import click
|
|
10
|
-
|
|
11
|
-
# ── Helpers ──────────────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
def _read_input(input_path: str | None, content: str | None) -> str:
|
|
14
|
-
"""Read content from --input file, --content arg, or stdin."""
|
|
15
|
-
if input_path:
|
|
16
|
-
with open(input_path, "r", encoding="utf-8") as f:
|
|
17
|
-
return f.read()
|
|
18
|
-
if content:
|
|
19
|
-
return content
|
|
20
|
-
if not sys.stdin.isatty():
|
|
21
|
-
return sys.stdin.read()
|
|
22
|
-
raise click.UsageError("Provide --input, --content, or pipe via stdin.")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _read_json(input_path: str | None, content: str | None) -> object:
|
|
26
|
-
"""Read and parse JSON from input."""
|
|
27
|
-
raw = _read_input(input_path, content)
|
|
28
|
-
return json.loads(raw)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _json_option(value: str | None, name: str) -> dict | list | None:
|
|
32
|
-
if not value:
|
|
33
|
-
return None
|
|
34
|
-
try:
|
|
35
|
-
return json.loads(value)
|
|
36
|
-
except json.JSONDecodeError as exc:
|
|
37
|
-
raise click.BadParameter(f"{name} must be valid JSON") from exc
|
|
38
|
-
|
|
39
|
-
|
|
10
|
+
|
|
11
|
+
# ── Helpers ──────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
def _read_input(input_path: str | None, content: str | None) -> str:
|
|
14
|
+
"""Read content from --input file, --content arg, or stdin."""
|
|
15
|
+
if input_path:
|
|
16
|
+
with open(input_path, "r", encoding="utf-8") as f:
|
|
17
|
+
return f.read()
|
|
18
|
+
if content:
|
|
19
|
+
return content
|
|
20
|
+
if not sys.stdin.isatty():
|
|
21
|
+
return sys.stdin.read()
|
|
22
|
+
raise click.UsageError("Provide --input, --content, or pipe via stdin.")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _read_json(input_path: str | None, content: str | None) -> object:
|
|
26
|
+
"""Read and parse JSON from input."""
|
|
27
|
+
raw = _read_input(input_path, content)
|
|
28
|
+
return json.loads(raw)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _json_option(value: str | None, name: str) -> dict | list | None:
|
|
32
|
+
if not value:
|
|
33
|
+
return None
|
|
34
|
+
try:
|
|
35
|
+
return json.loads(value)
|
|
36
|
+
except json.JSONDecodeError as exc:
|
|
37
|
+
raise click.BadParameter(f"{name} must be valid JSON") from exc
|
|
38
|
+
|
|
39
|
+
|
|
40
40
|
def _echo_json(value: object) -> None:
|
|
41
41
|
if hasattr(value, "to_dict"):
|
|
42
42
|
value = value.to_dict()
|
|
@@ -88,7 +88,7 @@ def _echo_path(path: object) -> None:
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
# ── CLI Group ────────────────────────────────────────────────────────────────
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
@click.group()
|
|
93
93
|
def cli():
|
|
94
94
|
"""LICOS Dev - file generation and model CLI for AI agents."""
|
|
@@ -115,584 +115,584 @@ def credit_check(estimated_points, resource_type, resource_code, request_id):
|
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
# ── PDF ──────────────────────────────────────────────────────────────────────
|
|
118
|
-
|
|
119
|
-
@cli.command()
|
|
120
|
-
@click.option("-i", "--input", "input_path", help="Input file (Markdown or HTML)")
|
|
121
|
-
@click.option("-c", "--content", help="Direct content")
|
|
122
|
-
@click.option("-f", "--filename", required=True, help="Output filename (no extension)")
|
|
123
|
-
@click.option("-o", "--output-dir", default=None, help="Output directory")
|
|
124
|
-
@click.option("--format", "content_type", default="markdown", help="Content type: markdown|html")
|
|
125
|
-
@click.option("--page-size", default="A4", help="Page size: A4|LETTER")
|
|
126
|
-
def pdf(input_path, content, filename, output_dir, content_type, page_size):
|
|
127
|
-
"""Generate PDF from Markdown or HTML."""
|
|
118
|
+
|
|
119
|
+
@cli.command()
|
|
120
|
+
@click.option("-i", "--input", "input_path", help="Input file (Markdown or HTML)")
|
|
121
|
+
@click.option("-c", "--content", help="Direct content")
|
|
122
|
+
@click.option("-f", "--filename", required=True, help="Output filename (no extension)")
|
|
123
|
+
@click.option("-o", "--output-dir", default=None, help="Output directory")
|
|
124
|
+
@click.option("--format", "content_type", default="markdown", help="Content type: markdown|html")
|
|
125
|
+
@click.option("--page-size", default="A4", help="Page size: A4|LETTER")
|
|
126
|
+
def pdf(input_path, content, filename, output_dir, content_type, page_size):
|
|
127
|
+
"""Generate PDF from Markdown or HTML."""
|
|
128
128
|
from licos_dev_sdk import create_pdf
|
|
129
129
|
text = _read_input(input_path, content)
|
|
130
130
|
path = create_pdf(text, filename, content_type=content_type, output_dir=output_dir, page_size=page_size)
|
|
131
131
|
_echo_path(path)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# ── DOCX ─────────────────────────────────────────────────────────────────────
|
|
135
|
-
|
|
136
|
-
@cli.command()
|
|
137
|
-
@click.option("-i", "--input", "input_path")
|
|
138
|
-
@click.option("-c", "--content")
|
|
139
|
-
@click.option("-f", "--filename", required=True)
|
|
140
|
-
@click.option("-o", "--output-dir", default=None)
|
|
141
|
-
@click.option("--format", "content_type", default="markdown")
|
|
142
|
-
@click.option("--font", default="Arial")
|
|
143
|
-
@click.option("--font-size", default=11, type=int)
|
|
144
|
-
def docx(input_path, content, filename, output_dir, content_type, font, font_size):
|
|
145
|
-
"""Generate DOCX from Markdown or HTML."""
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# ── DOCX ─────────────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
@cli.command()
|
|
137
|
+
@click.option("-i", "--input", "input_path")
|
|
138
|
+
@click.option("-c", "--content")
|
|
139
|
+
@click.option("-f", "--filename", required=True)
|
|
140
|
+
@click.option("-o", "--output-dir", default=None)
|
|
141
|
+
@click.option("--format", "content_type", default="markdown")
|
|
142
|
+
@click.option("--font", default="Arial")
|
|
143
|
+
@click.option("--font-size", default=11, type=int)
|
|
144
|
+
def docx(input_path, content, filename, output_dir, content_type, font, font_size):
|
|
145
|
+
"""Generate DOCX from Markdown or HTML."""
|
|
146
146
|
from licos_dev_sdk import create_docx
|
|
147
147
|
text = _read_input(input_path, content)
|
|
148
148
|
path = create_docx(text, filename, content_type=content_type, output_dir=output_dir, font_name=font, font_size=font_size)
|
|
149
149
|
_echo_path(path)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@cli.command("docx-template")
|
|
153
|
-
@click.option("-t", "--template", "template_path", required=True, help="DOCX template path")
|
|
154
|
-
@click.option("-i", "--input", "input_path", help="JSON data file")
|
|
155
|
-
@click.option("-d", "--data", "data_content", help="JSON data")
|
|
156
|
-
@click.option("-f", "--filename", required=True)
|
|
157
|
-
@click.option("-o", "--output-dir", default=None)
|
|
158
|
-
def docx_template(template_path, input_path, data_content, filename, output_dir):
|
|
159
|
-
"""Generate DOCX by rendering a DOCX template with JSON data."""
|
|
160
|
-
from licos_dev_sdk import create_docx_from_template
|
|
161
|
-
data = _read_json(input_path, data_content)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@cli.command("docx-template")
|
|
153
|
+
@click.option("-t", "--template", "template_path", required=True, help="DOCX template path")
|
|
154
|
+
@click.option("-i", "--input", "input_path", help="JSON data file")
|
|
155
|
+
@click.option("-d", "--data", "data_content", help="JSON data")
|
|
156
|
+
@click.option("-f", "--filename", required=True)
|
|
157
|
+
@click.option("-o", "--output-dir", default=None)
|
|
158
|
+
def docx_template(template_path, input_path, data_content, filename, output_dir):
|
|
159
|
+
"""Generate DOCX by rendering a DOCX template with JSON data."""
|
|
160
|
+
from licos_dev_sdk import create_docx_from_template
|
|
161
|
+
data = _read_json(input_path, data_content)
|
|
162
162
|
if not isinstance(data, dict):
|
|
163
163
|
raise click.BadParameter("template data must be a JSON object")
|
|
164
164
|
path = create_docx_from_template(template_path, data, filename, output_dir=output_dir)
|
|
165
165
|
_echo_path(path)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# ── XLSX ─────────────────────────────────────────────────────────────────────
|
|
169
|
-
|
|
170
|
-
@cli.command()
|
|
171
|
-
@click.option("-i", "--input", "input_path")
|
|
172
|
-
@click.option("-c", "--content", help="JSON data")
|
|
173
|
-
@click.option("-f", "--filename", required=True)
|
|
174
|
-
@click.option("-o", "--output-dir", default=None)
|
|
175
|
-
@click.option("--sheet-name", default="Sheet1")
|
|
176
|
-
@click.option("--header-color", default="4472C4")
|
|
177
|
-
def xlsx(input_path, content, filename, output_dir, sheet_name, header_color):
|
|
178
|
-
"""Generate XLSX from JSON data."""
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# ── XLSX ─────────────────────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
@cli.command()
|
|
171
|
+
@click.option("-i", "--input", "input_path")
|
|
172
|
+
@click.option("-c", "--content", help="JSON data")
|
|
173
|
+
@click.option("-f", "--filename", required=True)
|
|
174
|
+
@click.option("-o", "--output-dir", default=None)
|
|
175
|
+
@click.option("--sheet-name", default="Sheet1")
|
|
176
|
+
@click.option("--header-color", default="4472C4")
|
|
177
|
+
def xlsx(input_path, content, filename, output_dir, sheet_name, header_color):
|
|
178
|
+
"""Generate XLSX from JSON data."""
|
|
179
179
|
from licos_dev_sdk import create_xlsx
|
|
180
180
|
data = _read_json(input_path, content)
|
|
181
181
|
path = create_xlsx(data, filename, output_dir=output_dir, sheet_name=sheet_name, header_color=header_color)
|
|
182
182
|
_echo_path(path)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
@cli.command("xlsx-workbook")
|
|
186
|
-
@click.option("-i", "--input", "input_path")
|
|
187
|
-
@click.option("-c", "--content", help="JSON workbook data")
|
|
188
|
-
@click.option("-f", "--filename", required=True)
|
|
189
|
-
@click.option("-o", "--output-dir", default=None)
|
|
190
|
-
@click.option("--header-color", default="4472C4")
|
|
191
|
-
@click.option("--freeze-header/--no-freeze-header", default=True)
|
|
192
|
-
@click.option("--autofilter/--no-autofilter", default=True)
|
|
193
|
-
def xlsx_workbook(input_path, content, filename, output_dir, header_color, freeze_header, autofilter):
|
|
194
|
-
"""Generate multi-sheet XLSX from JSON workbook data."""
|
|
195
|
-
from licos_dev_sdk import create_xlsx_workbook
|
|
196
|
-
data = _read_json(input_path, content)
|
|
197
|
-
path = create_xlsx_workbook(
|
|
198
|
-
data,
|
|
199
|
-
filename,
|
|
200
|
-
output_dir=output_dir,
|
|
201
|
-
header_color=header_color,
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@cli.command("xlsx-workbook")
|
|
186
|
+
@click.option("-i", "--input", "input_path")
|
|
187
|
+
@click.option("-c", "--content", help="JSON workbook data")
|
|
188
|
+
@click.option("-f", "--filename", required=True)
|
|
189
|
+
@click.option("-o", "--output-dir", default=None)
|
|
190
|
+
@click.option("--header-color", default="4472C4")
|
|
191
|
+
@click.option("--freeze-header/--no-freeze-header", default=True)
|
|
192
|
+
@click.option("--autofilter/--no-autofilter", default=True)
|
|
193
|
+
def xlsx_workbook(input_path, content, filename, output_dir, header_color, freeze_header, autofilter):
|
|
194
|
+
"""Generate multi-sheet XLSX from JSON workbook data."""
|
|
195
|
+
from licos_dev_sdk import create_xlsx_workbook
|
|
196
|
+
data = _read_json(input_path, content)
|
|
197
|
+
path = create_xlsx_workbook(
|
|
198
|
+
data,
|
|
199
|
+
filename,
|
|
200
|
+
output_dir=output_dir,
|
|
201
|
+
header_color=header_color,
|
|
202
202
|
freeze_header=freeze_header,
|
|
203
203
|
autofilter=autofilter,
|
|
204
204
|
)
|
|
205
205
|
_echo_path(path)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
# ── CSV ──────────────────────────────────────────────────────────────────────
|
|
209
|
-
|
|
210
|
-
@cli.command()
|
|
211
|
-
@click.option("-i", "--input", "input_path")
|
|
212
|
-
@click.option("-c", "--content", help="JSON data")
|
|
213
|
-
@click.option("-f", "--filename", required=True)
|
|
214
|
-
@click.option("-o", "--output-dir", default=None)
|
|
215
|
-
def csv(input_path, content, filename, output_dir):
|
|
216
|
-
"""Generate CSV from JSON data."""
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# ── CSV ──────────────────────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
@cli.command()
|
|
211
|
+
@click.option("-i", "--input", "input_path")
|
|
212
|
+
@click.option("-c", "--content", help="JSON data")
|
|
213
|
+
@click.option("-f", "--filename", required=True)
|
|
214
|
+
@click.option("-o", "--output-dir", default=None)
|
|
215
|
+
def csv(input_path, content, filename, output_dir):
|
|
216
|
+
"""Generate CSV from JSON data."""
|
|
217
217
|
from licos_dev_sdk import create_csv
|
|
218
218
|
data = _read_json(input_path, content)
|
|
219
219
|
path = create_csv(data, filename, output_dir=output_dir)
|
|
220
220
|
_echo_path(path)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
# ── PPTX ─────────────────────────────────────────────────────────────────────
|
|
224
|
-
|
|
225
|
-
@cli.command()
|
|
226
|
-
@click.option("-i", "--input", "input_path")
|
|
227
|
-
@click.option("-c", "--content")
|
|
228
|
-
@click.option("-f", "--filename", required=True)
|
|
229
|
-
@click.option("-o", "--output-dir", default=None)
|
|
230
|
-
@click.option("--format", "content_type", default="markdown")
|
|
231
|
-
def pptx(input_path, content, filename, output_dir, content_type):
|
|
232
|
-
"""Generate PPTX from Markdown."""
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# ── PPTX ─────────────────────────────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
@cli.command()
|
|
226
|
+
@click.option("-i", "--input", "input_path")
|
|
227
|
+
@click.option("-c", "--content")
|
|
228
|
+
@click.option("-f", "--filename", required=True)
|
|
229
|
+
@click.option("-o", "--output-dir", default=None)
|
|
230
|
+
@click.option("--format", "content_type", default="markdown")
|
|
231
|
+
def pptx(input_path, content, filename, output_dir, content_type):
|
|
232
|
+
"""Generate PPTX from Markdown."""
|
|
233
233
|
from licos_dev_sdk import create_pptx
|
|
234
234
|
text = _read_input(input_path, content)
|
|
235
235
|
path = create_pptx(text, filename, content_type=content_type, output_dir=output_dir)
|
|
236
236
|
_echo_path(path)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
# ── Chart ────────────────────────────────────────────────────────────────────
|
|
240
|
-
|
|
241
|
-
@cli.command()
|
|
242
|
-
@click.option("-i", "--input", "input_path")
|
|
243
|
-
@click.option("-c", "--content", help="JSON chart data")
|
|
244
|
-
@click.option("-f", "--filename", required=True)
|
|
245
|
-
@click.option("-o", "--output-dir", default=None)
|
|
246
|
-
@click.option("--type", "chart_type", required=True, help="bar|line|pie|scatter|heatmap")
|
|
247
|
-
@click.option("--format", "fmt", default="png", help="png|svg")
|
|
248
|
-
@click.option("--title", default="")
|
|
249
|
-
@click.option("--width", default=800, type=int)
|
|
250
|
-
@click.option("--height", default=600, type=int)
|
|
251
|
-
def chart(input_path, content, filename, output_dir, chart_type, fmt, title, width, height):
|
|
252
|
-
"""Generate chart image from JSON data."""
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# ── Chart ────────────────────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
@cli.command()
|
|
242
|
+
@click.option("-i", "--input", "input_path")
|
|
243
|
+
@click.option("-c", "--content", help="JSON chart data")
|
|
244
|
+
@click.option("-f", "--filename", required=True)
|
|
245
|
+
@click.option("-o", "--output-dir", default=None)
|
|
246
|
+
@click.option("--type", "chart_type", required=True, help="bar|line|pie|scatter|heatmap")
|
|
247
|
+
@click.option("--format", "fmt", default="png", help="png|svg")
|
|
248
|
+
@click.option("--title", default="")
|
|
249
|
+
@click.option("--width", default=800, type=int)
|
|
250
|
+
@click.option("--height", default=600, type=int)
|
|
251
|
+
def chart(input_path, content, filename, output_dir, chart_type, fmt, title, width, height):
|
|
252
|
+
"""Generate chart image from JSON data."""
|
|
253
253
|
from licos_dev_sdk import create_chart
|
|
254
254
|
data = _read_json(input_path, content)
|
|
255
255
|
path = create_chart(chart_type, data, filename, output_dir=output_dir, format=fmt, title=title, width=width, height=height)
|
|
256
256
|
_echo_path(path)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
# ── Diagram ──────────────────────────────────────────────────────────────────
|
|
260
|
-
|
|
261
|
-
@cli.command()
|
|
262
|
-
@click.option("-i", "--input", "input_path")
|
|
263
|
-
@click.option("-c", "--content", help="DOT source")
|
|
264
|
-
@click.option("-f", "--filename", required=True)
|
|
265
|
-
@click.option("-o", "--output-dir", default=None)
|
|
266
|
-
@click.option("--format", "fmt", default="png", help="png|svg|pdf")
|
|
267
|
-
def diagram(input_path, content, filename, output_dir, fmt):
|
|
268
|
-
"""Generate diagram from Graphviz DOT source."""
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# ── Diagram ──────────────────────────────────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
@cli.command()
|
|
262
|
+
@click.option("-i", "--input", "input_path")
|
|
263
|
+
@click.option("-c", "--content", help="DOT source")
|
|
264
|
+
@click.option("-f", "--filename", required=True)
|
|
265
|
+
@click.option("-o", "--output-dir", default=None)
|
|
266
|
+
@click.option("--format", "fmt", default="png", help="png|svg|pdf")
|
|
267
|
+
def diagram(input_path, content, filename, output_dir, fmt):
|
|
268
|
+
"""Generate diagram from Graphviz DOT source."""
|
|
269
269
|
from licos_dev_sdk import create_diagram
|
|
270
270
|
text = _read_input(input_path, content)
|
|
271
271
|
path = create_diagram(text, filename, output_dir=output_dir, format=fmt)
|
|
272
272
|
_echo_path(path)
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
# ── QR Code ──────────────────────────────────────────────────────────────────
|
|
276
|
-
|
|
277
|
-
@cli.command()
|
|
278
|
-
@click.option("-d", "--data", required=True, help="Data to encode")
|
|
279
|
-
@click.option("-f", "--filename", required=True)
|
|
280
|
-
@click.option("-o", "--output-dir", default=None)
|
|
281
|
-
@click.option("--size", default=300, type=int)
|
|
282
|
-
def qrcode(data, filename, output_dir, size):
|
|
283
|
-
"""Generate QR code PNG image."""
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ── QR Code ──────────────────────────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
@cli.command()
|
|
278
|
+
@click.option("-d", "--data", required=True, help="Data to encode")
|
|
279
|
+
@click.option("-f", "--filename", required=True)
|
|
280
|
+
@click.option("-o", "--output-dir", default=None)
|
|
281
|
+
@click.option("--size", default=300, type=int)
|
|
282
|
+
def qrcode(data, filename, output_dir, size):
|
|
283
|
+
"""Generate QR code PNG image."""
|
|
284
284
|
from licos_dev_sdk import create_qrcode
|
|
285
285
|
path = create_qrcode(data, filename, output_dir=output_dir, size=size)
|
|
286
286
|
_echo_path(path)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
# ── Barcode ──────────────────────────────────────────────────────────────────
|
|
290
|
-
|
|
291
|
-
@cli.command()
|
|
292
|
-
@click.option("-d", "--data", required=True, help="Data to encode")
|
|
293
|
-
@click.option("-f", "--filename", required=True)
|
|
294
|
-
@click.option("-o", "--output-dir", default=None)
|
|
295
|
-
@click.option("--type", "barcode_type", default="code128", help="code128|ean13|ean8|isbn13|upc")
|
|
296
|
-
def barcode(data, filename, output_dir, barcode_type):
|
|
297
|
-
"""Generate barcode PNG image."""
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ── Barcode ──────────────────────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
@cli.command()
|
|
292
|
+
@click.option("-d", "--data", required=True, help="Data to encode")
|
|
293
|
+
@click.option("-f", "--filename", required=True)
|
|
294
|
+
@click.option("-o", "--output-dir", default=None)
|
|
295
|
+
@click.option("--type", "barcode_type", default="code128", help="code128|ean13|ean8|isbn13|upc")
|
|
296
|
+
def barcode(data, filename, output_dir, barcode_type):
|
|
297
|
+
"""Generate barcode PNG image."""
|
|
298
298
|
from licos_dev_sdk import create_barcode
|
|
299
299
|
path = create_barcode(data, filename, output_dir=output_dir, barcode_type=barcode_type)
|
|
300
300
|
_echo_path(path)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
# ── ZIP ──────────────────────────────────────────────────────────────────────
|
|
304
|
-
|
|
305
|
-
@cli.command("zip")
|
|
306
|
-
@click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
|
|
307
|
-
@click.option("-f", "--filename", required=True)
|
|
308
|
-
@click.option("-o", "--output-dir", default=None)
|
|
309
|
-
def zip_cmd(paths, filename, output_dir):
|
|
310
|
-
"""Create ZIP archive."""
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# ── ZIP ──────────────────────────────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
@cli.command("zip")
|
|
306
|
+
@click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
|
|
307
|
+
@click.option("-f", "--filename", required=True)
|
|
308
|
+
@click.option("-o", "--output-dir", default=None)
|
|
309
|
+
def zip_cmd(paths, filename, output_dir):
|
|
310
|
+
"""Create ZIP archive."""
|
|
311
311
|
from licos_dev_sdk import create_zip
|
|
312
312
|
source_paths = [p.strip() for p in paths.split(",")]
|
|
313
313
|
path = create_zip(source_paths, filename, output_dir=output_dir)
|
|
314
314
|
_echo_path(path)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
# ── TAR.GZ ───────────────────────────────────────────────────────────────────
|
|
318
|
-
|
|
319
|
-
@cli.command("tar")
|
|
320
|
-
@click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
|
|
321
|
-
@click.option("-f", "--filename", required=True)
|
|
322
|
-
@click.option("-o", "--output-dir", default=None)
|
|
323
|
-
def tar_cmd(paths, filename, output_dir):
|
|
324
|
-
"""Create TAR.GZ archive."""
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# ── TAR.GZ ───────────────────────────────────────────────────────────────────
|
|
318
|
+
|
|
319
|
+
@cli.command("tar")
|
|
320
|
+
@click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
|
|
321
|
+
@click.option("-f", "--filename", required=True)
|
|
322
|
+
@click.option("-o", "--output-dir", default=None)
|
|
323
|
+
def tar_cmd(paths, filename, output_dir):
|
|
324
|
+
"""Create TAR.GZ archive."""
|
|
325
325
|
from licos_dev_sdk import create_tar_gz
|
|
326
326
|
source_paths = [p.strip() for p in paths.split(",")]
|
|
327
327
|
path = create_tar_gz(source_paths, filename, output_dir=output_dir)
|
|
328
328
|
_echo_path(path)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
# ── JSON ─────────────────────────────────────────────────────────────────────
|
|
332
|
-
|
|
333
|
-
@cli.command("json")
|
|
334
|
-
@click.option("-i", "--input", "input_path")
|
|
335
|
-
@click.option("-c", "--content", help="JSON or YAML content")
|
|
336
|
-
@click.option("-f", "--filename", required=True)
|
|
337
|
-
@click.option("-o", "--output-dir", default=None)
|
|
338
|
-
def json_cmd(input_path, content, filename, output_dir):
|
|
339
|
-
"""Generate JSON file (accepts JSON or YAML input)."""
|
|
340
|
-
from licos_dev_sdk import create_json
|
|
341
|
-
raw = _read_input(input_path, content)
|
|
342
|
-
try:
|
|
343
|
-
data = json.loads(raw)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
# ── JSON ─────────────────────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
@cli.command("json")
|
|
334
|
+
@click.option("-i", "--input", "input_path")
|
|
335
|
+
@click.option("-c", "--content", help="JSON or YAML content")
|
|
336
|
+
@click.option("-f", "--filename", required=True)
|
|
337
|
+
@click.option("-o", "--output-dir", default=None)
|
|
338
|
+
def json_cmd(input_path, content, filename, output_dir):
|
|
339
|
+
"""Generate JSON file (accepts JSON or YAML input)."""
|
|
340
|
+
from licos_dev_sdk import create_json
|
|
341
|
+
raw = _read_input(input_path, content)
|
|
342
|
+
try:
|
|
343
|
+
data = json.loads(raw)
|
|
344
344
|
except json.JSONDecodeError:
|
|
345
345
|
import yaml
|
|
346
346
|
data = yaml.safe_load(raw)
|
|
347
347
|
path = create_json(data, filename, output_dir=output_dir)
|
|
348
348
|
_echo_path(path)
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
# ── YAML ─────────────────────────────────────────────────────────────────────
|
|
352
|
-
|
|
353
|
-
@cli.command("yaml")
|
|
354
|
-
@click.option("-i", "--input", "input_path")
|
|
355
|
-
@click.option("-c", "--content", help="JSON or YAML content")
|
|
356
|
-
@click.option("-f", "--filename", required=True)
|
|
357
|
-
@click.option("-o", "--output-dir", default=None)
|
|
358
|
-
def yaml_cmd(input_path, content, filename, output_dir):
|
|
359
|
-
"""Generate YAML file (accepts JSON or YAML input)."""
|
|
360
|
-
from licos_dev_sdk import create_yaml
|
|
361
|
-
import yaml as yaml_lib
|
|
362
|
-
raw = _read_input(input_path, content)
|
|
363
|
-
try:
|
|
364
|
-
data = json.loads(raw)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# ── YAML ─────────────────────────────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
@cli.command("yaml")
|
|
354
|
+
@click.option("-i", "--input", "input_path")
|
|
355
|
+
@click.option("-c", "--content", help="JSON or YAML content")
|
|
356
|
+
@click.option("-f", "--filename", required=True)
|
|
357
|
+
@click.option("-o", "--output-dir", default=None)
|
|
358
|
+
def yaml_cmd(input_path, content, filename, output_dir):
|
|
359
|
+
"""Generate YAML file (accepts JSON or YAML input)."""
|
|
360
|
+
from licos_dev_sdk import create_yaml
|
|
361
|
+
import yaml as yaml_lib
|
|
362
|
+
raw = _read_input(input_path, content)
|
|
363
|
+
try:
|
|
364
|
+
data = json.loads(raw)
|
|
365
365
|
except json.JSONDecodeError:
|
|
366
366
|
data = yaml_lib.safe_load(raw)
|
|
367
367
|
path = create_yaml(data, filename, output_dir=output_dir)
|
|
368
368
|
_echo_path(path)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
# ── XML ──────────────────────────────────────────────────────────────────────
|
|
372
|
-
|
|
373
|
-
@cli.command("xml")
|
|
374
|
-
@click.option("-i", "--input", "input_path")
|
|
375
|
-
@click.option("-c", "--content", help="JSON content")
|
|
376
|
-
@click.option("-f", "--filename", required=True)
|
|
377
|
-
@click.option("-o", "--output-dir", default=None)
|
|
378
|
-
@click.option("--root-tag", default="root")
|
|
379
|
-
def xml_cmd(input_path, content, filename, output_dir, root_tag):
|
|
380
|
-
"""Generate XML file from JSON data."""
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
# ── XML ──────────────────────────────────────────────────────────────────────
|
|
372
|
+
|
|
373
|
+
@cli.command("xml")
|
|
374
|
+
@click.option("-i", "--input", "input_path")
|
|
375
|
+
@click.option("-c", "--content", help="JSON content")
|
|
376
|
+
@click.option("-f", "--filename", required=True)
|
|
377
|
+
@click.option("-o", "--output-dir", default=None)
|
|
378
|
+
@click.option("--root-tag", default="root")
|
|
379
|
+
def xml_cmd(input_path, content, filename, output_dir, root_tag):
|
|
380
|
+
"""Generate XML file from JSON data."""
|
|
381
381
|
from licos_dev_sdk import create_xml
|
|
382
382
|
data = _read_json(input_path, content)
|
|
383
383
|
path = create_xml(data, filename, output_dir=output_dir, root_tag=root_tag)
|
|
384
384
|
_echo_path(path)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
# ── HTML ─────────────────────────────────────────────────────────────────────
|
|
388
|
-
|
|
389
|
-
@cli.command("html")
|
|
390
|
-
@click.option("-i", "--input", "input_path")
|
|
391
|
-
@click.option("-c", "--content")
|
|
392
|
-
@click.option("-f", "--filename", required=True)
|
|
393
|
-
@click.option("-o", "--output-dir", default=None)
|
|
394
|
-
@click.option("--format", "content_type", default="markdown", help="markdown|html")
|
|
395
|
-
def html_cmd(input_path, content, filename, output_dir, content_type):
|
|
396
|
-
"""Generate HTML file from Markdown or raw HTML."""
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
# ── HTML ─────────────────────────────────────────────────────────────────────
|
|
388
|
+
|
|
389
|
+
@cli.command("html")
|
|
390
|
+
@click.option("-i", "--input", "input_path")
|
|
391
|
+
@click.option("-c", "--content")
|
|
392
|
+
@click.option("-f", "--filename", required=True)
|
|
393
|
+
@click.option("-o", "--output-dir", default=None)
|
|
394
|
+
@click.option("--format", "content_type", default="markdown", help="markdown|html")
|
|
395
|
+
def html_cmd(input_path, content, filename, output_dir, content_type):
|
|
396
|
+
"""Generate HTML file from Markdown or raw HTML."""
|
|
397
397
|
from licos_dev_sdk import create_html
|
|
398
398
|
text = _read_input(input_path, content)
|
|
399
399
|
path = create_html(text, filename, output_dir=output_dir, content_type=content_type)
|
|
400
400
|
_echo_path(path)
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
# ── Model Catalog ────────────────────────────────────────────────────────────
|
|
404
|
-
|
|
405
|
-
@cli.command("model-catalog")
|
|
406
|
-
@click.option("--refresh", is_flag=True, help="Bypass local catalog cache")
|
|
407
|
-
@click.option("--workspace-id", default=None, help="Workspace id; defaults to runtime workspace")
|
|
408
|
-
@click.option("--input-capabilities", default=None, help="Optional input capabilities filter, for example text or image")
|
|
409
|
-
@click.option(
|
|
410
|
-
"--category-code",
|
|
411
|
-
default="llm",
|
|
412
|
-
help="Model category code, for example llm, vision_llm, image_generation",
|
|
413
|
-
)
|
|
414
|
-
def model_catalog(refresh, workspace_id, input_capabilities, category_code):
|
|
415
|
-
"""Print platform model capability catalog."""
|
|
416
|
-
from licos_dev_sdk import fetch_model_catalogs
|
|
417
|
-
|
|
418
|
-
_echo_json(
|
|
419
|
-
fetch_model_catalogs(
|
|
420
|
-
refresh=refresh,
|
|
421
|
-
workspace_id=workspace_id,
|
|
422
|
-
category_code=category_code,
|
|
423
|
-
input_capabilities=input_capabilities,
|
|
424
|
-
)
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
# ── LLM ──────────────────────────────────────────────────────────────────────
|
|
429
|
-
|
|
430
|
-
@cli.group("llm")
|
|
431
|
-
def llm_group():
|
|
432
|
-
"""Call chat/text model capabilities."""
|
|
433
|
-
pass
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
@llm_group.command("invoke")
|
|
437
|
-
@click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
|
|
438
|
-
@click.option("--messages", help="OpenAI-style messages JSON array")
|
|
439
|
-
@click.option("--model", default=None)
|
|
440
|
-
@click.option("--temperature", default=None, type=float)
|
|
441
|
-
@click.option("--max-completion-tokens", default=None, type=int)
|
|
442
|
-
@click.option("--extra", default=None, help="Extra request fields as JSON object")
|
|
443
|
-
def llm_invoke(prompt, messages, model, temperature, max_completion_tokens, extra):
|
|
444
|
-
"""Run a non-streaming chat completion."""
|
|
445
|
-
from licos_dev_sdk import LLMClient
|
|
446
|
-
|
|
447
|
-
if messages:
|
|
448
|
-
message_payload = _json_option(messages, "--messages")
|
|
449
|
-
else:
|
|
450
|
-
message_payload = prompt if prompt is not None else _read_input(None, None)
|
|
451
|
-
extra_payload = _json_option(extra, "--extra") or {}
|
|
452
|
-
if not isinstance(extra_payload, dict):
|
|
453
|
-
raise click.BadParameter("--extra must be a JSON object")
|
|
454
|
-
result = LLMClient().invoke(
|
|
455
|
-
message_payload,
|
|
456
|
-
model=model,
|
|
457
|
-
temperature=temperature,
|
|
458
|
-
max_completion_tokens=max_completion_tokens,
|
|
459
|
-
**extra_payload,
|
|
460
|
-
)
|
|
461
|
-
_echo_json(result)
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
@llm_group.command("stream")
|
|
465
|
-
@click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
|
|
466
|
-
@click.option("--messages", help="OpenAI-style messages JSON array")
|
|
467
|
-
@click.option("--model", default=None)
|
|
468
|
-
@click.option("--temperature", default=None, type=float)
|
|
469
|
-
@click.option("--max-completion-tokens", default=None, type=int)
|
|
470
|
-
@click.option("--extra", default=None, help="Extra request fields as JSON object")
|
|
471
|
-
def llm_stream(prompt, messages, model, temperature, max_completion_tokens, extra):
|
|
472
|
-
"""Run a streaming chat completion and print text chunks."""
|
|
473
|
-
from licos_dev_sdk import LLMClient
|
|
474
|
-
|
|
475
|
-
if messages:
|
|
476
|
-
message_payload = _json_option(messages, "--messages")
|
|
477
|
-
else:
|
|
478
|
-
message_payload = prompt if prompt is not None else _read_input(None, None)
|
|
479
|
-
extra_payload = _json_option(extra, "--extra") or {}
|
|
480
|
-
if not isinstance(extra_payload, dict):
|
|
481
|
-
raise click.BadParameter("--extra must be a JSON object")
|
|
482
|
-
for chunk in LLMClient().stream(
|
|
483
|
-
message_payload,
|
|
484
|
-
model=model,
|
|
485
|
-
temperature=temperature,
|
|
486
|
-
max_completion_tokens=max_completion_tokens,
|
|
487
|
-
**extra_payload,
|
|
488
|
-
):
|
|
489
|
-
click.echo(chunk, nl=False)
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
# ── Vision ───────────────────────────────────────────────────────────────────
|
|
493
|
-
|
|
494
|
-
@cli.group("vision")
|
|
495
|
-
def vision_group():
|
|
496
|
-
"""Call chat vision model capabilities."""
|
|
497
|
-
pass
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
@vision_group.command("understand")
|
|
501
|
-
@click.option("--image", "image", multiple=True, help="Image URL, Base64 data URI, or plain Base64 image data. Can be repeated.")
|
|
502
|
-
@click.option("--image-url", "image_url", multiple=True, help="Alias for --image. Can be repeated.")
|
|
503
|
-
@click.option("--file-path", "file_path", multiple=True, help="Local image file path. Can be repeated.")
|
|
504
|
-
@click.option("-p", "--prompt", default="Describe this image.")
|
|
505
|
-
@click.option("--model", default=None)
|
|
506
|
-
@click.option("--mime-type", default=None, help="MIME type for plain Base64 image inputs. Defaults to image/png.")
|
|
507
|
-
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
508
|
-
def vision_understand(image, image_url, file_path, prompt, model, mime_type, raw_request):
|
|
509
|
-
"""Understand one or more images with the catalog vision model."""
|
|
510
|
-
from licos_dev_sdk import VisionClient
|
|
511
|
-
|
|
512
|
-
raw_payload = _json_option(raw_request, "--raw-request")
|
|
513
|
-
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
514
|
-
raise click.BadParameter("--raw-request must be a JSON object")
|
|
515
|
-
if raw_payload is None and not image_url and not image and not file_path:
|
|
516
|
-
raise click.UsageError("Provide --image, --image-url, --file-path, or --raw-request.")
|
|
517
|
-
result = VisionClient().understand(
|
|
518
|
-
images=[*image, *image_url],
|
|
519
|
-
file_paths=list(file_path),
|
|
520
|
-
prompt=prompt,
|
|
521
|
-
model=model,
|
|
522
|
-
mime_type=mime_type,
|
|
523
|
-
raw_request=raw_payload,
|
|
524
|
-
)
|
|
525
|
-
_echo_json(result)
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
# ── Image Generation ─────────────────────────────────────────────────────────
|
|
529
|
-
|
|
530
|
-
@cli.group("image-generation")
|
|
531
|
-
def image_generation_group():
|
|
532
|
-
"""Call image generation model capabilities."""
|
|
533
|
-
pass
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
@image_generation_group.command("generate")
|
|
537
|
-
@click.option("-p", "--prompt")
|
|
538
|
-
@click.option("--image", "image", multiple=True, help="Source/reference image URL or Base64 data URI")
|
|
539
|
-
@click.option("--image-url", "image_url", multiple=True, help="Alias for --image")
|
|
540
|
-
@click.option("--reference-image", default=None, help="Alias for --image")
|
|
541
|
-
@click.option("--file-path", "file_path", multiple=True, help="Local source/reference image path")
|
|
542
|
-
@click.option("--input-capabilities", default=None, help="Override image model input filter: text or image")
|
|
543
|
-
@click.option("--negative-prompt", default=None)
|
|
544
|
-
@click.option("--count", default=1, type=int)
|
|
545
|
-
@click.option("--size", default=None)
|
|
546
|
-
@click.option("--model", default=None)
|
|
547
|
-
@click.option("--parameters", default=None, help="Parameters JSON object")
|
|
548
|
-
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
549
|
-
@click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
|
|
550
|
-
def image_generate(
|
|
551
|
-
prompt,
|
|
552
|
-
image,
|
|
553
|
-
image_url,
|
|
554
|
-
reference_image,
|
|
555
|
-
file_path,
|
|
556
|
-
input_capabilities,
|
|
557
|
-
negative_prompt,
|
|
558
|
-
count,
|
|
559
|
-
size,
|
|
560
|
-
model,
|
|
561
|
-
parameters,
|
|
562
|
-
raw_request,
|
|
563
|
-
no_wait,
|
|
564
|
-
):
|
|
565
|
-
"""Generate images from text, image URLs/Base64, or local image files."""
|
|
566
|
-
from licos_dev_sdk import ImageGenerationClient
|
|
567
|
-
|
|
568
|
-
parameter_payload = _json_option(parameters, "--parameters") or {}
|
|
569
|
-
raw_payload = _json_option(raw_request, "--raw-request")
|
|
570
|
-
if not isinstance(parameter_payload, dict):
|
|
571
|
-
raise click.BadParameter("--parameters must be a JSON object")
|
|
572
|
-
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
573
|
-
raise click.BadParameter("--raw-request must be a JSON object")
|
|
574
|
-
if raw_payload is None and not prompt:
|
|
575
|
-
raise click.UsageError("Provide --prompt or --raw-request.")
|
|
576
|
-
result = ImageGenerationClient().generate(
|
|
577
|
-
prompt or "",
|
|
578
|
-
negative_prompt=negative_prompt,
|
|
579
|
-
count=count,
|
|
580
|
-
size=size,
|
|
581
|
-
images=[*image, *image_url],
|
|
582
|
-
reference_image=reference_image,
|
|
583
|
-
file_paths=list(file_path),
|
|
584
|
-
input_capabilities=input_capabilities,
|
|
585
|
-
model=model,
|
|
586
|
-
parameters=parameter_payload,
|
|
587
|
-
raw_request=raw_payload,
|
|
588
|
-
wait=not no_wait,
|
|
589
|
-
)
|
|
590
|
-
_echo_json(result)
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
# ── Video Generation ─────────────────────────────────────────────────────────
|
|
594
|
-
|
|
595
|
-
@cli.group("video-generation")
|
|
596
|
-
def video_generation_group():
|
|
597
|
-
"""Call video generation model capabilities."""
|
|
598
|
-
pass
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
@video_generation_group.command("generate")
|
|
602
|
-
@click.option("-p", "--prompt")
|
|
603
|
-
@click.option("--image", default=None, help="Source/reference image URL, Base64 data URI, or plain Base64 image data")
|
|
604
|
-
@click.option("--image-url", default=None, help="Alias for --image")
|
|
605
|
-
@click.option("--reference-image", default=None, help="Alias for --image")
|
|
606
|
-
@click.option("--first-frame-url", default=None, help="Alias for --image")
|
|
607
|
-
@click.option("--file-path", default=None, help="Local source/reference image path")
|
|
608
|
-
@click.option("--model", default=None)
|
|
609
|
-
@click.option("--input-capabilities", default=None, help="Override video model input filter: text or image")
|
|
610
|
-
@click.option("--mime-type", default=None, help="MIME type for plain Base64 image inputs. Defaults to image/png.")
|
|
611
|
-
@click.option("--parameters", default=None, help="Parameters JSON object")
|
|
612
|
-
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
613
|
-
@click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
|
|
614
|
-
def video_generate(
|
|
615
|
-
prompt,
|
|
616
|
-
image,
|
|
617
|
-
image_url,
|
|
618
|
-
reference_image,
|
|
619
|
-
first_frame_url,
|
|
620
|
-
file_path,
|
|
621
|
-
model,
|
|
622
|
-
input_capabilities,
|
|
623
|
-
mime_type,
|
|
624
|
-
parameters,
|
|
625
|
-
raw_request,
|
|
626
|
-
no_wait,
|
|
627
|
-
):
|
|
628
|
-
"""Generate a video from a text prompt and optional first-frame image."""
|
|
629
|
-
from licos_dev_sdk import VideoGenerationClient
|
|
630
|
-
|
|
631
|
-
parameter_payload = _json_option(parameters, "--parameters") or {}
|
|
632
|
-
raw_payload = _json_option(raw_request, "--raw-request")
|
|
633
|
-
if not isinstance(parameter_payload, dict):
|
|
634
|
-
raise click.BadParameter("--parameters must be a JSON object")
|
|
635
|
-
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
636
|
-
raise click.BadParameter("--raw-request must be a JSON object")
|
|
637
|
-
if raw_payload is None and not prompt:
|
|
638
|
-
raise click.UsageError("Provide --prompt or --raw-request.")
|
|
639
|
-
result = VideoGenerationClient().generate(
|
|
640
|
-
prompt or "",
|
|
641
|
-
image=image,
|
|
642
|
-
image_url=image_url,
|
|
643
|
-
reference_image=reference_image,
|
|
644
|
-
first_frame_url=first_frame_url,
|
|
645
|
-
file_path=file_path,
|
|
646
|
-
model=model,
|
|
647
|
-
input_capabilities=input_capabilities,
|
|
648
|
-
mime_type=mime_type,
|
|
649
|
-
parameters=parameter_payload,
|
|
650
|
-
raw_request=raw_payload,
|
|
651
|
-
wait=not no_wait,
|
|
652
|
-
)
|
|
653
|
-
_echo_json(result)
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
# ── Audio / ASR ──────────────────────────────────────────────────────────────
|
|
657
|
-
|
|
658
|
-
@cli.group("audio")
|
|
659
|
-
def audio_group():
|
|
660
|
-
"""Call audio model capabilities."""
|
|
661
|
-
pass
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
@audio_group.command("recognize")
|
|
665
|
-
@click.option("--audio", "audio", multiple=True, help="Audio URL, Base64 data URI, or plain Base64 audio data. Can be repeated.")
|
|
666
|
-
@click.option("--audio-url", "audio_url", multiple=True, help="Alias for --audio. Can be repeated.")
|
|
667
|
-
@click.option("--file-path", "file_path", multiple=True, help="Local audio file path. Can be repeated.")
|
|
668
|
-
@click.option("--model", default=None)
|
|
669
|
-
@click.option("--mime-type", default=None, help="MIME type for plain Base64 audio inputs. Defaults to audio/wav.")
|
|
670
|
-
@click.option("--parameters", default=None, help="Parameters JSON object")
|
|
671
|
-
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
672
|
-
@click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
|
|
673
|
-
def audio_recognize(audio, audio_url, file_path, model, mime_type, parameters, raw_request, no_wait):
|
|
674
|
-
"""Recognize speech from one or more audio inputs."""
|
|
675
|
-
from licos_dev_sdk import SpeechRecognitionClient
|
|
676
|
-
|
|
677
|
-
parameter_payload = _json_option(parameters, "--parameters") or {}
|
|
678
|
-
raw_payload = _json_option(raw_request, "--raw-request")
|
|
679
|
-
if not isinstance(parameter_payload, dict):
|
|
680
|
-
raise click.BadParameter("--parameters must be a JSON object")
|
|
681
|
-
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
682
|
-
raise click.BadParameter("--raw-request must be a JSON object")
|
|
683
|
-
if raw_payload is None and not audio and not audio_url and not file_path:
|
|
684
|
-
raise click.UsageError("Provide --audio, --audio-url, --file-path, or --raw-request.")
|
|
685
|
-
result = SpeechRecognitionClient().recognize(
|
|
686
|
-
audios=[*audio, *audio_url],
|
|
687
|
-
file_paths=list(file_path),
|
|
688
|
-
model=model,
|
|
689
|
-
mime_type=mime_type,
|
|
690
|
-
parameters=parameter_payload,
|
|
691
|
-
raw_request=raw_payload,
|
|
692
|
-
wait=not no_wait,
|
|
693
|
-
)
|
|
694
|
-
_echo_json(result)
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
if __name__ == "__main__":
|
|
698
|
-
cli()
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ── Model Catalog ────────────────────────────────────────────────────────────
|
|
404
|
+
|
|
405
|
+
@cli.command("model-catalog")
|
|
406
|
+
@click.option("--refresh", is_flag=True, help="Bypass local catalog cache")
|
|
407
|
+
@click.option("--workspace-id", default=None, help="Workspace id; defaults to runtime workspace")
|
|
408
|
+
@click.option("--input-capabilities", default=None, help="Optional input capabilities filter, for example text or image")
|
|
409
|
+
@click.option(
|
|
410
|
+
"--category-code",
|
|
411
|
+
default="llm",
|
|
412
|
+
help="Model category code, for example llm, vision_llm, image_generation",
|
|
413
|
+
)
|
|
414
|
+
def model_catalog(refresh, workspace_id, input_capabilities, category_code):
|
|
415
|
+
"""Print platform model capability catalog."""
|
|
416
|
+
from licos_dev_sdk import fetch_model_catalogs
|
|
417
|
+
|
|
418
|
+
_echo_json(
|
|
419
|
+
fetch_model_catalogs(
|
|
420
|
+
refresh=refresh,
|
|
421
|
+
workspace_id=workspace_id,
|
|
422
|
+
category_code=category_code,
|
|
423
|
+
input_capabilities=input_capabilities,
|
|
424
|
+
)
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
# ── LLM ──────────────────────────────────────────────────────────────────────
|
|
429
|
+
|
|
430
|
+
@cli.group("llm")
|
|
431
|
+
def llm_group():
|
|
432
|
+
"""Call chat/text model capabilities."""
|
|
433
|
+
pass
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
@llm_group.command("invoke")
|
|
437
|
+
@click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
|
|
438
|
+
@click.option("--messages", help="OpenAI-style messages JSON array")
|
|
439
|
+
@click.option("--model", default=None)
|
|
440
|
+
@click.option("--temperature", default=None, type=float)
|
|
441
|
+
@click.option("--max-completion-tokens", default=None, type=int)
|
|
442
|
+
@click.option("--extra", default=None, help="Extra request fields as JSON object")
|
|
443
|
+
def llm_invoke(prompt, messages, model, temperature, max_completion_tokens, extra):
|
|
444
|
+
"""Run a non-streaming chat completion."""
|
|
445
|
+
from licos_dev_sdk import LLMClient
|
|
446
|
+
|
|
447
|
+
if messages:
|
|
448
|
+
message_payload = _json_option(messages, "--messages")
|
|
449
|
+
else:
|
|
450
|
+
message_payload = prompt if prompt is not None else _read_input(None, None)
|
|
451
|
+
extra_payload = _json_option(extra, "--extra") or {}
|
|
452
|
+
if not isinstance(extra_payload, dict):
|
|
453
|
+
raise click.BadParameter("--extra must be a JSON object")
|
|
454
|
+
result = LLMClient().invoke(
|
|
455
|
+
message_payload,
|
|
456
|
+
model=model,
|
|
457
|
+
temperature=temperature,
|
|
458
|
+
max_completion_tokens=max_completion_tokens,
|
|
459
|
+
**extra_payload,
|
|
460
|
+
)
|
|
461
|
+
_echo_json(result)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
@llm_group.command("stream")
|
|
465
|
+
@click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
|
|
466
|
+
@click.option("--messages", help="OpenAI-style messages JSON array")
|
|
467
|
+
@click.option("--model", default=None)
|
|
468
|
+
@click.option("--temperature", default=None, type=float)
|
|
469
|
+
@click.option("--max-completion-tokens", default=None, type=int)
|
|
470
|
+
@click.option("--extra", default=None, help="Extra request fields as JSON object")
|
|
471
|
+
def llm_stream(prompt, messages, model, temperature, max_completion_tokens, extra):
|
|
472
|
+
"""Run a streaming chat completion and print text chunks."""
|
|
473
|
+
from licos_dev_sdk import LLMClient
|
|
474
|
+
|
|
475
|
+
if messages:
|
|
476
|
+
message_payload = _json_option(messages, "--messages")
|
|
477
|
+
else:
|
|
478
|
+
message_payload = prompt if prompt is not None else _read_input(None, None)
|
|
479
|
+
extra_payload = _json_option(extra, "--extra") or {}
|
|
480
|
+
if not isinstance(extra_payload, dict):
|
|
481
|
+
raise click.BadParameter("--extra must be a JSON object")
|
|
482
|
+
for chunk in LLMClient().stream(
|
|
483
|
+
message_payload,
|
|
484
|
+
model=model,
|
|
485
|
+
temperature=temperature,
|
|
486
|
+
max_completion_tokens=max_completion_tokens,
|
|
487
|
+
**extra_payload,
|
|
488
|
+
):
|
|
489
|
+
click.echo(chunk, nl=False)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# ── Vision ───────────────────────────────────────────────────────────────────
|
|
493
|
+
|
|
494
|
+
@cli.group("vision")
|
|
495
|
+
def vision_group():
|
|
496
|
+
"""Call chat vision model capabilities."""
|
|
497
|
+
pass
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
@vision_group.command("understand")
|
|
501
|
+
@click.option("--image", "image", multiple=True, help="Image URL, Base64 data URI, or plain Base64 image data. Can be repeated.")
|
|
502
|
+
@click.option("--image-url", "image_url", multiple=True, help="Alias for --image. Can be repeated.")
|
|
503
|
+
@click.option("--file-path", "file_path", multiple=True, help="Local image file path. Can be repeated.")
|
|
504
|
+
@click.option("-p", "--prompt", default="Describe this image.")
|
|
505
|
+
@click.option("--model", default=None)
|
|
506
|
+
@click.option("--mime-type", default=None, help="MIME type for plain Base64 image inputs. Defaults to image/png.")
|
|
507
|
+
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
508
|
+
def vision_understand(image, image_url, file_path, prompt, model, mime_type, raw_request):
|
|
509
|
+
"""Understand one or more images with the catalog vision model."""
|
|
510
|
+
from licos_dev_sdk import VisionClient
|
|
511
|
+
|
|
512
|
+
raw_payload = _json_option(raw_request, "--raw-request")
|
|
513
|
+
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
514
|
+
raise click.BadParameter("--raw-request must be a JSON object")
|
|
515
|
+
if raw_payload is None and not image_url and not image and not file_path:
|
|
516
|
+
raise click.UsageError("Provide --image, --image-url, --file-path, or --raw-request.")
|
|
517
|
+
result = VisionClient().understand(
|
|
518
|
+
images=[*image, *image_url],
|
|
519
|
+
file_paths=list(file_path),
|
|
520
|
+
prompt=prompt,
|
|
521
|
+
model=model,
|
|
522
|
+
mime_type=mime_type,
|
|
523
|
+
raw_request=raw_payload,
|
|
524
|
+
)
|
|
525
|
+
_echo_json(result)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
# ── Image Generation ─────────────────────────────────────────────────────────
|
|
529
|
+
|
|
530
|
+
@cli.group("image-generation")
|
|
531
|
+
def image_generation_group():
|
|
532
|
+
"""Call image generation model capabilities."""
|
|
533
|
+
pass
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
@image_generation_group.command("generate")
|
|
537
|
+
@click.option("-p", "--prompt")
|
|
538
|
+
@click.option("--image", "image", multiple=True, help="Source/reference image URL or Base64 data URI")
|
|
539
|
+
@click.option("--image-url", "image_url", multiple=True, help="Alias for --image")
|
|
540
|
+
@click.option("--reference-image", default=None, help="Alias for --image")
|
|
541
|
+
@click.option("--file-path", "file_path", multiple=True, help="Local source/reference image path")
|
|
542
|
+
@click.option("--input-capabilities", default=None, help="Override image model input filter: text or image")
|
|
543
|
+
@click.option("--negative-prompt", default=None)
|
|
544
|
+
@click.option("--count", default=1, type=int)
|
|
545
|
+
@click.option("--size", default=None)
|
|
546
|
+
@click.option("--model", default=None)
|
|
547
|
+
@click.option("--parameters", default=None, help="Parameters JSON object")
|
|
548
|
+
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
549
|
+
@click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
|
|
550
|
+
def image_generate(
|
|
551
|
+
prompt,
|
|
552
|
+
image,
|
|
553
|
+
image_url,
|
|
554
|
+
reference_image,
|
|
555
|
+
file_path,
|
|
556
|
+
input_capabilities,
|
|
557
|
+
negative_prompt,
|
|
558
|
+
count,
|
|
559
|
+
size,
|
|
560
|
+
model,
|
|
561
|
+
parameters,
|
|
562
|
+
raw_request,
|
|
563
|
+
no_wait,
|
|
564
|
+
):
|
|
565
|
+
"""Generate images from text, image URLs/Base64, or local image files."""
|
|
566
|
+
from licos_dev_sdk import ImageGenerationClient
|
|
567
|
+
|
|
568
|
+
parameter_payload = _json_option(parameters, "--parameters") or {}
|
|
569
|
+
raw_payload = _json_option(raw_request, "--raw-request")
|
|
570
|
+
if not isinstance(parameter_payload, dict):
|
|
571
|
+
raise click.BadParameter("--parameters must be a JSON object")
|
|
572
|
+
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
573
|
+
raise click.BadParameter("--raw-request must be a JSON object")
|
|
574
|
+
if raw_payload is None and not prompt:
|
|
575
|
+
raise click.UsageError("Provide --prompt or --raw-request.")
|
|
576
|
+
result = ImageGenerationClient().generate(
|
|
577
|
+
prompt or "",
|
|
578
|
+
negative_prompt=negative_prompt,
|
|
579
|
+
count=count,
|
|
580
|
+
size=size,
|
|
581
|
+
images=[*image, *image_url],
|
|
582
|
+
reference_image=reference_image,
|
|
583
|
+
file_paths=list(file_path),
|
|
584
|
+
input_capabilities=input_capabilities,
|
|
585
|
+
model=model,
|
|
586
|
+
parameters=parameter_payload,
|
|
587
|
+
raw_request=raw_payload,
|
|
588
|
+
wait=not no_wait,
|
|
589
|
+
)
|
|
590
|
+
_echo_json(result)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
# ── Video Generation ─────────────────────────────────────────────────────────
|
|
594
|
+
|
|
595
|
+
@cli.group("video-generation")
|
|
596
|
+
def video_generation_group():
|
|
597
|
+
"""Call video generation model capabilities."""
|
|
598
|
+
pass
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
@video_generation_group.command("generate")
|
|
602
|
+
@click.option("-p", "--prompt")
|
|
603
|
+
@click.option("--image", default=None, help="Source/reference image URL, Base64 data URI, or plain Base64 image data")
|
|
604
|
+
@click.option("--image-url", default=None, help="Alias for --image")
|
|
605
|
+
@click.option("--reference-image", default=None, help="Alias for --image")
|
|
606
|
+
@click.option("--first-frame-url", default=None, help="Alias for --image")
|
|
607
|
+
@click.option("--file-path", default=None, help="Local source/reference image path")
|
|
608
|
+
@click.option("--model", default=None)
|
|
609
|
+
@click.option("--input-capabilities", default=None, help="Override video model input filter: text or image")
|
|
610
|
+
@click.option("--mime-type", default=None, help="MIME type for plain Base64 image inputs. Defaults to image/png.")
|
|
611
|
+
@click.option("--parameters", default=None, help="Parameters JSON object")
|
|
612
|
+
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
613
|
+
@click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
|
|
614
|
+
def video_generate(
|
|
615
|
+
prompt,
|
|
616
|
+
image,
|
|
617
|
+
image_url,
|
|
618
|
+
reference_image,
|
|
619
|
+
first_frame_url,
|
|
620
|
+
file_path,
|
|
621
|
+
model,
|
|
622
|
+
input_capabilities,
|
|
623
|
+
mime_type,
|
|
624
|
+
parameters,
|
|
625
|
+
raw_request,
|
|
626
|
+
no_wait,
|
|
627
|
+
):
|
|
628
|
+
"""Generate a video from a text prompt and optional first-frame image."""
|
|
629
|
+
from licos_dev_sdk import VideoGenerationClient
|
|
630
|
+
|
|
631
|
+
parameter_payload = _json_option(parameters, "--parameters") or {}
|
|
632
|
+
raw_payload = _json_option(raw_request, "--raw-request")
|
|
633
|
+
if not isinstance(parameter_payload, dict):
|
|
634
|
+
raise click.BadParameter("--parameters must be a JSON object")
|
|
635
|
+
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
636
|
+
raise click.BadParameter("--raw-request must be a JSON object")
|
|
637
|
+
if raw_payload is None and not prompt:
|
|
638
|
+
raise click.UsageError("Provide --prompt or --raw-request.")
|
|
639
|
+
result = VideoGenerationClient().generate(
|
|
640
|
+
prompt or "",
|
|
641
|
+
image=image,
|
|
642
|
+
image_url=image_url,
|
|
643
|
+
reference_image=reference_image,
|
|
644
|
+
first_frame_url=first_frame_url,
|
|
645
|
+
file_path=file_path,
|
|
646
|
+
model=model,
|
|
647
|
+
input_capabilities=input_capabilities,
|
|
648
|
+
mime_type=mime_type,
|
|
649
|
+
parameters=parameter_payload,
|
|
650
|
+
raw_request=raw_payload,
|
|
651
|
+
wait=not no_wait,
|
|
652
|
+
)
|
|
653
|
+
_echo_json(result)
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
# ── Audio / ASR ──────────────────────────────────────────────────────────────
|
|
657
|
+
|
|
658
|
+
@cli.group("audio")
|
|
659
|
+
def audio_group():
|
|
660
|
+
"""Call audio model capabilities."""
|
|
661
|
+
pass
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
@audio_group.command("recognize")
|
|
665
|
+
@click.option("--audio", "audio", multiple=True, help="Audio URL, Base64 data URI, or plain Base64 audio data. Can be repeated.")
|
|
666
|
+
@click.option("--audio-url", "audio_url", multiple=True, help="Alias for --audio. Can be repeated.")
|
|
667
|
+
@click.option("--file-path", "file_path", multiple=True, help="Local audio file path. Can be repeated.")
|
|
668
|
+
@click.option("--model", default=None)
|
|
669
|
+
@click.option("--mime-type", default=None, help="MIME type for plain Base64 audio inputs. Defaults to audio/wav.")
|
|
670
|
+
@click.option("--parameters", default=None, help="Parameters JSON object")
|
|
671
|
+
@click.option("--raw-request", default=None, help="Raw JSON request body")
|
|
672
|
+
@click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
|
|
673
|
+
def audio_recognize(audio, audio_url, file_path, model, mime_type, parameters, raw_request, no_wait):
|
|
674
|
+
"""Recognize speech from one or more audio inputs."""
|
|
675
|
+
from licos_dev_sdk import SpeechRecognitionClient
|
|
676
|
+
|
|
677
|
+
parameter_payload = _json_option(parameters, "--parameters") or {}
|
|
678
|
+
raw_payload = _json_option(raw_request, "--raw-request")
|
|
679
|
+
if not isinstance(parameter_payload, dict):
|
|
680
|
+
raise click.BadParameter("--parameters must be a JSON object")
|
|
681
|
+
if raw_payload is not None and not isinstance(raw_payload, dict):
|
|
682
|
+
raise click.BadParameter("--raw-request must be a JSON object")
|
|
683
|
+
if raw_payload is None and not audio and not audio_url and not file_path:
|
|
684
|
+
raise click.UsageError("Provide --audio, --audio-url, --file-path, or --raw-request.")
|
|
685
|
+
result = SpeechRecognitionClient().recognize(
|
|
686
|
+
audios=[*audio, *audio_url],
|
|
687
|
+
file_paths=list(file_path),
|
|
688
|
+
model=model,
|
|
689
|
+
mime_type=mime_type,
|
|
690
|
+
parameters=parameter_payload,
|
|
691
|
+
raw_request=raw_payload,
|
|
692
|
+
wait=not no_wait,
|
|
693
|
+
)
|
|
694
|
+
_echo_json(result)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
if __name__ == "__main__":
|
|
698
|
+
cli()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|