ferp 0.7.1__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.
- ferp/__init__.py +3 -0
- ferp/__main__.py +4 -0
- ferp/__version__.py +1 -0
- ferp/app.py +9 -0
- ferp/cli.py +160 -0
- ferp/core/__init__.py +0 -0
- ferp/core/app.py +1312 -0
- ferp/core/bundle_installer.py +245 -0
- ferp/core/command_provider.py +77 -0
- ferp/core/dependency_manager.py +59 -0
- ferp/core/fs_controller.py +70 -0
- ferp/core/fs_watcher.py +144 -0
- ferp/core/messages.py +49 -0
- ferp/core/path_actions.py +124 -0
- ferp/core/paths.py +3 -0
- ferp/core/protocols.py +8 -0
- ferp/core/script_controller.py +515 -0
- ferp/core/script_protocol.py +35 -0
- ferp/core/script_runner.py +421 -0
- ferp/core/settings.py +16 -0
- ferp/core/settings_store.py +69 -0
- ferp/core/state.py +156 -0
- ferp/core/task_store.py +164 -0
- ferp/core/transcript_logger.py +95 -0
- ferp/domain/__init__.py +0 -0
- ferp/domain/scripts.py +29 -0
- ferp/fscp/host/__init__.py +11 -0
- ferp/fscp/host/host.py +439 -0
- ferp/fscp/host/managed_process.py +113 -0
- ferp/fscp/host/process_registry.py +124 -0
- ferp/fscp/protocol/__init__.py +13 -0
- ferp/fscp/protocol/errors.py +2 -0
- ferp/fscp/protocol/messages.py +55 -0
- ferp/fscp/protocol/schemas/__init__.py +0 -0
- ferp/fscp/protocol/schemas/fscp/1.0/cancel.json +16 -0
- ferp/fscp/protocol/schemas/fscp/1.0/definitions.json +29 -0
- ferp/fscp/protocol/schemas/fscp/1.0/discriminator.json +14 -0
- ferp/fscp/protocol/schemas/fscp/1.0/envelope.json +13 -0
- ferp/fscp/protocol/schemas/fscp/1.0/exit.json +20 -0
- ferp/fscp/protocol/schemas/fscp/1.0/init.json +36 -0
- ferp/fscp/protocol/schemas/fscp/1.0/input_response.json +21 -0
- ferp/fscp/protocol/schemas/fscp/1.0/log.json +21 -0
- ferp/fscp/protocol/schemas/fscp/1.0/message.json +23 -0
- ferp/fscp/protocol/schemas/fscp/1.0/progress.json +23 -0
- ferp/fscp/protocol/schemas/fscp/1.0/request_input.json +47 -0
- ferp/fscp/protocol/schemas/fscp/1.0/result.json +16 -0
- ferp/fscp/protocol/schemas/fscp/__init__.py +0 -0
- ferp/fscp/protocol/state.py +16 -0
- ferp/fscp/protocol/validator.py +123 -0
- ferp/fscp/scripts/__init__.py +0 -0
- ferp/fscp/scripts/runtime/__init__.py +4 -0
- ferp/fscp/scripts/runtime/__main__.py +40 -0
- ferp/fscp/scripts/runtime/errors.py +14 -0
- ferp/fscp/scripts/runtime/io.py +64 -0
- ferp/fscp/scripts/runtime/script.py +149 -0
- ferp/fscp/scripts/runtime/state.py +17 -0
- ferp/fscp/scripts/runtime/worker.py +13 -0
- ferp/fscp/scripts/sdk.py +548 -0
- ferp/fscp/transcript/__init__.py +3 -0
- ferp/fscp/transcript/events.py +14 -0
- ferp/resources/__init__.py +0 -0
- ferp/services/__init__.py +3 -0
- ferp/services/file_listing.py +120 -0
- ferp/services/monday_sync.py +155 -0
- ferp/services/releases.py +214 -0
- ferp/services/scripts.py +90 -0
- ferp/services/update_check.py +130 -0
- ferp/styles/index.tcss +638 -0
- ferp/themes/themes.py +238 -0
- ferp/widgets/__init__.py +17 -0
- ferp/widgets/dialogs.py +167 -0
- ferp/widgets/file_tree.py +991 -0
- ferp/widgets/forms.py +146 -0
- ferp/widgets/output_panel.py +244 -0
- ferp/widgets/panels.py +13 -0
- ferp/widgets/process_list.py +158 -0
- ferp/widgets/readme_modal.py +59 -0
- ferp/widgets/scripts.py +192 -0
- ferp/widgets/task_capture.py +74 -0
- ferp/widgets/task_list.py +493 -0
- ferp/widgets/top_bar.py +110 -0
- ferp-0.7.1.dist-info/METADATA +128 -0
- ferp-0.7.1.dist-info/RECORD +87 -0
- ferp-0.7.1.dist-info/WHEEL +5 -0
- ferp-0.7.1.dist-info/entry_points.txt +2 -0
- ferp-0.7.1.dist-info/licenses/LICENSE +21 -0
- ferp-0.7.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ferp.fscp.protocol.messages import Message, MessageType
|
|
4
|
+
from ferp.fscp.protocol.validator import Endpoint, ProtocolValidator
|
|
5
|
+
from ferp.fscp.scripts.runtime.errors import (
|
|
6
|
+
InvalidStateTransition,
|
|
7
|
+
ProtocolViolation,
|
|
8
|
+
)
|
|
9
|
+
from ferp.fscp.scripts.runtime.io import read_message, write_message
|
|
10
|
+
from ferp.fscp.scripts.runtime.state import ScriptState
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ScriptRuntime:
|
|
14
|
+
"""
|
|
15
|
+
Reference ferp.fscp script runtime.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
self.state: ScriptState = ScriptState.S0_BOOT
|
|
20
|
+
self.validator = ProtocolValidator()
|
|
21
|
+
self.pending_input_id: Optional[str] = None
|
|
22
|
+
|
|
23
|
+
def run(self) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Main blocking event loop.
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
while self.state is not ScriptState.S5_EXITING:
|
|
29
|
+
raw = read_message()
|
|
30
|
+
msg = Message.from_dict(raw)
|
|
31
|
+
|
|
32
|
+
# Validate schema-level correctness
|
|
33
|
+
self.validator.validate(msg, sender=Endpoint.HOST)
|
|
34
|
+
|
|
35
|
+
self._handle_message(msg)
|
|
36
|
+
|
|
37
|
+
except EOFError:
|
|
38
|
+
# Host disappeared — nothing more to do
|
|
39
|
+
self.state = ScriptState.S_ERR_FATAL
|
|
40
|
+
|
|
41
|
+
except Exception as exc:
|
|
42
|
+
self.state = ScriptState.S_ERR_FATAL
|
|
43
|
+
self._emit_fatal_error(exc)
|
|
44
|
+
|
|
45
|
+
finally:
|
|
46
|
+
self._emit_exit(code=1 if self.state.name.startswith("S_ERR") else 0)
|
|
47
|
+
|
|
48
|
+
# -------------------------
|
|
49
|
+
# Message handlers
|
|
50
|
+
# -------------------------
|
|
51
|
+
|
|
52
|
+
def _handle_message(self, msg: Message) -> None:
|
|
53
|
+
if msg.type == MessageType.INIT:
|
|
54
|
+
self._handle_init(msg)
|
|
55
|
+
elif msg.type == MessageType.INPUT_RESPONSE:
|
|
56
|
+
self._handle_input_response(msg)
|
|
57
|
+
elif msg.type == MessageType.CANCEL:
|
|
58
|
+
self._handle_cancel(msg)
|
|
59
|
+
else:
|
|
60
|
+
raise ProtocolViolation(f"Unhandled message type: {msg.type}")
|
|
61
|
+
|
|
62
|
+
def _handle_init(self, msg: Message) -> None:
|
|
63
|
+
if self.state is not ScriptState.S0_BOOT:
|
|
64
|
+
raise InvalidStateTransition("init received outside BOOT")
|
|
65
|
+
|
|
66
|
+
self.state = ScriptState.S1_READY
|
|
67
|
+
|
|
68
|
+
# Example log
|
|
69
|
+
self._emit_log("info", "Script initialized")
|
|
70
|
+
|
|
71
|
+
# Transition into work
|
|
72
|
+
self.state = ScriptState.S2_WORKING
|
|
73
|
+
self._do_work()
|
|
74
|
+
|
|
75
|
+
def _handle_input_response(self, msg: Message) -> None:
|
|
76
|
+
if self.state is not ScriptState.S3_WAITING_INPUT:
|
|
77
|
+
raise InvalidStateTransition("input_response while not waiting")
|
|
78
|
+
|
|
79
|
+
payload = msg.payload or {}
|
|
80
|
+
if payload.get("id") != self.pending_input_id:
|
|
81
|
+
raise ProtocolViolation("input_response id mismatch")
|
|
82
|
+
|
|
83
|
+
value = payload.get("value")
|
|
84
|
+
self.pending_input_id = None
|
|
85
|
+
|
|
86
|
+
self.state = ScriptState.S2_WORKING
|
|
87
|
+
self._emit_result({"input": value})
|
|
88
|
+
|
|
89
|
+
def _handle_cancel(self, msg: Message) -> None:
|
|
90
|
+
if self.state in {ScriptState.S5_EXITING, ScriptState.S_ERR_FATAL}:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
self.state = ScriptState.S4_CANCELLING
|
|
94
|
+
self._emit_log("warn", "Cancellation requested")
|
|
95
|
+
|
|
96
|
+
self.state = ScriptState.S5_EXITING
|
|
97
|
+
|
|
98
|
+
# -------------------------
|
|
99
|
+
# Script logic (example)
|
|
100
|
+
# -------------------------
|
|
101
|
+
|
|
102
|
+
def _do_work(self) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Example workload.
|
|
105
|
+
"""
|
|
106
|
+
# Potentially use FatalScriptError here
|
|
107
|
+
self.pending_input_id = "example"
|
|
108
|
+
self.state = ScriptState.S3_WAITING_INPUT
|
|
109
|
+
|
|
110
|
+
self._emit_request_input(
|
|
111
|
+
id="example",
|
|
112
|
+
prompt="Enter a value",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# -------------------------
|
|
116
|
+
# Emit helpers
|
|
117
|
+
# -------------------------
|
|
118
|
+
|
|
119
|
+
def _emit_log(self, level: str, message: str) -> None:
|
|
120
|
+
msg = Message(
|
|
121
|
+
type=MessageType.LOG,
|
|
122
|
+
payload={"level": level, "message": message},
|
|
123
|
+
)
|
|
124
|
+
write_message(msg.to_dict())
|
|
125
|
+
|
|
126
|
+
def _emit_request_input(self, *, id: str, prompt: str) -> None:
|
|
127
|
+
msg = Message(
|
|
128
|
+
type=MessageType.REQUEST_INPUT,
|
|
129
|
+
payload={"id": id, "prompt": prompt},
|
|
130
|
+
)
|
|
131
|
+
write_message(msg.to_dict())
|
|
132
|
+
|
|
133
|
+
def _emit_result(self, payload: dict) -> None:
|
|
134
|
+
msg = Message(
|
|
135
|
+
type=MessageType.RESULT,
|
|
136
|
+
payload=payload,
|
|
137
|
+
)
|
|
138
|
+
write_message(msg.to_dict())
|
|
139
|
+
self.state = ScriptState.S5_EXITING
|
|
140
|
+
|
|
141
|
+
def _emit_exit(self, *, code: int) -> None:
|
|
142
|
+
msg = Message(
|
|
143
|
+
type=MessageType.EXIT,
|
|
144
|
+
payload={"code": code},
|
|
145
|
+
)
|
|
146
|
+
write_message(msg.to_dict())
|
|
147
|
+
|
|
148
|
+
def _emit_fatal_error(self, exc: Exception) -> None:
|
|
149
|
+
self._emit_log("error", str(exc))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ScriptState(Enum):
|
|
5
|
+
"""
|
|
6
|
+
Authoritative FSCP script-side state machine.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
S0_BOOT = auto() # Process started, nothing received
|
|
10
|
+
S1_READY = auto() # Valid init received
|
|
11
|
+
S2_WORKING = auto() # Performing work, emitting messages
|
|
12
|
+
S3_WAITING_INPUT = auto() # request_input sent, awaiting input_response
|
|
13
|
+
S4_CANCELLING = auto() # cancel received, cleaning up
|
|
14
|
+
S5_EXITING = auto() # exit emitted, shutting down
|
|
15
|
+
|
|
16
|
+
S_ERR_PROTOCOL = auto() # Host violated protocol
|
|
17
|
+
S_ERR_FATAL = auto() # Unhandled exception
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from multiprocessing.connection import Connection
|
|
2
|
+
|
|
3
|
+
from ferp.fscp.scripts.runtime.io import configure_connection
|
|
4
|
+
from ferp.fscp.scripts.runtime.script import ScriptRuntime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def run_runtime(conn: Connection) -> None:
|
|
8
|
+
"""
|
|
9
|
+
Entry point for running ScriptRuntime under multiprocessing.Pipe transport.
|
|
10
|
+
"""
|
|
11
|
+
configure_connection(conn)
|
|
12
|
+
runtime = ScriptRuntime()
|
|
13
|
+
runtime.run()
|