@passioncode-ai/passioncode 0.2.42 → 0.2.43
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.
- package/CHANGELOG.md +9 -0
- package/family.json +1 -1
- package/package.json +1 -1
- package/payload/.claude-plugin/marketplace.json +2 -2
- package/payload/manifest.json +8 -8
- package/payload/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/payload/plugins/fabric-agent-adapter/hooks/hooks.json +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +7 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +82 -11
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +2 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-link.mjs +33 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +146 -7
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_link.py +34 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +149 -45
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +181 -5
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +13 -1
- package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +15 -10
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +4 -1
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +38 -9
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +11 -0
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +12 -3
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +11 -1
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mcp.py +358 -14
- package/payload/plugins/passioncode/.claude-plugin/plugin.json +1 -1
|
@@ -10,12 +10,7 @@ import secrets
|
|
|
10
10
|
import threading
|
|
11
11
|
import time
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
from typing import Any
|
|
14
|
-
from typing import Callable
|
|
15
|
-
from typing import Dict
|
|
16
|
-
from typing import List
|
|
17
|
-
from typing import Optional
|
|
18
|
-
from typing import Sequence
|
|
13
|
+
from typing import Any, Callable, Dict, List, Optional, Sequence
|
|
19
14
|
|
|
20
15
|
import sys as _sys # noqa: E402
|
|
21
16
|
from pathlib import Path as _Path # noqa: E402
|
|
@@ -24,15 +19,7 @@ if str(_Path(__file__).resolve().parent) not in _sys.path: # the kit loads from
|
|
|
24
19
|
import fabric_hub as _hub
|
|
25
20
|
import fabric_service as _fs
|
|
26
21
|
from fabric_board_shapes import ( # noqa: E402 - the kit (FAA-65)
|
|
27
|
-
BoardError,
|
|
28
|
-
LEASE_SECONDS,
|
|
29
|
-
REQUEST_STATES,
|
|
30
|
-
SUMMARY_MAX,
|
|
31
|
-
Stopped,
|
|
32
|
-
_CAPABILITY,
|
|
33
|
-
_is_int,
|
|
34
|
-
outcome_problems,
|
|
35
|
-
receipt_problems,
|
|
22
|
+
BoardError, LEASE_SECONDS, REQUEST_STATES, SUMMARY_MAX, Stopped, _CAPABILITY, _is_int, outcome_problems, receipt_problems,
|
|
36
23
|
)
|
|
37
24
|
from fabric_board_link import ( # noqa: E402,F401 - re-exported: callers import them from here since 0.40.0
|
|
38
25
|
BoardRequest,
|
|
@@ -43,33 +30,25 @@ from fabric_board_link import ( # noqa: E402,F401 - re-exported: callers import
|
|
|
43
30
|
|
|
44
31
|
RENEW_SECONDS = 20.0
|
|
45
32
|
|
|
46
|
-
|
|
47
33
|
POLL_SECONDS = 15.0
|
|
48
34
|
|
|
49
|
-
|
|
50
35
|
POLL_JITTER_SECONDS = 5.0
|
|
51
36
|
|
|
52
|
-
|
|
53
37
|
DRAIN_SECONDS = 10.0
|
|
54
38
|
|
|
55
|
-
|
|
56
39
|
EVENT_SCHEMA = "urn:fabric:schema:board-responder-event:1"
|
|
57
40
|
|
|
58
|
-
|
|
59
41
|
#: On these the attempt is no longer ours: stop acting at once (C4).
|
|
60
42
|
STOP_CODES = frozenset(["fenced", "lease_expired", "authority_changed"])
|
|
61
43
|
|
|
62
|
-
|
|
63
44
|
TERMINAL = frozenset(["completed", "failed_known", "cancelled", "expired"])
|
|
64
45
|
#: No longer this responder's to work on: back in the queue, claimed again, or answered by the target Project's
|
|
65
46
|
#: participant reply (DEC-0098, DEC-0102; not terminal, the requester may still cancel it, and nobody claims it).
|
|
66
47
|
NOT_OURS = frozenset(["queued", "claimed", "answered"])
|
|
67
48
|
|
|
68
|
-
|
|
69
49
|
#: A lost call is sent again this long before the lease it keeps would lapse (FAA-77, finding 38).
|
|
70
50
|
LEASE_MARGIN_SECONDS = 10.0
|
|
71
51
|
|
|
72
|
-
|
|
73
52
|
#: How long a held slot waits before it asks again: doubling from the poll interval, never past this.
|
|
74
53
|
HOLD_RETRY_MAX_SECONDS = 900.0
|
|
75
54
|
|
|
@@ -105,6 +84,8 @@ class _Work:
|
|
|
105
84
|
|
|
106
85
|
Handler = Callable[[BoardRequest, WorkContext], Dict[str, Any]]
|
|
107
86
|
|
|
87
|
+
_whole = lambda v: int(v) if isinstance(v, float) and v.is_integer() else v # noqa: E731 - 2.0 is 2, as Node reads it
|
|
88
|
+
|
|
108
89
|
|
|
109
90
|
class Responder:
|
|
110
91
|
"""One service's responder for one Project and one or more capabilities, one request at a time.
|
|
@@ -150,6 +131,7 @@ class Responder:
|
|
|
150
131
|
#: One fenced call at a time, and the entry edits beside them (FAA-77, finding 36).
|
|
151
132
|
self._lock = threading.RLock()
|
|
152
133
|
self._to_resume: List[str] = [] # transferred requests waiting until nothing runs (finding 39)
|
|
134
|
+
self._suspect: set = set() # capabilities a fenced call was refused on: com.status says whether they moved (FAA-155)
|
|
153
135
|
|
|
154
136
|
# -- events -------------------------------------------------------------------------------------------------
|
|
155
137
|
def _emit(self, kind: str, level: str, text: str, notify: bool = False, episode: Optional[str] = None, **value: Any) -> None:
|
|
@@ -230,12 +212,14 @@ class Responder:
|
|
|
230
212
|
def _enroll(self, capability: str) -> None:
|
|
231
213
|
args = {"idempotency": self._idem(self._key("enroll")), "capability": capability}
|
|
232
214
|
try:
|
|
233
|
-
receipt = self._receipt("com.enroll", self.
|
|
215
|
+
receipt = self._receipt("com.enroll", self._ask(capability, "com.enroll", args))
|
|
234
216
|
except BoardError as exc:
|
|
235
217
|
if exc.code == "held":
|
|
236
218
|
self._pause(capability)
|
|
237
219
|
return
|
|
238
220
|
if exc.code == "fenced":
|
|
221
|
+
if self._gone(capability):
|
|
222
|
+
return
|
|
239
223
|
self.slots[capability] = {"state": "contested", "renewed_at": self.now()}
|
|
240
224
|
self._emit("board.slot.contested", "warn", "Another responder holds the %s slot on this Project; this service "
|
|
241
225
|
"waits for it to lapse and never takes it over by itself." % capability, notify=True,
|
|
@@ -243,12 +227,12 @@ class Responder:
|
|
|
243
227
|
return
|
|
244
228
|
raise
|
|
245
229
|
self.slots[capability] = {"state": "held", "generation": receipt["slot"]["generation"], "renewed_at": self.now()}
|
|
230
|
+
self._held_at(capability, receipt["slot"]["generation"])
|
|
246
231
|
self._said.pop("board.slot.contested", None)
|
|
247
232
|
self._lifted(capability)
|
|
248
233
|
|
|
249
234
|
def _pause(self, capability: str) -> None:
|
|
250
|
-
"""The operator holds this slot or its Project (DEC-0079): ask again later, the pause doubling
|
|
251
|
-
never act around the hold. Accepted work in progress continues; only new work waits."""
|
|
235
|
+
"""The operator holds this slot or its Project (DEC-0079): ask again later, the pause doubling; work goes on."""
|
|
252
236
|
slot = self.slots.get(capability) or {}
|
|
253
237
|
pauses = slot.get("pauses", 0) + 1 if slot.get("state") == "paused" else 1
|
|
254
238
|
wait = min(HOLD_RETRY_MAX_SECONDS, self.hold_delay * 2 ** (pauses - 1))
|
|
@@ -264,6 +248,108 @@ class Responder:
|
|
|
264
248
|
self._emit("board.slot.released", "info", "The operator's hold on %s was lifted; the service takes new work "
|
|
265
249
|
"again." % capability, capability=capability)
|
|
266
250
|
|
|
251
|
+
# -- a slot moved to another session (FAA-155, DEC-0140 PR-2) ------------------------------------------------
|
|
252
|
+
def _held_at(self, capability: str, generation: int) -> None:
|
|
253
|
+
"""The generation this responder holds the slot at, kept beside its checkpoint so a restart knows it."""
|
|
254
|
+
self._note(capability, {"project": self.project, "generation": generation})
|
|
255
|
+
|
|
256
|
+
def _note(self, capability: str, record: Optional[Dict[str, Any]]) -> None:
|
|
257
|
+
"""The slot record, best effort: one that cannot be written now is written at the next receipt."""
|
|
258
|
+
try:
|
|
259
|
+
self.checkpoint.slot(capability, record)
|
|
260
|
+
except BoardError:
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
def _ask(self, capability: str, op: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
264
|
+
"""An enroll or replace that can move the generation is recorded with its key first (a lost answer is no move)."""
|
|
265
|
+
held = self.checkpoint.slots.get(capability) or {}
|
|
266
|
+
if held.get("project") != self.project:
|
|
267
|
+
return self._call(op, args) # no record: nothing a lost answer could be mistaken for
|
|
268
|
+
slot = self.slots.get(capability) or {}
|
|
269
|
+
if op == "com.enroll" and slot.get("state") == "held" and slot.get("generation") == held.get("generation") \
|
|
270
|
+
and self.now() - slot.get("renewed_at", 0.0) < LEASE_SECONDS - LEASE_MARGIN_SECONDS and "asking" not in held:
|
|
271
|
+
return self._call(op, args) # a live holder's renew cannot change its generation: nothing to record
|
|
272
|
+
if isinstance(held.get("asking"), dict):
|
|
273
|
+
if self._gone(capability): # an earlier lost ask is settled first; it may show the slot moved
|
|
274
|
+
raise BoardError("fenced", "The %s slot moved to another session." % capability, op=op)
|
|
275
|
+
held = self.checkpoint.slots.get(capability) or {}
|
|
276
|
+
try:
|
|
277
|
+
self.checkpoint.slot(capability, dict(held, asking={"op": op, "args": args}))
|
|
278
|
+
return self._call(op, args)
|
|
279
|
+
except BoardError as exc:
|
|
280
|
+
if exc.code == "checkpoint":
|
|
281
|
+
return self._call(op, args)
|
|
282
|
+
if exc.code not in ("unreachable", "session-ended"):
|
|
283
|
+
self._note(capability, held)
|
|
284
|
+
raise
|
|
285
|
+
|
|
286
|
+
def _replay(self, capability: str, held: Dict[str, Any], before: Any) -> Dict[str, Any]:
|
|
287
|
+
"""Asks a lost enroll or replace again under its key. `repeated: true`: the generation is ours. Else it ran only now: past
|
|
288
|
+
a move (`before` beyond the record) the move is read. A refusal drops the record; a lost answer keeps it."""
|
|
289
|
+
asking = held.get("asking")
|
|
290
|
+
rest = {k: v for k, v in held.items() if k != "asking"}
|
|
291
|
+
if isinstance(asking, dict) and asking.get("op") in ("com.enroll", "com.responder_replace"):
|
|
292
|
+
try:
|
|
293
|
+
answer = self._receipt(asking["op"], self._call(asking["op"], asking.get("args")))
|
|
294
|
+
generation, moved = _whole(answer["slot"]["generation"]), _is_int(before, 1) and before > rest.get("generation", before)
|
|
295
|
+
if _is_int(generation, 1) and (answer.get("repeated") is True or not moved):
|
|
296
|
+
rest = {"project": self.project, "generation": generation}
|
|
297
|
+
except BoardError as exc:
|
|
298
|
+
if exc.code in ("unreachable", "session-ended"):
|
|
299
|
+
raise
|
|
300
|
+
if exc.code == "idempotency_window_expired":
|
|
301
|
+
rest = {} # a retired epoch refuses even a repeat: unknown, so no record (a first start, never a move)
|
|
302
|
+
except (KeyError, TypeError):
|
|
303
|
+
pass
|
|
304
|
+
self._note(capability, rest or None)
|
|
305
|
+
return rest
|
|
306
|
+
|
|
307
|
+
def _gone(self, capability: str, status: Optional[Dict[str, Any]] = None) -> bool:
|
|
308
|
+
"""True when `com.status` shows the slot's generation past the record (the slot is left `moved`); else False."""
|
|
309
|
+
held = self.checkpoint.slots.get(capability) or {}
|
|
310
|
+
if held.get("project") != self.project or (not _is_int(held.get("generation"), 1) and "asking" not in held):
|
|
311
|
+
return False
|
|
312
|
+
status = self._status() if status is None else status
|
|
313
|
+
now = _whole(next((r.get("generation") for r in status.get("responders") or [] if isinstance(r, dict) and r.get("capability") == capability), None))
|
|
314
|
+
held = self._replay(capability, held, now) if "asking" in held else held # its own lost ask first
|
|
315
|
+
if "moved" in held and _is_int(held.get("moved"), 1) and not (_is_int(now, 1) and now > held.get("generation", now)):
|
|
316
|
+
now = held["moved"] # a recorded move stays until take_back, whatever com.status says of the slot now
|
|
317
|
+
if not _is_int(now, 1) or not _is_int(held.get("generation"), 1) or now <= held["generation"]:
|
|
318
|
+
return False
|
|
319
|
+
with self._lock:
|
|
320
|
+
work = self.current
|
|
321
|
+
if work is not None and work.entry["capability"] == capability:
|
|
322
|
+
work.ctx.stopped.set()
|
|
323
|
+
self.current = None
|
|
324
|
+
for entry in [dict(e) for e in self.checkpoint.entries.values() if e.get("capability") == capability]:
|
|
325
|
+
if entry.get("step") == "claimed":
|
|
326
|
+
self.checkpoint.drop(entry["request"]) # the move returned the claim to the queue
|
|
327
|
+
elif entry.get("step") not in ("unknown", "lapsed"):
|
|
328
|
+
# Begun: outcome_unknown, which the slot's holder reconciles. Accepted: held on the board, continued only
|
|
329
|
+
# by a transfer if the slot is this service's again; never a com.responder_replace on its own.
|
|
330
|
+
entry["step"] = "unknown" if entry.get("effect") else "lapsed"
|
|
331
|
+
self.checkpoint.put(entry)
|
|
332
|
+
self._to_resume = [r for r in self._to_resume if (self.checkpoint.entries.get(r) or {}).get("capability") != capability]
|
|
333
|
+
self.slots[capability] = {"state": "moved", "generation": now, "renewed_at": self.now()}
|
|
334
|
+
if "moved" not in held: # once, even across restarts
|
|
335
|
+
self._emit("board.slot.moved", "warn", "The %s slot on this Project was moved to another session. This service stopped "
|
|
336
|
+
"answering it there, left its accepted work held (work already begun is the new holder's to settle) and does not "
|
|
337
|
+
"take the slot back by itself. Its take-back action brings it back once the other session lets it go; without one, ask its "
|
|
338
|
+
"maintainer." % capability,
|
|
339
|
+
notify=True, episode=capability, capability=capability, generation=now, held=held["generation"])
|
|
340
|
+
self._note(capability, dict(held, moved=now)) # said again at the next start if it cannot be written
|
|
341
|
+
return True
|
|
342
|
+
|
|
343
|
+
def take_back(self, capability: str) -> None:
|
|
344
|
+
"""The operator's act, never the kit's: forget the move; the next tick enrolls once the slot is free (FAA-155)."""
|
|
345
|
+
if capability not in self.capabilities:
|
|
346
|
+
raise BoardError("invalid_arguments", "This responder does not answer %s." % capability)
|
|
347
|
+
with self._lock:
|
|
348
|
+
self.checkpoint.slot(capability, None)
|
|
349
|
+
if (self.slots.get(capability) or {}).get("state") == "moved":
|
|
350
|
+
self.slots[capability] = {"state": "contested", "renewed_at": 0.0}
|
|
351
|
+
self._said.pop("board.slot.moved", None)
|
|
352
|
+
|
|
267
353
|
# -- restart: reconcile and transfer -------------------------------------------------------------------------
|
|
268
354
|
def _read_state(self, entry: Dict[str, Any]) -> Optional[str]:
|
|
269
355
|
message = self._call("com.get", {"message": entry["message"]})
|
|
@@ -293,17 +379,20 @@ class Responder:
|
|
|
293
379
|
else: # in_progress (lost result once a new generation holds the slot) or outcome_unknown
|
|
294
380
|
reconcile.append(entry)
|
|
295
381
|
responders = [r for r in status.get("responders") or [] if isinstance(r, dict)]
|
|
296
|
-
generations = {r.get("capability"): r.get("generation", 0) for r in responders}
|
|
382
|
+
generations = {r.get("capability"): _whole(r.get("generation", 0)) for r in responders}
|
|
297
383
|
on_hold = {r.get("capability") for r in responders if r.get("held") is True} # DEC-0079
|
|
298
384
|
for capability in capabilities:
|
|
299
|
-
held = transfer.get(capability)
|
|
385
|
+
held, was = transfer.get(capability), (self.checkpoint.slots.get(capability) or {}).get("generation")
|
|
386
|
+
if self._gone(capability, status):
|
|
387
|
+
continue # moved away: never replaced at recovery (FAA-155)
|
|
300
388
|
if capability in on_hold:
|
|
301
389
|
for entry in held or []:
|
|
302
390
|
entry["step"] = "lapsed" # taken back by a transfer once the hold is lifted
|
|
303
391
|
self.checkpoint.put(entry)
|
|
304
392
|
self._pause(capability)
|
|
305
|
-
elif held:
|
|
306
|
-
self.
|
|
393
|
+
elif held: # at the generation a replay of its own lost ask just recorded, else com.status's (seam 6)
|
|
394
|
+
mine = (self.checkpoint.slots.get(capability) or {}).get("generation")
|
|
395
|
+
self._replace(capability, held, mine if _is_int(mine, 1) and mine != was else generations.get(capability, 0))
|
|
307
396
|
else:
|
|
308
397
|
self._enroll(capability)
|
|
309
398
|
for entry in reconcile:
|
|
@@ -321,8 +410,10 @@ class Responder:
|
|
|
321
410
|
args["transfer"] = {"requests": [e["request"] for e in entries][:64],
|
|
322
411
|
"checkpoint": {"summary": "Resumed by the same service after a restart; no effect had begun."}}
|
|
323
412
|
try:
|
|
324
|
-
receipt = self._receipt("com.responder_replace", self.
|
|
413
|
+
receipt = self._receipt("com.responder_replace", self._ask(capability, "com.responder_replace", args))
|
|
325
414
|
except BoardError as exc:
|
|
415
|
+
if exc.code == "fenced" and (self.slots.get(capability) or {}).get("state") == "moved":
|
|
416
|
+
return # an earlier lost ask showed the slot moved: said once as board.slot.moved, no outage
|
|
326
417
|
if exc.code != "held":
|
|
327
418
|
raise
|
|
328
419
|
for entry in entries:
|
|
@@ -331,6 +422,7 @@ class Responder:
|
|
|
331
422
|
self._pause(capability)
|
|
332
423
|
return
|
|
333
424
|
self.slots[capability] = {"state": "held", "generation": receipt["slot"]["generation"], "renewed_at": self.now()}
|
|
425
|
+
self._held_at(capability, receipt["slot"]["generation"])
|
|
334
426
|
self._lifted(capability)
|
|
335
427
|
moved = {item["request"]: item for item in receipt.get("transferred") or []}
|
|
336
428
|
for entry in entries:
|
|
@@ -471,7 +563,8 @@ class Responder:
|
|
|
471
563
|
return min(renewed) + LEASE_SECONDS - self.now() if renewed and self.session is not None else 0.0
|
|
472
564
|
|
|
473
565
|
def _fault(self, exc: Exception) -> float:
|
|
474
|
-
code = getattr(exc, "code", "unreachable")
|
|
566
|
+
code, slots = getattr(exc, "code", "unreachable"), self.slots.values()
|
|
567
|
+
connecting = self.state == "connecting" and not any(s.get("state") == "held" for s in slots)
|
|
475
568
|
wait = self.backoff.next()
|
|
476
569
|
left = self._lease_left() - LEASE_MARGIN_SECONDS
|
|
477
570
|
keep = code == "unreachable" and left > 0 # finding 38: the lease holds, so the session and the work stay
|
|
@@ -496,15 +589,15 @@ class Responder:
|
|
|
496
589
|
notify=True, episode=code, reason=code)
|
|
497
590
|
else:
|
|
498
591
|
self.state = "down"
|
|
499
|
-
|
|
592
|
+
# In recovery with no slot held a refusal reconnects (seam 6); with some held they are served (seam 7).
|
|
593
|
+
if (connecting or code in ("session-ended", "unreachable", "not-running", "not-a-hub", "hub-unavailable", "protocol")) and not keep:
|
|
500
594
|
self._drop_session()
|
|
501
595
|
self._emit("board.down", "warn", "Fabric's board cannot be reached; the responder tries again with a growing pause.",
|
|
502
596
|
episode="down", reason=code)
|
|
503
597
|
return wait
|
|
504
598
|
|
|
505
599
|
def _drop_session(self) -> None:
|
|
506
|
-
work, self.current = self.current, None
|
|
507
|
-
self._to_resume = [] # the next session transfers them again
|
|
600
|
+
work, self.current, self._to_resume = self.current, None, [] # the next session transfers them again
|
|
508
601
|
if work is not None:
|
|
509
602
|
entry = work.entry
|
|
510
603
|
if work.done.is_set() and work.failure is None and not outcome_problems(work.outcome, bool(entry.get("effect"))):
|
|
@@ -528,7 +621,12 @@ class Responder:
|
|
|
528
621
|
else:
|
|
529
622
|
self._renew(work)
|
|
530
623
|
held_by_work = work.entry["capability"] if work is not None and self.current is not None else None
|
|
624
|
+
for capability in list(self._suspect): # kept until com.status answered, so it is asked before any enroll
|
|
625
|
+
self._gone(capability)
|
|
626
|
+
self._suspect.discard(capability)
|
|
531
627
|
for capability, slot in list(self.slots.items()):
|
|
628
|
+
if slot.get("state") == "moved":
|
|
629
|
+
continue # never enrolled again on its own, even when the new holder's lease lapses (FAA-155)
|
|
532
630
|
if slot.get("state") == "paused":
|
|
533
631
|
if capability != held_by_work and self.now() >= slot.get("retry_at", 0.0):
|
|
534
632
|
self._enroll(capability) # still held: a longer pause; lifted: the slot is ours again
|
|
@@ -557,8 +655,7 @@ class Responder:
|
|
|
557
655
|
self._replace(capability, live, self.slots[capability].get("generation", 0))
|
|
558
656
|
|
|
559
657
|
def _reconcile_pending(self) -> None:
|
|
560
|
-
"""Work whose outcome became unknown while running
|
|
561
|
-
again every five minutes: the slot's holder reconciles from what `observe` reads, or it stays unknown."""
|
|
658
|
+
"""Work whose outcome became unknown while running is reconciled from `observe` every five minutes, or stays so."""
|
|
562
659
|
if self.now() < getattr(self, "_reconcile_at", 0.0):
|
|
563
660
|
return
|
|
564
661
|
self._reconcile_at = self.now() + 300.0
|
|
@@ -575,6 +672,7 @@ class Responder:
|
|
|
575
672
|
answer = self._receipt("com.claim", self._call("com.claim", {"idempotency": self._idem(key), "capability": capability}))
|
|
576
673
|
except BoardError as exc:
|
|
577
674
|
if exc.code == "lease_expired":
|
|
675
|
+
self.slots[capability]["renewed_at"] = float("-inf") # lapsed: the enroll below is recorded first
|
|
578
676
|
self._enroll(capability) # the slot lapsed: enroll again (a new generation)
|
|
579
677
|
continue
|
|
580
678
|
if exc.code in ("idempotency_window_expired", "capacity_exceeded"):
|
|
@@ -582,7 +680,8 @@ class Responder:
|
|
|
582
680
|
self._status()
|
|
583
681
|
continue
|
|
584
682
|
if exc.code == "fenced":
|
|
585
|
-
|
|
683
|
+
if not self._gone(capability):
|
|
684
|
+
self.slots[capability] = {"state": "contested", "renewed_at": self.now()}
|
|
586
685
|
continue
|
|
587
686
|
if exc.code == "held":
|
|
588
687
|
self._pause(capability)
|
|
@@ -678,8 +777,7 @@ class Responder:
|
|
|
678
777
|
self._finish(work)
|
|
679
778
|
|
|
680
779
|
def _resume(self, entry: Dict[str, Any]) -> None:
|
|
681
|
-
"""A transferred request continues:
|
|
682
|
-
effect had begun, so nothing happened yet)."""
|
|
780
|
+
"""A transferred request continues: a known outcome is sent; else the handler runs again (no effect had begun)."""
|
|
683
781
|
if isinstance(entry.get("outcome"), dict):
|
|
684
782
|
self._complete(entry)
|
|
685
783
|
return
|
|
@@ -706,6 +804,8 @@ class Responder:
|
|
|
706
804
|
self._read_state(entry)
|
|
707
805
|
continue
|
|
708
806
|
if exc.code in STOP_CODES or exc.code in ("revision_conflict", "invalid_transition", "digest_conflict"):
|
|
807
|
+
if exc.code == "fenced":
|
|
808
|
+
self._suspect.add(entry["capability"]) # the next tick asks com.status whether the slot moved
|
|
709
809
|
self._lose(entry, exc.code)
|
|
710
810
|
if exc.code == "authority_changed":
|
|
711
811
|
raise
|
|
@@ -722,13 +822,16 @@ class Responder:
|
|
|
722
822
|
self._emit("board.reconcile.needed", "warn", "The attempt was lost after its effect began; the outcome stays "
|
|
723
823
|
"unknown until this service observes what happened.", notify=True, episode=entry["request"],
|
|
724
824
|
request=entry["request"], capability=entry["capability"], reason=code)
|
|
725
|
-
elif
|
|
726
|
-
|
|
727
|
-
#
|
|
825
|
+
elif entry.get("step") not in ("claimed", "accepting") and (code == "lease_expired" or (
|
|
826
|
+
code == "fenced" and entry.get("capability") in self.checkpoint.slots)):
|
|
827
|
+
# Accepted work whose lease lapsed, or fenced off a slot this responder held (moved, FAA-155): the board holds
|
|
828
|
+
# it (C4). It continues only by a checkpoint transfer to this responder while it holds the slot.
|
|
728
829
|
entry["step"] = "lapsed"
|
|
729
830
|
self.checkpoint.put(entry)
|
|
730
|
-
self._emit("board.attempt.lost", "warn", "This service's lease on an accepted request lapsed; it stopped working "
|
|
731
|
-
"on it and takes it back by a transfer."
|
|
831
|
+
self._emit("board.attempt.lost", "warn", ("This service's lease on an accepted request lapsed; it stopped working "
|
|
832
|
+
"on it and takes it back by a transfer." if code == "lease_expired" else "The board fenced an accepted "
|
|
833
|
+
"request away from this service; it stopped working on it, and nothing had been done outside it."),
|
|
834
|
+
request=entry["request"], capability=entry["capability"], reason=code)
|
|
732
835
|
else:
|
|
733
836
|
self.checkpoint.drop(entry["request"])
|
|
734
837
|
self._emit("board.attempt.lost", "warn", "The board took a request back from this service (%s); it stopped working "
|
|
@@ -893,4 +996,5 @@ class Responder:
|
|
|
893
996
|
if self.session is not None:
|
|
894
997
|
self.session.close()
|
|
895
998
|
self.session = None
|
|
999
|
+
self.checkpoint.leave(self.project) # the slots were released: no record of them can read as a move
|
|
896
1000
|
self.state = "stopped"
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fabric-service-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.1",
|
|
4
4
|
"about": "The service kit's modules in dependency order: a module imports only modules listed before it. Written by fabric_kit.py build; edit the order and layers by hand, never the digests.",
|
|
5
5
|
"python": [
|
|
6
6
|
{
|
|
@@ -426,7 +426,7 @@
|
|
|
426
426
|
"fabric_hub",
|
|
427
427
|
"fabric_service"
|
|
428
428
|
],
|
|
429
|
-
"sha256": "
|
|
429
|
+
"sha256": "55b552354deb60e50268c672912647bca57951fd6a3a27fb5d6dae11ec5a4fd7"
|
|
430
430
|
},
|
|
431
431
|
{
|
|
432
432
|
"module": "fabric_board_session",
|
|
@@ -438,7 +438,7 @@
|
|
|
438
438
|
"fabric_hub",
|
|
439
439
|
"fabric_service"
|
|
440
440
|
],
|
|
441
|
-
"sha256": "
|
|
441
|
+
"sha256": "e2b45c274e6eadcb020ec075950750a4f88d6293149b19b6b78a8d41ab669918"
|
|
442
442
|
},
|
|
443
443
|
{
|
|
444
444
|
"module": "fabric_board_fixture",
|
|
@@ -898,7 +898,7 @@
|
|
|
898
898
|
"fabric-hub",
|
|
899
899
|
"fabric-service"
|
|
900
900
|
],
|
|
901
|
-
"sha256": "
|
|
901
|
+
"sha256": "ed93ad15e0f48627e545b561c4a562007d1b256c55f650126333aa6566876b12"
|
|
902
902
|
},
|
|
903
903
|
{
|
|
904
904
|
"module": "fabric-board-session",
|
|
@@ -910,7 +910,7 @@
|
|
|
910
910
|
"fabric-hub",
|
|
911
911
|
"fabric-service"
|
|
912
912
|
],
|
|
913
|
-
"sha256": "
|
|
913
|
+
"sha256": "9a70016543dded33bbbfe9dc7705e684387abd1d889598b579f1ca26927023cb"
|
|
914
914
|
},
|
|
915
915
|
{
|
|
916
916
|
"module": "fabric-board-fixture",
|
|
@@ -3766,6 +3766,182 @@
|
|
|
3766
3766
|
"fabric-openrouter.mjs": "ab6c4c7251d563980d895061ee19976bde2f364c21b6354efdd9a9027b1ea139",
|
|
3767
3767
|
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
3768
3768
|
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
3769
|
+
},
|
|
3770
|
+
"0.53.0": {
|
|
3771
|
+
"fabric_jsvalue.py": "12888a2c934a885d35999ac9782eaedc8d04946d9775213344a235a8814d3d28",
|
|
3772
|
+
"fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
|
|
3773
|
+
"fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
|
|
3774
|
+
"fabric_secrets.py": "68a0b5e9e90c468ec9ac67b2f0fd1848e08813d6ec1ec14a2a47337eb1a98911",
|
|
3775
|
+
"fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
|
|
3776
|
+
"fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
|
|
3777
|
+
"fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
|
|
3778
|
+
"fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
|
|
3779
|
+
"fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
|
|
3780
|
+
"fabric_estate.py": "b3cd56dcb86e04d252980322ac8054da1aaef24ac64690f7aa80e7c765ee6f60",
|
|
3781
|
+
"fabric_mandate.py": "050c43d0166a7dfba82e29c4e978f05c364cf6da06d0a781bd023a566777c17b",
|
|
3782
|
+
"fabric_delegation.py": "650b8bb0827f2e56c7f6dc98bd975acff19627491fd713d6897e14f59f107eaa",
|
|
3783
|
+
"fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
|
|
3784
|
+
"fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
|
|
3785
|
+
"fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
|
|
3786
|
+
"fabric_package.py": "ff533f1b3f92b4ea0f532fcec50b68c610d47549930eef8ad5247692a64dfb3c",
|
|
3787
|
+
"fabric_protocols.py": "54ff0f26a8bda640c7edd826cf59151b6ff0483fe72e9c0626c0ba1b79738cd4",
|
|
3788
|
+
"fabric_actions.py": "6f7c4110b755e31f948645fba8d15823f350b7e7a3a973b01606702d0f0a0d94",
|
|
3789
|
+
"fabric_connections.py": "77d3ccf863c70e9209d21ef45c46a6db9c06237019bc20d54f8d3946873f3f11",
|
|
3790
|
+
"fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
|
|
3791
|
+
"fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
|
|
3792
|
+
"fabric_lock.py": "ba2399d2137c7a7791b7133b9db9cca36693f4eb7e30955b25a75ade814e95ba",
|
|
3793
|
+
"fabric_usage.py": "b1f335cab4625171a616cf68cf3fd892b18bdadcf4baec3a3a0fdcf12785c170",
|
|
3794
|
+
"fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
|
|
3795
|
+
"fabric_descriptor.py": "c973def06b68dce7ef369da77186542ce3afe357d4579a7d409604f06e4bf131",
|
|
3796
|
+
"fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
|
|
3797
|
+
"fabric_auth.py": "71115d18c67b7b806cb57b64b77e01ad97fc0e63e6f49ee850f778123617d905",
|
|
3798
|
+
"fabric_events.py": "0086886c2c29dbd5c9d152be9d375c5bdf4a29f89b8095baff8490c2bcd64e47",
|
|
3799
|
+
"fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
|
|
3800
|
+
"fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
|
|
3801
|
+
"fabric_service.py": "a2e813469a47d4d3eea996628635c8903007498bdb6a1ae032a7a5ce6f49fe2f",
|
|
3802
|
+
"fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
|
|
3803
|
+
"fabric_interop_rules.py": "adbc61f490f142092cd27847aa7e2560fc81e5dc892f5cec423c802eca0e99d5",
|
|
3804
|
+
"fabric_interop.py": "e467cee268c1202f86214149004b58718cb587d32650861e456414002493b9e6",
|
|
3805
|
+
"fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
|
|
3806
|
+
"fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
|
|
3807
|
+
"fabric_board_link.py": "55b552354deb60e50268c672912647bca57951fd6a3a27fb5d6dae11ec5a4fd7",
|
|
3808
|
+
"fabric_board_session.py": "e2b45c274e6eadcb020ec075950750a4f88d6293149b19b6b78a8d41ab669918",
|
|
3809
|
+
"fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
|
|
3810
|
+
"fabric_board.py": "0f075894b3e352194fcaf3fc7d08dee4e9f1c215e859769ed85fa5010e4a1599",
|
|
3811
|
+
"fabric_http.py": "5d7f72af3a73fdbe8c1f487ecf60cef23a7c46289eba542b03aff4706b8218d4",
|
|
3812
|
+
"fabric_openrouter.py": "309f588f9c976b1061323f3161e35d86248afd5b59b16391afbfc4e1099ecd65",
|
|
3813
|
+
"fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
|
|
3814
|
+
"fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
|
|
3815
|
+
"fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
|
|
3816
|
+
"fabric-secrets.mjs": "6ad8ab18ac899ecc2384e8ec6a3fb689b92be3b9a5ce3a59b92a37d52da5d766",
|
|
3817
|
+
"fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
|
|
3818
|
+
"fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
|
|
3819
|
+
"fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
|
|
3820
|
+
"fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
|
|
3821
|
+
"fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
|
|
3822
|
+
"fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
|
|
3823
|
+
"fabric-mandate.mjs": "deaab2bfb929d1bd35ae1bad71e7b9ad9902cc23b8eb51819ed592fd80eddf95",
|
|
3824
|
+
"fabric-delegation.mjs": "ddef0956002a3ce7eded792d6aebb1c6515f1a70e88efd603f9dfb6c4db92adb",
|
|
3825
|
+
"fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
|
|
3826
|
+
"fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
|
|
3827
|
+
"fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
|
|
3828
|
+
"fabric-package.mjs": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88",
|
|
3829
|
+
"fabric-protocols.mjs": "7da65440a7a08bf27d90a06e9158b1c4ee6d7f9204eecb712d452406bfe59962",
|
|
3830
|
+
"fabric-actions.mjs": "42e7b6261a886444b74a3cd5694ab5d5cf46c54d8c9540b090e617e7a93ebd21",
|
|
3831
|
+
"fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
|
|
3832
|
+
"fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
|
|
3833
|
+
"fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
|
|
3834
|
+
"fabric-lock.mjs": "ac219ab0a326d5c43c7c4c255e0c3843d4794de795a60e3af661e7d5350ea052",
|
|
3835
|
+
"fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
|
|
3836
|
+
"fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
|
|
3837
|
+
"fabric-descriptor.mjs": "84c35c7ba0ae09fde1dc3f6ab796cd578d008707e77815a0f7b7a80306fa4cce",
|
|
3838
|
+
"fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
|
|
3839
|
+
"fabric-auth.mjs": "782c2aa732f8320f604194660117640a7ea145e12b8155c04e88cef7c516a5b3",
|
|
3840
|
+
"fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
|
|
3841
|
+
"fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
|
|
3842
|
+
"fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
|
|
3843
|
+
"fabric-service.mjs": "6aa673657427211b491a9b63f94b9847a7e6aa3ee1b7353e1af11f46469803a3",
|
|
3844
|
+
"fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
|
|
3845
|
+
"fabric-interop-rules.mjs": "8a55a794c9055e381c761c6f5b4df9fa93d705526fa0bce5dba978aed1b4a804",
|
|
3846
|
+
"fabric-interop.mjs": "b66fccf4edc1448b3cda31cf036a789574cf07b5044d8433e4a5bf12a9c9aa7a",
|
|
3847
|
+
"fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
|
|
3848
|
+
"fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
|
|
3849
|
+
"fabric-board-link.mjs": "ed93ad15e0f48627e545b561c4a562007d1b256c55f650126333aa6566876b12",
|
|
3850
|
+
"fabric-board-session.mjs": "9a70016543dded33bbbfe9dc7705e684387abd1d889598b579f1ca26927023cb",
|
|
3851
|
+
"fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
|
|
3852
|
+
"fabric-board.mjs": "9e7bfe4d412521982224ef12bebfc2ff255eeeaf8d96cf40234ac18de68e7a59",
|
|
3853
|
+
"fabric-http.mjs": "f886bcb49c4353d039387ac9568738dda26697a94dd07fc6a6204500f0bc27bf",
|
|
3854
|
+
"fabric-openrouter.mjs": "ab6c4c7251d563980d895061ee19976bde2f364c21b6354efdd9a9027b1ea139",
|
|
3855
|
+
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
3856
|
+
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
3857
|
+
},
|
|
3858
|
+
"0.53.1": {
|
|
3859
|
+
"fabric_jsvalue.py": "12888a2c934a885d35999ac9782eaedc8d04946d9775213344a235a8814d3d28",
|
|
3860
|
+
"fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
|
|
3861
|
+
"fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
|
|
3862
|
+
"fabric_secrets.py": "68a0b5e9e90c468ec9ac67b2f0fd1848e08813d6ec1ec14a2a47337eb1a98911",
|
|
3863
|
+
"fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
|
|
3864
|
+
"fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
|
|
3865
|
+
"fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
|
|
3866
|
+
"fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
|
|
3867
|
+
"fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
|
|
3868
|
+
"fabric_estate.py": "b3cd56dcb86e04d252980322ac8054da1aaef24ac64690f7aa80e7c765ee6f60",
|
|
3869
|
+
"fabric_mandate.py": "050c43d0166a7dfba82e29c4e978f05c364cf6da06d0a781bd023a566777c17b",
|
|
3870
|
+
"fabric_delegation.py": "650b8bb0827f2e56c7f6dc98bd975acff19627491fd713d6897e14f59f107eaa",
|
|
3871
|
+
"fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
|
|
3872
|
+
"fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
|
|
3873
|
+
"fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
|
|
3874
|
+
"fabric_package.py": "ff533f1b3f92b4ea0f532fcec50b68c610d47549930eef8ad5247692a64dfb3c",
|
|
3875
|
+
"fabric_protocols.py": "54ff0f26a8bda640c7edd826cf59151b6ff0483fe72e9c0626c0ba1b79738cd4",
|
|
3876
|
+
"fabric_actions.py": "6f7c4110b755e31f948645fba8d15823f350b7e7a3a973b01606702d0f0a0d94",
|
|
3877
|
+
"fabric_connections.py": "77d3ccf863c70e9209d21ef45c46a6db9c06237019bc20d54f8d3946873f3f11",
|
|
3878
|
+
"fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
|
|
3879
|
+
"fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
|
|
3880
|
+
"fabric_lock.py": "ba2399d2137c7a7791b7133b9db9cca36693f4eb7e30955b25a75ade814e95ba",
|
|
3881
|
+
"fabric_usage.py": "b1f335cab4625171a616cf68cf3fd892b18bdadcf4baec3a3a0fdcf12785c170",
|
|
3882
|
+
"fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
|
|
3883
|
+
"fabric_descriptor.py": "c973def06b68dce7ef369da77186542ce3afe357d4579a7d409604f06e4bf131",
|
|
3884
|
+
"fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
|
|
3885
|
+
"fabric_auth.py": "71115d18c67b7b806cb57b64b77e01ad97fc0e63e6f49ee850f778123617d905",
|
|
3886
|
+
"fabric_events.py": "0086886c2c29dbd5c9d152be9d375c5bdf4a29f89b8095baff8490c2bcd64e47",
|
|
3887
|
+
"fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
|
|
3888
|
+
"fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
|
|
3889
|
+
"fabric_service.py": "a2e813469a47d4d3eea996628635c8903007498bdb6a1ae032a7a5ce6f49fe2f",
|
|
3890
|
+
"fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
|
|
3891
|
+
"fabric_interop_rules.py": "adbc61f490f142092cd27847aa7e2560fc81e5dc892f5cec423c802eca0e99d5",
|
|
3892
|
+
"fabric_interop.py": "e467cee268c1202f86214149004b58718cb587d32650861e456414002493b9e6",
|
|
3893
|
+
"fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
|
|
3894
|
+
"fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
|
|
3895
|
+
"fabric_board_link.py": "55b552354deb60e50268c672912647bca57951fd6a3a27fb5d6dae11ec5a4fd7",
|
|
3896
|
+
"fabric_board_session.py": "e2b45c274e6eadcb020ec075950750a4f88d6293149b19b6b78a8d41ab669918",
|
|
3897
|
+
"fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
|
|
3898
|
+
"fabric_board.py": "0f075894b3e352194fcaf3fc7d08dee4e9f1c215e859769ed85fa5010e4a1599",
|
|
3899
|
+
"fabric_http.py": "5d7f72af3a73fdbe8c1f487ecf60cef23a7c46289eba542b03aff4706b8218d4",
|
|
3900
|
+
"fabric_openrouter.py": "309f588f9c976b1061323f3161e35d86248afd5b59b16391afbfc4e1099ecd65",
|
|
3901
|
+
"fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
|
|
3902
|
+
"fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
|
|
3903
|
+
"fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
|
|
3904
|
+
"fabric-secrets.mjs": "6ad8ab18ac899ecc2384e8ec6a3fb689b92be3b9a5ce3a59b92a37d52da5d766",
|
|
3905
|
+
"fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
|
|
3906
|
+
"fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
|
|
3907
|
+
"fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
|
|
3908
|
+
"fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
|
|
3909
|
+
"fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
|
|
3910
|
+
"fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
|
|
3911
|
+
"fabric-mandate.mjs": "deaab2bfb929d1bd35ae1bad71e7b9ad9902cc23b8eb51819ed592fd80eddf95",
|
|
3912
|
+
"fabric-delegation.mjs": "ddef0956002a3ce7eded792d6aebb1c6515f1a70e88efd603f9dfb6c4db92adb",
|
|
3913
|
+
"fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
|
|
3914
|
+
"fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
|
|
3915
|
+
"fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
|
|
3916
|
+
"fabric-package.mjs": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88",
|
|
3917
|
+
"fabric-protocols.mjs": "7da65440a7a08bf27d90a06e9158b1c4ee6d7f9204eecb712d452406bfe59962",
|
|
3918
|
+
"fabric-actions.mjs": "42e7b6261a886444b74a3cd5694ab5d5cf46c54d8c9540b090e617e7a93ebd21",
|
|
3919
|
+
"fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
|
|
3920
|
+
"fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
|
|
3921
|
+
"fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
|
|
3922
|
+
"fabric-lock.mjs": "ac219ab0a326d5c43c7c4c255e0c3843d4794de795a60e3af661e7d5350ea052",
|
|
3923
|
+
"fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
|
|
3924
|
+
"fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
|
|
3925
|
+
"fabric-descriptor.mjs": "84c35c7ba0ae09fde1dc3f6ab796cd578d008707e77815a0f7b7a80306fa4cce",
|
|
3926
|
+
"fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
|
|
3927
|
+
"fabric-auth.mjs": "782c2aa732f8320f604194660117640a7ea145e12b8155c04e88cef7c516a5b3",
|
|
3928
|
+
"fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
|
|
3929
|
+
"fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
|
|
3930
|
+
"fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
|
|
3931
|
+
"fabric-service.mjs": "6aa673657427211b491a9b63f94b9847a7e6aa3ee1b7353e1af11f46469803a3",
|
|
3932
|
+
"fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
|
|
3933
|
+
"fabric-interop-rules.mjs": "8a55a794c9055e381c761c6f5b4df9fa93d705526fa0bce5dba978aed1b4a804",
|
|
3934
|
+
"fabric-interop.mjs": "b66fccf4edc1448b3cda31cf036a789574cf07b5044d8433e4a5bf12a9c9aa7a",
|
|
3935
|
+
"fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
|
|
3936
|
+
"fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
|
|
3937
|
+
"fabric-board-link.mjs": "ed93ad15e0f48627e545b561c4a562007d1b256c55f650126333aa6566876b12",
|
|
3938
|
+
"fabric-board-session.mjs": "9a70016543dded33bbbfe9dc7705e684387abd1d889598b579f1ca26927023cb",
|
|
3939
|
+
"fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
|
|
3940
|
+
"fabric-board.mjs": "9e7bfe4d412521982224ef12bebfc2ff255eeeaf8d96cf40234ac18de68e7a59",
|
|
3941
|
+
"fabric-http.mjs": "f886bcb49c4353d039387ac9568738dda26697a94dd07fc6a6204500f0bc27bf",
|
|
3942
|
+
"fabric-openrouter.mjs": "ab6c4c7251d563980d895061ee19976bde2f364c21b6354efdd9a9027b1ea139",
|
|
3943
|
+
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
3944
|
+
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
3769
3945
|
}
|
|
3770
3946
|
},
|
|
3771
3947
|
"data": {
|