cuhkit 1.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.
cuhkit/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ """
2
+ ----------------------------------------------
3
+ cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).
4
+ https://github.com/cuhHub/cuhkit
5
+ ----------------------------------------------
6
+
7
+ Copyright (C) 2026 cuhHub
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
20
+ """
21
+
22
+ # // Main
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ __VERSION__ = "1.0.0"
27
+ CUHKIT_DATA_PATH = Path.home() / ".cuhkit"
28
+ CUHKIT_PACKAGE_PATH = Path(__file__).parent
29
+
30
+ if getattr(sys, "frozen", False): # pyinstaller context
31
+ CUHKIT_PACKAGE_PATH = Path(sys._MEIPASS) / "cuhkit"
32
+
33
+ from . import exceptions
34
+ from . import log
35
+ from . import libs
36
+ from . import projects
37
+ from . import credentials
38
+
39
+ from . import cli_context
40
+ from .cli import cli
cuhkit/__main__.py ADDED
@@ -0,0 +1,27 @@
1
+ """
2
+ ----------------------------------------------
3
+ cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).
4
+ https://github.com/cuhHub/cuhkit
5
+ ----------------------------------------------
6
+
7
+ Copyright (C) 2026 cuhHub
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
20
+ """
21
+
22
+ # // Imports
23
+ from . import cli
24
+
25
+ # // Main
26
+ if __name__ == "__main__":
27
+ cli()
cuhkit/cli.py ADDED
@@ -0,0 +1,243 @@
1
+ """
2
+ ----------------------------------------------
3
+ cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).
4
+ https://github.com/cuhHub/cuhkit
5
+ ----------------------------------------------
6
+
7
+ Copyright (C) 2026 cuhHub
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
20
+ """
21
+
22
+ # // Imports
23
+ import click
24
+ from pathlib import Path
25
+ from functools import wraps
26
+ from typing import Callable
27
+
28
+ from cuhkit import __VERSION__
29
+ from cuhkit import projects
30
+ from cuhkit import cli_context
31
+ from cuhkit.log import set_logging_verbose, logger
32
+ from cuhkit.exceptions import CredentialsException
33
+ from cuhkit.projects import POSITION_PERSISTENT_DATA_KEY
34
+
35
+ # // Main
36
+ def requires_project(project_types: list[projects.ProjectType] | None = None):
37
+ """
38
+ Decorator for click commands that require a cuhkit project.
39
+
40
+ Args:
41
+ project_type (projects.ProjectType | None): The type of project required, or None to allow any.
42
+ """
43
+
44
+ def decorator(function: Callable):
45
+ @wraps(function)
46
+ def wrapper(*args, **kwargs):
47
+ context = cli_context.get_context()
48
+
49
+ if context.project is None:
50
+ logger.error("No cuhkit project found in current directory, this command must be run in a cuhkit project directory.")
51
+ return
52
+
53
+ if project_types is not None and context.project.project_type not in project_types:
54
+ logger.error(f"This command is not compatible with this cuhkit project. Current cuhkit project is not any of required types {project_types}, got {context.project.project_type}.")
55
+ return
56
+
57
+ return function(*args, **kwargs, project = context.project)
58
+
59
+ return wrapper
60
+
61
+ return decorator
62
+
63
+ @click.group()
64
+ @click.version_option(__VERSION__)
65
+ @click.help_option()
66
+ @click.pass_context
67
+ @click.option("verbose", "--verbose", "-v", is_flag = True, help = "Enables verbose output.")
68
+ def cli(context: click.Context, verbose: bool):
69
+ """
70
+ Main CLI entry point.
71
+ """
72
+
73
+ logger.info("cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).")
74
+
75
+ cli_context.setup_context(context)
76
+
77
+ if verbose:
78
+ set_logging_verbose(verbose)
79
+
80
+ @cli.command()
81
+ @click.argument(
82
+ "name",
83
+ type = str,
84
+ required = True
85
+ )
86
+ @click.option(
87
+ "--path", "-p",
88
+ type = click.Path(exists = True, file_okay = False, dir_okay = True, resolve_path = True, path_type = Path),
89
+ default = ".",
90
+ help = "The directory to create the project in."
91
+ )
92
+ @click.option(
93
+ "--type", "-t", "project_type",
94
+ type = click.Choice(projects.ProjectType, case_sensitive = False),
95
+ required = True,
96
+ help = "The type of the project to create."
97
+ )
98
+ def new(name: str, path: Path, project_type: projects.ProjectType):
99
+ """
100
+ Create a new cuhkit project.
101
+ """
102
+
103
+ if projects.does_project_exist_at_path(path):
104
+ logger.error(f"A cuhkit project already exists at {path}.")
105
+ return
106
+
107
+ if project_type == projects.ProjectType.ADDON:
108
+ project = projects.create_addon_project(name, path)
109
+ elif project_type == projects.ProjectType.MOD:
110
+ project = projects.create_mod_project(name, path)
111
+
112
+ project.first_time_setup()
113
+
114
+ logger.info(f"Created cuhkit project at {path}.")
115
+
116
+ @cli.command()
117
+ @cli_context.pass_context
118
+ @requires_project()
119
+ @click.confirmation_option(prompt = "Are you sure you want to delete this cuhkit project? Only the project file will be deleted. Any files you created will remain.")
120
+ def delete(context: cli_context.CLIContext, project: projects.Project):
121
+ """
122
+ Deletes a cuhkit project.
123
+ """
124
+
125
+ project.delete()
126
+ logger.info("Deleted cuhkit project.")
127
+
128
+ @cli.command()
129
+ @cli_context.pass_context
130
+ @requires_project([projects.ProjectType.ADDON, projects.ProjectType.MOD])
131
+ def build(context: cli_context.CLIContext, project: projects.AddonProject | projects.ModProject):
132
+ """
133
+ Builds and syncs a cuhkit project.
134
+ """
135
+
136
+ project.build()
137
+ project.sync()
138
+ logger.info("Build complete.")
139
+
140
+ @cli.command()
141
+ @cli_context.pass_context
142
+ @requires_project([projects.ProjectType.ADDON, projects.ProjectType.MOD])
143
+ def sync(context: cli_context.CLIContext, project: projects.AddonProject | projects.ModProject):
144
+ """
145
+ Sync a cuhkit project to Stormworks.
146
+ Note that the 'build' command also does this, but builds beforehand.
147
+ """
148
+
149
+ project.sync()
150
+ logger.info("Sync complete.")
151
+
152
+ @cli.command()
153
+ @cli_context.pass_context
154
+ @requires_project([projects.ProjectType.ADDON])
155
+ def get_position(context: cli_context.CLIContext, project: projects.AddonProject):
156
+ """
157
+ Returns the last saved in-game position.
158
+ To use this, save a formatted matrix position to `_debug_pos` (key might be outdated, see source code) via cuhHub persistent data service.
159
+ """
160
+
161
+ position = project.get_position()
162
+
163
+ if position is None:
164
+ logger.error(f"No position found. Save a formatted position to '{POSITION_PERSISTENT_DATA_KEY}' to cuhHub persistent data service via an addon.")
165
+ return
166
+
167
+ logger.info(f"Position: {position} (copied to clipboard)")
168
+
169
+ @cli.command()
170
+ @cli_context.pass_context
171
+ @requires_project([projects.ProjectType.ADDON])
172
+ def setup(context: cli_context.CLIContext, project: projects.AddonProject):
173
+ """
174
+ Sets up a cuhkit addon project for the first time.
175
+ Should only really be used after cloning an existing addon project.
176
+ """
177
+
178
+ project.setup()
179
+ logger.info("Addon project setup complete.")
180
+
181
+ @cli.command()
182
+ @cli_context.pass_context
183
+ @requires_project([projects.ProjectType.ADDON, projects.ProjectType.MOD])
184
+ @click.option(
185
+ "--server", "-s", "server_id",
186
+ type = int,
187
+ required = True,
188
+ help = "The ID of the server to publish to, or -1 for all servers."
189
+ )
190
+ @click.option(
191
+ "--dev", "-d", "is_dev",
192
+ is_flag = True,
193
+ help = "Whether or not to publish as a development build."
194
+ )
195
+ @click.confirmation_option(prompt = "Are you sure you want to publish this cuhkit project?")
196
+ def publish(context: cli_context.CLIContext, project: projects.AddonProject | projects.ModProject, server_id: int, is_dev: bool):
197
+ """
198
+ Publish a cuhkit project to cuhHub.
199
+ """
200
+
201
+ if isinstance(project, projects.AddonProject):
202
+ try:
203
+ project.publish(server_id, is_dev)
204
+ logger.info("Published cuhkit addon project.")
205
+ except CredentialsException:
206
+ logger.error("No API token found in credentials. Please set one using the `set-api-token` command.")
207
+ except FileNotFoundError:
208
+ logger.error("Missing `playlist.xml` file in addon project directory. Try building the addon or running first-time setup. If neither work, please manually create one.")
209
+ elif isinstance(project, projects.ModProject):
210
+ try:
211
+ project.publish(server_id, is_dev)
212
+ logger.info("Published cuhkit mod project.")
213
+ except CredentialsException:
214
+ logger.error("No API token found in credentials. Please set one using the `set-api-token` command.")
215
+
216
+ @cli.command()
217
+ @cli_context.pass_context
218
+ @click.argument(
219
+ "api_token",
220
+ type = click.UUID,
221
+ required = True
222
+ )
223
+ def set_api_token(context: cli_context.CLIContext, api_token: str):
224
+ """
225
+ Sets the cuhHub API token in credentials.
226
+ """
227
+
228
+ context.credentials.api_token = api_token
229
+ context.credentials.save()
230
+
231
+ @cli.command()
232
+ @cli_context.pass_context
233
+ @click.confirmation_option(prompt = "Are you sure you want to delete your cuhkit credentials (API token, etc.)?")
234
+ def delete_credentials(context: cli_context.CLIContext):
235
+ """
236
+ Deletes cuhkit credentials.
237
+ """
238
+
239
+ context.credentials.remove()
240
+ logger.info("Deleted cuhkit credentials.")
241
+
242
+ if __name__ == "__main__":
243
+ cli()
cuhkit/cli_context.py ADDED
@@ -0,0 +1,83 @@
1
+ """
2
+ ----------------------------------------------
3
+ cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).
4
+ https://github.com/cuhHub/cuhkit
5
+ ----------------------------------------------
6
+
7
+ Copyright (C) 2026 cuhHub
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
20
+ """
21
+
22
+ # // Imports
23
+ import click
24
+ from dataclasses import dataclass
25
+ from typing import Callable
26
+ from functools import wraps
27
+ from pathlib import Path
28
+
29
+ from cuhkit import projects
30
+
31
+ from cuhkit.credentials import (
32
+ credentials,
33
+ Credentials
34
+ )
35
+
36
+ # // Main
37
+ def pass_context(function: Callable):
38
+ """
39
+ Decorator to pass CLI context to functions.
40
+
41
+ Args:
42
+ function (Callable): The function to decorate.
43
+ """
44
+
45
+ @wraps(function)
46
+ def wrapper(*args, **kwargs):
47
+ return function(*args, **kwargs, context = get_context())
48
+
49
+ return wrapper
50
+
51
+ @dataclass
52
+ class CLIContext:
53
+ """
54
+ Context object for the cuhkit CLI.
55
+ """
56
+
57
+ project: projects.AddonProject | projects.ModProject | None
58
+ credentials: Credentials
59
+
60
+ def setup_context(context: click.Context):
61
+ """
62
+ Sets up the click context.
63
+
64
+ Args:
65
+ context (click.Context): The click context.
66
+ """
67
+
68
+ context.ensure_object(dict)
69
+
70
+ context.obj["context"] = CLIContext(
71
+ project = projects.load_project_at_path(Path.cwd()) if projects.does_project_exist_at_path(Path.cwd()) else None,
72
+ credentials = credentials
73
+ )
74
+
75
+ def get_context() -> CLIContext:
76
+ """
77
+ Returns the CLI context.
78
+
79
+ Returns:
80
+ CLIContext: The CLI context.
81
+ """
82
+
83
+ return click.get_current_context().obj["context"]
cuhkit/credentials.py ADDED
@@ -0,0 +1,106 @@
1
+ """
2
+ ----------------------------------------------
3
+ cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).
4
+ https://github.com/cuhHub/cuhkit
5
+ ----------------------------------------------
6
+
7
+ Copyright (C) 2026 cuhHub
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
20
+ """
21
+
22
+ # // Imports
23
+ from pydantic import (
24
+ BaseModel,
25
+ UUID4
26
+ )
27
+
28
+ from pathlib import Path
29
+
30
+ from cuhkit import CUHKIT_DATA_PATH
31
+ from cuhkit.log import logger
32
+
33
+ # // Main
34
+ CREDENTIALS_PATH = CUHKIT_DATA_PATH / "credentials.json"
35
+
36
+ class Credentials(BaseModel):
37
+ """
38
+ Base class for credentials
39
+ """
40
+
41
+ api_token: UUID4 | None = None
42
+ path: Path
43
+
44
+ def save(self):
45
+ """
46
+ Save the credentials.
47
+ """
48
+
49
+ logger.info(f"Saving credentials to {self.path}")
50
+ self.path.write_text(self.model_dump_json(indent = 3))
51
+
52
+ def remove(self):
53
+ """
54
+ Remove the credentials.
55
+ """
56
+
57
+ logger.info(f"Removing credentials from {self.path}")
58
+ self.path.unlink()
59
+
60
+ @classmethod
61
+ def create_new(cls, path: Path):
62
+ """
63
+ Creates new credentials, saving it automatically.
64
+
65
+ Args:
66
+ path (Path): The path to the credentials file.
67
+ """
68
+
69
+ credentials = cls(path = path)
70
+ credentials.save()
71
+
72
+ return credentials
73
+
74
+ @classmethod
75
+ def try_load(cls, path: Path) -> Credentials:
76
+ """
77
+ Loads credentials from the file, or creating new and saving if it doesn't exist.
78
+
79
+ Args:
80
+ path (Path): The path to the credentials file.
81
+
82
+ Returns:
83
+ Credentials: The loaded credentials.
84
+ """
85
+
86
+ if not path.exists():
87
+ logger.warning(f"No credentials found at {path}. Creating new.")
88
+ return cls.create_new(path)
89
+
90
+ logger.debug(f"Getting credentials from: {path}")
91
+
92
+ content = path.read_text()
93
+ return cls.model_validate_json(content)
94
+
95
+ def get_credentials() -> Credentials:
96
+ """
97
+ Loads credentials from a file.
98
+
99
+ Returns:
100
+ Credentials: The loaded credentials.
101
+ """
102
+
103
+ CREDENTIALS_PATH.parent.mkdir(parents = True, exist_ok = True)
104
+ return Credentials.try_load(CREDENTIALS_PATH)
105
+
106
+ credentials = get_credentials()
cuhkit/exceptions.py ADDED
@@ -0,0 +1,80 @@
1
+ """
2
+ ----------------------------------------------
3
+ cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).
4
+ https://github.com/cuhHub/cuhkit
5
+ ----------------------------------------------
6
+
7
+ Copyright (C) 2026 cuhHub
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
20
+ """
21
+
22
+ # // Main
23
+ __all__ = [
24
+ "CuhkitException",
25
+ "APIException",
26
+ "ProjectException",
27
+ "ProjectNotFoundException",
28
+ "ProjectLoadFailureException",
29
+ "ProjectAlreadyExistsException",
30
+ "CredentialsException"
31
+ ]
32
+
33
+ class CuhkitException(Exception):
34
+ """
35
+ Base exception class for cuhkit.
36
+ """
37
+
38
+ pass
39
+
40
+ class APIException(CuhkitException):
41
+ """
42
+ Exception class for API errors.
43
+ """
44
+
45
+ pass
46
+
47
+ class ProjectException(CuhkitException):
48
+ """
49
+ Exception class for project errors.
50
+ """
51
+
52
+ pass
53
+
54
+ class ProjectNotFoundException(ProjectException):
55
+ """
56
+ Exception class for when a cuhkit project is not found.
57
+ """
58
+
59
+ pass
60
+
61
+ class ProjectLoadFailureException(ProjectException):
62
+ """
63
+ Exception class for when a cuhkit project fails to load.
64
+ """
65
+
66
+ pass
67
+
68
+ class ProjectAlreadyExistsException(ProjectException):
69
+ """
70
+ Exception class for when a cuhkit project already exists.
71
+ """
72
+
73
+ pass
74
+
75
+ class CredentialsException(CuhkitException):
76
+ """
77
+ Exception class for bad credentials.
78
+ """
79
+
80
+ pass
@@ -0,0 +1,28 @@
1
+ """
2
+ ----------------------------------------------
3
+ cuhkit - A CLI-oriented Python package for handling cuhHub Stormworks projects (addons/mods).
4
+ https://github.com/cuhHub/cuhkit
5
+ ----------------------------------------------
6
+
7
+ Copyright (C) 2026 cuhHub
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
20
+ """
21
+
22
+ # // Imports
23
+ from . import api
24
+ from . import mod_builder
25
+ from . import addon_builder
26
+ from . import timeit
27
+ from . import templates
28
+ from . import requests