jvcli 2.0.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.
- jvcli/__init__.py +8 -0
- jvcli/api.py +318 -0
- jvcli/auth.py +45 -0
- jvcli/cli.py +36 -0
- jvcli/client/assets/index-BtFItD2q.js +156 -0
- jvcli/client/assets/index-CIEsu-TC.css +1 -0
- jvcli/client/index.html +15 -0
- jvcli/client/jac_logo.png +0 -0
- jvcli/client/tauri.svg +6 -0
- jvcli/client/vite.svg +1 -0
- jvcli/commands/__init__.py +1 -0
- jvcli/commands/auth.py +46 -0
- jvcli/commands/create.py +452 -0
- jvcli/commands/download.py +111 -0
- jvcli/commands/info.py +91 -0
- jvcli/commands/publish.py +207 -0
- jvcli/commands/studio.py +96 -0
- jvcli/commands/update.py +67 -0
- jvcli/templates/2.0.0/action_info.yaml +14 -0
- jvcli/templates/2.0.0/agent_descriptor.yaml +30 -0
- jvcli/templates/2.0.0/agent_info.yaml +10 -0
- jvcli/templates/2.0.0/agent_knowledge.yaml +14 -0
- jvcli/templates/2.0.0/agent_memory.yaml +1 -0
- jvcli/templates/CHANGELOG.md +2 -0
- jvcli/templates/README.md +4 -0
- jvcli/utils.py +209 -0
- jvcli-2.0.0.dist-info/LICENSE +201 -0
- jvcli-2.0.0.dist-info/METADATA +24 -0
- jvcli-2.0.0.dist-info/RECORD +32 -0
- jvcli-2.0.0.dist-info/WHEEL +5 -0
- jvcli-2.0.0.dist-info/entry_points.txt +2 -0
- jvcli-2.0.0.dist-info/top_level.txt +1 -0
jvcli/commands/create.py
ADDED
@@ -0,0 +1,452 @@
|
|
1
|
+
"""Create commands for the CLI tool."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
|
5
|
+
import click
|
6
|
+
|
7
|
+
from jvcli import __supported__jivas__versions__, __version__
|
8
|
+
from jvcli.api import RegistryAPI
|
9
|
+
from jvcli.auth import load_token, save_token
|
10
|
+
from jvcli.utils import TEMPLATES_DIR, validate_name, validate_snake_case
|
11
|
+
|
12
|
+
TYPE_SUFFIX_MAP = {
|
13
|
+
"action": "_action",
|
14
|
+
"interact_action": "_interact_action",
|
15
|
+
"vector_store_action": "_vector_store_action",
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
@click.group()
|
20
|
+
def create() -> None:
|
21
|
+
"""Group for creating resources like actions"""
|
22
|
+
pass # pragma: no cover
|
23
|
+
|
24
|
+
|
25
|
+
@create.command(name="action")
|
26
|
+
@click.option(
|
27
|
+
"--name",
|
28
|
+
prompt=True,
|
29
|
+
callback=validate_snake_case,
|
30
|
+
help="Name of the action (must be snake_case).",
|
31
|
+
)
|
32
|
+
@click.option(
|
33
|
+
"--version", default="0.0.1", show_default=True, help="Version of the action."
|
34
|
+
)
|
35
|
+
@click.option(
|
36
|
+
"--jivas_version",
|
37
|
+
default=max(__supported__jivas__versions__),
|
38
|
+
show_default=True,
|
39
|
+
help="Version of Jivas.",
|
40
|
+
)
|
41
|
+
@click.option(
|
42
|
+
"--description",
|
43
|
+
default="No description provided.",
|
44
|
+
help="Description of the action.",
|
45
|
+
)
|
46
|
+
@click.option(
|
47
|
+
"--type",
|
48
|
+
"--type",
|
49
|
+
default="action",
|
50
|
+
type=click.Choice(TYPE_SUFFIX_MAP.keys(), case_sensitive=False),
|
51
|
+
show_default=True,
|
52
|
+
help="Type of the action.",
|
53
|
+
)
|
54
|
+
@click.option(
|
55
|
+
"--singleton",
|
56
|
+
default=True,
|
57
|
+
type=bool,
|
58
|
+
show_default=True,
|
59
|
+
help="Indicate if the action is singleton.",
|
60
|
+
)
|
61
|
+
@click.option(
|
62
|
+
"--path",
|
63
|
+
default="./actions",
|
64
|
+
show_default=True,
|
65
|
+
help="Directory to create the action folder in.",
|
66
|
+
)
|
67
|
+
@click.option(
|
68
|
+
"--namespace",
|
69
|
+
default=None,
|
70
|
+
help="Namespace for the action. Defaults to the username in the token.",
|
71
|
+
)
|
72
|
+
@click.option(
|
73
|
+
"--singleton",
|
74
|
+
default=True,
|
75
|
+
type=bool,
|
76
|
+
show_default=True,
|
77
|
+
help="Indicate if the action is singleton.",
|
78
|
+
)
|
79
|
+
@click.option(
|
80
|
+
"--path",
|
81
|
+
default="./actions",
|
82
|
+
show_default=True,
|
83
|
+
help="Directory to create the action folder in.",
|
84
|
+
)
|
85
|
+
def create_action(
|
86
|
+
name: str,
|
87
|
+
version: str,
|
88
|
+
jivas_version: str,
|
89
|
+
description: str,
|
90
|
+
singleton: bool,
|
91
|
+
type: str,
|
92
|
+
path: str,
|
93
|
+
namespace: str,
|
94
|
+
) -> None:
|
95
|
+
"""Create a new action with its folder, associated files, and an app folder."""
|
96
|
+
|
97
|
+
# Retrieve the email from the token or set it as blank
|
98
|
+
token = load_token()
|
99
|
+
namespace = namespace or token.get("namespaces", {}).get("default", "anonymous")
|
100
|
+
author = token.get("email", "unknown@example.com")
|
101
|
+
|
102
|
+
suffix = TYPE_SUFFIX_MAP[type]
|
103
|
+
if not name.endswith(suffix):
|
104
|
+
name += suffix
|
105
|
+
|
106
|
+
# Format the full action name with namespace
|
107
|
+
full_name = f"{namespace}/{name}"
|
108
|
+
|
109
|
+
# Generate class name (CamelCase)
|
110
|
+
architype = "".join(word.capitalize() for word in name.split("_"))
|
111
|
+
|
112
|
+
# Validate the Jivas version
|
113
|
+
if str(jivas_version) not in __supported__jivas__versions__:
|
114
|
+
click.secho(
|
115
|
+
f"Jivas version {jivas_version} is not supported. Supported versions are: {__supported__jivas__versions__}.",
|
116
|
+
fg="red",
|
117
|
+
)
|
118
|
+
return
|
119
|
+
|
120
|
+
# Prepare the template path
|
121
|
+
template_path = os.path.join(TEMPLATES_DIR, jivas_version, "action_info.yaml")
|
122
|
+
if not os.path.exists(template_path):
|
123
|
+
click.secho(
|
124
|
+
f"Template for version {jivas_version} not found in {TEMPLATES_DIR}.",
|
125
|
+
fg="red",
|
126
|
+
)
|
127
|
+
return
|
128
|
+
|
129
|
+
# Prepare target folder
|
130
|
+
namespace_dir = os.path.join(path, namespace)
|
131
|
+
action_dir = os.path.join(namespace_dir, name)
|
132
|
+
os.makedirs(action_dir, exist_ok=True)
|
133
|
+
|
134
|
+
# Load and substitute YAML template
|
135
|
+
with open(template_path, "r") as file:
|
136
|
+
template = file.read()
|
137
|
+
|
138
|
+
title = name.replace("_", " ").title()
|
139
|
+
|
140
|
+
data = {
|
141
|
+
"name": full_name, # Include namespace in the package name
|
142
|
+
"author": author,
|
143
|
+
"architype": architype,
|
144
|
+
"version": version,
|
145
|
+
"title": title,
|
146
|
+
"description": description,
|
147
|
+
"group": "contrib",
|
148
|
+
"type": type,
|
149
|
+
"singleton": str(singleton).lower(),
|
150
|
+
"jivas_version": jivas_version,
|
151
|
+
}
|
152
|
+
|
153
|
+
yaml_content = template
|
154
|
+
for key, value in data.items():
|
155
|
+
yaml_content = yaml_content.replace(f"{{{{{key}}}}}", str(value))
|
156
|
+
|
157
|
+
# Write info.yaml
|
158
|
+
yaml_path = os.path.join(action_dir, "info.yaml")
|
159
|
+
with open(yaml_path, "w") as file:
|
160
|
+
file.write(yaml_content)
|
161
|
+
|
162
|
+
# Create lib.jac
|
163
|
+
lib_path = os.path.join(action_dir, "lib.jac")
|
164
|
+
with open(lib_path, "w") as file:
|
165
|
+
file.write(f"include:jac {name};\n")
|
166
|
+
|
167
|
+
# Create action-specific .jac file
|
168
|
+
action_jac_path = os.path.join(action_dir, f"{name}.jac")
|
169
|
+
with open(action_jac_path, "w") as file:
|
170
|
+
node_class = {
|
171
|
+
"action": "Action",
|
172
|
+
"interact_action": "InteractAction",
|
173
|
+
"vector_store_action": "VectorStoreAction",
|
174
|
+
}[type]
|
175
|
+
|
176
|
+
import_statement = f"import:jac from agent.action.{type} {{ {node_class} }}"
|
177
|
+
|
178
|
+
abilities = """
|
179
|
+
#* (Abilities - Uncomment and implement as needed)
|
180
|
+
can on_register {
|
181
|
+
# override to execute operations upon registration of action
|
182
|
+
}
|
183
|
+
|
184
|
+
can post_register {
|
185
|
+
# override to execute any setup code when all actions are in place
|
186
|
+
}
|
187
|
+
|
188
|
+
can on_enable {
|
189
|
+
# override to execute operations upon enabling of action
|
190
|
+
}
|
191
|
+
|
192
|
+
can on_disable {
|
193
|
+
# override to execute operations upon disabling of action
|
194
|
+
}
|
195
|
+
|
196
|
+
can on_deregister {
|
197
|
+
# override to execute operations upon deregistration of action
|
198
|
+
}
|
199
|
+
|
200
|
+
can touch(visitor: interact_graph_walker) -> bool {
|
201
|
+
# override to authorize, redirect or deny the interact walker from running execute
|
202
|
+
}
|
203
|
+
|
204
|
+
can execute(visitor: interact_graph_walker) -> dict {
|
205
|
+
# override to implement action execution
|
206
|
+
}
|
207
|
+
|
208
|
+
can pulse() {
|
209
|
+
# override to implement pulse operation
|
210
|
+
}
|
211
|
+
*#
|
212
|
+
"""
|
213
|
+
node_content = f"""
|
214
|
+
# Define your custom action code here
|
215
|
+
{import_statement}
|
216
|
+
|
217
|
+
node {architype} :{node_class}: {{
|
218
|
+
# Declare your has variables to be persisted here
|
219
|
+
# e.g has var_a : str = "string";
|
220
|
+
|
221
|
+
{abilities}
|
222
|
+
}}
|
223
|
+
"""
|
224
|
+
file.write(node_content.strip())
|
225
|
+
|
226
|
+
# Create the 'app' folder and default 'app.py'
|
227
|
+
app_dir = os.path.join(action_dir, "app")
|
228
|
+
os.makedirs(app_dir, exist_ok=True)
|
229
|
+
app_file_path = os.path.join(app_dir, "app.py")
|
230
|
+
with open(app_file_path, "w") as app_file:
|
231
|
+
app_code = """
|
232
|
+
\"\"\" This module renders the streamlit app for the {title}. \"\"\"
|
233
|
+
|
234
|
+
from jvclient.client.lib.widgets import app_controls, app_header, app_update_action
|
235
|
+
|
236
|
+
from streamlit_router import StreamlitRouter
|
237
|
+
|
238
|
+
def render(router: StreamlitRouter, agent_id: str, action_id: str, info: dict) -> None:
|
239
|
+
\"\"\"Render the Streamlit app for the {title}.
|
240
|
+
:param router: The StreamlitRouter instance
|
241
|
+
:param agent_id: The agent ID
|
242
|
+
:param action_id: The action ID
|
243
|
+
:param info: The action info dict
|
244
|
+
\"\"\"
|
245
|
+
|
246
|
+
# Add app header controls
|
247
|
+
(model_key, module_root) = app_header(agent_id, action_id, info)
|
248
|
+
|
249
|
+
# Add app main controls
|
250
|
+
app_controls(agent_id, action_id)
|
251
|
+
|
252
|
+
# Add update button to apply changes
|
253
|
+
app_update_action(agent_id, action_id)
|
254
|
+
"""
|
255
|
+
app_code = app_code.replace("{title}", title)
|
256
|
+
app_file.write(app_code)
|
257
|
+
|
258
|
+
create_docs(action_dir, title, version, "action", description)
|
259
|
+
|
260
|
+
click.secho(
|
261
|
+
f"Action '{name}' created successfully in {action_dir}!", fg="green", bold=True
|
262
|
+
)
|
263
|
+
|
264
|
+
|
265
|
+
@create.command(name="namespace")
|
266
|
+
@click.option(
|
267
|
+
"--name", prompt=True, callback=validate_name, help="Name of the namespace."
|
268
|
+
)
|
269
|
+
def create_namespace(name: str) -> None:
|
270
|
+
"""
|
271
|
+
Create a new namespace through the API and update the local token file.
|
272
|
+
|
273
|
+
name: The name of the new namespace to create.
|
274
|
+
"""
|
275
|
+
# Load the token data
|
276
|
+
token_data = load_token()
|
277
|
+
if not token_data:
|
278
|
+
click.secho(
|
279
|
+
"You are not logged in. Please log in before creating a namespace.",
|
280
|
+
fg="red",
|
281
|
+
)
|
282
|
+
return
|
283
|
+
|
284
|
+
# Extract the token
|
285
|
+
token = token_data.get("token")
|
286
|
+
if not token:
|
287
|
+
click.secho(
|
288
|
+
"Token missing from the local configuration. Please log in again.", fg="red"
|
289
|
+
)
|
290
|
+
return
|
291
|
+
|
292
|
+
# Call the API to create the namespace
|
293
|
+
updated_namespaces = RegistryAPI.create_namespace(name, token)
|
294
|
+
|
295
|
+
if updated_namespaces:
|
296
|
+
|
297
|
+
# Fetch namespace object
|
298
|
+
namespaces = token_data.get(
|
299
|
+
"namespaces", {"default": "anonymous", "groups": []}
|
300
|
+
) # TODO: Remove default when API is updated
|
301
|
+
|
302
|
+
if name not in namespaces["groups"]:
|
303
|
+
namespaces["groups"].append(name)
|
304
|
+
|
305
|
+
click.secho(f"Namespace '{name}' created successfully!", fg="green", bold=True)
|
306
|
+
|
307
|
+
# Update the local token file with the new namespaces
|
308
|
+
save_token(token, namespaces, str(token_data.get("email")))
|
309
|
+
|
310
|
+
|
311
|
+
@create.command(name="agent")
|
312
|
+
@click.option(
|
313
|
+
"--name",
|
314
|
+
prompt=True,
|
315
|
+
callback=validate_snake_case,
|
316
|
+
help="Name of the agent (must be snake_case).",
|
317
|
+
)
|
318
|
+
@click.option(
|
319
|
+
"--version", default="0.0.1", show_default=True, help="Version of the agent."
|
320
|
+
)
|
321
|
+
@click.option(
|
322
|
+
"--jivas_version",
|
323
|
+
default=max(__supported__jivas__versions__),
|
324
|
+
show_default=True,
|
325
|
+
help="Version of Jivas.",
|
326
|
+
)
|
327
|
+
@click.option(
|
328
|
+
"--description",
|
329
|
+
default="A jivas agent autocreated by the jvcli.",
|
330
|
+
help="Description of the agent.",
|
331
|
+
)
|
332
|
+
@click.option(
|
333
|
+
"--path",
|
334
|
+
default="./dafs",
|
335
|
+
show_default=True,
|
336
|
+
help="Directory to create the agent.",
|
337
|
+
)
|
338
|
+
@click.option(
|
339
|
+
"--namespace",
|
340
|
+
default=None,
|
341
|
+
help="Namespace for the agent. Defaults to the username in the token.",
|
342
|
+
)
|
343
|
+
def create_agent(
|
344
|
+
name: str,
|
345
|
+
version: str,
|
346
|
+
jivas_version: str,
|
347
|
+
description: str,
|
348
|
+
path: str,
|
349
|
+
namespace: str,
|
350
|
+
) -> None:
|
351
|
+
"""Create a new agent with its folder and associated files."""
|
352
|
+
|
353
|
+
# Retrieve token info
|
354
|
+
token = load_token()
|
355
|
+
namespace = namespace or token.get("namespaces", {}).get("default", "anonymous")
|
356
|
+
author = token.get("email", "unknown@example.com")
|
357
|
+
|
358
|
+
# Validate Jivas version
|
359
|
+
if str(jivas_version) not in __supported__jivas__versions__:
|
360
|
+
click.secho(
|
361
|
+
f"Jivas version {jivas_version} is not supported. Supported versions are: {__supported__jivas__versions__}.",
|
362
|
+
fg="red",
|
363
|
+
)
|
364
|
+
return
|
365
|
+
|
366
|
+
# Prepare paths
|
367
|
+
namespace_dir = os.path.join(path, namespace)
|
368
|
+
daf_dir = os.path.join(namespace_dir, name)
|
369
|
+
os.makedirs(daf_dir, exist_ok=True)
|
370
|
+
|
371
|
+
# Load templates
|
372
|
+
template_paths = {
|
373
|
+
"info.yaml": os.path.join(TEMPLATES_DIR, __version__, "agent_info.yaml"),
|
374
|
+
"descriptor.yaml": os.path.join(
|
375
|
+
TEMPLATES_DIR, __version__, "agent_descriptor.yaml"
|
376
|
+
),
|
377
|
+
"knowledge.yaml": os.path.join(
|
378
|
+
TEMPLATES_DIR, __version__, "agent_knowledge.yaml"
|
379
|
+
),
|
380
|
+
"memory.yaml": os.path.join(TEMPLATES_DIR, __version__, "agent_memory.yaml"),
|
381
|
+
}
|
382
|
+
|
383
|
+
# Check if all templates exist
|
384
|
+
for filename, template_path in template_paths.items():
|
385
|
+
if not os.path.exists(template_path):
|
386
|
+
click.secho(f"Template {filename} not found in TEMPLATES_DIR.", fg="red")
|
387
|
+
return
|
388
|
+
|
389
|
+
# Read templates
|
390
|
+
templates = {}
|
391
|
+
for key, path in template_paths.items():
|
392
|
+
with open(path, "r") as file:
|
393
|
+
templates[key] = file.read()
|
394
|
+
|
395
|
+
# Replace placeholders
|
396
|
+
data = {
|
397
|
+
"name": f"{namespace}/{name}",
|
398
|
+
"author": author,
|
399
|
+
"version": version,
|
400
|
+
"title": name.replace("_", " ").title(),
|
401
|
+
"description": description,
|
402
|
+
"type": "agent",
|
403
|
+
"jivas_version": jivas_version,
|
404
|
+
}
|
405
|
+
|
406
|
+
for filename, template_content in templates.items():
|
407
|
+
for key, value in data.items():
|
408
|
+
template_content = template_content.replace(f"{{{{{key}}}}}", str(value))
|
409
|
+
|
410
|
+
with open(os.path.join(daf_dir, filename), "w") as file:
|
411
|
+
file.write(template_content)
|
412
|
+
|
413
|
+
# Create documentation
|
414
|
+
create_docs(daf_dir, name, version, "agent", description)
|
415
|
+
|
416
|
+
# Success message
|
417
|
+
click.secho(
|
418
|
+
f"Agent '{name}' created successfully in {daf_dir}!", fg="green", bold=True
|
419
|
+
)
|
420
|
+
|
421
|
+
|
422
|
+
def create_docs(
|
423
|
+
path: str, name: str, version: str, package_type: str, description: str = ""
|
424
|
+
) -> None:
|
425
|
+
"""Update README.md and CHANGELOG.md templates with name and version."""
|
426
|
+
|
427
|
+
# Create README
|
428
|
+
readme_template = os.path.join(TEMPLATES_DIR, "README.md")
|
429
|
+
if os.path.exists(readme_template):
|
430
|
+
with open(readme_template, "r") as file:
|
431
|
+
readme_content = file.read()
|
432
|
+
|
433
|
+
readme_content = readme_content.replace("{{version}}", version)
|
434
|
+
readme_content = readme_content.replace("{{name}}", name)
|
435
|
+
readme_content = readme_content.replace("{{description}}", description)
|
436
|
+
|
437
|
+
target_readme = os.path.join(path, "README.md")
|
438
|
+
with open(target_readme, "w") as file:
|
439
|
+
file.write(readme_content)
|
440
|
+
|
441
|
+
# Create CHANGELOG
|
442
|
+
changelog_template = os.path.join(TEMPLATES_DIR, "CHANGELOG.md")
|
443
|
+
if os.path.exists(changelog_template):
|
444
|
+
with open(changelog_template, "r") as file:
|
445
|
+
changelog_content = file.read()
|
446
|
+
|
447
|
+
changelog_content = changelog_content.replace("{{version}}", version)
|
448
|
+
changelog_content = changelog_content.replace("{{package_type}}", package_type)
|
449
|
+
|
450
|
+
target_changelog = os.path.join(path, "CHANGELOG.md")
|
451
|
+
with open(target_changelog, "w") as file:
|
452
|
+
file.write(changelog_content)
|
@@ -0,0 +1,111 @@
|
|
1
|
+
"""Download commands for the Jivas Package Repository CLI."""
|
2
|
+
|
3
|
+
import io
|
4
|
+
import os
|
5
|
+
import tarfile
|
6
|
+
|
7
|
+
import click
|
8
|
+
import requests
|
9
|
+
from pyaml import yaml
|
10
|
+
|
11
|
+
from jvcli.api import RegistryAPI
|
12
|
+
from jvcli.auth import load_token
|
13
|
+
|
14
|
+
|
15
|
+
@click.group()
|
16
|
+
def download() -> None:
|
17
|
+
"""Group for downloading resources like actions."""
|
18
|
+
pass # pragma: no cover
|
19
|
+
|
20
|
+
|
21
|
+
@download.command(name="action")
|
22
|
+
@click.argument("name")
|
23
|
+
@click.argument("version", required=False)
|
24
|
+
@click.option(
|
25
|
+
"--path",
|
26
|
+
required=False,
|
27
|
+
help="Directory to download the action.",
|
28
|
+
)
|
29
|
+
def download_action(name: str, version: str, path: str) -> None:
|
30
|
+
"""Download a JIVAS action package."""
|
31
|
+
_download_package(name, version, path, "action")
|
32
|
+
|
33
|
+
|
34
|
+
@download.command(name="agent")
|
35
|
+
@click.argument("name")
|
36
|
+
@click.argument("version", required=False)
|
37
|
+
@click.option(
|
38
|
+
"--path",
|
39
|
+
required=False,
|
40
|
+
help="Directory to download the agent.",
|
41
|
+
)
|
42
|
+
def download_agent(name: str, version: str, path: str) -> None:
|
43
|
+
"""Download a JIVAS agent package."""
|
44
|
+
_download_package(name, version, path, "agent")
|
45
|
+
|
46
|
+
|
47
|
+
def _download_package(name: str, version: str, path: str, pkg_type: str) -> None:
|
48
|
+
token = load_token().get("token")
|
49
|
+
|
50
|
+
if not version:
|
51
|
+
version = "latest"
|
52
|
+
|
53
|
+
click.echo(f"Downloading {name} version {version}...")
|
54
|
+
|
55
|
+
try:
|
56
|
+
package_data = RegistryAPI.download_package(name, version, token=token)
|
57
|
+
if not package_data:
|
58
|
+
click.secho("Failed to download the package.", fg="red")
|
59
|
+
return
|
60
|
+
|
61
|
+
package_file = requests.get(package_data["file"])
|
62
|
+
info_file = None
|
63
|
+
target_dir = None
|
64
|
+
|
65
|
+
with tarfile.open(
|
66
|
+
fileobj=io.BytesIO(package_file.content), mode="r:gz"
|
67
|
+
) as tar_file:
|
68
|
+
for member in tar_file.getmembers():
|
69
|
+
|
70
|
+
if "__MACOSX" in member.name:
|
71
|
+
continue
|
72
|
+
|
73
|
+
if member.name in [
|
74
|
+
"info.yaml",
|
75
|
+
"info.yml",
|
76
|
+
"./info.yaml",
|
77
|
+
"./info.yml",
|
78
|
+
]:
|
79
|
+
info_file = tar_file.extractfile(member)
|
80
|
+
break
|
81
|
+
|
82
|
+
if info_file:
|
83
|
+
info_content = yaml.safe_load(info_file)
|
84
|
+
package_type = (
|
85
|
+
info_content.get("package", {}).get("meta", {}).get("type")
|
86
|
+
)
|
87
|
+
|
88
|
+
# checking for both daf and agent to maintain backward compatibility
|
89
|
+
if pkg_type == "agent" and package_type in ["agent", "daf"]:
|
90
|
+
base_dir = "dafs"
|
91
|
+
elif pkg_type == "action" and package_type.endswith("action"):
|
92
|
+
base_dir = "actions"
|
93
|
+
else:
|
94
|
+
click.secho(
|
95
|
+
f"Invalid package type for {pkg_type} download", fg="red"
|
96
|
+
)
|
97
|
+
return
|
98
|
+
|
99
|
+
target_dir = os.path.join(path if path else f"./{base_dir}", name)
|
100
|
+
os.makedirs(target_dir, exist_ok=True)
|
101
|
+
tar_file.extractall(target_dir)
|
102
|
+
else:
|
103
|
+
click.echo("No info.yaml file found in the package.")
|
104
|
+
|
105
|
+
if target_dir:
|
106
|
+
click.secho(
|
107
|
+
f"Package '{name}' (version: {version}) downloaded to {target_dir}!",
|
108
|
+
fg="green",
|
109
|
+
)
|
110
|
+
except Exception as e:
|
111
|
+
click.secho(f"Error downloading the package: {e}", fg="red")
|
jvcli/commands/info.py
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
"""Info command group for getting info about resources on the Jivas Package Repository."""
|
2
|
+
|
3
|
+
import sys
|
4
|
+
|
5
|
+
import click
|
6
|
+
from pyaml import yaml
|
7
|
+
|
8
|
+
from jvcli.api import RegistryAPI
|
9
|
+
from jvcli.auth import load_token
|
10
|
+
|
11
|
+
|
12
|
+
@click.group()
|
13
|
+
def info() -> None:
|
14
|
+
"""Group for getting info about resources like actions."""
|
15
|
+
pass # pragma: no cover
|
16
|
+
|
17
|
+
|
18
|
+
@info.command(name="action")
|
19
|
+
@click.argument("name")
|
20
|
+
@click.argument("version", required=False)
|
21
|
+
def get_action_info(name: str, version: str) -> None:
|
22
|
+
"""
|
23
|
+
Get info for an action package by name and version.
|
24
|
+
If version is not provided, the latest version will be fetched.
|
25
|
+
"""
|
26
|
+
|
27
|
+
token = load_token().get("token")
|
28
|
+
|
29
|
+
# If version is not provided, fetch latest version
|
30
|
+
if not version:
|
31
|
+
click.echo("Checking the latest version of the action...")
|
32
|
+
version = "latest"
|
33
|
+
|
34
|
+
# Use the API function to fetch the action
|
35
|
+
try:
|
36
|
+
package_info = RegistryAPI.get_package_info(name, version, token=token)
|
37
|
+
|
38
|
+
if not package_info:
|
39
|
+
click.secho("Failed to locate the action package.", fg="red")
|
40
|
+
return
|
41
|
+
|
42
|
+
click.secho("======= PACKAGE INFO ========", fg="green")
|
43
|
+
yaml.safe_dump(
|
44
|
+
package_info,
|
45
|
+
sys.stdout,
|
46
|
+
width=100,
|
47
|
+
allow_unicode=True,
|
48
|
+
default_flow_style=False,
|
49
|
+
)
|
50
|
+
click.secho("=============================", fg="green")
|
51
|
+
|
52
|
+
except Exception as e:
|
53
|
+
click.secho(f"Error retrieving the action info: {e}", fg="red")
|
54
|
+
|
55
|
+
|
56
|
+
@info.command(name="agent")
|
57
|
+
@click.argument("name")
|
58
|
+
@click.argument("version", required=False)
|
59
|
+
def get_agent_info(name: str, version: str) -> None:
|
60
|
+
"""
|
61
|
+
Get info for an agent package by name and version.
|
62
|
+
If version is not provided, the latest version will be fetched.
|
63
|
+
"""
|
64
|
+
|
65
|
+
token = load_token().get("token")
|
66
|
+
|
67
|
+
# If version is not provided, fetch latest version
|
68
|
+
if not version:
|
69
|
+
click.echo("Checking the latest version of the agent package...")
|
70
|
+
version = "latest"
|
71
|
+
|
72
|
+
# Use the API function to fetch the agent
|
73
|
+
try:
|
74
|
+
package_info = RegistryAPI.get_package_info(name, version, token=token)
|
75
|
+
|
76
|
+
if not package_info:
|
77
|
+
click.secho("Failed to locate the agent package.", fg="red")
|
78
|
+
return
|
79
|
+
|
80
|
+
click.secho("======= PACKAGE INFO ========", fg="green")
|
81
|
+
yaml.safe_dump(
|
82
|
+
package_info,
|
83
|
+
sys.stdout,
|
84
|
+
width=100,
|
85
|
+
allow_unicode=True,
|
86
|
+
default_flow_style=False,
|
87
|
+
)
|
88
|
+
click.secho("=============================", fg="green")
|
89
|
+
|
90
|
+
except Exception as e:
|
91
|
+
click.secho(f"Error retrieving the agent package info: {e}", fg="red")
|