rpaforge-core 0.3.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.
- rpaforge/__init__.py +23 -0
- rpaforge/bridge/__init__.py +57 -0
- rpaforge/bridge/__main__.py +8 -0
- rpaforge/bridge/events.py +304 -0
- rpaforge/bridge/handlers.py +601 -0
- rpaforge/bridge/protocol.py +160 -0
- rpaforge/bridge/server.py +332 -0
- rpaforge/codegen/__init__.py +18 -0
- rpaforge/codegen/python_generator.py +699 -0
- rpaforge/core/__init__.py +33 -0
- rpaforge/core/activity.py +354 -0
- rpaforge/core/diagram_converter.py +247 -0
- rpaforge/core/execution.py +229 -0
- rpaforge/core/executor.py +546 -0
- rpaforge/core/runner.py +494 -0
- rpaforge/core/safe_evaluator.py +153 -0
- rpaforge/core/subprocess_executor.py +126 -0
- rpaforge/engine/__init__.py +39 -0
- rpaforge/utils/__init__.py +12 -0
- rpaforge/utils/ipc.py +229 -0
- rpaforge/version.py +1 -0
- rpaforge_core-0.3.0.dist-info/METADATA +82 -0
- rpaforge_core-0.3.0.dist-info/RECORD +25 -0
- rpaforge_core-0.3.0.dist-info/WHEEL +5 -0
- rpaforge_core-0.3.0.dist-info/top_level.txt +1 -0
rpaforge/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RPAForge Core - Open Source RPA Studio Engine.
|
|
3
|
+
|
|
4
|
+
Native Python execution engine without Robot Framework dependencies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from rpaforge.bridge import BridgeServer
|
|
8
|
+
from rpaforge.codegen import CodeGenerator
|
|
9
|
+
from rpaforge.core.runner import (
|
|
10
|
+
Breakpoint,
|
|
11
|
+
ProcessRunner,
|
|
12
|
+
RunnerState,
|
|
13
|
+
StudioEngine,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"StudioEngine",
|
|
18
|
+
"ProcessRunner",
|
|
19
|
+
"Breakpoint",
|
|
20
|
+
"RunnerState",
|
|
21
|
+
"BridgeServer",
|
|
22
|
+
"CodeGenerator",
|
|
23
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RPAForge Bridge Module.
|
|
3
|
+
|
|
4
|
+
IPC communication between Electron UI and Python Engine.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from rpaforge.bridge.events import (
|
|
10
|
+
BreakpointHitEvent,
|
|
11
|
+
BridgeEvent,
|
|
12
|
+
CallStackChangedEvent,
|
|
13
|
+
ErrorEvent,
|
|
14
|
+
EventType,
|
|
15
|
+
KeywordFinishedEvent,
|
|
16
|
+
KeywordStartedEvent,
|
|
17
|
+
LogEvent,
|
|
18
|
+
ProcessFinishedEvent,
|
|
19
|
+
ProcessPausedEvent,
|
|
20
|
+
ProcessResumedEvent,
|
|
21
|
+
ProcessStartedEvent,
|
|
22
|
+
VariablesChangedEvent,
|
|
23
|
+
)
|
|
24
|
+
from rpaforge.bridge.handlers import BridgeHandlers
|
|
25
|
+
from rpaforge.bridge.protocol import (
|
|
26
|
+
JSONRPCError,
|
|
27
|
+
JSONRPCErrorCode,
|
|
28
|
+
JSONRPCNotification,
|
|
29
|
+
JSONRPCRequest,
|
|
30
|
+
JSONRPCResponse,
|
|
31
|
+
parse_message,
|
|
32
|
+
)
|
|
33
|
+
from rpaforge.bridge.server import BridgeServer
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"BridgeServer",
|
|
37
|
+
"BridgeHandlers",
|
|
38
|
+
"BridgeEvent",
|
|
39
|
+
"EventType",
|
|
40
|
+
"LogEvent",
|
|
41
|
+
"BreakpointHitEvent",
|
|
42
|
+
"ProcessStartedEvent",
|
|
43
|
+
"ProcessFinishedEvent",
|
|
44
|
+
"ProcessPausedEvent",
|
|
45
|
+
"ProcessResumedEvent",
|
|
46
|
+
"VariablesChangedEvent",
|
|
47
|
+
"CallStackChangedEvent",
|
|
48
|
+
"KeywordStartedEvent",
|
|
49
|
+
"KeywordFinishedEvent",
|
|
50
|
+
"ErrorEvent",
|
|
51
|
+
"JSONRPCRequest",
|
|
52
|
+
"JSONRPCResponse",
|
|
53
|
+
"JSONRPCNotification",
|
|
54
|
+
"JSONRPCError",
|
|
55
|
+
"JSONRPCErrorCode",
|
|
56
|
+
"parse_message",
|
|
57
|
+
]
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RPAForge Bridge Events.
|
|
3
|
+
|
|
4
|
+
Event types for IPC communication between Python engine and UI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EventType(str, Enum):
|
|
16
|
+
"""Types of events that can be emitted."""
|
|
17
|
+
|
|
18
|
+
LOG = "log"
|
|
19
|
+
BREAKPOINT_HIT = "breakpointHit"
|
|
20
|
+
PROCESS_STARTED = "processStarted"
|
|
21
|
+
PROCESS_FINISHED = "processFinished"
|
|
22
|
+
PROCESS_STOPPED = "processStopped"
|
|
23
|
+
PROCESS_PAUSED = "processPaused"
|
|
24
|
+
PROCESS_RESUMED = "processResumed"
|
|
25
|
+
VARIABLES_CHANGED = "variablesChanged"
|
|
26
|
+
CALL_STACK_CHANGED = "callStackChanged"
|
|
27
|
+
KEYWORD_STARTED = "keywordStarted"
|
|
28
|
+
KEYWORD_FINISHED = "keywordFinished"
|
|
29
|
+
ERROR = "error"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class BridgeEvent:
|
|
34
|
+
"""Base class for bridge events."""
|
|
35
|
+
|
|
36
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
37
|
+
|
|
38
|
+
def to_dict(self) -> dict[str, Any]:
|
|
39
|
+
return {"type": self.event_type(), "timestamp": self.timestamp.isoformat()}
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def event_type(cls) -> str:
|
|
43
|
+
"""Return the event type string."""
|
|
44
|
+
return "event"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class LogEvent(BridgeEvent):
|
|
49
|
+
"""Event for log messages."""
|
|
50
|
+
|
|
51
|
+
level: str = "info"
|
|
52
|
+
message: str = ""
|
|
53
|
+
source: str | None = None
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def event_type(cls) -> str:
|
|
57
|
+
return EventType.LOG.value
|
|
58
|
+
|
|
59
|
+
def to_dict(self) -> dict[str, Any]:
|
|
60
|
+
result = super().to_dict()
|
|
61
|
+
result.update(
|
|
62
|
+
{
|
|
63
|
+
"level": self.level,
|
|
64
|
+
"message": self.message,
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
if self.source:
|
|
68
|
+
result["source"] = self.source
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class BreakpointHitEvent(BridgeEvent):
|
|
74
|
+
"""Event when a breakpoint is hit."""
|
|
75
|
+
|
|
76
|
+
breakpoint_id: str = ""
|
|
77
|
+
file: str = ""
|
|
78
|
+
line: int = 0
|
|
79
|
+
condition: str | None = None
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def event_type(cls) -> str:
|
|
83
|
+
return EventType.BREAKPOINT_HIT.value
|
|
84
|
+
|
|
85
|
+
def to_dict(self) -> dict[str, Any]:
|
|
86
|
+
result = super().to_dict()
|
|
87
|
+
result.update(
|
|
88
|
+
{
|
|
89
|
+
"breakpointId": self.breakpoint_id,
|
|
90
|
+
"file": self.file,
|
|
91
|
+
"line": self.line,
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
if self.condition:
|
|
95
|
+
result["condition"] = self.condition
|
|
96
|
+
return result
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class ProcessStartedEvent(BridgeEvent):
|
|
101
|
+
"""Event when a process starts."""
|
|
102
|
+
|
|
103
|
+
process_id: str = ""
|
|
104
|
+
name: str = ""
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def event_type(cls) -> str:
|
|
108
|
+
return EventType.PROCESS_STARTED.value
|
|
109
|
+
|
|
110
|
+
def to_dict(self) -> dict[str, Any]:
|
|
111
|
+
result = super().to_dict()
|
|
112
|
+
result.update(
|
|
113
|
+
{
|
|
114
|
+
"processId": self.process_id,
|
|
115
|
+
"name": self.name,
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
return result
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass
|
|
122
|
+
class ProcessFinishedEvent(BridgeEvent):
|
|
123
|
+
"""Event when a process finishes."""
|
|
124
|
+
|
|
125
|
+
status: str = "pass"
|
|
126
|
+
duration: float = 0.0
|
|
127
|
+
message: str | None = None
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def event_type(cls) -> str:
|
|
131
|
+
return EventType.PROCESS_FINISHED.value
|
|
132
|
+
|
|
133
|
+
def to_dict(self) -> dict[str, Any]:
|
|
134
|
+
result = super().to_dict()
|
|
135
|
+
result.update(
|
|
136
|
+
{
|
|
137
|
+
"status": self.status,
|
|
138
|
+
"duration": self.duration,
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
if self.message:
|
|
142
|
+
result["message"] = self.message
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class ProcessStoppedEvent(BridgeEvent):
|
|
148
|
+
"""Event when a process is stopped/cancelled by user."""
|
|
149
|
+
|
|
150
|
+
reason: str = "user"
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def event_type(cls) -> str:
|
|
154
|
+
return EventType.PROCESS_STOPPED.value
|
|
155
|
+
|
|
156
|
+
def to_dict(self) -> dict[str, Any]:
|
|
157
|
+
result = super().to_dict()
|
|
158
|
+
result["reason"] = self.reason
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dataclass
|
|
163
|
+
class VariablesChangedEvent(BridgeEvent):
|
|
164
|
+
"""Event when variables change."""
|
|
165
|
+
|
|
166
|
+
variables: list[dict[str, Any]] = field(default_factory=list)
|
|
167
|
+
|
|
168
|
+
@classmethod
|
|
169
|
+
def event_type(cls) -> str:
|
|
170
|
+
return EventType.VARIABLES_CHANGED.value
|
|
171
|
+
|
|
172
|
+
def to_dict(self) -> dict[str, Any]:
|
|
173
|
+
result = super().to_dict()
|
|
174
|
+
result["variables"] = self.variables
|
|
175
|
+
return result
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@dataclass
|
|
179
|
+
class CallStackChangedEvent(BridgeEvent):
|
|
180
|
+
"""Event when call stack changes."""
|
|
181
|
+
|
|
182
|
+
call_stack: list[dict[str, Any]] = field(default_factory=list)
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def event_type(cls) -> str:
|
|
186
|
+
return EventType.CALL_STACK_CHANGED.value
|
|
187
|
+
|
|
188
|
+
def to_dict(self) -> dict[str, Any]:
|
|
189
|
+
result = super().to_dict()
|
|
190
|
+
result["callStack"] = self.call_stack
|
|
191
|
+
return result
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass
|
|
195
|
+
class KeywordStartedEvent(BridgeEvent):
|
|
196
|
+
"""Event when a keyword starts."""
|
|
197
|
+
|
|
198
|
+
name: str = ""
|
|
199
|
+
file: str = ""
|
|
200
|
+
line: int = 0
|
|
201
|
+
args: list[Any] = field(default_factory=list)
|
|
202
|
+
|
|
203
|
+
@classmethod
|
|
204
|
+
def event_type(cls) -> str:
|
|
205
|
+
return EventType.KEYWORD_STARTED.value
|
|
206
|
+
|
|
207
|
+
def to_dict(self) -> dict[str, Any]:
|
|
208
|
+
result = super().to_dict()
|
|
209
|
+
result.update(
|
|
210
|
+
{
|
|
211
|
+
"name": self.name,
|
|
212
|
+
"file": self.file,
|
|
213
|
+
"line": self.line,
|
|
214
|
+
"args": self.args,
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
return result
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@dataclass
|
|
221
|
+
class KeywordFinishedEvent(BridgeEvent):
|
|
222
|
+
"""Event when a keyword finishes."""
|
|
223
|
+
|
|
224
|
+
name: str = ""
|
|
225
|
+
status: str = "pass"
|
|
226
|
+
result: Any | None = None
|
|
227
|
+
|
|
228
|
+
@classmethod
|
|
229
|
+
def event_type(cls) -> str:
|
|
230
|
+
return EventType.KEYWORD_FINISHED.value
|
|
231
|
+
|
|
232
|
+
def to_dict(self) -> dict[str, Any]:
|
|
233
|
+
result = super().to_dict()
|
|
234
|
+
result.update(
|
|
235
|
+
{
|
|
236
|
+
"name": self.name,
|
|
237
|
+
"status": self.status,
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
if self.result is not None:
|
|
241
|
+
result["result"] = self.result
|
|
242
|
+
return result
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@dataclass
|
|
246
|
+
class ErrorEvent(BridgeEvent):
|
|
247
|
+
"""Event for errors."""
|
|
248
|
+
|
|
249
|
+
code: int = 0
|
|
250
|
+
message: str = ""
|
|
251
|
+
details: str | None = None
|
|
252
|
+
|
|
253
|
+
@classmethod
|
|
254
|
+
def event_type(cls) -> str:
|
|
255
|
+
return EventType.ERROR.value
|
|
256
|
+
|
|
257
|
+
def to_dict(self) -> dict[str, Any]:
|
|
258
|
+
result = super().to_dict()
|
|
259
|
+
result.update(
|
|
260
|
+
{
|
|
261
|
+
"code": self.code,
|
|
262
|
+
"message": self.message,
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
if self.details:
|
|
266
|
+
result["details"] = self.details
|
|
267
|
+
return result
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass
|
|
271
|
+
class ProcessPausedEvent(BridgeEvent):
|
|
272
|
+
"""Event when a process pauses at breakpoint or step."""
|
|
273
|
+
|
|
274
|
+
file: str | None = None
|
|
275
|
+
line: int | None = None
|
|
276
|
+
node_id: str | None = None
|
|
277
|
+
reason: str = "breakpoint"
|
|
278
|
+
|
|
279
|
+
@classmethod
|
|
280
|
+
def event_type(cls) -> str:
|
|
281
|
+
return EventType.PROCESS_PAUSED.value
|
|
282
|
+
|
|
283
|
+
def to_dict(self) -> dict[str, Any]:
|
|
284
|
+
result = super().to_dict()
|
|
285
|
+
result["reason"] = self.reason
|
|
286
|
+
if self.file is not None:
|
|
287
|
+
result["file"] = self.file
|
|
288
|
+
if self.line is not None:
|
|
289
|
+
result["line"] = self.line
|
|
290
|
+
if self.node_id is not None:
|
|
291
|
+
result["nodeId"] = self.node_id
|
|
292
|
+
return result
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@dataclass
|
|
296
|
+
class ProcessResumedEvent(BridgeEvent):
|
|
297
|
+
"""Event when a process resumes from pause."""
|
|
298
|
+
|
|
299
|
+
@classmethod
|
|
300
|
+
def event_type(cls) -> str:
|
|
301
|
+
return EventType.PROCESS_RESUMED.value
|
|
302
|
+
|
|
303
|
+
def to_dict(self) -> dict[str, Any]:
|
|
304
|
+
return super().to_dict()
|