griptape-nodes 0.60.4__py3-none-any.whl → 0.62.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.
Files changed (47) hide show
  1. griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +0 -1
  2. griptape_nodes/common/macro_parser/__init__.py +16 -1
  3. griptape_nodes/common/macro_parser/core.py +19 -7
  4. griptape_nodes/common/macro_parser/exceptions.py +99 -0
  5. griptape_nodes/common/macro_parser/formats.py +13 -4
  6. griptape_nodes/common/macro_parser/matching.py +5 -2
  7. griptape_nodes/common/macro_parser/parsing.py +48 -8
  8. griptape_nodes/common/macro_parser/resolution.py +23 -5
  9. griptape_nodes/common/project_templates/__init__.py +49 -0
  10. griptape_nodes/common/project_templates/default_project_template.py +87 -0
  11. griptape_nodes/common/project_templates/defaults/README.md +36 -0
  12. griptape_nodes/common/project_templates/directory.py +67 -0
  13. griptape_nodes/common/project_templates/loader.py +342 -0
  14. griptape_nodes/common/project_templates/project.py +252 -0
  15. griptape_nodes/common/project_templates/situation.py +143 -0
  16. griptape_nodes/common/project_templates/validation.py +140 -0
  17. griptape_nodes/exe_types/core_types.py +36 -3
  18. griptape_nodes/exe_types/node_types.py +4 -2
  19. griptape_nodes/exe_types/param_components/progress_bar_component.py +57 -0
  20. griptape_nodes/exe_types/param_types/parameter_audio.py +243 -0
  21. griptape_nodes/exe_types/param_types/parameter_image.py +243 -0
  22. griptape_nodes/exe_types/param_types/parameter_three_d.py +215 -0
  23. griptape_nodes/exe_types/param_types/parameter_video.py +243 -0
  24. griptape_nodes/node_library/workflow_registry.py +1 -1
  25. griptape_nodes/retained_mode/events/execution_events.py +41 -0
  26. griptape_nodes/retained_mode/events/node_events.py +90 -1
  27. griptape_nodes/retained_mode/events/os_events.py +108 -0
  28. griptape_nodes/retained_mode/events/parameter_events.py +1 -1
  29. griptape_nodes/retained_mode/events/project_events.py +528 -0
  30. griptape_nodes/retained_mode/events/workflow_events.py +19 -1
  31. griptape_nodes/retained_mode/griptape_nodes.py +9 -1
  32. griptape_nodes/retained_mode/managers/agent_manager.py +18 -24
  33. griptape_nodes/retained_mode/managers/event_manager.py +6 -9
  34. griptape_nodes/retained_mode/managers/flow_manager.py +63 -0
  35. griptape_nodes/retained_mode/managers/library_manager.py +55 -42
  36. griptape_nodes/retained_mode/managers/mcp_manager.py +14 -6
  37. griptape_nodes/retained_mode/managers/node_manager.py +232 -0
  38. griptape_nodes/retained_mode/managers/os_manager.py +399 -6
  39. griptape_nodes/retained_mode/managers/project_manager.py +1067 -0
  40. griptape_nodes/retained_mode/managers/settings.py +6 -0
  41. griptape_nodes/retained_mode/managers/sync_manager.py +4 -1
  42. griptape_nodes/retained_mode/managers/workflow_manager.py +8 -79
  43. griptape_nodes/traits/button.py +19 -0
  44. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/METADATA +5 -3
  45. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/RECORD +47 -32
  46. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/WHEEL +1 -1
  47. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,528 @@
