@passioncode-ai/fabric-agent-adapter 0.39.1 → 0.40.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 +79 -0
- package/README.md +9 -3
- package/package.json +1 -1
- package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +15 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +64 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/config.example.json +15 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/knowledge.example.json +13 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support_agent.py +264 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/connectors.md +93 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +128 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/usage.md +2 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +10 -183
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +204 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +286 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-backups.mjs +327 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-base.mjs +47 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-fixture.mjs +79 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +840 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-shapes.mjs +340 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board.mjs +42 -1237
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-ceremonies.mjs +1 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-crypto.mjs +41 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +13 -45
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +387 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +8 -67
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-events.mjs +52 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-http.mjs +231 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-jsvalue.mjs +67 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-lock.mjs +202 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-logs.mjs +136 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-openrouter.mjs +335 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +2 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-paths.mjs +69 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-platform.mjs +164 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-profiles.mjs +1 -32
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +122 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +177 -2541
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-share.mjs +1 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +400 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-time.mjs +46 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-usage.mjs +458 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +402 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_backups.py +399 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_base.py +78 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board.py +64 -1398
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_fixture.py +116 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +902 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_shapes.py +457 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_ceremonies.py +36 -38
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_crypto.py +289 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +15 -180
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +586 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +39 -400
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_events.py +137 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +402 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +1 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_jsvalue.py +310 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_kit.py +243 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_lock.py +308 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_logs.py +202 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_openrouter.py +431 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +21 -43
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_paths.py +102 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_platform.py +338 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_profiles.py +31 -38
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +173 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +298 -3450
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_share.py +8 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +412 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_time.py +120 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_usage.py +697 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +1526 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/models-and-providers.md +3 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +116 -1392
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +270 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +508 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_hosts.py +265 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mail.py +155 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mcp.py +335 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/host_hooks.py +1 -0
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Protocol revisions (DEC-0067) and an event's machine-readable data (DEC-0070, the feed-data protocols): parse,
|
|
2
|
+
accept, negotiate and check them. Part of the service kit (FAA-65, layer L3)."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
7
|
+
import sys as _sys # noqa: E402
|
|
8
|
+
from pathlib import Path as _Path # noqa: E402
|
|
9
|
+
if str(_Path(__file__).resolve().parent) not in _sys.path: # the kit loads from any working directory (FAA-65)
|
|
10
|
+
_sys.path.insert(0, str(_Path(__file__).resolve().parent))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
#: Each contract protocol's published revisions, oldest first (the contract's `PROTOCOL_REVISIONS`, DEC-0067); a test
|
|
14
|
+
#: holds it equal to the contract's register at the pin.
|
|
15
|
+
PROTOCOL_REVISIONS: Dict[str, Tuple[str, ...]] = {
|
|
16
|
+
"access-log": ("1",), "approval": ("1",), "activity-summary": ("1",), "coding-agents": ("1",), "enroll-token": ("1",), "entitlement": ("1",),
|
|
17
|
+
"entitlement-key-set": ("1",), "estate-record": ("1",), "execution-packet": ("1",),
|
|
18
|
+
"fabric-activity": ("0.1",), "fabric-dashboard": ("0.1",), "fabric-delegation": ("0.1",), "fabric-device": ("0.1",),
|
|
19
|
+
"fabric-interop": ("0.1", "0.2"), "fabric-local-channel": ("0.1",), "fabric-local-runner": ("0.1",),
|
|
20
|
+
"fabric-operator-channel": ("0.1",), "fabric-package": ("1",), "fabric-project-comms": ("0.1",), "fabric-proposal": ("1",),
|
|
21
|
+
"fabric-provider": ("0.1",), "fabric-relay": ("0.1",), "fabric-secret-envelope": ("1",), "fabric-service": ("0.1",),
|
|
22
|
+
"fabric-settings-backup": ("1",), "heartbeat": ("1",), "learning-note": ("1",), "memory": ("0.1",), "pairing-message": ("1",), "pipeline": ("0.1",),
|
|
23
|
+
"revocation-delta": ("1",), "session-handoff": ("1",), "share-registration": ("1",),
|
|
24
|
+
"share-registration-receipt": ("1",), "support-quality-daily": ("1",), "window-lease": ("1",),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
MAX_PROTOCOLS = 64
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_REVISION = re.compile(r"([a-z][a-z0-9.-]{0,63})/([0-9]{1,4}(?:\.[0-9]{1,4}){0,2})", re.ASCII)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def parse_revision(value: Any) -> Optional[Tuple[str, str]]:
|
|
35
|
+
"""`<name>/<revision>` split, or None."""
|
|
36
|
+
match = _REVISION.fullmatch(value) if isinstance(value, str) else None
|
|
37
|
+
return (match.group(1), match.group(2)) if match else None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def accepted_revisions(name: str, newest: Optional[str] = None) -> List[str]:
|
|
41
|
+
"""The revisions a receiver implementing ``name`` up to ``newest`` accepts: N and, when the register has one, N−1."""
|
|
42
|
+
known = PROTOCOL_REVISIONS.get(name, ())
|
|
43
|
+
if not known:
|
|
44
|
+
return []
|
|
45
|
+
top = len(known) - 1 if newest is None else (known.index(newest) if newest in known else -1)
|
|
46
|
+
if top < 0:
|
|
47
|
+
return []
|
|
48
|
+
return ["%s/%s" % (name, r) for r in reversed(known[max(0, top - 1):top + 1])]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def negotiate_revision(received: Any, accepted: Sequence[Any]) -> Dict[str, Any]:
|
|
52
|
+
"""A receiver's answer to a document naming ``received`` (DEC-0067, the contract's `negotiateRevision`):
|
|
53
|
+
``{"ok": True}``; the `schema-unsupported` refusal naming what was received (at most 80 characters) and the accepted
|
|
54
|
+
revisions of that protocol (all of them when it names none the receiver reads); or ``{"fault":
|
|
55
|
+
"no-accepted-revisions"}``, the receiver's own, when it accepts no well-formed revision. Revisions compare exactly."""
|
|
56
|
+
usable: List[str] = []
|
|
57
|
+
for entry in accepted if isinstance(accepted, (list, tuple)) else []:
|
|
58
|
+
if parse_revision(entry) and entry not in usable:
|
|
59
|
+
usable.append(entry)
|
|
60
|
+
usable = usable[:MAX_PROTOCOLS]
|
|
61
|
+
if not usable:
|
|
62
|
+
return {"fault": "no-accepted-revisions"}
|
|
63
|
+
if isinstance(received, str) and received in usable:
|
|
64
|
+
return {"ok": True}
|
|
65
|
+
parsed = parse_revision(received)
|
|
66
|
+
same = [e for e in usable if parsed and parse_revision(e)[0] == parsed[0]]
|
|
67
|
+
return {"error": {"code": "schema-unsupported", "message": "this receiver does not read that protocol revision",
|
|
68
|
+
"data": {"protocol": received[:80] if isinstance(received, str) else "", "accepted": same or usable}}}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def protocols_findings(protocols: Any, prefix: str = "/protocols") -> List[Dict[str, str]]:
|
|
72
|
+
"""FAC-SEM-099 (DEC-0067): 1 to 64 entries, each `<name>/<revision>` once; at most two revisions of one protocol (an
|
|
73
|
+
unpublished one refused and not counted), and two of a contract protocol are N and N−1, consecutive in the register."""
|
|
74
|
+
flag = lambda at, message: {"code": "FAC-SEM-099", "instancePath": at, "message": message} # noqa: E731
|
|
75
|
+
if not isinstance(protocols, list):
|
|
76
|
+
return [flag(prefix, "protocols is a list of protocol revisions")]
|
|
77
|
+
if not 1 <= len(protocols) <= MAX_PROTOCOLS:
|
|
78
|
+
return [flag(prefix, "protocols lists 1 to %d revisions" % MAX_PROTOCOLS)]
|
|
79
|
+
findings: List[Dict[str, str]] = []
|
|
80
|
+
by_name: Dict[str, List[int]] = {}
|
|
81
|
+
seen: List[str] = []
|
|
82
|
+
for i, entry in enumerate(protocols):
|
|
83
|
+
at = "%s/%d" % (prefix, i)
|
|
84
|
+
parsed = parse_revision(entry)
|
|
85
|
+
if not parsed:
|
|
86
|
+
findings.append(flag(at, "a protocol revision is <name>/<revision>, such as fabric-service/0.1"))
|
|
87
|
+
continue
|
|
88
|
+
if entry in seen:
|
|
89
|
+
findings.append(flag(at, "each protocol revision once"))
|
|
90
|
+
continue
|
|
91
|
+
seen.append(entry)
|
|
92
|
+
known = PROTOCOL_REVISIONS.get(parsed[0])
|
|
93
|
+
if known and parsed[1] not in known:
|
|
94
|
+
findings.append(flag(at, "%s has no published revision %s" % parsed))
|
|
95
|
+
continue
|
|
96
|
+
indices = by_name.setdefault(parsed[0], [])
|
|
97
|
+
indices.append(known.index(parsed[1]) if known else -1)
|
|
98
|
+
if len(indices) > 2:
|
|
99
|
+
findings.append(flag(at, "at most 2 revisions of one protocol: N and N−1"))
|
|
100
|
+
elif known and len(indices) == 2 and abs(indices[0] - indices[1]) != 1:
|
|
101
|
+
findings.append(flag(at, "two revisions of one protocol are N and N−1, consecutive in the register"))
|
|
102
|
+
return findings
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
_SCHEMA_URI = re.compile(r"[A-Za-z][A-Za-z0-9+.-]*:[^\s]+", re.ASCII)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _scalar(value: Any) -> bool:
|
|
109
|
+
return value is None or isinstance(value, (bool, int, float)) or (isinstance(value, str) and len(value) <= 500)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def feed_data_problems(data: Any) -> List[str]:
|
|
113
|
+
"""DEC-0070's `data {schema, value}` as the schema bounds it, then FAC-SEM-101: no string in `value`, at either
|
|
114
|
+
level, under a credential's name or of a credential's shape (named by its place, never its value)."""
|
|
115
|
+
import fabric_secrets # noqa: E402 - beside this file; the contract's detector
|
|
116
|
+
if not isinstance(data, dict) or set(data) != {"schema", "value"}:
|
|
117
|
+
return ["data is {schema, value}"]
|
|
118
|
+
problems: List[str] = []
|
|
119
|
+
if not (isinstance(data["schema"], str) and _SCHEMA_URI.fullmatch(data["schema"])):
|
|
120
|
+
problems.append("data.schema is the absolute URI of a payload schema the service publishes")
|
|
121
|
+
value = data["value"]
|
|
122
|
+
if not isinstance(value, dict) or len(value) > 32:
|
|
123
|
+
return problems + ["data.value is an object of at most 32 members"]
|
|
124
|
+
for key, member in value.items():
|
|
125
|
+
if _scalar(member):
|
|
126
|
+
continue
|
|
127
|
+
if isinstance(member, list) and len(member) <= 32 and all(_scalar(m) for m in member):
|
|
128
|
+
continue
|
|
129
|
+
if isinstance(member, dict) and len(member) <= 32 and all(_scalar(m) for m in member.values()):
|
|
130
|
+
continue
|
|
131
|
+
problems.append("data.value.%s is a scalar (a string of at most 500 characters), a list of at most 32 scalars or an object of at most 32 scalar members" % key)
|
|
132
|
+
if problems:
|
|
133
|
+
return problems
|
|
134
|
+
for finding in feed_data_findings({"events": [{"data": data}]}):
|
|
135
|
+
problems.append("FAC-SEM-101: %s %s" % (finding["instancePath"].replace("/events/0", ""), finding["message"]))
|
|
136
|
+
return problems
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _pointer_token(name: str) -> str:
|
|
140
|
+
return str(name).replace("~", "~0").replace("/", "~1")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def feed_data_findings(page: Any) -> List[Dict[str, str]]:
|
|
144
|
+
"""FAC-SEM-101 over an events page (the contract's `feedDataFindings`): a list's items and an object's members sit
|
|
145
|
+
under the outer name too; a number is a count, never a credential."""
|
|
146
|
+
import fabric_secrets # noqa: E402
|
|
147
|
+
findings: List[Dict[str, str]] = []
|
|
148
|
+
|
|
149
|
+
def check(named: bool, member: Any, at: str) -> None:
|
|
150
|
+
if not isinstance(member, str):
|
|
151
|
+
return
|
|
152
|
+
if named:
|
|
153
|
+
findings.append({"code": "FAC-SEM-101", "instancePath": at, "message": "an event's data puts no string under a credential's name"})
|
|
154
|
+
elif fabric_secrets.secret_value(member) or fabric_secrets.secret_url(member):
|
|
155
|
+
findings.append({"code": "FAC-SEM-101", "instancePath": at, "message": "an event's data carries no value shaped like a credential"})
|
|
156
|
+
|
|
157
|
+
events = page.get("events") if isinstance(page, dict) else None
|
|
158
|
+
for i, event in enumerate(events if isinstance(events, list) else []):
|
|
159
|
+
data = event.get("data") if isinstance(event, dict) else None
|
|
160
|
+
value = data.get("value") if isinstance(data, dict) else None
|
|
161
|
+
if not isinstance(value, dict):
|
|
162
|
+
continue
|
|
163
|
+
for key, member in value.items():
|
|
164
|
+
at = "/events/%d/data/value/%s" % (i, _pointer_token(key))
|
|
165
|
+
named = fabric_secrets.credential_name(key)
|
|
166
|
+
check(named, member, at)
|
|
167
|
+
if isinstance(member, list):
|
|
168
|
+
for j, item in enumerate(member):
|
|
169
|
+
check(named, item, "%s/%d" % (at, j))
|
|
170
|
+
elif isinstance(member, dict):
|
|
171
|
+
for inner, leaf in member.items():
|
|
172
|
+
check(named or fabric_secrets.credential_name(inner), leaf, "%s/%s" % (at, _pointer_token(inner)))
|
|
173
|
+
return findings
|