griptape-nodes 0.52.1__py3-none-any.whl → 0.53.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.
- griptape_nodes/__init__.py +6 -943
- griptape_nodes/__main__.py +6 -0
- griptape_nodes/app/app.py +45 -61
- griptape_nodes/cli/__init__.py +1 -0
- griptape_nodes/cli/commands/__init__.py +1 -0
- griptape_nodes/cli/commands/config.py +71 -0
- griptape_nodes/cli/commands/engine.py +80 -0
- griptape_nodes/cli/commands/init.py +548 -0
- griptape_nodes/cli/commands/libraries.py +90 -0
- griptape_nodes/cli/commands/self.py +117 -0
- griptape_nodes/cli/main.py +46 -0
- griptape_nodes/cli/shared.py +84 -0
- griptape_nodes/common/__init__.py +1 -0
- griptape_nodes/common/directed_graph.py +55 -0
- griptape_nodes/drivers/storage/local_storage_driver.py +7 -2
- griptape_nodes/exe_types/core_types.py +60 -2
- griptape_nodes/exe_types/node_types.py +38 -24
- griptape_nodes/machines/control_flow.py +86 -22
- griptape_nodes/machines/fsm.py +10 -1
- griptape_nodes/machines/parallel_resolution.py +570 -0
- griptape_nodes/machines/{node_resolution.py → sequential_resolution.py} +22 -51
- griptape_nodes/retained_mode/events/base_events.py +2 -2
- griptape_nodes/retained_mode/events/node_events.py +4 -3
- griptape_nodes/retained_mode/griptape_nodes.py +25 -12
- griptape_nodes/retained_mode/managers/agent_manager.py +9 -5
- griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +3 -1
- griptape_nodes/retained_mode/managers/context_manager.py +6 -5
- griptape_nodes/retained_mode/managers/flow_manager.py +117 -204
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +1 -1
- griptape_nodes/retained_mode/managers/library_manager.py +35 -25
- griptape_nodes/retained_mode/managers/node_manager.py +81 -199
- griptape_nodes/retained_mode/managers/object_manager.py +11 -5
- griptape_nodes/retained_mode/managers/os_manager.py +24 -9
- griptape_nodes/retained_mode/managers/secrets_manager.py +8 -4
- griptape_nodes/retained_mode/managers/settings.py +32 -1
- griptape_nodes/retained_mode/managers/static_files_manager.py +8 -3
- griptape_nodes/retained_mode/managers/sync_manager.py +8 -5
- griptape_nodes/retained_mode/managers/workflow_manager.py +110 -122
- griptape_nodes/traits/add_param_button.py +1 -1
- griptape_nodes/traits/button.py +216 -6
- griptape_nodes/traits/color_picker.py +66 -0
- griptape_nodes/traits/traits.json +4 -0
- {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/METADATA +2 -1
- {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/RECORD +46 -32
- {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/entry_points.txt +0 -0
griptape_nodes/__init__.py
CHANGED
|
@@ -1,92 +1,7 @@
|
|
|
1
1
|
"""Griptape Nodes package."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
console = Console()
|
|
6
|
-
|
|
7
|
-
with console.status("Loading Griptape Nodes...") as status:
|
|
8
|
-
import argparse
|
|
9
|
-
import asyncio
|
|
10
|
-
import json
|
|
11
|
-
import os
|
|
12
|
-
import shutil
|
|
13
|
-
import sys
|
|
14
|
-
import tarfile
|
|
15
|
-
import tempfile
|
|
16
|
-
from dataclasses import dataclass
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
from typing import Any
|
|
19
|
-
|
|
20
|
-
import httpx
|
|
21
|
-
from rich.box import HEAVY_EDGE
|
|
22
|
-
from rich.panel import Panel
|
|
23
|
-
from rich.progress import Progress
|
|
24
|
-
from rich.prompt import Confirm, Prompt
|
|
25
|
-
from rich.table import Table
|
|
26
|
-
from xdg_base_dirs import xdg_config_home, xdg_data_home
|
|
27
|
-
|
|
28
|
-
from griptape_nodes.app import start_app
|
|
29
|
-
from griptape_nodes.drivers.storage import StorageBackend
|
|
30
|
-
from griptape_nodes.drivers.storage.griptape_cloud_storage_driver import GriptapeCloudStorageDriver
|
|
31
|
-
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
32
|
-
from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
|
|
33
|
-
from griptape_nodes.retained_mode.managers.os_manager import OSManager
|
|
34
|
-
from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
|
|
35
|
-
from griptape_nodes.utils.uv_utils import find_uv_bin
|
|
36
|
-
from griptape_nodes.utils.version_utils import (
|
|
37
|
-
get_complete_version_string,
|
|
38
|
-
get_current_version,
|
|
39
|
-
get_install_source,
|
|
40
|
-
get_latest_version_git,
|
|
41
|
-
get_latest_version_pypi,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
CONFIG_DIR = xdg_config_home() / "griptape_nodes"
|
|
45
|
-
DATA_DIR = xdg_data_home() / "griptape_nodes"
|
|
46
|
-
ENV_FILE = CONFIG_DIR / ".env"
|
|
47
|
-
CONFIG_FILE = CONFIG_DIR / "griptape_nodes_config.json"
|
|
48
|
-
LATEST_TAG = "latest"
|
|
49
|
-
PACKAGE_NAME = "griptape-nodes"
|
|
50
|
-
NODES_APP_URL = "https://nodes.griptape.ai"
|
|
51
|
-
NODES_TARBALL_URL = "https://github.com/griptape-ai/griptape-nodes/archive/refs/tags/{tag}.tar.gz"
|
|
52
|
-
PYPI_UPDATE_URL = "https://pypi.org/pypi/{package}/json"
|
|
53
|
-
GITHUB_UPDATE_URL = "https://api.github.com/repos/griptape-ai/{package}/git/refs/tags/{revision}"
|
|
54
|
-
GT_CLOUD_BASE_URL = os.getenv("GT_CLOUD_BASE_URL", "https://cloud.griptape.ai")
|
|
55
|
-
|
|
56
|
-
# Environment variable defaults for init configuration
|
|
57
|
-
ENV_WORKSPACE_DIRECTORY = os.getenv("GTN_WORKSPACE_DIRECTORY")
|
|
58
|
-
ENV_API_KEY = os.getenv("GTN_API_KEY")
|
|
59
|
-
ENV_STORAGE_BACKEND = os.getenv("GTN_STORAGE_BACKEND")
|
|
60
|
-
ENV_REGISTER_ADVANCED_LIBRARY = (
|
|
61
|
-
os.getenv("GTN_REGISTER_ADVANCED_LIBRARY", "false").lower() == "true"
|
|
62
|
-
if os.getenv("GTN_REGISTER_ADVANCED_LIBRARY") is not None
|
|
63
|
-
else None
|
|
64
|
-
)
|
|
65
|
-
ENV_LIBRARIES_SYNC = (
|
|
66
|
-
os.getenv("GTN_LIBRARIES_SYNC", "false").lower() == "true" if os.getenv("GTN_LIBRARIES_SYNC") is not None else None
|
|
67
|
-
)
|
|
68
|
-
ENV_GTN_BUCKET_NAME = os.getenv("GTN_BUCKET_NAME")
|
|
69
|
-
ENV_LIBRARIES_BASE_DIR = os.getenv("GTN_LIBRARIES_BASE_DIR", str(DATA_DIR / "libraries"))
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@dataclass
|
|
73
|
-
class InitConfig:
|
|
74
|
-
"""Configuration for initialization."""
|
|
75
|
-
|
|
76
|
-
interactive: bool = True
|
|
77
|
-
workspace_directory: str | None = None
|
|
78
|
-
api_key: str | None = None
|
|
79
|
-
storage_backend: str | None = None
|
|
80
|
-
register_advanced_library: bool | None = None
|
|
81
|
-
config_values: dict[str, Any] | None = None
|
|
82
|
-
secret_values: dict[str, str] | None = None
|
|
83
|
-
libraries_sync: bool | None = None
|
|
84
|
-
bucket_name: str | None = None
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
config_manager = ConfigManager()
|
|
88
|
-
secrets_manager = SecretsManager(config_manager)
|
|
89
|
-
os_manager = OSManager()
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
90
5
|
|
|
91
6
|
|
|
92
7
|
def main() -> None:
|
|
@@ -97,862 +12,10 @@ def main() -> None:
|
|
|
97
12
|
# but current engine relies on importing files rather than packages.
|
|
98
13
|
sys.path.append(str(Path.cwd()))
|
|
99
14
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _run_init(config: InitConfig) -> None:
|
|
105
|
-
"""Runs through the engine init steps.
|
|
106
|
-
|
|
107
|
-
Args:
|
|
108
|
-
config: Initialization configuration.
|
|
109
|
-
"""
|
|
110
|
-
__init_system_config()
|
|
111
|
-
|
|
112
|
-
# Run configuration flow
|
|
113
|
-
_run_init_configuration(config)
|
|
114
|
-
|
|
115
|
-
# Sync libraries
|
|
116
|
-
if config.libraries_sync is not False:
|
|
117
|
-
asyncio.run(_sync_libraries())
|
|
118
|
-
|
|
119
|
-
console.print("[bold green]Initialization complete![/bold green]")
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _handle_api_key_config(config: InitConfig) -> str | None:
|
|
123
|
-
"""Handle API key configuration step."""
|
|
124
|
-
api_key = config.api_key
|
|
125
|
-
|
|
126
|
-
if config.interactive:
|
|
127
|
-
api_key = _prompt_for_api_key(default_api_key=api_key)
|
|
128
|
-
|
|
129
|
-
if api_key is not None:
|
|
130
|
-
secrets_manager.set_secret("GT_CLOUD_API_KEY", api_key)
|
|
131
|
-
console.print("[bold green]Griptape API Key set")
|
|
132
|
-
|
|
133
|
-
return api_key
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _handle_workspace_config(config: InitConfig) -> str | None:
|
|
137
|
-
"""Handle workspace directory configuration step."""
|
|
138
|
-
workspace_directory = config.workspace_directory
|
|
139
|
-
|
|
140
|
-
if config.interactive:
|
|
141
|
-
workspace_directory = _prompt_for_workspace(default_workspace_directory=workspace_directory)
|
|
142
|
-
|
|
143
|
-
if workspace_directory is not None:
|
|
144
|
-
config_manager.set_config_value("workspace_directory", workspace_directory)
|
|
145
|
-
console.print(f"[bold green]Workspace directory set to: {workspace_directory}[/bold green]")
|
|
146
|
-
|
|
147
|
-
return workspace_directory
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def _handle_storage_backend_config(config: InitConfig) -> str | None:
|
|
151
|
-
"""Handle storage backend configuration step."""
|
|
152
|
-
storage_backend = config.storage_backend
|
|
153
|
-
|
|
154
|
-
if config.interactive:
|
|
155
|
-
storage_backend = _prompt_for_storage_backend(default_storage_backend=storage_backend)
|
|
156
|
-
|
|
157
|
-
if storage_backend is not None:
|
|
158
|
-
config_manager.set_config_value("storage_backend", storage_backend)
|
|
159
|
-
console.print(f"[bold green]Storage backend set to: {storage_backend}")
|
|
160
|
-
|
|
161
|
-
return storage_backend
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def _handle_bucket_config(config: InitConfig) -> str | None:
|
|
165
|
-
"""Handle bucket configuration step (depends on API key)."""
|
|
166
|
-
bucket_id = None
|
|
167
|
-
|
|
168
|
-
if config.interactive:
|
|
169
|
-
# First ask if they want to configure a bucket
|
|
170
|
-
configure_bucket = _prompt_for_bucket_configuration()
|
|
171
|
-
if configure_bucket:
|
|
172
|
-
bucket_id = _prompt_for_gtc_bucket_name(default_bucket_name=config.bucket_name)
|
|
173
|
-
elif config.bucket_name is not None:
|
|
174
|
-
bucket_id = _get_or_create_bucket_id(config.bucket_name)
|
|
175
|
-
|
|
176
|
-
if bucket_id is not None:
|
|
177
|
-
secrets_manager.set_secret("GT_CLOUD_BUCKET_ID", bucket_id)
|
|
178
|
-
console.print(f"[bold green]Bucket ID set to: {bucket_id}[/bold green]")
|
|
179
|
-
|
|
180
|
-
return bucket_id
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def _handle_advanced_library_config(config: InitConfig) -> bool | None:
|
|
184
|
-
"""Handle advanced library configuration step."""
|
|
185
|
-
register_advanced_library = config.register_advanced_library
|
|
186
|
-
|
|
187
|
-
if config.interactive:
|
|
188
|
-
register_advanced_library = _prompt_for_advanced_media_library(
|
|
189
|
-
default_prompt_for_advanced_media_library=register_advanced_library
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
if register_advanced_library is not None:
|
|
193
|
-
libraries_to_register = __build_libraries_list(register_advanced_library=register_advanced_library)
|
|
194
|
-
config_manager.set_config_value(
|
|
195
|
-
"app_events.on_app_initialization_complete.libraries_to_register", libraries_to_register
|
|
196
|
-
)
|
|
197
|
-
console.print(f"[bold green]Libraries to register set to: {', '.join(libraries_to_register)}[/bold green]")
|
|
198
|
-
|
|
199
|
-
return register_advanced_library
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def _handle_arbitrary_configs(config: InitConfig) -> None:
|
|
203
|
-
"""Handle arbitrary config and secret values."""
|
|
204
|
-
# Set arbitrary config values
|
|
205
|
-
if config.config_values:
|
|
206
|
-
for key, value in config.config_values.items():
|
|
207
|
-
config_manager.set_config_value(key, value)
|
|
208
|
-
console.print(f"[bold green]Config '{key}' set to: {value}[/bold green]")
|
|
209
|
-
|
|
210
|
-
# Set arbitrary secret values
|
|
211
|
-
if config.secret_values:
|
|
212
|
-
for key, value in config.secret_values.items():
|
|
213
|
-
secrets_manager.set_secret(key, value)
|
|
214
|
-
console.print(f"[bold green]Secret '{key}' set[/bold green]")
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def _run_init_configuration(config: InitConfig) -> None:
|
|
218
|
-
"""Handle initialization with proper dependency ordering."""
|
|
219
|
-
_handle_api_key_config(config)
|
|
220
|
-
|
|
221
|
-
_handle_workspace_config(config)
|
|
222
|
-
|
|
223
|
-
_handle_storage_backend_config(config)
|
|
224
|
-
|
|
225
|
-
_handle_bucket_config(config)
|
|
226
|
-
|
|
227
|
-
_handle_advanced_library_config(config)
|
|
228
|
-
|
|
229
|
-
_handle_arbitrary_configs(config)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
def _start_engine(*, no_update: bool = False) -> None:
|
|
233
|
-
"""Starts the Griptape Nodes engine.
|
|
234
|
-
|
|
235
|
-
Args:
|
|
236
|
-
no_update (bool): If True, skips the auto-update check.
|
|
237
|
-
"""
|
|
238
|
-
if not CONFIG_DIR.exists():
|
|
239
|
-
# Default init flow if there is no config directory
|
|
240
|
-
console.print("[bold green]Config directory not found. Initializing...[/bold green]")
|
|
241
|
-
_run_init(
|
|
242
|
-
InitConfig(
|
|
243
|
-
workspace_directory=ENV_WORKSPACE_DIRECTORY,
|
|
244
|
-
api_key=ENV_API_KEY,
|
|
245
|
-
storage_backend=ENV_STORAGE_BACKEND,
|
|
246
|
-
register_advanced_library=ENV_REGISTER_ADVANCED_LIBRARY,
|
|
247
|
-
interactive=True,
|
|
248
|
-
config_values=None,
|
|
249
|
-
secret_values=None,
|
|
250
|
-
libraries_sync=ENV_LIBRARIES_SYNC,
|
|
251
|
-
bucket_name=ENV_GTN_BUCKET_NAME,
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
# Confusing double negation -- If `no_update` is set, we want to skip the update
|
|
256
|
-
if not no_update:
|
|
257
|
-
_auto_update_self()
|
|
258
|
-
|
|
259
|
-
console.print("[bold green]Starting Griptape Nodes engine...[/bold green]")
|
|
260
|
-
start_app()
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
def _get_args() -> argparse.Namespace:
|
|
264
|
-
"""Parse CLI arguments for the *griptape-nodes* entry-point."""
|
|
265
|
-
parser = argparse.ArgumentParser(
|
|
266
|
-
prog="griptape-nodes",
|
|
267
|
-
description="Griptape Nodes Engine.",
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
# Global options (apply to every command)
|
|
271
|
-
parser.add_argument(
|
|
272
|
-
"--no-update",
|
|
273
|
-
action="store_true",
|
|
274
|
-
help="Skip the auto-update check.",
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
subparsers = parser.add_subparsers(
|
|
278
|
-
dest="command",
|
|
279
|
-
metavar="COMMAND",
|
|
280
|
-
required=False,
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
init_parser = subparsers.add_parser("init", help="Initialize engine configuration.")
|
|
284
|
-
init_parser.add_argument(
|
|
285
|
-
"--api-key",
|
|
286
|
-
help="Set the Griptape Nodes API key.",
|
|
287
|
-
default=ENV_API_KEY,
|
|
288
|
-
)
|
|
289
|
-
init_parser.add_argument(
|
|
290
|
-
"--workspace-directory",
|
|
291
|
-
help="Set the Griptape Nodes workspace directory.",
|
|
292
|
-
default=ENV_WORKSPACE_DIRECTORY,
|
|
293
|
-
)
|
|
294
|
-
init_parser.add_argument(
|
|
295
|
-
"--storage-backend",
|
|
296
|
-
help="Set the storage backend ('local' or 'gtc').",
|
|
297
|
-
choices=list(StorageBackend),
|
|
298
|
-
default=ENV_STORAGE_BACKEND,
|
|
299
|
-
)
|
|
300
|
-
init_parser.add_argument(
|
|
301
|
-
"--bucket-name",
|
|
302
|
-
help="Name for the bucket (existing or new) when using 'gtc' storage backend.",
|
|
303
|
-
default=ENV_GTN_BUCKET_NAME,
|
|
304
|
-
)
|
|
305
|
-
init_parser.add_argument(
|
|
306
|
-
"--register-advanced-library",
|
|
307
|
-
help="Install the Griptape Nodes Advanced Image Library.",
|
|
308
|
-
default=ENV_REGISTER_ADVANCED_LIBRARY,
|
|
309
|
-
)
|
|
310
|
-
init_parser.add_argument(
|
|
311
|
-
"--libraries-sync",
|
|
312
|
-
help="Sync the Griptape Nodes libraries.",
|
|
313
|
-
default=ENV_LIBRARIES_SYNC,
|
|
314
|
-
)
|
|
315
|
-
init_parser.add_argument(
|
|
316
|
-
"--no-interactive",
|
|
317
|
-
action="store_true",
|
|
318
|
-
help="Run init in non-interactive mode (no prompts).",
|
|
319
|
-
)
|
|
320
|
-
init_parser.add_argument(
|
|
321
|
-
"--config",
|
|
322
|
-
action="append",
|
|
323
|
-
metavar="KEY=VALUE",
|
|
324
|
-
help="Set arbitrary config values as key=value pairs (can be used multiple times). Example: --config log_level=DEBUG --config workspace_directory=/tmp",
|
|
325
|
-
)
|
|
326
|
-
init_parser.add_argument(
|
|
327
|
-
"--secret",
|
|
328
|
-
action="append",
|
|
329
|
-
metavar="KEY=VALUE",
|
|
330
|
-
help="Set arbitrary secret values as key=value pairs (can be used multiple times). Example: --secret MY_API_KEY=abc123 --secret OTHER_KEY=xyz789",
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
# engine
|
|
334
|
-
subparsers.add_parser("engine", help="Run the Griptape Nodes engine.")
|
|
335
|
-
|
|
336
|
-
# config
|
|
337
|
-
config_parser = subparsers.add_parser("config", help="Manage configuration.")
|
|
338
|
-
config_subparsers = config_parser.add_subparsers(
|
|
339
|
-
dest="subcommand",
|
|
340
|
-
metavar="SUBCOMMAND",
|
|
341
|
-
required=True,
|
|
342
|
-
)
|
|
343
|
-
config_show_parser = config_subparsers.add_parser("show", help="Show configuration values.")
|
|
344
|
-
config_show_parser.add_argument(
|
|
345
|
-
"config_path",
|
|
346
|
-
nargs="?",
|
|
347
|
-
help="Optional config path to show specific value (e.g., 'workspace_directory').",
|
|
348
|
-
)
|
|
349
|
-
config_subparsers.add_parser("list", help="List configuration values.")
|
|
350
|
-
config_subparsers.add_parser("reset", help="Reset configuration to defaults.")
|
|
351
|
-
|
|
352
|
-
# self
|
|
353
|
-
self_parser = subparsers.add_parser("self", help="Manage this CLI installation.")
|
|
354
|
-
self_subparsers = self_parser.add_subparsers(
|
|
355
|
-
dest="subcommand",
|
|
356
|
-
metavar="SUBCOMMAND",
|
|
357
|
-
required=True,
|
|
358
|
-
)
|
|
359
|
-
self_subparsers.add_parser("update", help="Update the CLI.")
|
|
360
|
-
self_subparsers.add_parser("uninstall", help="Uninstall the CLI.")
|
|
361
|
-
self_subparsers.add_parser("version", help="Print the CLI version.")
|
|
362
|
-
|
|
363
|
-
# libraries
|
|
364
|
-
libraries_parser = subparsers.add_parser("libraries", help="Manage local libraries.")
|
|
365
|
-
libraries_subparsers = libraries_parser.add_subparsers(
|
|
366
|
-
dest="subcommand",
|
|
367
|
-
metavar="SUBCOMMAND",
|
|
368
|
-
required=True,
|
|
369
|
-
)
|
|
370
|
-
libraries_subparsers.add_parser("sync", help="Sync libraries with your current engine version.")
|
|
371
|
-
|
|
372
|
-
args = parser.parse_args()
|
|
373
|
-
|
|
374
|
-
# Default to the `engine` command when none is given.
|
|
375
|
-
if args.command is None:
|
|
376
|
-
args.command = "engine"
|
|
377
|
-
|
|
378
|
-
return args
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
def _prompt_for_api_key(default_api_key: str | None = None) -> str:
|
|
382
|
-
"""Prompts the user for their GT_CLOUD_API_KEY unless it's provided."""
|
|
383
|
-
if default_api_key is None:
|
|
384
|
-
default_api_key = secrets_manager.get_secret("GT_CLOUD_API_KEY", should_error_on_not_found=False)
|
|
385
|
-
explainer = f"""[bold cyan]Griptape API Key[/bold cyan]
|
|
386
|
-
A Griptape API Key is needed to proceed.
|
|
387
|
-
This key allows the Griptape Nodes Engine to communicate with the Griptape Nodes Editor.
|
|
388
|
-
In order to get your key, return to the [link={NODES_APP_URL}]{NODES_APP_URL}[/link] tab in your browser and click the button
|
|
389
|
-
"Generate API Key".
|
|
390
|
-
Once the key is generated, copy and paste its value here to proceed."""
|
|
391
|
-
console.print(Panel(explainer, expand=False))
|
|
392
|
-
|
|
393
|
-
while True:
|
|
394
|
-
api_key = Prompt.ask(
|
|
395
|
-
"Griptape API Key",
|
|
396
|
-
default=default_api_key,
|
|
397
|
-
show_default=True,
|
|
398
|
-
)
|
|
399
|
-
if api_key:
|
|
400
|
-
break
|
|
401
|
-
|
|
402
|
-
return api_key
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
def _prompt_for_workspace(*, default_workspace_directory: str | None = None) -> str:
|
|
406
|
-
"""Prompts the user for their workspace directory."""
|
|
407
|
-
if default_workspace_directory is None:
|
|
408
|
-
default_workspace_directory = config_manager.get_config_value("workspace_directory")
|
|
409
|
-
explainer = """[bold cyan]Workspace Directory[/bold cyan]
|
|
410
|
-
Select the workspace directory. This is the location where Griptape Nodes will store your saved workflows.
|
|
411
|
-
You may enter a custom directory or press Return to accept the default workspace directory"""
|
|
412
|
-
console.print(Panel(explainer, expand=False))
|
|
413
|
-
|
|
414
|
-
while True:
|
|
415
|
-
try:
|
|
416
|
-
workspace_to_test = Prompt.ask(
|
|
417
|
-
"Workspace Directory",
|
|
418
|
-
default=default_workspace_directory,
|
|
419
|
-
show_default=True,
|
|
420
|
-
)
|
|
421
|
-
if workspace_to_test:
|
|
422
|
-
workspace_directory = str(Path(workspace_to_test).expanduser().resolve())
|
|
423
|
-
break
|
|
424
|
-
except OSError as e:
|
|
425
|
-
console.print(f"[bold red]Invalid workspace directory: {e}[/bold red]")
|
|
426
|
-
except json.JSONDecodeError as e:
|
|
427
|
-
console.print(f"[bold red]Error reading config file: {e}[/bold red]")
|
|
428
|
-
|
|
429
|
-
return workspace_directory
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
def _prompt_for_storage_backend(*, default_storage_backend: str | None = None) -> str:
|
|
433
|
-
"""Prompts the user for their storage backend."""
|
|
434
|
-
if default_storage_backend is None:
|
|
435
|
-
default_storage_backend = config_manager.get_config_value("storage_backend")
|
|
436
|
-
explainer = """[bold cyan]Storage Backend[/bold cyan]
|
|
437
|
-
Select the storage backend. This is where Griptape Nodes will store your static files.
|
|
438
|
-
Enter 'gtc' to use Griptape Cloud Bucket Storage, or press Return to accept the default of the local static file server."""
|
|
439
|
-
console.print(Panel(explainer, expand=False))
|
|
440
|
-
|
|
441
|
-
while True:
|
|
442
|
-
try:
|
|
443
|
-
storage_backend = Prompt.ask(
|
|
444
|
-
"Storage Backend",
|
|
445
|
-
choices=list(StorageBackend),
|
|
446
|
-
default=default_storage_backend,
|
|
447
|
-
show_default=True,
|
|
448
|
-
)
|
|
449
|
-
if storage_backend:
|
|
450
|
-
break
|
|
451
|
-
except json.JSONDecodeError as e:
|
|
452
|
-
console.print(f"[bold red]Error reading config file: {e}[/bold red]")
|
|
453
|
-
|
|
454
|
-
return storage_backend
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
def _get_griptape_cloud_buckets_and_display_table() -> tuple[list[str], dict[str, str], Table]:
|
|
458
|
-
"""Fetches the list of Griptape Cloud Buckets from the API.
|
|
459
|
-
|
|
460
|
-
Returns:
|
|
461
|
-
tuple: (bucket_names, name_to_id_mapping, display_table)
|
|
462
|
-
"""
|
|
463
|
-
api_key = secrets_manager.get_secret("GT_CLOUD_API_KEY")
|
|
464
|
-
bucket_names: list[str] = []
|
|
465
|
-
name_to_id: dict[str, str] = {}
|
|
466
|
-
|
|
467
|
-
if api_key is None:
|
|
468
|
-
msg = "Griptape Cloud API Key not found."
|
|
469
|
-
raise RuntimeError(msg)
|
|
470
|
-
|
|
471
|
-
table = Table(show_header=True, box=HEAVY_EDGE, show_lines=True, expand=True)
|
|
472
|
-
table.add_column("Bucket Name", style="green")
|
|
473
|
-
table.add_column("Bucket ID", style="green")
|
|
474
|
-
|
|
475
|
-
try:
|
|
476
|
-
buckets = GriptapeCloudStorageDriver.list_buckets(base_url=GT_CLOUD_BASE_URL, api_key=api_key)
|
|
477
|
-
for bucket in buckets:
|
|
478
|
-
bucket_name = bucket["name"]
|
|
479
|
-
bucket_id = bucket["bucket_id"]
|
|
480
|
-
bucket_names.append(bucket_name)
|
|
481
|
-
name_to_id[bucket_name] = bucket_id
|
|
482
|
-
table.add_row(bucket_name, bucket_id)
|
|
483
|
-
except RuntimeError as e:
|
|
484
|
-
console.print(f"[red]Error fetching buckets: {e}[/red]")
|
|
485
|
-
|
|
486
|
-
return bucket_names, name_to_id, table
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
def _prompt_for_bucket_configuration() -> bool:
|
|
490
|
-
"""Prompts the user whether to configure a bucket for multi-machine workflow and asset syncing."""
|
|
491
|
-
# Check if there's already a bucket configured
|
|
492
|
-
current_bucket_id = secrets_manager.get_secret("GT_CLOUD_BUCKET_ID", should_error_on_not_found=False)
|
|
493
|
-
|
|
494
|
-
if current_bucket_id:
|
|
495
|
-
explainer = f"""[bold cyan]Griptape Cloud Bucket Configuration[/bold cyan]
|
|
496
|
-
You currently have a bucket configured (ID: {current_bucket_id}).
|
|
497
|
-
|
|
498
|
-
Buckets are used for multi-machine workflow and asset syncing, allowing you to:
|
|
499
|
-
- Share workflows and assets across multiple devices
|
|
500
|
-
- Sync generated content between different Griptape Nodes instances
|
|
501
|
-
- Access your work from anywhere
|
|
502
|
-
|
|
503
|
-
Would you like to change your selected bucket or keep the current one?"""
|
|
504
|
-
prompt_text = "Change selected Griptape Cloud bucket?"
|
|
505
|
-
default_value = False
|
|
506
|
-
else:
|
|
507
|
-
explainer = """[bold cyan]Griptape Cloud Bucket Configuration[/bold cyan]
|
|
508
|
-
Would you like to configure a Griptape Cloud bucket?
|
|
509
|
-
Buckets are used for multi-machine workflow and asset syncing, allowing you to:
|
|
510
|
-
- Share workflows and assets across multiple devices
|
|
511
|
-
- Sync generated content between different Griptape Nodes instances
|
|
512
|
-
- Access your work from anywhere
|
|
513
|
-
|
|
514
|
-
If you do not intend to use Griptape Nodes to collaborate or revision control your workflows, you can skip this step.
|
|
515
|
-
|
|
516
|
-
You can always configure a bucket later by running the initialization process again."""
|
|
517
|
-
prompt_text = "Configure Griptape Cloud bucket?"
|
|
518
|
-
default_value = False
|
|
519
|
-
|
|
520
|
-
console.print(Panel(explainer, expand=False))
|
|
521
|
-
return Confirm.ask(prompt_text, default=default_value)
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
def _prompt_for_gtc_bucket_name(default_bucket_name: str | None = None) -> str:
|
|
525
|
-
"""Prompts the user for a GTC bucket and returns the bucket ID."""
|
|
526
|
-
explainer = """[bold cyan]Storage Backend Bucket Selection[/bold cyan]
|
|
527
|
-
Select a Griptape Cloud Bucket to use for storage. This is the location where Griptape Nodes will store your static files."""
|
|
528
|
-
console.print(Panel(explainer, expand=False))
|
|
529
|
-
|
|
530
|
-
# Fetch existing buckets
|
|
531
|
-
bucket_names, name_to_id, table = _get_griptape_cloud_buckets_and_display_table()
|
|
532
|
-
if default_bucket_name is None:
|
|
533
|
-
# Default to "default" bucket if it exists
|
|
534
|
-
default_bucket_name = "default" if "default" in name_to_id else None
|
|
535
|
-
|
|
536
|
-
# Display existing buckets if any
|
|
537
|
-
if len(bucket_names) > 0:
|
|
538
|
-
console.print(table)
|
|
539
|
-
console.print("\n[dim]You can enter an existing bucket by name, or enter a new name to create one.[/dim]")
|
|
540
|
-
|
|
541
|
-
while True:
|
|
542
|
-
# Prompt user for bucket name
|
|
543
|
-
selected_bucket_name = Prompt.ask(
|
|
544
|
-
"Enter bucket name",
|
|
545
|
-
default=default_bucket_name,
|
|
546
|
-
show_default=bool(default_bucket_name),
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
if selected_bucket_name:
|
|
550
|
-
# Check if it's an existing bucket
|
|
551
|
-
if selected_bucket_name in name_to_id:
|
|
552
|
-
return name_to_id[selected_bucket_name]
|
|
553
|
-
# It's a new bucket name, confirm creation
|
|
554
|
-
create_bucket = Confirm.ask(
|
|
555
|
-
f"Bucket '{selected_bucket_name}' doesn't exist. Create it?",
|
|
556
|
-
default=True,
|
|
557
|
-
)
|
|
558
|
-
if create_bucket:
|
|
559
|
-
return __create_new_bucket(selected_bucket_name)
|
|
560
|
-
# If they don't want to create, continue the loop to ask again
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
def _get_or_create_bucket_id(bucket_name: str) -> str:
|
|
564
|
-
"""Gets the bucket ID for an existing bucket or creates a new one.
|
|
565
|
-
|
|
566
|
-
Args:
|
|
567
|
-
bucket_name: Name of the bucket to lookup or create
|
|
568
|
-
|
|
569
|
-
Returns:
|
|
570
|
-
The bucket ID
|
|
571
|
-
"""
|
|
572
|
-
# Fetch existing buckets to check if bucket_name exists
|
|
573
|
-
_, name_to_id, _ = _get_griptape_cloud_buckets_and_display_table()
|
|
574
|
-
|
|
575
|
-
# Check if bucket already exists
|
|
576
|
-
if bucket_name in name_to_id:
|
|
577
|
-
return name_to_id[bucket_name]
|
|
578
|
-
|
|
579
|
-
# Create the bucket
|
|
580
|
-
return __create_new_bucket(bucket_name)
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
def _prompt_for_advanced_media_library(*, default_prompt_for_advanced_media_library: bool | None = None) -> bool:
|
|
584
|
-
"""Prompts the user whether to register the advanced media library."""
|
|
585
|
-
if default_prompt_for_advanced_media_library is None:
|
|
586
|
-
default_prompt_for_advanced_media_library = False
|
|
587
|
-
explainer = """[bold cyan]Advanced Media Library[/bold cyan]
|
|
588
|
-
Would you like to install the Griptape Nodes Advanced Media Library?
|
|
589
|
-
This node library makes advanced media generation and manipulation nodes available.
|
|
590
|
-
For example, nodes are available for Flux AI image upscaling, or to leverage CUDA for GPU-accelerated image generation.
|
|
591
|
-
CAVEAT: Installing this library requires additional dependencies to download and install, which can take several minutes.
|
|
592
|
-
The Griptape Nodes Advanced Media Library can be added later by following instructions here: [bold blue][link=https://docs.griptapenodes.com]https://docs.griptapenodes.com[/link][/bold blue].
|
|
593
|
-
"""
|
|
594
|
-
console.print(Panel(explainer, expand=False))
|
|
595
|
-
|
|
596
|
-
return Confirm.ask("Register Advanced Media Library?", default=default_prompt_for_advanced_media_library)
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
def __build_libraries_list(*, register_advanced_library: bool) -> list[str]:
|
|
600
|
-
"""Builds the list of libraries to register based on the advanced library setting."""
|
|
601
|
-
# TODO: https://github.com/griptape-ai/griptape-nodes/issues/929
|
|
602
|
-
libraries_key = "app_events.on_app_initialization_complete.libraries_to_register"
|
|
603
|
-
library_base_dir = Path(ENV_LIBRARIES_BASE_DIR)
|
|
604
|
-
|
|
605
|
-
current_libraries = config_manager.get_config_value(
|
|
606
|
-
libraries_key,
|
|
607
|
-
config_source="user_config",
|
|
608
|
-
default=config_manager.get_config_value(libraries_key, config_source="default_config", default=[]),
|
|
609
|
-
)
|
|
610
|
-
new_libraries = current_libraries.copy()
|
|
611
|
-
|
|
612
|
-
def _get_library_identifier(library_path: str) -> str:
|
|
613
|
-
"""Get the unique identifier for a library based on parent/filename."""
|
|
614
|
-
path = Path(library_path)
|
|
615
|
-
return f"{path.parent.name}/{path.name}"
|
|
616
|
-
|
|
617
|
-
# Create a set of current library identifiers for fast lookup
|
|
618
|
-
current_identifiers = {_get_library_identifier(lib) for lib in current_libraries}
|
|
619
|
-
|
|
620
|
-
default_library = str(library_base_dir / "griptape_nodes_library/griptape_nodes_library.json")
|
|
621
|
-
default_identifier = _get_library_identifier(default_library)
|
|
622
|
-
# If somehow the user removed the default library, add it back
|
|
623
|
-
if default_identifier not in current_identifiers:
|
|
624
|
-
new_libraries.append(default_library)
|
|
625
|
-
|
|
626
|
-
advanced_media_library = str(library_base_dir / "griptape_nodes_advanced_media_library/griptape_nodes_library.json")
|
|
627
|
-
advanced_identifier = _get_library_identifier(advanced_media_library)
|
|
628
|
-
if register_advanced_library:
|
|
629
|
-
# If the advanced media library is not registered, add it
|
|
630
|
-
if advanced_identifier not in current_identifiers:
|
|
631
|
-
new_libraries.append(advanced_media_library)
|
|
632
|
-
else:
|
|
633
|
-
# If the advanced media library is registered, remove it
|
|
634
|
-
libraries_to_remove = [lib for lib in new_libraries if _get_library_identifier(lib) == advanced_identifier]
|
|
635
|
-
for lib in libraries_to_remove:
|
|
636
|
-
new_libraries.remove(lib)
|
|
637
|
-
|
|
638
|
-
return new_libraries
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
def _get_latest_version(package: str, install_source: str) -> str:
|
|
642
|
-
"""Fetches the latest release tag from PyPI.
|
|
643
|
-
|
|
644
|
-
Args:
|
|
645
|
-
package: The name of the package to fetch the latest version for.
|
|
646
|
-
install_source: The source from which the package is installed (e.g., "pypi", "git", "file").
|
|
647
|
-
|
|
648
|
-
Returns:
|
|
649
|
-
str: Latest release tag (e.g., "v0.31.4")
|
|
650
|
-
"""
|
|
651
|
-
if install_source == "pypi":
|
|
652
|
-
return get_latest_version_pypi(package, PYPI_UPDATE_URL)
|
|
653
|
-
if install_source == "git":
|
|
654
|
-
return get_latest_version_git(package, GITHUB_UPDATE_URL, LATEST_TAG)
|
|
655
|
-
# If the package is installed from a file, just return the current version since the user is likely managing it manually
|
|
656
|
-
return get_current_version()
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
def _auto_update_self() -> None:
|
|
660
|
-
"""Automatically updates the script to the latest version if the user confirms."""
|
|
661
|
-
console.print("[bold green]Checking for updates...[/bold green]")
|
|
662
|
-
source, commit_id = get_install_source()
|
|
663
|
-
current_version = get_current_version()
|
|
664
|
-
latest_version = _get_latest_version(PACKAGE_NAME, source)
|
|
665
|
-
|
|
666
|
-
if source == "git" and commit_id is not None:
|
|
667
|
-
can_update = commit_id != latest_version
|
|
668
|
-
update_message = f"Your current engine version, {current_version} ({source} - {commit_id}), doesn't match the latest release, {latest_version}. Update now?"
|
|
669
|
-
else:
|
|
670
|
-
can_update = current_version < latest_version
|
|
671
|
-
update_message = f"Your current engine version, {current_version}, is behind the latest release, {latest_version}. Update now?"
|
|
672
|
-
|
|
673
|
-
if can_update:
|
|
674
|
-
update = Confirm.ask(update_message, default=True)
|
|
675
|
-
|
|
676
|
-
if update:
|
|
677
|
-
_update_self()
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
def _update_self() -> None:
|
|
681
|
-
"""Installs the latest release of the CLI *and* refreshes bundled libraries."""
|
|
682
|
-
console.print("[bold green]Starting updater...[/bold green]")
|
|
683
|
-
|
|
684
|
-
os_manager.replace_process([sys.executable, "-m", "griptape_nodes.updater"])
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
async def _sync_libraries() -> None:
|
|
688
|
-
"""Download and sync Griptape Nodes libraries, copying only directories from synced libraries."""
|
|
689
|
-
install_source, _ = get_install_source()
|
|
690
|
-
# Unless we're installed from PyPi, grab libraries from the 'latest' tag
|
|
691
|
-
if install_source == "pypi":
|
|
692
|
-
version = get_current_version()
|
|
693
|
-
else:
|
|
694
|
-
version = LATEST_TAG
|
|
695
|
-
|
|
696
|
-
console.print(f"[bold cyan]Fetching Griptape Nodes libraries ({version})...[/bold cyan]")
|
|
697
|
-
|
|
698
|
-
tar_url = NODES_TARBALL_URL.format(tag=version)
|
|
699
|
-
console.print(f"[green]Downloading from {tar_url}[/green]")
|
|
700
|
-
dest_nodes = Path(ENV_LIBRARIES_BASE_DIR)
|
|
701
|
-
|
|
702
|
-
with tempfile.TemporaryDirectory() as tmp:
|
|
703
|
-
tar_path = Path(tmp) / "nodes.tar.gz"
|
|
704
|
-
|
|
705
|
-
# Streaming download with a tiny progress bar
|
|
706
|
-
with httpx.stream("GET", tar_url, follow_redirects=True) as r, Progress() as progress:
|
|
707
|
-
task = progress.add_task("[green]Downloading...", total=int(r.headers.get("Content-Length", 0)))
|
|
708
|
-
progress.start()
|
|
709
|
-
try:
|
|
710
|
-
r.raise_for_status()
|
|
711
|
-
except httpx.HTTPStatusError as e:
|
|
712
|
-
console.print(f"[red]Error fetching libraries: {e}[/red]")
|
|
713
|
-
return
|
|
714
|
-
with tar_path.open("wb") as f:
|
|
715
|
-
for chunk in r.iter_bytes():
|
|
716
|
-
f.write(chunk)
|
|
717
|
-
progress.update(task, advance=len(chunk))
|
|
718
|
-
|
|
719
|
-
console.print("[green]Extracting...[/green]")
|
|
720
|
-
# Extract and locate extracted directory
|
|
721
|
-
with tarfile.open(tar_path) as tar:
|
|
722
|
-
tar.extractall(tmp, filter="data")
|
|
723
|
-
|
|
724
|
-
extracted_root = next(Path(tmp).glob("griptape-nodes-*"))
|
|
725
|
-
extracted_libs = extracted_root / "libraries"
|
|
726
|
-
|
|
727
|
-
# Copy directories from synced libraries without removing existing content
|
|
728
|
-
console.print(f"[green]Syncing libraries to {dest_nodes.resolve()}...[/green]")
|
|
729
|
-
dest_nodes.mkdir(parents=True, exist_ok=True)
|
|
730
|
-
for library_dir in extracted_libs.iterdir():
|
|
731
|
-
if library_dir.is_dir():
|
|
732
|
-
dest_library_dir = dest_nodes / library_dir.name
|
|
733
|
-
if dest_library_dir.exists():
|
|
734
|
-
shutil.rmtree(dest_library_dir)
|
|
735
|
-
shutil.copytree(library_dir, dest_library_dir)
|
|
736
|
-
console.print(f"[green]Synced library: {library_dir.name}[/green]")
|
|
737
|
-
|
|
738
|
-
# Re-initialize all libraries from config
|
|
739
|
-
console.print("[bold cyan]Initializing libraries...[/bold cyan]")
|
|
740
|
-
try:
|
|
741
|
-
await GriptapeNodes.LibraryManager().load_all_libraries_from_config()
|
|
742
|
-
console.print("[bold green]Libraries Initialized successfully.[/bold green]")
|
|
743
|
-
except Exception as e:
|
|
744
|
-
console.print(f"[red]Error initializing libraries: {e}[/red]")
|
|
745
|
-
|
|
746
|
-
console.print("[bold green]Libraries synced.[/bold green]")
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
def _print_current_version() -> None:
|
|
750
|
-
"""Prints the current version of the script."""
|
|
751
|
-
version_string = get_complete_version_string()
|
|
752
|
-
console.print(f"[bold green]{version_string}[/bold green]")
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
def _print_user_config(config_path: str | None = None) -> None:
|
|
756
|
-
"""Prints the user configuration from the config file.
|
|
757
|
-
|
|
758
|
-
Args:
|
|
759
|
-
config_path: Optional path to specific config value. If None, prints entire config.
|
|
760
|
-
"""
|
|
761
|
-
if config_path is None:
|
|
762
|
-
config = config_manager.merged_config
|
|
763
|
-
sys.stdout.write(json.dumps(config, indent=2))
|
|
764
|
-
else:
|
|
765
|
-
try:
|
|
766
|
-
value = config_manager.get_config_value(config_path)
|
|
767
|
-
if isinstance(value, (dict, list)):
|
|
768
|
-
sys.stdout.write(json.dumps(value, indent=2))
|
|
769
|
-
else:
|
|
770
|
-
sys.stdout.write(str(value))
|
|
771
|
-
except (KeyError, AttributeError, ValueError):
|
|
772
|
-
console.print(f"[bold red]Config path '{config_path}' not found[/bold red]")
|
|
773
|
-
sys.exit(1)
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
def _list_user_configs() -> None:
|
|
777
|
-
"""Lists user configuration files in ascending precedence."""
|
|
778
|
-
num_config_files = len(config_manager.config_files)
|
|
779
|
-
console.print(
|
|
780
|
-
f"[bold]User Configuration Files (lowest precedence (1.) ⟶ highest precedence ({num_config_files}.)):[/bold]"
|
|
781
|
-
)
|
|
782
|
-
for idx, config in enumerate(config_manager.config_files):
|
|
783
|
-
console.print(f"[green]{idx + 1}. {config}[/green]")
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
def _reset_user_config() -> None:
|
|
787
|
-
"""Resets the user configuration to the default values."""
|
|
788
|
-
console.print("[bold]Resetting user configuration to default values...[/bold]")
|
|
789
|
-
config_manager.reset_user_config()
|
|
790
|
-
console.print("[bold green]User configuration reset complete![/bold green]")
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
def _uninstall_self() -> None:
|
|
794
|
-
"""Uninstalls itself by removing config/data directories and the executable."""
|
|
795
|
-
console.print("[bold]Uninstalling Griptape Nodes...[/bold]")
|
|
796
|
-
|
|
797
|
-
# Remove config and data directories
|
|
798
|
-
console.print("[bold]Removing config and data directories...[/bold]")
|
|
799
|
-
dirs = [(CONFIG_DIR, "Config Dir"), (DATA_DIR, "Data Dir")]
|
|
800
|
-
caveats = []
|
|
801
|
-
for dir_path, dir_name in dirs:
|
|
802
|
-
if dir_path.exists():
|
|
803
|
-
console.print(f"[bold]Removing {dir_name} '{dir_path}'...[/bold]")
|
|
804
|
-
try:
|
|
805
|
-
shutil.rmtree(dir_path)
|
|
806
|
-
except OSError as exc:
|
|
807
|
-
console.print(f"[red]Error removing {dir_name} '{dir_path}': {exc}[/red]")
|
|
808
|
-
caveats.append(
|
|
809
|
-
f"- [red]Error removing {dir_name} '{dir_path}'. You may want remove this directory manually.[/red]"
|
|
810
|
-
)
|
|
811
|
-
else:
|
|
812
|
-
console.print(f"[yellow]{dir_name} '{dir_path}' does not exist; skipping.[/yellow]")
|
|
813
|
-
|
|
814
|
-
# Handle any remaining config files not removed by design
|
|
815
|
-
remaining_config_files = config_manager.config_files
|
|
816
|
-
if remaining_config_files:
|
|
817
|
-
caveats.append("- Some config files were intentionally not removed:")
|
|
818
|
-
caveats.extend(f"\t[yellow]- {file}[/yellow]" for file in remaining_config_files)
|
|
819
|
-
|
|
820
|
-
# If there were any caveats to the uninstallation process, print them
|
|
821
|
-
if caveats:
|
|
822
|
-
console.print("[bold]Caveats:[/bold]")
|
|
823
|
-
for line in caveats:
|
|
824
|
-
console.print(line)
|
|
825
|
-
|
|
826
|
-
# Remove the executable
|
|
827
|
-
console.print("[bold]Removing the executable...[/bold]")
|
|
828
|
-
console.print("[bold yellow]When done, press Enter to exit.[/bold yellow]")
|
|
829
|
-
|
|
830
|
-
# Remove the tool using UV
|
|
831
|
-
uv_path = find_uv_bin()
|
|
832
|
-
os_manager.replace_process([uv_path, "tool", "uninstall", "griptape-nodes"])
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
def _parse_key_value_pairs(pairs: list[str] | None) -> dict[str, Any] | None:
|
|
836
|
-
"""Parse key=value pairs from a list of strings.
|
|
837
|
-
|
|
838
|
-
Args:
|
|
839
|
-
pairs: List of strings in the format "key=value"
|
|
840
|
-
|
|
841
|
-
Returns:
|
|
842
|
-
Dictionary of key-value pairs, or None if no pairs provided
|
|
843
|
-
"""
|
|
844
|
-
if not pairs:
|
|
845
|
-
return None
|
|
846
|
-
|
|
847
|
-
result = {}
|
|
848
|
-
for pair in pairs:
|
|
849
|
-
if "=" not in pair:
|
|
850
|
-
console.print(f"[bold red]Invalid key=value pair: {pair}. Expected format: key=value[/bold red]")
|
|
851
|
-
continue
|
|
852
|
-
# Split only on the first = to handle values that contain =
|
|
853
|
-
key, value = pair.split("=", 1)
|
|
854
|
-
key = key.strip()
|
|
855
|
-
value = value.strip()
|
|
856
|
-
|
|
857
|
-
if not key:
|
|
858
|
-
console.print(f"[bold red]Empty key in pair: {pair}[/bold red]")
|
|
859
|
-
continue
|
|
860
|
-
|
|
861
|
-
# Try to parse value as JSON, fall back to string if it fails
|
|
862
|
-
try:
|
|
863
|
-
parsed_value = json.loads(value)
|
|
864
|
-
result[key] = parsed_value
|
|
865
|
-
except (json.JSONDecodeError, ValueError):
|
|
866
|
-
# If JSON parsing fails, use the original string value
|
|
867
|
-
result[key] = value
|
|
868
|
-
|
|
869
|
-
return result if result else None
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
def _process_args(args: argparse.Namespace) -> None: # noqa: C901, PLR0912
|
|
873
|
-
if args.command == "init":
|
|
874
|
-
config_values = _parse_key_value_pairs(getattr(args, "config", None))
|
|
875
|
-
secret_values = _parse_key_value_pairs(getattr(args, "secret", None))
|
|
876
|
-
|
|
877
|
-
_run_init(
|
|
878
|
-
InitConfig(
|
|
879
|
-
interactive=not args.no_interactive,
|
|
880
|
-
workspace_directory=args.workspace_directory,
|
|
881
|
-
api_key=args.api_key,
|
|
882
|
-
storage_backend=args.storage_backend,
|
|
883
|
-
register_advanced_library=args.register_advanced_library,
|
|
884
|
-
config_values=config_values,
|
|
885
|
-
secret_values=secret_values,
|
|
886
|
-
libraries_sync=args.libraries_sync,
|
|
887
|
-
bucket_name=args.bucket_name,
|
|
888
|
-
)
|
|
889
|
-
)
|
|
890
|
-
elif args.command == "engine":
|
|
891
|
-
_start_engine(no_update=args.no_update)
|
|
892
|
-
elif args.command == "config":
|
|
893
|
-
if args.subcommand == "list":
|
|
894
|
-
_list_user_configs()
|
|
895
|
-
elif args.subcommand == "reset":
|
|
896
|
-
_reset_user_config()
|
|
897
|
-
elif args.subcommand == "show":
|
|
898
|
-
_print_user_config(args.config_path)
|
|
899
|
-
elif args.command == "self":
|
|
900
|
-
if args.subcommand == "update":
|
|
901
|
-
_update_self()
|
|
902
|
-
elif args.subcommand == "uninstall":
|
|
903
|
-
_uninstall_self()
|
|
904
|
-
elif args.subcommand == "version":
|
|
905
|
-
_print_current_version()
|
|
906
|
-
elif args.command == "libraries":
|
|
907
|
-
if args.subcommand == "sync":
|
|
908
|
-
asyncio.run(_sync_libraries())
|
|
909
|
-
else:
|
|
910
|
-
msg = f"Unknown command: {args.command}"
|
|
911
|
-
raise ValueError(msg)
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
def __init_system_config() -> None:
|
|
915
|
-
"""Initializes the system config directory if it doesn't exist."""
|
|
916
|
-
if not CONFIG_DIR.exists():
|
|
917
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
918
|
-
|
|
919
|
-
files_to_create = [
|
|
920
|
-
(ENV_FILE, ""),
|
|
921
|
-
(CONFIG_FILE, "{}"),
|
|
922
|
-
]
|
|
923
|
-
|
|
924
|
-
for file_name in files_to_create:
|
|
925
|
-
file_path = CONFIG_DIR / file_name[0]
|
|
926
|
-
if not file_path.exists():
|
|
927
|
-
with Path.open(file_path, "w", encoding="utf-8") as file:
|
|
928
|
-
file.write(file_name[1])
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
def __create_new_bucket(bucket_name: str) -> str:
|
|
932
|
-
"""Create a new Griptape Cloud bucket.
|
|
933
|
-
|
|
934
|
-
Args:
|
|
935
|
-
bucket_name: Name for the bucket
|
|
936
|
-
|
|
937
|
-
Returns:
|
|
938
|
-
The bucket ID of the created bucket.
|
|
939
|
-
"""
|
|
940
|
-
api_key = secrets_manager.get_secret("GT_CLOUD_API_KEY")
|
|
941
|
-
if api_key is None:
|
|
942
|
-
msg = "GT_CLOUD_API_KEY secret is required to create a bucket."
|
|
943
|
-
raise ValueError(msg)
|
|
15
|
+
# Import and run the new CLI
|
|
16
|
+
from griptape_nodes.cli.main import app
|
|
944
17
|
|
|
945
|
-
|
|
946
|
-
bucket_id = GriptapeCloudStorageDriver.create_bucket(
|
|
947
|
-
bucket_name=bucket_name, base_url=GT_CLOUD_BASE_URL, api_key=api_key
|
|
948
|
-
)
|
|
949
|
-
except Exception as e:
|
|
950
|
-
console.print(f"[bold red]Failed to create bucket: {e}[/bold red]")
|
|
951
|
-
raise
|
|
952
|
-
else:
|
|
953
|
-
console.print(f"[bold green]Successfully created bucket '{bucket_name}' with ID: {bucket_id}[/bold green]")
|
|
954
|
-
return bucket_id
|
|
18
|
+
app()
|
|
955
19
|
|
|
956
20
|
|
|
957
|
-
|
|
958
|
-
main()
|
|
21
|
+
__all__ = ["main"]
|