@passioncode-ai/fabric-agent-adapter 0.40.0 → 0.41.0
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 +124 -0
- package/README.md +9 -3
- package/SECURITY.md +27 -6
- package/bin/fabric-agent-adapter.js +71 -6
- package/package.json +2 -2
- package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +42 -66
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +4 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/operator-channel-and-repository.md +55 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +9 -3
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/verification.md +15 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +181 -26
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/check_dashboard_link.py +66 -5
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +81 -23
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_secrets.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +28 -30
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +21 -9
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.css +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.js +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/kit.json +9 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +47 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support_agent.py +230 -29
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +21 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +5 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +3 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +5 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_layout.mjs +48 -19
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_link.py +66 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/dashboard_kit.py +53 -19
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-link.mjs +145 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +196 -188
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-shapes.mjs +5 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board.mjs +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-ceremonies.mjs +6 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +3 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +48 -14
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-lock.mjs +34 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +144 -20
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +7 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +12 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_backups.py +6 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_base.py +17 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board.py +3 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_link.py +162 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +207 -227
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_shapes.py +4 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_ceremonies.py +0 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_crypto.py +52 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +101 -45
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +12 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +5 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_events.py +13 -10
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_hub.py +6 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +118 -71
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +272 -74
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_profiles.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +36 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_usage.py +3 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +130 -35
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +25 -7
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +29 -60
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +4 -2
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/operator-channel-and-repository.md +55 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py +66 -5
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +3 -3
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/native-host-hooks.md +2 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +26 -13
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +6 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +30 -22
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +37 -6
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_hosts.py +72 -13
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mail.py +25 -7
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mcp.py +199 -43
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/host_hooks.py +10 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/hosts/context.mjs +25 -2
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/hosts/openclaw/index.mjs +4 -2
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/hosts/opencode/fabric.mjs +2 -1
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_link.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""The board link (DEC-0078, FAA-12b): the board session over the hub, the fsynced checkpoint and a claimed request as
|
|
2
|
+
data. Part of the board connector (FAA-65, layer L4), below the responder in fabric_board_session, which re-exports these
|
|
3
|
+
names. Standard library only, Python 3.9+."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import threading
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
from typing import Callable
|
|
11
|
+
from typing import Dict
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
import sys as _sys # noqa: E402
|
|
15
|
+
from pathlib import Path as _Path # noqa: E402
|
|
16
|
+
if str(_Path(__file__).resolve().parent) not in _sys.path: # the kit loads from any working directory (FAA-65)
|
|
17
|
+
_sys.path.insert(0, str(_Path(__file__).resolve().parent))
|
|
18
|
+
import fabric_hub as _hub
|
|
19
|
+
import fabric_service as _fs
|
|
20
|
+
from fabric_board_shapes import ( # noqa: E402 - the kit (FAA-65)
|
|
21
|
+
BoardError,
|
|
22
|
+
RESPOND_TOOLS,
|
|
23
|
+
refusal_problems,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BoardSession:
|
|
28
|
+
"""MCP over HTTP with a board session's one-session bearer: `open()` spends it with `initialize` and keeps the
|
|
29
|
+
`mcp-session-id`; `call(tool, args)` answers the tool's value or raises `BoardError`; `close()` sends the session's
|
|
30
|
+
DELETE, which releases every slot it holds (DEC-0073 §5). Thread-safe; the bearer never leaves the headers."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, grant: _hub.BoardGrant, *, timeout: float = 15.0, post: Callable[..., Any] = _hub.post_mcp):
|
|
33
|
+
self.grant, self.timeout, self.post = grant, timeout, post
|
|
34
|
+
self.mcp_session: Optional[str] = None
|
|
35
|
+
self.protocol_version = _hub.MCP_PROTOCOL_VERSION
|
|
36
|
+
self._lock = threading.Lock()
|
|
37
|
+
self._rpc = 0
|
|
38
|
+
|
|
39
|
+
def _send(self, body: Dict[str, Any], method: str = "POST"):
|
|
40
|
+
try:
|
|
41
|
+
return self.post(self.grant.origin, body, self.grant.bearer(), session=self.mcp_session, timeout=self.timeout,
|
|
42
|
+
method=method, protocol_version=self.protocol_version if self.mcp_session else None)
|
|
43
|
+
except _hub.HubError as exc:
|
|
44
|
+
raise BoardError("unreachable", "Fabric's hub did not answer; a write may have been stored, so send it again with "
|
|
45
|
+
"the same idempotency key.", True) from exc
|
|
46
|
+
|
|
47
|
+
def open(self) -> None:
|
|
48
|
+
with self._lock:
|
|
49
|
+
self._rpc += 1
|
|
50
|
+
status, headers, parsed = self._send({"jsonrpc": "2.0", "id": self._rpc, "method": "initialize", "params": {
|
|
51
|
+
"protocolVersion": _hub.MCP_PROTOCOL_VERSION, "capabilities": {},
|
|
52
|
+
"clientInfo": {"name": "fabric-agent-adapter-board", "version": "1"}}})
|
|
53
|
+
if status == 401:
|
|
54
|
+
raise BoardError("session-ended", "Fabric refused the board session's bearer; join again.", True)
|
|
55
|
+
if status != 200 or not isinstance(parsed, dict) or not isinstance(parsed.get("result"), dict) or not headers.get("mcp-session-id"):
|
|
56
|
+
raise BoardError("unreachable", "Fabric did not open the board session (HTTP %d)." % status, True)
|
|
57
|
+
self.mcp_session = headers["mcp-session-id"]
|
|
58
|
+
version = parsed["result"].get("protocolVersion")
|
|
59
|
+
if isinstance(version, str):
|
|
60
|
+
self.protocol_version = version
|
|
61
|
+
self._send({"jsonrpc": "2.0", "method": "notifications/initialized"})
|
|
62
|
+
|
|
63
|
+
def call(self, tool: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
64
|
+
if tool not in RESPOND_TOOLS:
|
|
65
|
+
raise BoardError("not-in-scope", "%s is not served to a board session; use a Project session for it." % tool, op=tool)
|
|
66
|
+
with self._lock:
|
|
67
|
+
if self.mcp_session is None:
|
|
68
|
+
raise BoardError("session-ended", "The board session is not open.", True, op=tool)
|
|
69
|
+
self._rpc += 1
|
|
70
|
+
status, _, parsed = self._send({"jsonrpc": "2.0", "id": self._rpc, "method": "tools/call",
|
|
71
|
+
"params": {"name": tool, "arguments": args}})
|
|
72
|
+
if status == 401:
|
|
73
|
+
raise BoardError("session-ended", "Fabric ended the board session (it left, was revoked or expired); join again.", True, op=tool)
|
|
74
|
+
try:
|
|
75
|
+
value = _hub.tool_answer(status, parsed, tool)
|
|
76
|
+
except _hub.HubError as exc:
|
|
77
|
+
raise BoardError("unreachable" if exc.retryable else "receipt-shape", exc.message, exc.retryable, op=tool) from None
|
|
78
|
+
if "error" in value:
|
|
79
|
+
problems = refusal_problems(value)
|
|
80
|
+
if problems:
|
|
81
|
+
raise BoardError("receipt-shape", "Fabric refused %s in a form the contract does not name." % tool, op=tool)
|
|
82
|
+
e = value["error"]
|
|
83
|
+
raise BoardError(e["code"], e["message"], e["retryable"], op=tool)
|
|
84
|
+
return value
|
|
85
|
+
|
|
86
|
+
def close(self) -> None:
|
|
87
|
+
with self._lock:
|
|
88
|
+
if self.mcp_session is None:
|
|
89
|
+
return
|
|
90
|
+
try:
|
|
91
|
+
self._send({}, method="DELETE")
|
|
92
|
+
except BoardError:
|
|
93
|
+
pass # best effort: the lease lapses in 60 s anyway
|
|
94
|
+
self.mcp_session = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Checkpoint:
|
|
98
|
+
"""The responder's own journal: one JSON file (0600) holding every request it has claimed and not yet settled, each
|
|
99
|
+
with its fence and the idempotency key of the step in flight. Written atomically and fsynced BEFORE the step it
|
|
100
|
+
announces is sent; a write that fails raises `BoardError("checkpoint")` and the step is not taken."""
|
|
101
|
+
|
|
102
|
+
def __init__(self, path: Path):
|
|
103
|
+
self.path = Path(path)
|
|
104
|
+
self._lock = threading.Lock()
|
|
105
|
+
self.entries: Dict[str, Dict[str, Any]] = self._load()
|
|
106
|
+
|
|
107
|
+
def _load(self) -> Dict[str, Dict[str, Any]]:
|
|
108
|
+
try:
|
|
109
|
+
doc = json.loads(self.path.read_text(encoding="utf-8"))
|
|
110
|
+
except FileNotFoundError:
|
|
111
|
+
return {}
|
|
112
|
+
except (OSError, ValueError) as exc:
|
|
113
|
+
raise BoardError("checkpoint", "The board checkpoint %s cannot be read (%s); fix or move it before answering "
|
|
114
|
+
"requests." % (self.path, exc.__class__.__name__)) from None
|
|
115
|
+
entries = doc.get("requests") if isinstance(doc, dict) and doc.get("version") == 1 else None
|
|
116
|
+
if not isinstance(entries, dict):
|
|
117
|
+
raise BoardError("checkpoint", "The board checkpoint %s is not one this kit writes." % self.path)
|
|
118
|
+
return entries
|
|
119
|
+
|
|
120
|
+
def _write(self, entries: Dict[str, Dict[str, Any]]) -> None:
|
|
121
|
+
try:
|
|
122
|
+
_fs.ensure_private_dir(self.path.parent)
|
|
123
|
+
_fs.atomic_write(self.path, (json.dumps({"version": 1, "requests": entries}, indent=1, sort_keys=True) + "\n").encode())
|
|
124
|
+
except OSError as exc:
|
|
125
|
+
raise BoardError("checkpoint", "The board checkpoint could not be written (%s); nothing was sent." % (exc.strerror or exc.__class__.__name__)) from None
|
|
126
|
+
|
|
127
|
+
def put(self, entry: Dict[str, Any]) -> None:
|
|
128
|
+
with self._lock:
|
|
129
|
+
entries = dict(self.entries)
|
|
130
|
+
entries[entry["request"]] = json.loads(json.dumps(entry))
|
|
131
|
+
self._write(entries)
|
|
132
|
+
self.entries = entries
|
|
133
|
+
|
|
134
|
+
def drop(self, request_id: str) -> None:
|
|
135
|
+
with self._lock:
|
|
136
|
+
if request_id not in self.entries:
|
|
137
|
+
return
|
|
138
|
+
entries = dict(self.entries)
|
|
139
|
+
del entries[request_id]
|
|
140
|
+
self._write(entries)
|
|
141
|
+
self.entries = entries
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class BoardRequest:
|
|
145
|
+
"""A claimed request as data: never an instruction. `body` is the requester's text; `sender` its Project id and
|
|
146
|
+
principal (with provenance); `artifacts` its references, unverified."""
|
|
147
|
+
|
|
148
|
+
__slots__ = ("request_id", "message_id", "capability", "body", "body_format", "artifacts", "sender", "deadline", "thread")
|
|
149
|
+
|
|
150
|
+
def __init__(self, message: Dict[str, Any], capability: str):
|
|
151
|
+
req = message.get("request") or {}
|
|
152
|
+
body = message.get("body") or {}
|
|
153
|
+
self.request_id, self.message_id, self.capability = req.get("id"), message.get("id"), capability
|
|
154
|
+
self.body = body.get("text") if isinstance(body, dict) else None
|
|
155
|
+
self.body_format = body.get("format", "text/plain") if isinstance(body, dict) else "text/plain"
|
|
156
|
+
self.artifacts = list(message.get("artifacts") or [])
|
|
157
|
+
self.sender = message.get("sender") or {}
|
|
158
|
+
self.deadline = req.get("deadline")
|
|
159
|
+
self.thread = message.get("thread")
|
|
160
|
+
|
|
161
|
+
def __repr__(self) -> str: # never the body
|
|
162
|
+
return "BoardRequest(%r, %r)" % (self.request_id, self.capability)
|