@passioncode-ai/fabric-agent-adapter 0.81.0 → 0.82.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 +78 -0
- package/README.md +5 -3
- package/SECURITY.md +4 -2
- 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/adapting-projects-to-fabric/references/dashboard-links.md +9 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +9 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/check_dashboard_link.py +19 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_secrets.py +239 -7
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +3 -3
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_tree.py +3 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +23 -5
- 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/references/board.md +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md +15 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +9 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +22 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +11 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_link.py +19 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_channel_agent.py +7 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +22 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-connections.mjs +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +2 -0
- 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-holds.mjs +6 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-hub.mjs +16 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-lock.mjs +73 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-peer-owner.mjs +87 -20
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-secrets.mjs +29 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +3 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_connections.py +8 -153
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_holds.py +12 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_hub.py +35 -17
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_lock.py +77 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_peer_owner.py +156 -138
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py +239 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +3 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +117 -19
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +9 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py +19 -1
- package/plugins/fabric-agent-adapter/skills/observing-products/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +2 -2
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +16 -8
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +46 -31
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +18 -16
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +23 -13
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py
CHANGED
|
@@ -10,8 +10,11 @@ U+001C to U+001F, and not U+FEFF), `.` stopping at every line terminator, ASCII
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import bisect
|
|
14
|
+
import ipaddress
|
|
13
15
|
import re
|
|
14
|
-
from typing import Dict, List, Tuple
|
|
16
|
+
from typing import Dict, List, Optional, Sequence, Tuple
|
|
17
|
+
import unicodedata
|
|
15
18
|
import urllib.parse
|
|
16
19
|
|
|
17
20
|
CREDENTIAL_WORDS = (
|
|
@@ -30,6 +33,34 @@ CREDENTIAL_PAIRS: Dict[str, tuple] = {
|
|
|
30
33
|
JS_SPACE = "\t\n\x0b\x0c\r \xa0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff"
|
|
31
34
|
_NOT_LINE_END = "[^\n\r\u2028\u2029]" # ECMAScript's `.` without the s flag
|
|
32
35
|
JWT_SHAPE = re.compile(r"eyJ[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*")
|
|
36
|
+
_URL_BASE64_RUN = re.compile(r"[A-Za-z0-9_-]*")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def jwt_spans(text: str, first: bool = False) -> List[Tuple[int, int]]:
|
|
40
|
+
"""The spans JWT_SHAPE matches in a text, leftmost and not overlapping, read in one pass (the contract's `jwtSpans`,
|
|
41
|
+
FAA-313): the regular expression backtracks from every `eyJ` of a long run, quadratic in it, while every `eyJ` of
|
|
42
|
+
one run shares that run's end, so only its first is tried, and a match ends where its third segment's run ends.
|
|
43
|
+
`first` stops at the first match."""
|
|
44
|
+
spans: List[Tuple[int, int]] = []
|
|
45
|
+
at = text.find("eyJ")
|
|
46
|
+
while at >= 0:
|
|
47
|
+
one = _URL_BASE64_RUN.match(text, at + 3).end()
|
|
48
|
+
resume = one
|
|
49
|
+
if one - at - 3 >= 2 and text[one:one + 1] == ".":
|
|
50
|
+
two = _URL_BASE64_RUN.match(text, one + 1).end()
|
|
51
|
+
if two - one - 1 >= 2 and text[two:two + 1] == ".":
|
|
52
|
+
three = _URL_BASE64_RUN.match(text, two + 1).end()
|
|
53
|
+
spans.append((at, three))
|
|
54
|
+
if first:
|
|
55
|
+
break
|
|
56
|
+
resume = three
|
|
57
|
+
at = text.find("eyJ", resume)
|
|
58
|
+
return spans
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def has_jwt(text: str) -> bool:
|
|
62
|
+
"""Whether JWT_SHAPE matches anywhere in a text, read in one pass (the contract's `hasJwt`, FAA-313)."""
|
|
63
|
+
return bool(jwt_spans(text, True))
|
|
33
64
|
#: Where a provider key's prefix may start (DEC-0151): the start of a value, a character that is no ASCII letter or
|
|
34
65
|
#: digit, or an escape (a percent-escape, single or double, a backslash escape, an octal, `\\x` with two hex digits,
|
|
35
66
|
#: `\\u` with four, an ESC colour sequence, raw or escaped). A consuming group, as the contract writes it.
|
|
@@ -83,7 +114,7 @@ def _long_secret(value: str) -> bool:
|
|
|
83
114
|
|
|
84
115
|
def secret_value(value: str) -> bool:
|
|
85
116
|
"""A JWT anywhere, a provider key or `Bearer …`, or a long base64 run mixing upper case, lower case and digits."""
|
|
86
|
-
return bool(
|
|
117
|
+
return bool(has_jwt(value) or SECRET_VALUE.search(value) or _long_secret(value))
|
|
87
118
|
|
|
88
119
|
|
|
89
120
|
def _path_like(piece: str) -> bool:
|
|
@@ -95,7 +126,7 @@ def _path_like(piece: str) -> bool:
|
|
|
95
126
|
def secret_argument(value: str) -> bool:
|
|
96
127
|
"""The same for a launch argument or a server's command and URL: a long run is looked for between path separators,
|
|
97
128
|
so a folder path holding a session id is not taken for a key."""
|
|
98
|
-
if
|
|
129
|
+
if has_jwt(value) or SECRET_VALUE.search(value) or _BEARER_INSIDE.search(value):
|
|
99
130
|
return True
|
|
100
131
|
for piece in _ARGUMENT_SPLIT.split(value):
|
|
101
132
|
if _path_like(piece):
|
|
@@ -141,6 +172,155 @@ _C0_OR_SPACE = "".join(map(chr, range(0x21)))
|
|
|
141
172
|
_LONE_SURROGATE = re.compile("[\ud800-\udfff]")
|
|
142
173
|
|
|
143
174
|
|
|
175
|
+
# The WHATWG host parser, enough to know when `new URL` refuses a host (FAA-98, moved here from fabric_connections for
|
|
176
|
+
# secret_url in FAA-309, so the module still ships alone in adapting-projects-to-fabric).
|
|
177
|
+
_FORBIDDEN_HOST = set("\x00\t\n\r #/:<>?@[\\]^|")
|
|
178
|
+
_FORBIDDEN_DOMAIN = _FORBIDDEN_HOST | set(chr(c) for c in range(0x20)) | {"%", "\x7f"}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# FAA-98: UTS46 narrowed, not ported. A non-ASCII host is refused only where every Node from 20 to 26 refuses it, so
|
|
182
|
+
# the doubt stays on the closed side (its URL's credentials are read). The two tables are measured by
|
|
183
|
+
# test/fixtures/idna/make_idna_tables.mjs (idna-tables.json, held equal by test_the_idna_tables_are_the_measured_ones):
|
|
184
|
+
# code points refused in every context, and code points removed between letters. The contextual rules read Unicode 3.2
|
|
185
|
+
# classes (`unicodedata.ucd_3_2_0`) and skip what that Unicode does not know, since Node's own UTS46 data lags its
|
|
186
|
+
# Unicode: an Arabic letter of Unicode 14 is no right-to-left letter to it.
|
|
187
|
+
_IDNA_DISALLOWED = (
|
|
188
|
+
(0x80, 0xa0), (0xa8, 0xa8), (0xaf, 0xaf), (0xb4, 0xb4), (0xb8, 0xb8), (0x2d8, 0x2dd), (0x37a, 0x37a),
|
|
189
|
+
(0x384, 0x385), (0x600, 0x605), (0x61c, 0x61c), (0x6dd, 0x6dd), (0x70f, 0x70f), (0x890, 0x891), (0x8e2, 0x8e2),
|
|
190
|
+
(0x1680, 0x1680), (0x1fbd, 0x1fbd), (0x1fbf, 0x1fc1), (0x1fcd, 0x1fcf), (0x1fdd, 0x1fdf), (0x1fed, 0x1fee),
|
|
191
|
+
(0x1ffd, 0x1ffe), (0x2000, 0x200a), (0x200e, 0x200f), (0x2017, 0x2017), (0x2024, 0x2026), (0x2028, 0x202f),
|
|
192
|
+
(0x203e, 0x203e), (0x2047, 0x2049), (0x205f, 0x205f), (0x2066, 0x2069), (0x2100, 0x2101), (0x2105, 0x2106),
|
|
193
|
+
(0x2488, 0x249b), (0x2a74, 0x2a74), (0x2ff0, 0x3000), (0x309b, 0x309c), (0x31ef, 0x31ef), (0x33c2, 0x33c2),
|
|
194
|
+
(0x33c7, 0x33c7), (0x33d8, 0x33d8), (0xe000, 0xf8ff), (0xfc5e, 0xfc63), (0xfdfa, 0xfdfb), (0xfe12, 0xfe13),
|
|
195
|
+
(0xfe16, 0xfe16), (0xfe19, 0xfe19), (0xfe30, 0xfe30), (0xfe47, 0xfe4c), (0xfe52, 0xfe52), (0xfe55, 0xfe56),
|
|
196
|
+
(0xfe5f, 0xfe5f), (0xfe64, 0xfe65), (0xfe68, 0xfe68), (0xfe6a, 0xfe6b), (0xfe70, 0xfe70), (0xfe72, 0xfe72),
|
|
197
|
+
(0xfe74, 0xfe74), (0xfe76, 0xfe76), (0xfe78, 0xfe78), (0xfe7a, 0xfe7a), (0xfe7c, 0xfe7c), (0xfe7e, 0xfe7e),
|
|
198
|
+
(0xff03, 0xff03), (0xff05, 0xff05), (0xff0f, 0xff0f), (0xff1a, 0xff1a), (0xff1c, 0xff1c), (0xff1e, 0xff20),
|
|
199
|
+
(0xff3b, 0xff3e), (0xff5c, 0xff5c), (0xffe3, 0xffe3), (0xfff9, 0xfffd), (0x110bd, 0x110bd), (0x110cd, 0x110cd),
|
|
200
|
+
(0x13430, 0x1343f), (0x1f100, 0x1f100), (0xe0001, 0xe0001), (0xe0020, 0xe007f), (0xf0000, 0xffffd),
|
|
201
|
+
(0x100000, 0x10fffd),
|
|
202
|
+
)
|
|
203
|
+
_IDNA_IGNORED = (
|
|
204
|
+
(0xad, 0xad), (0x34f, 0x34f), (0x180b, 0x180d), (0x180f, 0x180f), (0x200b, 0x200b), (0x2060, 0x2060),
|
|
205
|
+
(0x2064, 0x2064), (0xfe00, 0xfe0f), (0xfeff, 0xfeff), (0x1bca0, 0x1bca3), (0xe0100, 0xe01ef),
|
|
206
|
+
)
|
|
207
|
+
_IDNA_DOTS = re.compile("[.\u3002\uff0e\uff61]")
|
|
208
|
+
_U32 = unicodedata.ucd_3_2_0
|
|
209
|
+
_RTL_OK = {"R", "AL", "AN", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"}
|
|
210
|
+
_JOINERS = "\u200c\u200d"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _in_ranges(ranges: Sequence[tuple], cp: int) -> bool:
|
|
214
|
+
i = bisect.bisect_right(ranges, (cp, 0x10ffff)) - 1
|
|
215
|
+
return i >= 0 and ranges[i][0] <= cp <= ranges[i][1]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _known(c: str) -> bool:
|
|
219
|
+
return _U32.category(c) != "Cn" and unicodedata.category(c) != "Cn"
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _joiner_refused(label: str, i: int) -> bool:
|
|
223
|
+
"""CheckJoiners as Node 20 to 26 all read it: they find a joining context, or a virama, anywhere in the label
|
|
224
|
+
(`\u0628a\u200c\u0627` and `\u0628\u200c\u0627\u200d` parse), so only a joiner with nothing but ASCII beside it
|
|
225
|
+
in its label has none for certain."""
|
|
226
|
+
return all(c.isascii() for j, c in enumerate(label) if j != i)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _bidi_refused(label: str) -> bool:
|
|
230
|
+
"""RFC 5893 as Node 20 to 26 all read it: only a label holding R, AL or AN is judged, and its first code point is
|
|
231
|
+
not (Node 26 alone refuses a label opening with a digit or a hyphen)."""
|
|
232
|
+
if not label or not all(_known(c) for c in label) or any(c in _JOINERS for c in label):
|
|
233
|
+
return False # Node 20 to 26 pass a label holding a joiner over (`\u05d0a\u094d\u200d` parses)
|
|
234
|
+
k = [_U32.bidirectional(c) for c in label]
|
|
235
|
+
if not any(x in ("R", "AL", "AN") for x in k):
|
|
236
|
+
return False
|
|
237
|
+
body = list(k)
|
|
238
|
+
while body and body[-1] == "NSM":
|
|
239
|
+
body.pop()
|
|
240
|
+
if not body or body[-1] not in ("R", "AL", "EN", "AN"):
|
|
241
|
+
return True
|
|
242
|
+
return k[0] in ("R", "AL") and (any(x not in _RTL_OK for x in k) or ("EN" in k and "AN" in k))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _idna_host(host: str) -> Optional[str]:
|
|
246
|
+
"""None where WHATWG's domain-to-ASCII refuses a non-ASCII host for certain; else the host as UTS46 maps it, read
|
|
247
|
+
as NFKC with case folding (exact enough for the ASCII rules that follow, which read digits and dots)."""
|
|
248
|
+
if any(_in_ranges(_IDNA_DISALLOWED, ord(c)) for c in host):
|
|
249
|
+
return None
|
|
250
|
+
kept = "".join(c for c in host if not _in_ranges(_IDNA_IGNORED, ord(c)))
|
|
251
|
+
if kept == "":
|
|
252
|
+
return None # nothing left: an empty host
|
|
253
|
+
if not all(_known(c) for c in kept):
|
|
254
|
+
return host # a code point this Python's Unicode does not know: the contextual rules are not read
|
|
255
|
+
mapped = unicodedata.normalize("NFC", unicodedata.normalize("NFKC", unicodedata.normalize("NFKC", kept).casefold()))
|
|
256
|
+
for label in _IDNA_DOTS.split(mapped):
|
|
257
|
+
first = label[:1]
|
|
258
|
+
if first and first not in "\u17b4\u17b5" and _U32.category(first)[0] == "M" and unicodedata.category(first)[0] == "M":
|
|
259
|
+
return None # a label opening with a mark (U+17B4 and U+17B5 are ignored by the newer Nodes)
|
|
260
|
+
if any(c in _JOINERS and _joiner_refused(label, i) for i, c in enumerate(label)):
|
|
261
|
+
return None
|
|
262
|
+
if _bidi_refused(label):
|
|
263
|
+
return None
|
|
264
|
+
return _IDNA_DOTS.sub(".", mapped)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _ipv4_number(part: str) -> Optional[int]:
|
|
268
|
+
if part == "":
|
|
269
|
+
return None # an empty part fails WHATWG's IPv4 parser (`1..2`); `0x` alone is 0
|
|
270
|
+
base = 10
|
|
271
|
+
if part[:2].lower() == "0x":
|
|
272
|
+
part, base = part[2:], 16
|
|
273
|
+
elif len(part) > 1 and part[0] == "0":
|
|
274
|
+
part, base = part[1:], 8
|
|
275
|
+
if part == "":
|
|
276
|
+
return 0
|
|
277
|
+
digits = {10: "0123456789", 16: "0123456789abcdefABCDEF", 8: "01234567"}[base]
|
|
278
|
+
return int(part, base) if all(c in digits for c in part) else None
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _domain_ok(host: str) -> bool:
|
|
282
|
+
"""The WHATWG host parser on a special scheme's host, enough to know when `new URL` refuses it."""
|
|
283
|
+
if host.startswith("["):
|
|
284
|
+
return host.endswith("]") and _ipv6_ok(host[1:-1])
|
|
285
|
+
if re.search(r"%(?![0-9A-Fa-f]{2})", host) is None:
|
|
286
|
+
try:
|
|
287
|
+
host = urllib.parse.unquote_to_bytes(host).decode("utf-8")
|
|
288
|
+
except UnicodeDecodeError:
|
|
289
|
+
return False
|
|
290
|
+
if any(c in _FORBIDDEN_DOMAIN for c in host):
|
|
291
|
+
return False
|
|
292
|
+
# A non-ASCII host goes through UTS46 in WHATWG; this module reads it narrowly (_idna_host, FAA-98). When unsure,
|
|
293
|
+
# the host is taken as valid, so its URL's credentials are read: the doubt then hides a list rather than publish one
|
|
294
|
+
# (round-2 reading). A host UTS46 maps to ASCII is read by the ASCII rules below (`127.0.0.1` is an address).
|
|
295
|
+
if any(ord(c) > 0x7f for c in host):
|
|
296
|
+
mapped = _idna_host(host)
|
|
297
|
+
if mapped is None:
|
|
298
|
+
return False
|
|
299
|
+
if not mapped.isascii() or any(c in _FORBIDDEN_DOMAIN for c in mapped):
|
|
300
|
+
return True
|
|
301
|
+
host = mapped
|
|
302
|
+
labels = host.split(".")
|
|
303
|
+
if labels and labels[-1] == "" and len(labels) > 1:
|
|
304
|
+
labels = labels[:-1]
|
|
305
|
+
last = labels[-1] if labels else ""
|
|
306
|
+
if not (last and (last.isdigit() and last.isascii() or re.fullmatch(r"0[xX][0-9A-Fa-f]*", last))):
|
|
307
|
+
return host != ""
|
|
308
|
+
numbers = [_ipv4_number(label) for label in labels]
|
|
309
|
+
if len(numbers) > 4 or any(n is None for n in numbers):
|
|
310
|
+
return False
|
|
311
|
+
return all(n <= 255 for n in numbers[:-1]) and numbers[-1] < 256 ** (5 - len(numbers))
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _ipv6_ok(text: str) -> bool:
|
|
315
|
+
if "%" in text:
|
|
316
|
+
return False # a zone id: `ipaddress` takes one, WHATWG does not
|
|
317
|
+
try:
|
|
318
|
+
ipaddress.IPv6Address(text)
|
|
319
|
+
return True
|
|
320
|
+
except ValueError:
|
|
321
|
+
return False
|
|
322
|
+
|
|
323
|
+
|
|
144
324
|
def _percent_encode(text: str, also: str) -> str:
|
|
145
325
|
"""WHATWG's percent-encoding of a query or a fragment: every C0 control, space, `"`, `<`, `>`, every code point
|
|
146
326
|
above U+007E and the characters in `also`, as UTF-8 %XX (a lone surrogate as U+FFFD, as a USVString reads it).
|
|
@@ -162,8 +342,8 @@ def url_parts(value: str) -> Tuple[str, str, str]:
|
|
|
162
342
|
after every slash or backslash that follows `scheme:` (the special authority ignore slashes state) and ends at the
|
|
163
343
|
next slash, backslash, `?` or `#`, so `<https>:u:p@h` and `<https>:\\u:p@h` carry the password p; any other scheme
|
|
164
344
|
has an authority only after `//`, and `file` has no user information. The user information ends at the authority's
|
|
165
|
-
last `@`, and the password follows its first `:`.
|
|
166
|
-
|
|
345
|
+
last `@`, and the password follows its first `:`. The host is not checked here: secret_url refuses first a URL
|
|
346
|
+
whose host or port `new URL` refuses (url_refused, FAA-309)."""
|
|
167
347
|
scheme, _, rest = value.partition(":")
|
|
168
348
|
rest, _, fragment = rest.partition("#")
|
|
169
349
|
rest, _, query = rest.partition("?")
|
|
@@ -182,10 +362,60 @@ def url_parts(value: str) -> Tuple[str, str, str]:
|
|
|
182
362
|
_url_parts = url_parts # the name before FAA-250, kept for code that reached it
|
|
183
363
|
|
|
184
364
|
|
|
365
|
+
def url_refused(value: str) -> bool:
|
|
366
|
+
"""Whether `new URL` refuses an absolute URL (trimmed as secret_url trims it) for its host or port (FAA-309), read
|
|
367
|
+
with the same authority url_parts reads: a special scheme's host through the WHATWG host parser and the IDNA reading
|
|
368
|
+
of FAA-98 (`_domain_ok`), never empty; `file`'s host, after `//`, the same, empty allowed; another scheme's host,
|
|
369
|
+
after `//`, without a forbidden host code point, and empty only with no user information and no `:` after it (an
|
|
370
|
+
empty port is refused too); a port of ASCII digits up to 65535. Where the reading is unsure, the host is taken as
|
|
371
|
+
valid (the detector's safe side)."""
|
|
372
|
+
value = _LONE_SURROGATE.sub("\ufffd", value)
|
|
373
|
+
scheme, _, rest = value.partition(":")
|
|
374
|
+
rest = rest.partition("#")[0].partition("?")[0]
|
|
375
|
+
lowered = scheme.lower()
|
|
376
|
+
if lowered in _SPECIAL:
|
|
377
|
+
authority = rest.replace("\\", "/").lstrip("/").partition("/")[0]
|
|
378
|
+
elif lowered == "file":
|
|
379
|
+
rest = rest.replace("\\", "/")
|
|
380
|
+
if not rest.startswith("//"):
|
|
381
|
+
return False
|
|
382
|
+
host = rest[2:].partition("/")[0]
|
|
383
|
+
return not (host == "" or re.fullmatch(r"[A-Za-z][:|]", host) or _domain_ok(host))
|
|
384
|
+
elif rest.startswith("//"):
|
|
385
|
+
authority = rest[2:].partition("/")[0]
|
|
386
|
+
else:
|
|
387
|
+
return False
|
|
388
|
+
hostport = authority.rpartition("@")[2]
|
|
389
|
+
if hostport.startswith("["):
|
|
390
|
+
close = hostport.find("]")
|
|
391
|
+
if close < 0:
|
|
392
|
+
return True
|
|
393
|
+
host, after = hostport[:close + 1], hostport[close + 1:]
|
|
394
|
+
if after and not after.startswith(":"):
|
|
395
|
+
return True
|
|
396
|
+
port = after[1:]
|
|
397
|
+
else:
|
|
398
|
+
host, _, port = hostport.partition(":")
|
|
399
|
+
if port and not (port.isascii() and port.isdigit() and int(port) <= 65535):
|
|
400
|
+
return True
|
|
401
|
+
if lowered in _SPECIAL:
|
|
402
|
+
return host == "" or not _domain_ok(host)
|
|
403
|
+
if host.startswith("["):
|
|
404
|
+
return not _ipv6_ok(host[1:-1])
|
|
405
|
+
return any(c in _FORBIDDEN_HOST for c in host) or (host == "" and ("@" in authority or ":" in hostport))
|
|
406
|
+
|
|
407
|
+
|
|
185
408
|
def secret_url(value: str) -> bool:
|
|
186
409
|
"""Whether a URL an MCP entry or a launch names carries a credential (LC-21): anything secret_argument finds, a
|
|
187
410
|
password in its user information, or a query or fragment parameter whose name can only carry a credential and has
|
|
188
|
-
a value, or whose value is shaped like one. A string that is not an absolute URL
|
|
411
|
+
a value, or whose value is shaped like one. A string that is not an absolute URL, or one whose host or port
|
|
412
|
+
`new URL` refuses (url_refused, FAA-309), is checked as an argument only, as the Node twin's secretUrl reads it."""
|
|
413
|
+
return _url_credential(value, hosts=True)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _url_credential(value: str, hosts: bool) -> bool:
|
|
417
|
+
"""secret_url; with `hosts` False the host is not checked, as repository_credential reads a repository's parameters
|
|
418
|
+
whatever its host (the Node twin's repositoryParts)."""
|
|
189
419
|
if secret_argument(value):
|
|
190
420
|
return True
|
|
191
421
|
# What WHATWG URL parsing does first: trim C0 controls and space from the ends (never other Unicode spaces, which it
|
|
@@ -193,6 +423,8 @@ def secret_url(value: str) -> bool:
|
|
|
193
423
|
value = re.sub(r"[\t\n\r]", "", value.strip(_C0_OR_SPACE))
|
|
194
424
|
if not _SCHEME.match(value):
|
|
195
425
|
return False # not an absolute URL (WHATWG URL parsing refuses it)
|
|
426
|
+
if hosts and url_refused(value):
|
|
427
|
+
return False # `new URL` refuses its host or port, so the Node twin reads it as an argument only (FAA-309)
|
|
196
428
|
password, query, fragment = url_parts(value)
|
|
197
429
|
if password:
|
|
198
430
|
return True
|
|
@@ -260,4 +492,4 @@ def repository_credential(value: str) -> bool:
|
|
|
260
492
|
odd = [piece for written in (path, ascii_decoded(path)) for piece in re.split(r"[/\\:]", written)
|
|
261
493
|
if piece and not repository_name(piece)]
|
|
262
494
|
# the scheme and authority decoded too: git sends `https://%67hp_...@h` with its user decoded (unit reading 12)
|
|
263
|
-
return
|
|
495
|
+
return _url_credential(head + tail, hosts=False) or secret_argument(ascii_decoded(head)) or any(secret_argument(piece) for piece in odd)
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py
CHANGED
|
@@ -88,8 +88,11 @@ from fabric_lock import ( # noqa: F401
|
|
|
88
88
|
LOCK_WAIT_SUPERVISED_SECONDS,
|
|
89
89
|
LOCK_BACKOFF_FIRST_SECONDS,
|
|
90
90
|
LOCK_BACKOFF_MAX_SECONDS,
|
|
91
|
+
LOCK_WATCH_SECONDS,
|
|
91
92
|
AlreadyRunning,
|
|
92
93
|
InstanceLock,
|
|
94
|
+
lost_lock_message,
|
|
95
|
+
stop_lost_holder,
|
|
93
96
|
lock_name,
|
|
94
97
|
_hold_lock_name,
|
|
95
98
|
_drop_lock_name,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fabric-service-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.82.0",
|
|
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
|
{
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
"file": "fabric_secrets.py",
|
|
32
32
|
"layer": "L0",
|
|
33
33
|
"imports": [],
|
|
34
|
-
"sha256": "
|
|
34
|
+
"sha256": "2ebbd53d4b162fb41586b07ccc31534cbc3a9c6632a4ff84f56d29e90bd4294f"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"module": "fabric_peer_owner",
|
|
38
38
|
"file": "fabric_peer_owner.py",
|
|
39
39
|
"layer": "L0",
|
|
40
40
|
"imports": [],
|
|
41
|
-
"sha256": "
|
|
41
|
+
"sha256": "7e007a74ab166e06046610c5961c134e523247e6d1ed010934369b7b27642bfc"
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"module": "fabric_tailnet",
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"fabric_base",
|
|
212
212
|
"fabric_secrets"
|
|
213
213
|
],
|
|
214
|
-
"sha256": "
|
|
214
|
+
"sha256": "ca6a4735b21d285a3ef2c60724f3ead08f5aa707fdfaa3cb5757e02a226132a0"
|
|
215
215
|
},
|
|
216
216
|
{
|
|
217
217
|
"module": "fabric_a2a",
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
"fabric_paths",
|
|
258
258
|
"fabric_platform"
|
|
259
259
|
],
|
|
260
|
-
"sha256": "
|
|
260
|
+
"sha256": "6dac051969f2c331f7cc6a90b6bb1a506a5a23a4082425d07e708ce6682aedfb"
|
|
261
261
|
},
|
|
262
262
|
{
|
|
263
263
|
"module": "fabric_usage",
|
|
@@ -298,7 +298,7 @@
|
|
|
298
298
|
"fabric_secrets",
|
|
299
299
|
"fabric_usage"
|
|
300
300
|
],
|
|
301
|
-
"sha256": "
|
|
301
|
+
"sha256": "10728dffb641c267a6dd89e47f84aed7e8cc28a00189e6545432c683bc33d85c"
|
|
302
302
|
},
|
|
303
303
|
{
|
|
304
304
|
"module": "fabric_context",
|
|
@@ -384,7 +384,7 @@
|
|
|
384
384
|
"fabric_time",
|
|
385
385
|
"fabric_usage"
|
|
386
386
|
],
|
|
387
|
-
"sha256": "
|
|
387
|
+
"sha256": "fb6bccc9f330df26a53287023da2ce4947f85e6b4fb2437db01c429ec809454d"
|
|
388
388
|
},
|
|
389
389
|
{
|
|
390
390
|
"module": "fabric_profiles",
|
|
@@ -441,7 +441,7 @@
|
|
|
441
441
|
"fabric_paths",
|
|
442
442
|
"fabric_secrets"
|
|
443
443
|
],
|
|
444
|
-
"sha256": "
|
|
444
|
+
"sha256": "277533ec3bf1766a5a583d852dec9a048f63d9a8d1984cfc6e32df3218849c59"
|
|
445
445
|
},
|
|
446
446
|
{
|
|
447
447
|
"module": "fabric_hub",
|
|
@@ -452,7 +452,7 @@
|
|
|
452
452
|
"fabric_peer_owner",
|
|
453
453
|
"fabric_service"
|
|
454
454
|
],
|
|
455
|
-
"sha256": "
|
|
455
|
+
"sha256": "8809de7a2add22f4ce28bee5191458502e7b3bcf96efd3ad67b60de1aaaa529d"
|
|
456
456
|
},
|
|
457
457
|
{
|
|
458
458
|
"module": "fabric_board_shapes",
|
|
@@ -529,7 +529,7 @@
|
|
|
529
529
|
"fabric_jsvalue",
|
|
530
530
|
"fabric_peer_owner"
|
|
531
531
|
],
|
|
532
|
-
"sha256": "
|
|
532
|
+
"sha256": "5a6d5ccfb76239777ef4d95fe60f96dcb156e2b8fad329a7f83a85c5c9ac4a61"
|
|
533
533
|
},
|
|
534
534
|
{
|
|
535
535
|
"module": "fabric_openrouter",
|
|
@@ -570,14 +570,14 @@
|
|
|
570
570
|
"file": "fabric-secrets.mjs",
|
|
571
571
|
"layer": "L0",
|
|
572
572
|
"imports": [],
|
|
573
|
-
"sha256": "
|
|
573
|
+
"sha256": "af4b7023c20eb40d418a431f60a5ab94a0618b01891051aacc13772e97532297"
|
|
574
574
|
},
|
|
575
575
|
{
|
|
576
576
|
"module": "fabric-peer-owner",
|
|
577
577
|
"file": "fabric-peer-owner.mjs",
|
|
578
578
|
"layer": "L0",
|
|
579
579
|
"imports": [],
|
|
580
|
-
"sha256": "
|
|
580
|
+
"sha256": "c58f444df44df74f8d755d95fbb3c751ccc856c5fc2fc4e178a44df26cdd12d7"
|
|
581
581
|
},
|
|
582
582
|
{
|
|
583
583
|
"module": "fabric-tailnet",
|
|
@@ -667,7 +667,7 @@
|
|
|
667
667
|
"fabric-paths",
|
|
668
668
|
"fabric-peer-owner"
|
|
669
669
|
],
|
|
670
|
-
"sha256": "
|
|
670
|
+
"sha256": "fbfefe8f48f9f926f6ecfe8dce1e2781eba2f4c4abef6eeec3f52837246344b6"
|
|
671
671
|
},
|
|
672
672
|
{
|
|
673
673
|
"module": "fabric-share",
|
|
@@ -749,7 +749,7 @@
|
|
|
749
749
|
"fabric-jsvalue",
|
|
750
750
|
"fabric-secrets"
|
|
751
751
|
],
|
|
752
|
-
"sha256": "
|
|
752
|
+
"sha256": "dd82998d7b854bfb4a11fa0fc442357e1037e379f849a91ec21f0f81d73a82fb"
|
|
753
753
|
},
|
|
754
754
|
{
|
|
755
755
|
"module": "fabric-a2a",
|
|
@@ -793,7 +793,7 @@
|
|
|
793
793
|
"fabric-base",
|
|
794
794
|
"fabric-paths"
|
|
795
795
|
],
|
|
796
|
-
"sha256": "
|
|
796
|
+
"sha256": "ffd569de66d214c18f2b739e1e5fd169d84fe8f034fee8ed3d1fcb1cd87325c7"
|
|
797
797
|
},
|
|
798
798
|
{
|
|
799
799
|
"module": "fabric-usage",
|
|
@@ -835,7 +835,7 @@
|
|
|
835
835
|
"fabric-secrets",
|
|
836
836
|
"fabric-usage"
|
|
837
837
|
],
|
|
838
|
-
"sha256": "
|
|
838
|
+
"sha256": "d2380f116d62c076c7510a4f1b8fd6e266ac88818d4e841d7f08d4000e819c6b"
|
|
839
839
|
},
|
|
840
840
|
{
|
|
841
841
|
"module": "fabric-context",
|
|
@@ -920,7 +920,7 @@
|
|
|
920
920
|
"fabric-supervisors",
|
|
921
921
|
"fabric-usage"
|
|
922
922
|
],
|
|
923
|
-
"sha256": "
|
|
923
|
+
"sha256": "0d21b3f6f4a51b3ede576e748d481ab831fec570f629883b95f138c8823ab320"
|
|
924
924
|
},
|
|
925
925
|
{
|
|
926
926
|
"module": "fabric-profiles",
|
|
@@ -971,7 +971,7 @@
|
|
|
971
971
|
"fabric-paths",
|
|
972
972
|
"fabric-secrets"
|
|
973
973
|
],
|
|
974
|
-
"sha256": "
|
|
974
|
+
"sha256": "bab47b2cdad663dc39a581d0d30d33c59bb847016431e18a63b326fefd612160"
|
|
975
975
|
},
|
|
976
976
|
{
|
|
977
977
|
"module": "fabric-hub",
|
|
@@ -982,7 +982,7 @@
|
|
|
982
982
|
"fabric-peer-owner",
|
|
983
983
|
"fabric-service"
|
|
984
984
|
],
|
|
985
|
-
"sha256": "
|
|
985
|
+
"sha256": "2813beb86f72cc1932601678f81596de16f9876c9d3630874f27addf66d7b082"
|
|
986
986
|
},
|
|
987
987
|
{
|
|
988
988
|
"module": "fabric-board-shapes",
|
|
@@ -6983,6 +6983,104 @@
|
|
|
6983
6983
|
"fabric-openrouter.mjs": "4e3e73a1b8882bdde4cbb311f6f4a5e2f048fa81fa245409a16df1e6cab03e19",
|
|
6984
6984
|
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
6985
6985
|
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
6986
|
+
},
|
|
6987
|
+
"0.82.0": {
|
|
6988
|
+
"fabric_jsvalue.py": "052f1b40bab18600b3731dbdca27cbff17588c21d9458fa6241efd1340d0e09a",
|
|
6989
|
+
"fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
|
|
6990
|
+
"fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
|
|
6991
|
+
"fabric_secrets.py": "2ebbd53d4b162fb41586b07ccc31534cbc3a9c6632a4ff84f56d29e90bd4294f",
|
|
6992
|
+
"fabric_peer_owner.py": "7e007a74ab166e06046610c5961c134e523247e6d1ed010934369b7b27642bfc",
|
|
6993
|
+
"fabric_tailnet.py": "c60416539be0199182f7969c24a37b14d0db50c8ea5e3e04cea43babfb483407",
|
|
6994
|
+
"fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
|
|
6995
|
+
"fabric_base.py": "d8c67f345e30332cce3664af77b0e6e07cd477b78c961ac009e9efee1be7e1b2",
|
|
6996
|
+
"fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
|
|
6997
|
+
"fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
|
|
6998
|
+
"fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
|
|
6999
|
+
"fabric_estate.py": "0737ae1d1bb12586bae49de1f15ef1348bc207b5007cced9dad4c55bb5870874",
|
|
7000
|
+
"fabric_mandate.py": "fb19ce5359606bd6e3352ab02b2a12f49605ec6e5c49b55cce1f79c831325044",
|
|
7001
|
+
"fabric_delegation.py": "e923be4d2d901d5e0d8bf16b05a637119464009a51cffb247bd20de5e9c1d0c9",
|
|
7002
|
+
"fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
|
|
7003
|
+
"fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
|
|
7004
|
+
"fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
|
|
7005
|
+
"fabric_package.py": "8b835fcf173b8c345ea6d229edf8ebb98b0f94e9af4894c4a6bd1301f175e2b1",
|
|
7006
|
+
"fabric_protocols.py": "3cba881f6f3468e8d8a8455a5d54c5567e748c37291211a1c7f22d195ea562c4",
|
|
7007
|
+
"fabric_actions.py": "ae21d1199614c4f74d1dcd1e0a87107a9d52434cd9e2d64aa24a7b1e196fb723",
|
|
7008
|
+
"fabric_connections.py": "ca6a4735b21d285a3ef2c60724f3ead08f5aa707fdfaa3cb5757e02a226132a0",
|
|
7009
|
+
"fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
|
|
7010
|
+
"fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
|
|
7011
|
+
"fabric_load.py": "0e97b6307108c5877a728be8f0c416686130ce98b31c4dfd5d788d9dc61a8860",
|
|
7012
|
+
"fabric_lock.py": "6dac051969f2c331f7cc6a90b6bb1a506a5a23a4082425d07e708ce6682aedfb",
|
|
7013
|
+
"fabric_usage.py": "c1a605a84e57c5ab5d3b2bd824412c20219c237dfc4bc13f778f75340905f51e",
|
|
7014
|
+
"fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
|
|
7015
|
+
"fabric_descriptor.py": "10728dffb641c267a6dd89e47f84aed7e8cc28a00189e6545432c683bc33d85c",
|
|
7016
|
+
"fabric_context.py": "59e39fb9fd91f713f6e92ea3ec7e8252107efd148847f05934892158b326db4d",
|
|
7017
|
+
"fabric_auth.py": "74ccc10239b65a3567cec5213a7b344cbd88d6eee1fd1c5f7be1d6daf985993c",
|
|
7018
|
+
"fabric_events.py": "da3fe3e3e19d4c9754b90c719e9c069a3ac85e080b9fffd07adbaf63b5bfcf0d",
|
|
7019
|
+
"fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
|
|
7020
|
+
"fabric_supervisors.py": "e5e77c7ddac742fd1882d42be33eb694906e7eb869abbee0e825d4f687936698",
|
|
7021
|
+
"fabric_service.py": "fb6bccc9f330df26a53287023da2ce4947f85e6b4fb2437db01c429ec809454d",
|
|
7022
|
+
"fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
|
|
7023
|
+
"fabric_interop_rules.py": "9bdb7989e57b9d0b0d15eb9982d396499823a5e2c528230b71c67866c736e0f5",
|
|
7024
|
+
"fabric_interop.py": "2b4d6c144f496dbd8788f616de9956bb8b6e89290713c406aa601511c82f4a46",
|
|
7025
|
+
"fabric_holds.py": "277533ec3bf1766a5a583d852dec9a048f63d9a8d1984cfc6e32df3218849c59",
|
|
7026
|
+
"fabric_hub.py": "8809de7a2add22f4ce28bee5191458502e7b3bcf96efd3ad67b60de1aaaa529d",
|
|
7027
|
+
"fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
|
|
7028
|
+
"fabric_board_link.py": "37aacd7afb0b7971c60f434d489fbfc556a70d8da87257179d891be4d2b661b0",
|
|
7029
|
+
"fabric_board_slots.py": "377407183d91f5cfe264274024f0895e58fa97a84cbcead51eaa78c671d5806d",
|
|
7030
|
+
"fabric_board_session.py": "21623dc42eb8be9f4586ab403c0dde597d1e3c38b366bf57621e1401ddbe5096",
|
|
7031
|
+
"fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
|
|
7032
|
+
"fabric_board.py": "f86c40b8291f8c818f6924910f4022b013d5fd107abaf8825dd0aa6c5e418638",
|
|
7033
|
+
"fabric_http.py": "5a6d5ccfb76239777ef4d95fe60f96dcb156e2b8fad329a7f83a85c5c9ac4a61",
|
|
7034
|
+
"fabric_openrouter.py": "a415db7475c3359fa15c85979a63a3a223a0ad7f85f878bcc2d638acccb44090",
|
|
7035
|
+
"fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
|
|
7036
|
+
"fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
|
|
7037
|
+
"fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
|
|
7038
|
+
"fabric-secrets.mjs": "af4b7023c20eb40d418a431f60a5ab94a0618b01891051aacc13772e97532297",
|
|
7039
|
+
"fabric-peer-owner.mjs": "c58f444df44df74f8d755d95fbb3c751ccc856c5fc2fc4e178a44df26cdd12d7",
|
|
7040
|
+
"fabric-tailnet.mjs": "aff3591552448bc042af306cd8a9e5cb9102c6b62d6dc05d53db35e35c51e347",
|
|
7041
|
+
"fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
|
|
7042
|
+
"fabric-base.mjs": "1a804bfc3ec5d2479ffc39d3c8d67b056b72af836e1b88102881f9ff632f1901",
|
|
7043
|
+
"fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
|
|
7044
|
+
"fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
|
|
7045
|
+
"fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
|
|
7046
|
+
"fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
|
|
7047
|
+
"fabric-mandate.mjs": "b70f3de03a659e60bba7b5a0580b3582825cd7b8a8e30a28c79621919ec5b7bb",
|
|
7048
|
+
"fabric-delegation.mjs": "fbfefe8f48f9f926f6ecfe8dce1e2781eba2f4c4abef6eeec3f52837246344b6",
|
|
7049
|
+
"fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
|
|
7050
|
+
"fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
|
|
7051
|
+
"fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
|
|
7052
|
+
"fabric-package.mjs": "aa75c4f6ff3dade68a47c6187666332a3845b3f5da98b50e3da7824d918e941f",
|
|
7053
|
+
"fabric-protocols.mjs": "26eeb8c63e9881d8fa56389455626f2bb80725bbc25b9d6ed756af88c05a848e",
|
|
7054
|
+
"fabric-actions.mjs": "06e42f5e24c96311d5417d0d1effa93479ef7128a7ab139903b2bcf59253f6be",
|
|
7055
|
+
"fabric-connections.mjs": "dd82998d7b854bfb4a11fa0fc442357e1037e379f849a91ec21f0f81d73a82fb",
|
|
7056
|
+
"fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
|
|
7057
|
+
"fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
|
|
7058
|
+
"fabric-load.mjs": "88d9fc5576ac605a038b7ca8a8498aa0dc5848dee94f66c4c7900bd9de48e935",
|
|
7059
|
+
"fabric-lock.mjs": "ffd569de66d214c18f2b739e1e5fd169d84fe8f034fee8ed3d1fcb1cd87325c7",
|
|
7060
|
+
"fabric-usage.mjs": "d42ae671a1182a542f94a83cc297b1ef2161376f4f56267a8915e7123c025087",
|
|
7061
|
+
"fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
|
|
7062
|
+
"fabric-descriptor.mjs": "d2380f116d62c076c7510a4f1b8fd6e266ac88818d4e841d7f08d4000e819c6b",
|
|
7063
|
+
"fabric-context.mjs": "ac88273a7ff0010a122c9bd309012a484c439ffd6e9e16923f103a3f8de7a539",
|
|
7064
|
+
"fabric-auth.mjs": "10ff37a39f9e3adb4d0f05db66c45f49b10eef582e9220c213907964109d7e52",
|
|
7065
|
+
"fabric-events.mjs": "c95c07e2745e52fddbb7237b89824123f240d4f8d6b14f5cb269a09e1845c3ba",
|
|
7066
|
+
"fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
|
|
7067
|
+
"fabric-supervisors.mjs": "6d9fecb0f19e82548c991be2e7e76e1bde5405fa34c6606f90025cc14128395e",
|
|
7068
|
+
"fabric-service.mjs": "0d21b3f6f4a51b3ede576e748d481ab831fec570f629883b95f138c8823ab320",
|
|
7069
|
+
"fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
|
|
7070
|
+
"fabric-interop-rules.mjs": "b4e386c359a48e49ffff8a842215760c18a7c98a0af4754a808a35fb2820d458",
|
|
7071
|
+
"fabric-interop.mjs": "989040aed33b2712a258244192bdf70e811a51076aa1b62f2adc2a6db051dbed",
|
|
7072
|
+
"fabric-holds.mjs": "bab47b2cdad663dc39a581d0d30d33c59bb847016431e18a63b326fefd612160",
|
|
7073
|
+
"fabric-hub.mjs": "2813beb86f72cc1932601678f81596de16f9876c9d3630874f27addf66d7b082",
|
|
7074
|
+
"fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
|
|
7075
|
+
"fabric-board-link.mjs": "a21fcfe6f4e701b1f5142a008ceeb7eea42c251a01688f9022539235b6f80b62",
|
|
7076
|
+
"fabric-board-slots.mjs": "721c5f3ddb0bd76d6b5d9e08807e50392302e71efa4385e351d9bee2ad06c103",
|
|
7077
|
+
"fabric-board-session.mjs": "ccce0f335e85ce676edbaf15dc6c048e7fb50a9e34310724759b84cafd4139a0",
|
|
7078
|
+
"fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
|
|
7079
|
+
"fabric-board.mjs": "15fbb4441fcf7d2f285375b75de64ad6cffea129c515084c110245376b07775f",
|
|
7080
|
+
"fabric-http.mjs": "7bb5e41d2fbd3ba2717946777be7fd91e41ec4514a92aae2351ae8c4f2439476",
|
|
7081
|
+
"fabric-openrouter.mjs": "4e3e73a1b8882bdde4cbb311f6f4a5e2f048fa81fa245409a16df1e6cab03e19",
|
|
7082
|
+
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
7083
|
+
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
6986
7084
|
}
|
|
6987
7085
|
},
|
|
6988
7086
|
"data": {
|
|
@@ -9,7 +9,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
|
9
9
|
compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
|
|
10
10
|
metadata:
|
|
11
11
|
author: PassionCode.ai
|
|
12
|
-
version: "0.
|
|
12
|
+
version: "0.82.0"
|
|
13
13
|
contract-version: "0.1.0"
|
|
14
14
|
contract-commit: "93e7db164c7829e2783b4b7705c4113bb5201131"
|
|
15
15
|
---
|
package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md
CHANGED
|
@@ -123,7 +123,15 @@ if_absent policy, the same device and `"listener_owner": "this-account"`: the ki
|
|
|
123
123
|
the host read for the listener on http_url's port (`fabric_peer_owner`'s
|
|
124
124
|
`this-account`, `another-account`, `system`, `unknown`, `none`, or
|
|
125
125
|
`check_service.py`'s `foreign-user`); any other, or none given, refuses the
|
|
126
|
-
browser (DEC-0154 PL-3, FAA-251). The
|
|
126
|
+
browser (DEC-0154 PL-3, FAA-251). The host reads that kind right before it opens
|
|
127
|
+
the page with the adapter's
|
|
128
|
+
`python3 building-fabric-services/scripts/fabric_peer_owner.py listener <port>`
|
|
129
|
+
(or `node building-fabric-services/scripts/fabric-peer-owner.mjs listener <port>`),
|
|
130
|
+
which prints it on one line (`--json` prints the whole owner) and sends nothing
|
|
131
|
+
to the port (FAA-315). Where the socket table is restricted (macOS 27.2 for an
|
|
132
|
+
ad-hoc-signed Python or Node, DEC-0155 RT-4) only this account's listener on
|
|
133
|
+
exactly `127.0.0.1` reads `this-account`; a wildcard or `::1` bind reads
|
|
134
|
+
`unknown` (`table-restricted`), and the browser is refused. The native link does not take it: Fabric
|
|
127
135
|
Dashboards checks its own listener. Contract DEC-0123 PL-3 also allows a browser
|
|
128
136
|
on `unsupported` (a system the host does not ship for); the gate treats
|
|
129
137
|
`unsupported` as no fallback, by the operator's rule that a browser opens only on a
|
package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py
CHANGED
|
@@ -28,6 +28,24 @@ CREDENTIAL_PAIRS = {
|
|
|
28
28
|
'session': {'token', 'id'},
|
|
29
29
|
'code': {'login', 'auth', 'access', 'session'}}
|
|
30
30
|
JWT_SHAPE = re.compile(r'eyJ[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*')
|
|
31
|
+
URL_BASE64_RUN = re.compile(r'[A-Za-z0-9_-]*')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def has_jwt(text):
|
|
35
|
+
# Whether JWT_SHAPE matches anywhere, read in one pass (the contract's hasJwt, FAA-313): the expression backtracks
|
|
36
|
+
# from every `eyJ` of a long run, quadratic in it, while every `eyJ` of one run shares that run's end, so only its
|
|
37
|
+
# first is tried. The kit's fabric_secrets.has_jwt reads the same way; this file stands alone, so it has its own.
|
|
38
|
+
at = text.find('eyJ')
|
|
39
|
+
while at >= 0:
|
|
40
|
+
one = URL_BASE64_RUN.match(text, at + 3).end()
|
|
41
|
+
if one - at - 3 >= 2 and text[one:one + 1] == '.':
|
|
42
|
+
two = URL_BASE64_RUN.match(text, one + 1).end()
|
|
43
|
+
if two - one - 1 >= 2 and text[two:two + 1] == '.':
|
|
44
|
+
return True
|
|
45
|
+
at = text.find('eyJ', one)
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
|
|
31
49
|
# The contract's SECRET_VALUE at the pin (DEC-0151): `Bearer ` at a value's start in any case, or a provider key with its
|
|
32
50
|
# prefix in its canonical case after a boundary (the start, a character that is no ASCII letter or digit, or an escape);
|
|
33
51
|
# an `sk-` key holds a digit and an upper-case letter with 16 or more characters, or a digit with 32 or more.
|
|
@@ -58,7 +76,7 @@ def credential_name(name):
|
|
|
58
76
|
def secret_value(value):
|
|
59
77
|
long_secret = any(re.search(r'[a-z]', run) and re.search(r'[A-Z]', run) and re.search(r'[0-9]', run)
|
|
60
78
|
for run in LONG_RUN.findall(value))
|
|
61
|
-
return bool(
|
|
79
|
+
return bool(has_jwt(value) or SECRET_VALUE.search(value) or long_secret)
|
|
62
80
|
|
|
63
81
|
|
|
64
82
|
def carries_credential(text):
|