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.
Files changed (87) hide show
  1. ferp/__init__.py +3 -0
  2. ferp/__main__.py +4 -0
  3. ferp/__version__.py +1 -0
  4. ferp/app.py +9 -0
  5. ferp/cli.py +160 -0
  6. ferp/core/__init__.py +0 -0
  7. ferp/core/app.py +1312 -0
  8. ferp/core/bundle_installer.py +245 -0
  9. ferp/core/command_provider.py +77 -0
  10. ferp/core/dependency_manager.py +59 -0
  11. ferp/core/fs_controller.py +70 -0
  12. ferp/core/fs_watcher.py +144 -0
  13. ferp/core/messages.py +49 -0
  14. ferp/core/path_actions.py +124 -0
  15. ferp/core/paths.py +3 -0
  16. ferp/core/protocols.py +8 -0
  17. ferp/core/script_controller.py +515 -0
  18. ferp/core/script_protocol.py +35 -0
  19. ferp/core/script_runner.py +421 -0
  20. ferp/core/settings.py +16 -0
  21. ferp/core/settings_store.py +69 -0
  22. ferp/core/state.py +156 -0
  23. ferp/core/task_store.py +164 -0
  24. ferp/core/transcript_logger.py +95 -0
  25. ferp/domain/__init__.py +0 -0
  26. ferp/domain/scripts.py +29 -0
  27. ferp/fscp/host/__init__.py +11 -0
  28. ferp/fscp/host/host.py +439 -0
  29. ferp/fscp/host/managed_process.py +113 -0
  30. ferp/fscp/host/process_registry.py +124 -0
  31. ferp/fscp/protocol/__init__.py +13 -0
  32. ferp/fscp/protocol/errors.py +2 -0
  33. ferp/fscp/protocol/messages.py +55 -0
  34. ferp/fscp/protocol/schemas/__init__.py +0 -0
  35. ferp/fscp/protocol/schemas/fscp/1.0/cancel.json +16 -0
  36. ferp/fscp/protocol/schemas/fscp/1.0/definitions.json +29 -0
  37. ferp/fscp/protocol/schemas/fscp/1.0/discriminator.json +14 -0
  38. ferp/fscp/protocol/schemas/fscp/1.0/envelope.json +13 -0
  39. ferp/fscp/protocol/schemas/fscp/1.0/exit.json +20 -0
  40. ferp/fscp/protocol/schemas/fscp/1.0/init.json +36 -0
  41. ferp/fscp/protocol/schemas/fscp/1.0/input_response.json +21 -0
  42. ferp/fscp/protocol/schemas/fscp/1.0/log.json +21 -0
  43. ferp/fscp/protocol/schemas/fscp/1.0/message.json +23 -0
  44. ferp/fscp/protocol/schemas/fscp/1.0/progress.json +23 -0
  45. ferp/fscp/protocol/schemas/fscp/1.0/request_input.json +47 -0
  46. ferp/fscp/protocol/schemas/fscp/1.0/result.json +16 -0
  47. ferp/fscp/protocol/schemas/fscp/__init__.py +0 -0
  48. ferp/fscp/protocol/state.py +16 -0
  49. ferp/fscp/protocol/validator.py +123 -0
  50. ferp/fscp/scripts/__init__.py +0 -0
  51. ferp/fscp/scripts/runtime/__init__.py +4 -0
  52. ferp/fscp/scripts/runtime/__main__.py +40 -0
  53. ferp/fscp/scripts/runtime/errors.py +14 -0
  54. ferp/fscp/scripts/runtime/io.py +64 -0
  55. ferp/fscp/scripts/runtime/script.py +149 -0
  56. ferp/fscp/scripts/runtime/state.py +17 -0
  57. ferp/fscp/scripts/runtime/worker.py +13 -0
  58. ferp/fscp/scripts/sdk.py +548 -0
  59. ferp/fscp/transcript/__init__.py +3 -0
  60. ferp/fscp/transcript/events.py +14 -0
  61. ferp/resources/__init__.py +0 -0
  62. ferp/services/__init__.py +3 -0
  63. ferp/services/file_listing.py +120 -0
  64. ferp/services/monday_sync.py +155 -0
  65. ferp/services/releases.py +214 -0
  66. ferp/services/scripts.py +90 -0
  67. ferp/services/update_check.py +130 -0
  68. ferp/styles/index.tcss +638 -0
  69. ferp/themes/themes.py +238 -0
  70. ferp/widgets/__init__.py +17 -0
  71. ferp/widgets/dialogs.py +167 -0
  72. ferp/widgets/file_tree.py +991 -0
  73. ferp/widgets/forms.py +146 -0
  74. ferp/widgets/output_panel.py +244 -0
  75. ferp/widgets/panels.py +13 -0
  76. ferp/widgets/process_list.py +158 -0
  77. ferp/widgets/readme_modal.py +59 -0
  78. ferp/widgets/scripts.py +192 -0
  79. ferp/widgets/task_capture.py +74 -0
  80. ferp/widgets/task_list.py +493 -0
  81. ferp/widgets/top_bar.py +110 -0
  82. ferp-0.7.1.dist-info/METADATA +128 -0
  83. ferp-0.7.1.dist-info/RECORD +87 -0
  84. ferp-0.7.1.dist-info/WHEEL +5 -0
  85. ferp-0.7.1.dist-info/entry_points.txt +2 -0
  86. ferp-0.7.1.dist-info/licenses/LICENSE +21 -0
  87. 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()