argocli 0.1.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.
- argocli/__init__.py +67 -0
- argocli/cli/main.py +232 -0
- argocli/commands/command.py +61 -0
- argocli/commands/workflow/__init__.py +56 -0
- argocli/commands/workflow/status.py +28 -0
- argocli/core/client.py +52 -0
- argocli-0.1.0.dist-info/METADATA +11 -0
- argocli-0.1.0.dist-info/RECORD +12 -0
- argocli-0.1.0.dist-info/WHEEL +5 -0
- argocli-0.1.0.dist-info/entry_points.txt +2 -0
- argocli-0.1.0.dist-info/licenses/LICENSE +21 -0
- argocli-0.1.0.dist-info/top_level.txt +1 -0
argocli/__init__.py
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# pylint: disable=broad-except, line-too-long
|
2
|
+
|
3
|
+
"""
|
4
|
+
module docstring
|
5
|
+
"""
|
6
|
+
|
7
|
+
# import os
|
8
|
+
import sys
|
9
|
+
from importlib import metadata
|
10
|
+
import cac_core as cac
|
11
|
+
|
12
|
+
# import yaml
|
13
|
+
# import keyring
|
14
|
+
import argocli.core.client as client
|
15
|
+
|
16
|
+
if sys.version_info < (3, 9):
|
17
|
+
print("This project requires Python 3.9 or higher.", file=sys.stderr)
|
18
|
+
sys.exit(1)
|
19
|
+
|
20
|
+
cac.updatechecker.check_package_for_updates(__name__)
|
21
|
+
|
22
|
+
try:
|
23
|
+
__version__ = metadata.version(__package__)
|
24
|
+
except Exception:
|
25
|
+
__version__ = "#N/A"
|
26
|
+
|
27
|
+
log = cac.logger.new(__name__)
|
28
|
+
log.debug("Initializing %s version %s", __name__, __version__)
|
29
|
+
|
30
|
+
CONFIG = cac.config.Config(__name__)
|
31
|
+
|
32
|
+
log.debug("user config path: %s", CONFIG.config_file)
|
33
|
+
|
34
|
+
# TODO: prompt user for server and username if not set
|
35
|
+
argo_server = CONFIG.get("server", "INVALID_DEFAULT") #.replace("https://", "")
|
36
|
+
if argo_server == "INVALID_DEFAULT":
|
37
|
+
log.error("Invalid server in %s: %s", CONFIG.config_file, argo_server)
|
38
|
+
sys.exit(1)
|
39
|
+
|
40
|
+
argo_namespace = CONFIG.get("namespace", "INVALID_DEFAULT")
|
41
|
+
if argo_namespace == "INVALID_DEFAULT":
|
42
|
+
log.error("Invalid namespace in %s: %s", CONFIG.config_file, argo_namespace)
|
43
|
+
sys.exit(1)
|
44
|
+
|
45
|
+
argo_username = CONFIG.get("username", "INVALID_DEFAULT")
|
46
|
+
if argo_username == "INVALID_DEFAULT":
|
47
|
+
log.error("Invalid username in %s: %s", CONFIG.config_file, argo_username)
|
48
|
+
sys.exit(1)
|
49
|
+
|
50
|
+
credentialmanager = cac.credentialmanager.CredentialManager(__name__)
|
51
|
+
argo_api_token = credentialmanager.get_credential(
|
52
|
+
argo_username,
|
53
|
+
"Argo API key",
|
54
|
+
)
|
55
|
+
|
56
|
+
if not argo_api_token:
|
57
|
+
# TODO: update the docs
|
58
|
+
log.error(
|
59
|
+
"API token not found for %s; see https://github.com/rpunt/%s/blob/main/README.md#authentication",
|
60
|
+
argo_username,
|
61
|
+
__name__.replace("_", "-"),
|
62
|
+
)
|
63
|
+
sys.exit(1)
|
64
|
+
|
65
|
+
ARGO_CLIENT = client.ArgoClient(argo_server, argo_namespace, argo_api_token)
|
66
|
+
|
67
|
+
__all__ = ["ARGO_CLIENT", "CONFIG", "log"]
|
argocli/cli/main.py
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# pylint: disable=line-too-long
|
3
|
+
|
4
|
+
import argparse
|
5
|
+
import importlib
|
6
|
+
import sys
|
7
|
+
import logging
|
8
|
+
import os
|
9
|
+
|
10
|
+
# import pkgutil
|
11
|
+
import cac_core as cac
|
12
|
+
|
13
|
+
|
14
|
+
def discover_commands():
|
15
|
+
"""
|
16
|
+
Discover available commands by scanning the commands directory.
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
list: A list of command names.
|
20
|
+
"""
|
21
|
+
commands = []
|
22
|
+
commands_dir = os.path.abspath(
|
23
|
+
os.path.join(os.path.dirname(__file__), "..", "commands")
|
24
|
+
)
|
25
|
+
|
26
|
+
# Check if the commands directory exists
|
27
|
+
if not os.path.exists(commands_dir) or not os.path.isdir(commands_dir):
|
28
|
+
return commands
|
29
|
+
|
30
|
+
# Get all subdirectories (which are packages) in the commands directory
|
31
|
+
for item in os.listdir(commands_dir):
|
32
|
+
item_path = os.path.join(commands_dir, item)
|
33
|
+
# Only consider directories that have an __init__.py file (Python packages)
|
34
|
+
if (
|
35
|
+
os.path.isdir(item_path)
|
36
|
+
and os.path.exists(os.path.join(item_path, "__init__.py"))
|
37
|
+
and item != "__pycache__"
|
38
|
+
):
|
39
|
+
commands.append(item)
|
40
|
+
|
41
|
+
return sorted(commands)
|
42
|
+
|
43
|
+
|
44
|
+
def discover_actions(command):
|
45
|
+
"""
|
46
|
+
Discover available actions for a given command by scanning its directory.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
command (str): The command name.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
list: A list of action names.
|
53
|
+
"""
|
54
|
+
actions = []
|
55
|
+
command_dir = os.path.abspath(
|
56
|
+
os.path.join(os.path.dirname(__file__), "..", "commands", command)
|
57
|
+
)
|
58
|
+
|
59
|
+
# Check if the command directory exists
|
60
|
+
if not os.path.exists(command_dir) or not os.path.isdir(command_dir):
|
61
|
+
return actions
|
62
|
+
|
63
|
+
# Get all Python modules in the command directory
|
64
|
+
for item in os.listdir(command_dir):
|
65
|
+
# Skip __init__.py, __pycache__, and non-Python files
|
66
|
+
if item == "__init__.py" or item == "__pycache__" or not item.endswith(".py"):
|
67
|
+
continue
|
68
|
+
|
69
|
+
# Extract action name (filename without .py extension)
|
70
|
+
action = item[:-3]
|
71
|
+
actions.append(action)
|
72
|
+
|
73
|
+
return sorted(actions)
|
74
|
+
|
75
|
+
|
76
|
+
def show_command_help(command):
|
77
|
+
"""
|
78
|
+
Show help for a specific command, listing all available actions.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
command (str): The command to show help for
|
82
|
+
"""
|
83
|
+
actions = discover_actions(command)
|
84
|
+
print(f"\nAvailable actions for '{command}':")
|
85
|
+
for action in sorted(actions):
|
86
|
+
try:
|
87
|
+
module_path = f"{__name__}.commands.{command}.{action}"
|
88
|
+
module = importlib.import_module(module_path)
|
89
|
+
doc = module.__doc__ or "No description available"
|
90
|
+
doc = doc.strip().split("\n")[0] # Get first line of docstring
|
91
|
+
print(f" {action.ljust(15)} - {doc}")
|
92
|
+
except Exception: # pylint: disable=broad-except
|
93
|
+
print(f" {action.ljust(15)} - No description available")
|
94
|
+
|
95
|
+
|
96
|
+
# def register_autocomplete(parser):
|
97
|
+
# """Set up command autocompletion if supported environment"""
|
98
|
+
# try:
|
99
|
+
# import argcomplete
|
100
|
+
# argcomplete.autocomplete(parser)
|
101
|
+
# except ImportError:
|
102
|
+
# # argcomplete is not installed, skip autocomplete setup
|
103
|
+
# pass
|
104
|
+
|
105
|
+
|
106
|
+
def setup_logging(verbose: bool) -> logging.Logger:
|
107
|
+
"""Configure logging based on command line arguments"""
|
108
|
+
log = cac.logger.new(__name__)
|
109
|
+
if verbose:
|
110
|
+
log.setLevel(logging.DEBUG)
|
111
|
+
# if getattr(args, 'show_log_format', False):
|
112
|
+
# print(f"Log format: {cac.logger.get_formatter_string()}")
|
113
|
+
# sys.exit(0)
|
114
|
+
return log
|
115
|
+
|
116
|
+
|
117
|
+
def main():
|
118
|
+
"""
|
119
|
+
Entry point for the Argo CLI tool.
|
120
|
+
|
121
|
+
This function sets up the argument parser with nested commands, and dynamically
|
122
|
+
loads and executes the appropriate module and action based on user input.
|
123
|
+
"""
|
124
|
+
log = cac.logger.new(__name__)
|
125
|
+
|
126
|
+
# Create parent parser for global arguments
|
127
|
+
parent_parser = argparse.ArgumentParser(add_help=False)
|
128
|
+
|
129
|
+
# Main parser that inherits from parent
|
130
|
+
parser = argparse.ArgumentParser(
|
131
|
+
prog="argocli", description="Argo CLI tool", parents=[parent_parser]
|
132
|
+
)
|
133
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
134
|
+
|
135
|
+
# Discover available commands by scanning the commands directory
|
136
|
+
commands = discover_commands()
|
137
|
+
log.debug("Discovered commands: %s", commands)
|
138
|
+
|
139
|
+
# Dictionary to map command names to their subparsers
|
140
|
+
command_subparsers = {}
|
141
|
+
|
142
|
+
# Set up command structure based on discovered commands
|
143
|
+
for command in commands:
|
144
|
+
command_parser = subparsers.add_parser(
|
145
|
+
command,
|
146
|
+
help=f"{command.capitalize()}-related commands",
|
147
|
+
parents=[parent_parser],
|
148
|
+
)
|
149
|
+
command_subparsers[command] = command_parser.add_subparsers(
|
150
|
+
dest="action", required=True
|
151
|
+
)
|
152
|
+
|
153
|
+
# Add all available action parsers up front by scanning directories
|
154
|
+
for command, subparser in command_subparsers.items():
|
155
|
+
actions = discover_actions(command)
|
156
|
+
log.debug("Discovered actions for %s: %s", command, actions)
|
157
|
+
|
158
|
+
for action in actions:
|
159
|
+
try:
|
160
|
+
# Load the module and class for this action
|
161
|
+
module_path = f"argocli.commands.{command}.{action}"
|
162
|
+
module = importlib.import_module(module_path)
|
163
|
+
|
164
|
+
class_name = f"{command.capitalize()}{action.capitalize()}"
|
165
|
+
action_class = getattr(module, class_name, None)
|
166
|
+
|
167
|
+
if action_class is None:
|
168
|
+
log.warning(
|
169
|
+
"Class '%s' not found in module '%s'", class_name, module_path
|
170
|
+
)
|
171
|
+
continue
|
172
|
+
|
173
|
+
# Instantiate the action class
|
174
|
+
action_instance = action_class()
|
175
|
+
|
176
|
+
# Create parser for this action and let the action define its arguments
|
177
|
+
action_parser = subparser.add_parser(
|
178
|
+
action, help=f"{action} {command}", parents=[parent_parser]
|
179
|
+
)
|
180
|
+
action_instance.define_arguments(action_parser)
|
181
|
+
|
182
|
+
# Store the class for later execution
|
183
|
+
action_parser.set_defaults(action_class=action_class)
|
184
|
+
|
185
|
+
except ModuleNotFoundError:
|
186
|
+
log.warning("Command module '%s' not found", module_path)
|
187
|
+
except Exception as e: # pylint: disable=broad-except
|
188
|
+
log.warning("Error setting up %s %s: %s", command, action, e)
|
189
|
+
|
190
|
+
# # Add autocomplete setup
|
191
|
+
# register_autocomplete(parser)
|
192
|
+
|
193
|
+
# Parse arguments
|
194
|
+
args = parser.parse_args()
|
195
|
+
|
196
|
+
log = setup_logging(args.verbose)
|
197
|
+
# log = setup_logging(False)
|
198
|
+
log.debug("Parsed arguments: %s", args)
|
199
|
+
|
200
|
+
# Add to main function, after argument parsing but before execution
|
201
|
+
if args.command is None:
|
202
|
+
parser.print_help()
|
203
|
+
print("\nAvailable commands:")
|
204
|
+
for cmd in sorted(commands):
|
205
|
+
print(f" {cmd}")
|
206
|
+
sys.exit(1)
|
207
|
+
|
208
|
+
# Execute the appropriate action
|
209
|
+
try:
|
210
|
+
# Get the action class from the parser defaults
|
211
|
+
action_class = getattr(args, "action_class", None)
|
212
|
+
|
213
|
+
if action_class is None:
|
214
|
+
log.error("No handler found for %s %s", args.command, args.action)
|
215
|
+
sys.exit(1)
|
216
|
+
|
217
|
+
# Instantiate and execute
|
218
|
+
if callable(action_class):
|
219
|
+
action_instance = action_class()
|
220
|
+
else:
|
221
|
+
log.error("Invalid action class for %s %s", args.command, args.action)
|
222
|
+
sys.exit(1)
|
223
|
+
|
224
|
+
log.debug("Executing action: %s %s", args.command, args.action)
|
225
|
+
action_instance.execute(args)
|
226
|
+
|
227
|
+
except Exception as e: # pylint: disable=broad-except
|
228
|
+
log.error("Error executing command: %s", e)
|
229
|
+
|
230
|
+
|
231
|
+
if __name__ == "__main__":
|
232
|
+
main()
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
"""
|
4
|
+
Base class for all Argo CLI commands.
|
5
|
+
|
6
|
+
This module provides a base class that all command actions should inherit from,
|
7
|
+
allowing for common functionality and arguments to be shared across different
|
8
|
+
command actions.
|
9
|
+
"""
|
10
|
+
|
11
|
+
import abc
|
12
|
+
from cac_core.command import Command
|
13
|
+
from argocli import CONFIG, ARGO_CLIENT, log
|
14
|
+
|
15
|
+
|
16
|
+
class ArgoCommand(Command):
|
17
|
+
"""
|
18
|
+
Base class for all Argo CLI commands.
|
19
|
+
|
20
|
+
This class defines common methods and properties that should be shared
|
21
|
+
across all command actions, such as common arguments, authentication,
|
22
|
+
and utility functions.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self):
|
26
|
+
"""
|
27
|
+
Initialize the command with a logger and Argo client.
|
28
|
+
"""
|
29
|
+
super().__init__()
|
30
|
+
self.log = log
|
31
|
+
self.argo_client = ARGO_CLIENT
|
32
|
+
self.config = CONFIG
|
33
|
+
|
34
|
+
@abc.abstractmethod
|
35
|
+
def define_arguments(self, parser):
|
36
|
+
"""
|
37
|
+
Define command-specific arguments.
|
38
|
+
|
39
|
+
This method must be implemented by subclasses to add
|
40
|
+
command-specific arguments to the parser.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
parser: The argument parser to add arguments to
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
The updated argument parser
|
47
|
+
"""
|
48
|
+
super().define_arguments(parser)
|
49
|
+
return parser
|
50
|
+
|
51
|
+
@abc.abstractmethod
|
52
|
+
def execute(self, args):
|
53
|
+
"""
|
54
|
+
Execute the command with the provided arguments.
|
55
|
+
|
56
|
+
This method must be implemented by subclasses.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
args: The parsed arguments
|
60
|
+
"""
|
61
|
+
raise NotImplementedError("Command subclasses must implement execute()")
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
"""
|
4
|
+
Base module for all issue-related commands.
|
5
|
+
|
6
|
+
This module defines the base ArgoCommand class that all issue-related
|
7
|
+
action classes should inherit from.
|
8
|
+
"""
|
9
|
+
|
10
|
+
import abc
|
11
|
+
from argocli.commands.command import ArgoCommand
|
12
|
+
|
13
|
+
|
14
|
+
class ArgoWorkflowCommand(ArgoCommand):
|
15
|
+
"""
|
16
|
+
Base class for all workflow-related actions.
|
17
|
+
|
18
|
+
This class defines common methods and properties that should be shared
|
19
|
+
across all workflow actions, such as workflow-specific arguments and utilities.
|
20
|
+
"""
|
21
|
+
|
22
|
+
@abc.abstractmethod
|
23
|
+
def define_arguments(self, parser):
|
24
|
+
"""
|
25
|
+
Define arguments specific to this command.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
parser: The argument parser to add arguments to
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
The parser with arguments added
|
32
|
+
"""
|
33
|
+
super().define_arguments(parser)
|
34
|
+
# Add workflow-specific common arguments
|
35
|
+
has_name = any(action.dest == "name" for action in parser._actions)
|
36
|
+
if not has_name:
|
37
|
+
parser.add_argument(
|
38
|
+
"-n",
|
39
|
+
"--name",
|
40
|
+
help="Workflow name",
|
41
|
+
required=True,
|
42
|
+
)
|
43
|
+
return parser
|
44
|
+
|
45
|
+
@abc.abstractmethod
|
46
|
+
def execute(self, args):
|
47
|
+
"""
|
48
|
+
Execute the command with the given arguments.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
args: The parsed arguments
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
The result of the command execution
|
55
|
+
"""
|
56
|
+
raise NotImplementedError("Subclasses must implement execute()")
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# pylint: disable=no-member
|
2
|
+
|
3
|
+
from argocli.commands.workflow import ArgoWorkflowCommand
|
4
|
+
|
5
|
+
class WorkflowStatus(ArgoWorkflowCommand):
|
6
|
+
"""
|
7
|
+
Command to check the status of a workflow.
|
8
|
+
"""
|
9
|
+
|
10
|
+
def define_arguments(self, parser):
|
11
|
+
"""
|
12
|
+
Define command-specific arguments for checking workflow status.
|
13
|
+
"""
|
14
|
+
super().define_arguments(parser)
|
15
|
+
return parser
|
16
|
+
|
17
|
+
def execute(self, args):
|
18
|
+
"""
|
19
|
+
Execute the command to check the workflow status.
|
20
|
+
"""
|
21
|
+
# print(f"Checking status for workflow: {args.name}")
|
22
|
+
client = self.argo_client
|
23
|
+
workflow_status = client.get_workflow(args.name)
|
24
|
+
print(
|
25
|
+
workflow_status["status"]["phase"]
|
26
|
+
if workflow_status
|
27
|
+
else "Workflow not found or error occurred."
|
28
|
+
)
|
argocli/core/client.py
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# pylint: disable=no-member
|
3
|
+
|
4
|
+
"""
|
5
|
+
Argo client module.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import cac_core as cac
|
9
|
+
import requests
|
10
|
+
|
11
|
+
log = cac.logger.new(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
class ArgoClient:
|
15
|
+
"""
|
16
|
+
Argo client class.
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self, server, namespace, api_token=None):
|
20
|
+
"""
|
21
|
+
Initialize the Argo client.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
server: The Argo server
|
25
|
+
username: The Argo username
|
26
|
+
api_token: The Argo API token
|
27
|
+
"""
|
28
|
+
self.server = server
|
29
|
+
self.namespace = namespace
|
30
|
+
self.api_token = api_token
|
31
|
+
|
32
|
+
def get_workflow(self, name):
|
33
|
+
"""
|
34
|
+
Get a workflow by name.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
name: The name of the workflow
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
The workflow object
|
41
|
+
"""
|
42
|
+
log.debug(f"Getting workflow {name} from server {self.server}")
|
43
|
+
response = requests.get(
|
44
|
+
f"{self.server}/api/v1/workflows/{self.namespace}/{name}",
|
45
|
+
headers={"Authorization": f"Bearer {self.api_token}"},
|
46
|
+
timeout=10
|
47
|
+
)
|
48
|
+
if response.status_code == 200:
|
49
|
+
return response.json()
|
50
|
+
else:
|
51
|
+
log.error(f"Failed to get workflow {name}: {response.status_code} {response.text}")
|
52
|
+
return None
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: argocli
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A command-line interface for Argo Workflows
|
5
|
+
Requires-Python: >=3.12
|
6
|
+
Description-Content-Type: text/markdown
|
7
|
+
License-File: LICENSE
|
8
|
+
Requires-Dist: cac-core>=0.4.1
|
9
|
+
Requires-Dist: packaging>=25.0
|
10
|
+
Requires-Dist: requests>=2.32.4
|
11
|
+
Dynamic: license-file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
argocli/__init__.py,sha256=weB2OKhqXNYNMZ9i23mwC99shvbIM1eochbN91Okakw,1874
|
2
|
+
argocli/cli/main.py,sha256=SGDmWCuR2XQS71g1p3p-BYQT1ivxmgrIpfO0G4ZEzJ0,7589
|
3
|
+
argocli/commands/command.py,sha256=K9ehMVba9_Rg_TsEPnFec4GP24BpXvHWdfp2A7wvmTc,1599
|
4
|
+
argocli/commands/workflow/__init__.py,sha256=6f-gZGuBNw_YM4iK8-gviJ20uDSfmhEtC7Lx8TpWMP8,1489
|
5
|
+
argocli/commands/workflow/status.py,sha256=YxNFmieCiBaqOBt6D-uyCDTVr6SC4cLxhgtqxJyxcLE,819
|
6
|
+
argocli/core/client.py,sha256=00NMaRbEFmgHGHea2OJPdg7bMAn9wa0gLuJT_kdJ_fU,1246
|
7
|
+
argocli-0.1.0.dist-info/licenses/LICENSE,sha256=9zKZw6P2NbYxuA1UuxzvAvTc3VhTW07CAgN3bLxCkqo,1066
|
8
|
+
argocli-0.1.0.dist-info/METADATA,sha256=5dwg-OC9EsVNFARFla9mZ02aWmZJ5E3iKWV-0kMeiF4,306
|
9
|
+
argocli-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
10
|
+
argocli-0.1.0.dist-info/entry_points.txt,sha256=cAplzeUUaQ8IXiVEBR_9p5_7iRHyAGZQgxl0hITGqrg,50
|
11
|
+
argocli-0.1.0.dist-info/top_level.txt,sha256=k62Rfx-hxDMUXTnXBBpjRw16LlSfB6I7rTai_DPoJuw,8
|
12
|
+
argocli-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Ryan Punt
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1 @@
|
|
1
|
+
argocli
|