1
+ """Events for project template management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from enum import StrEnum
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from griptape_nodes.retained_mode.events.base_events import (
10
+ RequestPayload,
11
+ ResultPayloadFailure,
12
+ ResultPayloadSuccess,
13
+ WorkflowNotAlteredMixin,
14
+ )
15
+ from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
16
+
17
+ if TYPE_CHECKING:
18
+ from pathlib import Path
19
+
20
+ from griptape_nodes.common.macro_parser import MacroMatchFailure, ParsedMacro, VariableInfo
21
+ from griptape_nodes.common.project_templates import ProjectTemplate, ProjectValidationInfo, SituationTemplate
22
+ from griptape_nodes.retained_mode.managers.project_manager import ProjectID, ProjectInfo
23
+
24
+ # Type alias for macro variable dictionaries (used by ParsedMacro)
25
+ MacroVariables = dict[str, str | int]
26
+
27
+
28
+ class PathResolutionFailureReason(StrEnum):
29
+ """Reason why path resolution from macro failed."""
30
+
31
+ MISSING_REQUIRED_VARIABLES = "MISSING_REQUIRED_VARIABLES"
32
+ MACRO_RESOLUTION_ERROR = "MACRO_RESOLUTION_ERROR"
33
+ DIRECTORY_OVERRIDE_ATTEMPTED = "DIRECTORY_OVERRIDE_ATTEMPTED"
34
+
35
+
36
+ @dataclass
37
+ @PayloadRegistry.register
38
+ class LoadProjectTemplateRequest(RequestPayload):
39
+ """Load user's project.yml and merge with system defaults.
40
+
41
+ Use when: User opens a workspace, user creates new project, user modifies project.yml.
42
+
43
+ Args:
44
+ project_path: Path to the project.yml file to load
45
+
46
+ Results: LoadProjectTemplateResultSuccess | LoadProjectTemplateResultFailure
47
+ """
48
+
49
+ project_path: Path
50
+
51
+
52
+ @dataclass
53
+ @PayloadRegistry.register
54
+ class LoadProjectTemplateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
55
+ """Project template loaded successfully.
56
+
57
+ Args:
58
+ project_id: The identifier for the loaded project
59
+ template: The merged ProjectTemplate (system defaults + user customizations)
60
+ validation: Validation info with status and any problems encountered
61
+ """
62
+
63
+ project_id: ProjectID
64
+ template: ProjectTemplate
65
+ validation: ProjectValidationInfo
66
+
67
+
68
+ @dataclass
69
+ @PayloadRegistry.register
70
+ class LoadProjectTemplateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
71
+ """Project template loading failed.
72
+
73
+ Args:
74
+ validation: Validation info with error details
75
+ """
76
+
77
+ validation: ProjectValidationInfo
78
+
79
+
80
+ @dataclass
81
+ @PayloadRegistry.register
82
+ class GetProjectTemplateRequest(RequestPayload):
83
+ """Get cached project template for a project ID.
84
+
85
+ Use when: Querying current project configuration, checking validation status.
86
+
87
+ Args:
88
+ project_id: Identifier of the project
89
+
90
+ Results: GetProjectTemplateResultSuccess | GetProjectTemplateResultFailure
91
+ """
92
+
93
+ project_id: ProjectID
94
+
95
+
96
+ @dataclass
97
+ @PayloadRegistry.register
98
+ class GetProjectTemplateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
99
+ """Project template retrieved from cache.
100
+
101
+ Args:
102
+ template: The successfully loaded ProjectTemplate
103
+ validation: Validation info for the template
104
+ """
105
+
106
+ template: ProjectTemplate
107
+ validation: ProjectValidationInfo
108
+
109
+
110
+ @dataclass
111
+ @PayloadRegistry.register
112
+ class GetProjectTemplateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
113
+ """Project template retrieval failed (not loaded yet)."""
114
+
115
+
116
+ @dataclass
117
+ class ProjectTemplateInfo:
118
+ """Information about a loaded or failed project template."""
119
+
120
+ project_id: ProjectID
121
+ validation: ProjectValidationInfo
122
+
123
+
124
+ @dataclass
125
+ @PayloadRegistry.register
126
+ class ListProjectTemplatesRequest(RequestPayload):
127
+ """List all project templates that have been loaded or attempted to load.
128
+
129
+ Use when: Displaying available projects, checking which projects are loaded.
130
+
131
+ Args:
132
+ include_system_builtins: Whether to include system builtin templates like SYSTEM_DEFAULTS_KEY
133
+
134
+ Results: ListProjectTemplatesResultSuccess
135
+ """
136
+
137
+ include_system_builtins: bool = False
138
+
139
+
140
+ @dataclass
141
+ @PayloadRegistry.register
142
+ class ListProjectTemplatesResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
143
+ """List of all project templates retrieved.
144
+
145
+ Args:
146
+ successfully_loaded: List of templates that loaded successfully
147
+ failed_to_load: List of templates that failed to load with validation errors
148
+ """
149
+
150
+ successfully_loaded: list[ProjectTemplateInfo]
151
+ failed_to_load: list[ProjectTemplateInfo]
152
+
153
+
154
+ @dataclass
155
+ @PayloadRegistry.register
156
+ class GetSituationRequest(RequestPayload):
157
+ """Get the full situation template for a specific situation.
158
+
159
+ Returns the complete SituationTemplate including macro and policy.
160
+
161
+ Use when: Need situation macro and/or policy for file operations.
162
+ Uses the current project for context.
163
+
164
+ Args:
165
+ situation_name: Name of the situation template (e.g., "save_node_output")
166
+
167
+ Results: GetSituationResultSuccess | GetSituationResultFailure
168
+ """
169
+
170
+ situation_name: str
171
+
172
+
173
+ @dataclass
174
+ @PayloadRegistry.register
175
+ class GetSituationResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
176
+ """Situation template retrieved successfully.
177
+
178
+ Args:
179
+ situation: The complete situation template including macro and policy.
180
+ Access via situation.macro, situation.policy.create_dirs, etc.
181
+ """
182
+
183
+ situation: SituationTemplate
184
+
185
+
186
+ @dataclass
187
+ @PayloadRegistry.register
188
+ class GetSituationResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
189
+ """Situation template retrieval failed (situation not found or template not loaded)."""
190
+
191
+
192
+ @dataclass
193
+ @PayloadRegistry.register
194
+ class GetPathForMacroRequest(RequestPayload):
195
+ """Resolve ANY macro schema with variables to produce final file path.
196
+
197
+ Use when: Resolving paths, saving files. Works with any macro string, not tied to situations.
198
+
199
+ Uses the current project for context. Caller must parse the macro string
200
+ into a ParsedMacro before creating this request.
201
+
202
+ Args:
203
+ parsed_macro: The parsed macro to resolve
204
+ variables: Variable values for macro substitution (e.g., {"file_name": "output", "file_ext": "png"})
205
+
206
+ Results: GetPathForMacroResultSuccess | GetPathForMacroResultFailure
207
+ """
208
+
209
+ parsed_macro: ParsedMacro
210
+ variables: MacroVariables
211
+
212
+
213
+ @dataclass
214
+ @PayloadRegistry.register
215
+ class GetPathForMacroResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
216
+ """Path resolved successfully from macro.
217
+
218
+ Args:
219
+ resolved_path: The relative project path after macro substitution (e.g., "outputs/file.png")
220
+ absolute_path: The absolute filesystem path (e.g., "/workspace/outputs/file.png")
221
+ """
222
+
223
+ resolved_path: Path
224
+ absolute_path: Path
225
+
226
+
227
+ @dataclass
228
+ @PayloadRegistry.register
229
+ class GetPathForMacroResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
230
+ """Path resolution failed.
231
+
232
+ Args:
233
+ failure_reason: Specific reason for failure
234
+ missing_variables: List of required variable names that were not provided (for MISSING_REQUIRED_VARIABLES)
235
+ conflicting_variables: List of variables that conflict with directory names (for DIRECTORY_OVERRIDE_ATTEMPTED)
236
+ """
237
+
238
+ failure_reason: PathResolutionFailureReason
239
+ missing_variables: set[str] | None = None
240
+ conflicting_variables: set[str] | None = None
241
+
242
+
243
+ @dataclass
244
+ @PayloadRegistry.register
245
+ class SetCurrentProjectRequest(RequestPayload):
246
+ """Set which project user has currently selected.
247
+
248
+ Use when: User switches between projects, opens a new workspace.
249
+
250
+ Args:
251
+ project_id: Identifier of the project to set as current (None to clear)
252
+
253
+ Results: SetCurrentProjectResultSuccess
254
+ """
255
+
256
+ project_id: ProjectID | None
257
+
258
+
259
+ @dataclass
260
+ @PayloadRegistry.register
261
+ class SetCurrentProjectResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
262
+ """Current project set successfully."""
263
+
264
+
265
+ @dataclass
266
+ @PayloadRegistry.register
267
+ class GetCurrentProjectRequest(RequestPayload):
268
+ """Get the currently selected project path.
269
+
270
+ Use when: Need to know which project user is working with.
271
+
272
+ Results: GetCurrentProjectResultSuccess | GetCurrentProjectResultFailure
273
+ """
274
+
275
+
276
+ @dataclass
277
+ @PayloadRegistry.register
278
+ class GetCurrentProjectResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
279
+ """Current project retrieved.
280
+
281
+ Args:
282
+ project_info: Complete information about the current project
283
+ """
284
+
285
+ project_info: ProjectInfo
286
+
287
+
288
+ @dataclass
289
+ @PayloadRegistry.register
290
+ class GetCurrentProjectResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
291
+ """No current project is set."""
292
+
293
+
294
+ @dataclass
295
+ @PayloadRegistry.register
296
+ class SaveProjectTemplateRequest(RequestPayload):
297
+ """Save user customizations to project.yml file.
298
+
299
+ Use when: User modifies project configuration, exports template.
300
+
301
+ Args:
302
+ project_path: Path where project.yml should be saved
303
+ template_data: Dict representation of the template to save
304
+
305
+ Results: SaveProjectTemplateResultSuccess | SaveProjectTemplateResultFailure
306
+ """
307
+
308
+ project_path: Path
309
+ template_data: dict[str, Any]
310
+
311
+
312
+ @dataclass
313
+ @PayloadRegistry.register
314
+ class SaveProjectTemplateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
315
+ """Project template saved successfully."""
316
+
317
+
318
+ @dataclass
319
+ @PayloadRegistry.register
320
+ class SaveProjectTemplateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
321
+ """Project template save failed.
322
+
323
+ Common causes:
324
+ - Permission denied
325
+ - Invalid path
326
+ - Disk full
327
+ """
328
+
329
+
330
+ @dataclass
331
+ @PayloadRegistry.register
332
+ class AttemptMatchPathAgainstMacroRequest(RequestPayload):
333
+ """Attempt to match a path against a macro schema and extract variables.
334
+
335
+ Use when: Validating paths, extracting info from file paths,
336
+ identifying which schema produced a file.
337
+
338
+ Uses the current project for context. Caller must parse the macro string
339
+ into a ParsedMacro before creating this request.
340
+
341
+ Pattern non-matches are returned as success with match_failure populated.
342
+ Only true system errors (missing SecretsManager, etc.) return failure.
343
+
344
+ Args:
345
+ parsed_macro: Parsed macro template to match against
346
+ file_path: Path string to test
347
+ known_variables: Variables we already know
348
+
349
+ Results: AttemptMatchPathAgainstMacroResultSuccess | AttemptMatchPathAgainstMacroResultFailure
350
+ """
351
+
352
+ parsed_macro: ParsedMacro
353
+ file_path: str
354
+ known_variables: MacroVariables
355
+
356
+
357
+ @dataclass
358
+ @PayloadRegistry.register
359
+ class AttemptMatchPathAgainstMacroResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
360
+ """Attempt completed (match succeeded or pattern didn't match).
361
+
362
+ Check match_failure to determine outcome:
363
+ - match_failure is None: Pattern matched, extracted_variables contains results
364
+ - match_failure is not None: Pattern didn't match (normal case, not an error)
365
+ """
366
+
367
+ extracted_variables: MacroVariables | None
368
+ match_failure: MacroMatchFailure | None
369
+
370
+
371
+ @dataclass
372
+ @PayloadRegistry.register
373
+ class AttemptMatchPathAgainstMacroResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
374
+ """System error occurred (missing SecretsManager, invalid configuration, etc.)."""
375
+
376
+
377
+ @dataclass
378
+ @PayloadRegistry.register
379
+ class GetStateForMacroRequest(RequestPayload):
380
+ """Analyze a macro and return comprehensive state information.
381
+
382
+ Use when: Building UI forms, real-time validation, checking if resolution
383
+ would succeed before actually resolving.
384
+
385
+ Uses the current project for context. Caller must parse the macro string
386
+ into a ParsedMacro before creating this request.
387
+
388
+ Args:
389
+ parsed_macro: The parsed macro to analyze
390
+ variables: Currently provided variable values
391
+
392
+ Results: GetStateForMacroResultSuccess | GetStateForMacroResultFailure
393
+ """
394
+
395
+ parsed_macro: ParsedMacro
396
+ variables: MacroVariables
397
+
398
+
399
+ @dataclass
400
+ @PayloadRegistry.register
401
+ class GetStateForMacroResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
402
+ """Macro state analysis completed successfully.
403
+
404
+ Args:
405
+ all_variables: All variables found in the macro
406
+ satisfied_variables: Variables that have values (from user, directories, or builtins)
407
+ missing_required_variables: Required variables that are missing values
408
+ conflicting_variables: Variables that conflict (e.g., user overriding builtin with different value)
409
+ can_resolve: Whether the macro can be fully resolved (no missing required vars, no conflicts)
410
+ """
411
+
412
+ all_variables: set[VariableInfo]
413
+ satisfied_variables: set[str]
414
+ missing_required_variables: set[str]
415
+ conflicting_variables: set[str]
416
+ can_resolve: bool
417
+
418
+
419
+ @dataclass
420
+ @PayloadRegistry.register
421
+ class GetStateForMacroResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
422
+ """Macro state analysis failed.
423
+
424
+ Failure occurs when:
425
+ - No current project is set
426
+ - Current project template is not loaded
427
+ - A builtin variable cannot be resolved (RuntimeError or NotImplementedError)
428
+ """
429
+
430
+
431
+ @dataclass
432
+ @PayloadRegistry.register
433
+ class AttemptMapAbsolutePathToProjectRequest(RequestPayload):
434
+ """Find out if an absolute path exists anywhere within a Project directory.
435
+
436
+ Use when: User selects or types an absolute path via FilePicker and you need to know:
437
+ 1. Is this path inside any project directory?
438
+ 2. If yes, what's the macro form (e.g., {outputs}/file.png)?
439
+
440
+ This enables automatic conversion of absolute paths to portable macro form for workflow portability.
441
+
442
+ Uses longest prefix matching to find the most specific directory match.
443
+ Returns Success with mapped_path if inside project, or Success with None if outside.
444
+ Returns Failure if operation cannot be performed (no project loaded, secrets unavailable).
445
+
446
+ Args:
447
+ absolute_path: The absolute filesystem path to check
448
+
449
+ Results: AttemptMapAbsolutePathToProjectResultSuccess | AttemptMapAbsolutePathToProjectResultFailure
450
+
451
+ Examples:
452
+ Path inside project directory:
453
+ Request: absolute_path = /Users/james/project/outputs/renders/image.png
454
+ Result: mapped_path = "{outputs}/renders/image.png"
455
+
456
+ Path outside project:
457
+ Request: absolute_path = /Users/james/Downloads/image.png
458
+ Result: mapped_path = None
459
+
460
+ Path at directory root:
461
+ Request: absolute_path = /Users/james/project/outputs
462
+ Result: mapped_path = "{outputs}"
463
+ """
464
+
465
+ absolute_path: Path
466
+
467
+
468
+ @dataclass
469
+ @PayloadRegistry.register
470
+ class AttemptMapAbsolutePathToProjectResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
471
+ """Path check completed successfully.
472
+
473
+ Success means the check was performed (not necessarily that a match was found).
474
+ - mapped_path is NOT None: Path is inside a project directory (macro form returned)
475
+ - mapped_path is None: Path is outside all project directories (valid answer)
476
+
477
+ Args:
478
+ mapped_path: The macro form if path is inside a project directory (e.g., "{outputs}/file.png"),
479
+ or None if path is outside all project directories
480
+
481
+ Examples:
482
+ Path inside project:
483
+ mapped_path = "{outputs}/renders/image.png"
484
+ result_details = "Successfully mapped absolute path to '{outputs}/renders/image.png'"
485
+
486
+ Path outside project:
487
+ mapped_path = None
488
+ result_details = "Attempted to map absolute path '/Users/james/Downloads/image.png'. Path is outside all project directories"
489
+ """
490
+
491
+ mapped_path: str | None
492
+
493
+
494
+ @dataclass
495
+ @PayloadRegistry.register
496
+ class AttemptMapAbsolutePathToProjectResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
497
+ """Path mapping attempt failed.
498
+
499
+ Returned when the operation cannot be performed (no current project, secrets manager unavailable).
500
+ This is distinct from "path is outside project" which returns Success with None values.
501
+
502
+ Examples:
503
+ No current project:
504
+ result_details = "Attempted to map absolute path. Failed because no current project is set"
505
+
506
+ Secrets manager unavailable:
507
+ result_details = "Attempted to map absolute path. Failed because SecretsManager not available"
508
+ """
509
+
510
+
511
+ @dataclass
512
+ @PayloadRegistry.register
513
+ class GetAllSituationsForProjectRequest(RequestPayload):
514
+ """Get all situation names and schemas from current project template."""
515
+
516
+
517
+ @dataclass
518
+ @PayloadRegistry.register
519
+ class GetAllSituationsForProjectResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
520
+ """Success result containing all situations."""
521
+
522
+ situations: dict[str, str]
523
+
524
+
525
+ @dataclass
526
+ @PayloadRegistry.register
527
+ class GetAllSituationsForProjectResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
528
+ """Failure result when cannot get situations."""
@@ -10,6 +10,7 @@ from griptape_nodes.retained_mode.events.base_events import (
10
10
  WorkflowAlteredMixin,
11
11
  WorkflowNotAlteredMixin,
12
12
  )
13
+ from griptape_nodes.retained_mode.events.execution_events import ExecutionPayload
13
14
  from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
14
15
 
15
16
  if TYPE_CHECKING:
@@ -388,9 +389,11 @@ class PublishWorkflowRequest(RequestPayload):
388
389
 
389
390
  workflow_name: str
390
391
  publisher_name: str
391
- execute_on_publish: bool = False
392
+ # This can be removed after GUI release
393
+ execute_on_publish: bool | None = None
392
394
  published_workflow_file_name: str | None = None
393
395
  pickle_control_flow_result: bool = False
396
+ metadata: dict | None = None
394
397
 
395
398
 
396
399
  @dataclass
@@ -403,6 +406,7 @@ class PublishWorkflowResultSuccess(ResultPayloadSuccess):
403
406
  """
404
407
 
405
408
  published_workflow_file_path: str
409
+ metadata: dict | None = None
406
410
 
407
411
 
408
412
  @dataclass
@@ -411,6 +415,20 @@ class PublishWorkflowResultFailure(ResultPayloadFailure):
411
415
  """Workflow publish failed. Common causes: workflow not found, publish error, file system error."""
412
416
 
413
417
 
418
+ @dataclass
419
+ @PayloadRegistry.register
420
+ class PublishWorkflowProgressEvent(ExecutionPayload):
421
+ """Event emitted to indicate progress during workflow publishing.
422
+
423
+ Args:
424
+ progress: Progress percentage (0-100)
425
+ message: Optional progress message
426
+ """
427
+
428
+ progress: float
429
+ message: str | None = None
430
+
431
+
414
432
  @dataclass
415
433
  @PayloadRegistry.register
416
434
  class BranchWorkflowRequest(RequestPayload):
@@ -51,6 +51,7 @@ if TYPE_CHECKING:
51
51
  OperationDepthManager,
52
52
  )
53
53
  from griptape_nodes.retained_mode.managers.os_manager import OSManager
54
+ from griptape_nodes.retained_mode.managers.project_manager import ProjectManager
54
55
  from griptape_nodes.retained_mode.managers.resource_manager import ResourceManager
55
56
  from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
56
57
  from griptape_nodes.retained_mode.managers.session_manager import SessionManager
@@ -95,8 +96,9 @@ class GriptapeNodes(metaclass=SingletonMeta):
95
96
  _resource_manager: ResourceManager
96
97
  _sync_manager: SyncManager
97
98
  _user_manager: UserManager
99
+ _project_manager: ProjectManager
98
100
 
99
- def __init__(self) -> None:
101
+ def __init__(self) -> None: # noqa: PLR0915
100
102
  from griptape_nodes.retained_mode.managers.agent_manager import AgentManager
101
103
  from griptape_nodes.retained_mode.managers.arbitrary_code_exec_manager import (
102
104
  ArbitraryCodeExecManager,
@@ -115,6 +117,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
115
117
  OperationDepthManager,
116
118
  )
117
119
  from griptape_nodes.retained_mode.managers.os_manager import OSManager
120
+ from griptape_nodes.retained_mode.managers.project_manager import ProjectManager
118
121
  from griptape_nodes.retained_mode.managers.resource_manager import ResourceManager
119
122
  from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
120
123
  from griptape_nodes.retained_mode.managers.session_manager import SessionManager
@@ -160,6 +163,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
160
163
  self._mcp_manager = MCPManager(self._event_manager, self._config_manager)
161
164
  self._sync_manager = SyncManager(self._event_manager, self._config_manager)
162
165
  self._user_manager = UserManager(self._secrets_manager)
166
+ self._project_manager = ProjectManager(self._event_manager, self._config_manager, self._secrets_manager)
163
167
 
164
168
  # Assign handlers now that these are created.
165
169
  self._event_manager.assign_manager_to_request_type(
@@ -330,6 +334,10 @@ class GriptapeNodes(metaclass=SingletonMeta):
330
334
  def UserManager(cls) -> UserManager:
331
335
  return GriptapeNodes.get_instance()._user_manager
332
336
 
337
+ @classmethod
338
+ def ProjectManager(cls) -> ProjectManager:
339
+ return GriptapeNodes.get_instance()._project_manager
340
+
333
341
  @classmethod
334
342
  def clear_data(cls) -> None:
335
343
  # Get canvas