@passioncode-ai/fabric-agent-adapter 0.57.0 → 0.59.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.
Files changed (21) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/COMMERCIAL-LICENSE.md +1 -1
  3. package/README.md +17 -6
  4. package/SECURITY.md +6 -1
  5. package/package.json +1 -1
  6. package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  7. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +18 -3
  8. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/operator-channel-and-repository.md +4 -1
  9. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +179 -0
  10. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +995 -0
  11. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_homes.py +72 -0
  12. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_ignore.py +185 -0
  13. package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +15 -17
  14. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +22 -0
  15. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +181 -1
  16. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +29 -8
  17. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/operator-channel-and-repository.md +4 -1
  18. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +179 -0
  19. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md +23 -1
  20. package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
  21. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env python3
2
+ """How fabric_source_check.py reads a home the owner's list declares (`home: <name>`, FAA-188 part 1): the name in
3
+ every form the table names, its parts joined by any separator a tool or a language writes, and its spellings for the
4
+ masks of printed paths. Beside fabric_source_check.py, which imports it. Standard library only.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from typing import Dict, Iterable, List, Set, Tuple
11
+
12
+ _SEP = r"(?:\\|/)+" # one way to read a run: two alternatives that both take a `\\` backtrack exponentially
13
+
14
+ #: How a `home: <name>` line of the owner's list is kept among the entries: a character no line can hold.
15
+ HOME_ENTRY = "\0"
16
+
17
+
18
+ def _owner_homes(entries: List[str]) -> Set[str]:
19
+ """The home names the owner's list declares (`home: <name>`), which are never neutral."""
20
+ return {e[1:] for e in entries if e.startswith(HOME_ENTRY)}
21
+
22
+
23
+ _DASHED: Dict[str, Tuple["re.Pattern[str]", ...]] = {}
24
+
25
+
26
+ #: Where a declared home name stands in each form the table names: the slashed, drive, URL-encoded and dashed homes.
27
+ _DECLARED_SLASHED = (r"(?<![A-Za-z0-9_.-])(?:/var)?/home/", r"(?<![A-Za-z0-9_.-])/Users/",
28
+ r"(?<![A-Za-z0-9_.-])(?:/mnt)?/[A-Za-z]/Users/",
29
+ r"(?<![A-Za-z0-9_.-])(?i:[A-Za-z]:%sUsers%s)" % (_SEP, _SEP),
30
+ r"%2[Ff](?:Users|home)%2[Ff]", r"(?i:%5CUsers%5C)")
31
+ _DECLARED_DASHED = (r"(?:^|[^A-Za-z0-9-])(?:-Users|-home|-var-home|(?:-mnt)?-[A-Za-z]-Users)-",
32
+ r"(?<![A-Za-z0-9])(?i:[A-Za-z]--Users-)")
33
+
34
+
35
+ #: One separator between the parts of a declared home name, in any spelling a tool or a language writes: a space, `.`,
36
+ #: `_`, `-`, `+`, `'`, an escaped `\'` or `\u0027`, an HTML `&#39;` or `&#x27;`, or a percent-encoding of one of them.
37
+ _NAME_JOIN = r"(?:[ ._+'-]|\\'|\\x27|\\u0027|&#0?39;|&#[xX]27;|&apos;|%2[07BbDdEe]|%5[Ff])"
38
+
39
+
40
+ #: Where an unfound declared name starts a word: no letter or digit before it, or one of `_NAME_JOIN`'s escapes that ends
41
+ #: in a letter or digit (a percent-encoding, `\x27`, `\u0027`); the escapes ending in `;` or `'` already leave no letter
42
+ #: or digit before the name. Kept beside `_NAME_JOIN`, so a separator added to one is added to the other.
43
+ _NAME_START = r"(?:(?<![A-Za-z0-9])|(?<=%[0-9A-Fa-f]{2})|(?<=\\x27)|(?<=\\u0027))"
44
+
45
+
46
+ def _name_parts(name: str) -> List[str]:
47
+ return [part for part in re.split(r"[ ._'-]+", name) if part]
48
+
49
+
50
+ def _spellings(found: Iterable[str], owner: Iterable[str]) -> Set[str]:
51
+ """The names found, and every spelling of a declared home one of them spells (its parts joined by each separator
52
+ a tool writes), so a printed path masks the other spellings of that home too."""
53
+ names = set(found)
54
+ for name in owner:
55
+ parts = _name_parts(name)
56
+ body = re.compile(r"(?i:%s)" % (_NAME_JOIN + "+").join(re.escape(part) for part in parts), re.ASCII)
57
+ if parts and any(body.fullmatch(n) for n in names):
58
+ names |= {name} | {sep.join(parts) for sep in (" ", ".", "_", "-", "+", "'", "%20", "%27")}
59
+ return names
60
+
61
+
62
+ def _dashed_home(name: str) -> Tuple["re.Pattern[str]", ...]:
63
+ """A declared home name in every form, read whole, its parts joined by any run of the separators a tool or a
64
+ language writes (`_NAME_JOIN`: a space, `.`, `_`, `-`, `+`, `'` and their escapes and percent-encodings), since
65
+ the generic forms end a name at a dash or a space, so `-Users-j-doe-proj` would read `j`. It ends only where a
66
+ letter or digit would continue it: a `.` or `_` after it (an extension, a sentence's end) is read as the declared
67
+ home, failing closed."""
68
+ if name not in _DASHED:
69
+ body = (_NAME_JOIN + "+").join(re.escape(part) for part in _name_parts(name)) or re.escape(name)
70
+ whole = r"((?i:%s))(?![A-Za-z0-9])" % body
71
+ _DASHED[name] = tuple(re.compile(prefix + whole, re.ASCII) for prefix in _DECLARED_SLASHED + _DECLARED_DASHED)
72
+ return _DASHED[name]
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env python3
2
+ """How fabric_source_check.py reads `.fabric-source-ignore` and `--exclude` (FAA-188 part 1): a line is a path from the
3
+ tree's root, matched byte by byte as Git matches (`*`, `?` and `[...]` inside one folder, `**` as a whole segment
4
+ across folders), and a line Git would read otherwise is refused. Beside fabric_source_check.py, which imports it.
5
+ Standard library only.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from pathlib import Path
12
+ import re
13
+ import warnings
14
+ from typing import Dict, Iterable, List, Optional
15
+
16
+ IGNORE_FILE = ".fabric-source-ignore"
17
+
18
+
19
+ class CheckError(Exception):
20
+ pass
21
+
22
+
23
+ _GLOBS: Dict[str, "re.Pattern[str]"] = {}
24
+
25
+
26
+ def _as_bytes(text: str) -> str:
27
+ """A path or a line as Git compares it: its UTF-8 bytes, one character each. An unpaired surrogate (a Windows file
28
+ name may hold one) is written as Git for Windows writes it (WTF-8), never a crash."""
29
+ if os.name == "nt": # Windows names are UTF-16: every surrogate in one is a character of the name, as WTF-8
30
+ return text.encode("utf-8", "surrogatepass").decode("latin-1")
31
+ try: # POSIX names are bytes: a surrogate here is one Python escaped, and surrogateescape gives that byte back
32
+ return text.encode("utf-8", "surrogateescape").decode("latin-1")
33
+ except UnicodeEncodeError:
34
+ return text.encode("utf-8", "surrogatepass").decode("latin-1")
35
+
36
+
37
+ def _name_bytes(name: str) -> str:
38
+ """A file name as Git compares it, one byte each: on POSIX the bytes on disk, encoded back with the file system's
39
+ own codec (os.walk and `_git_files` decoded it with that codec, so a name in any locale comes back exact); on
40
+ Windows its UTF-8 (WTF-8) bytes, as `_as_bytes` writes them. The ignore file is read as UTF-8 and stays `_as_bytes`."""
41
+ if os.name == "nt":
42
+ return _as_bytes(name)
43
+ try:
44
+ return os.fsencode(name).decode("latin-1")
45
+ except UnicodeEncodeError: # not a name os.walk or Git gave (a surrogate outside the escapes): read as `_as_bytes`
46
+ return _as_bytes(name)
47
+
48
+
49
+ def _glob(glob: str) -> "re.Pattern[str]":
50
+ """A line of the ignore file read from the tree's root: `*`, `?` and `[...]` stay inside one folder, and `**` as a
51
+ whole segment crosses folders (`**/` also matches none); any other run of stars is one `*`. Not a `.gitignore`
52
+ line: a line with no `/` matches at the root only, and `\\` or `//` is refused (`_line`).
53
+ fnmatch's `*` would cross `/`, so `vectors/*.json` would leave out `vectors/private/live.json`. Raises re.error
54
+ for a class Python cannot read (`[9-0]`)."""
55
+ key = glob
56
+ if key not in _GLOBS:
57
+ glob = _as_bytes(glob) # Git matches bytes: `?` and a class read one byte of a UTF-8 name, never a letter
58
+ out, i = [], 0
59
+ while i < len(glob):
60
+ c = glob[i]
61
+ if c == "*":
62
+ end = i
63
+ while end < len(glob) and glob[end] == "*":
64
+ end += 1
65
+ whole = end - i >= 2 and (i == 0 or glob[i - 1] == "/") and (end == len(glob) or glob[end] == "/")
66
+ if whole and end < len(glob):
67
+ out.append("(?:.*/)?")
68
+ end += 1
69
+ else:
70
+ out.append(".*" if whole else "[^/]*")
71
+ i = end
72
+ continue
73
+ elif c == "?":
74
+ out.append("[^/]")
75
+ elif c == "[":
76
+ j = i + 1 + (glob[i + 1:i + 2] in ("!", "^"))
77
+ j += glob[j:j + 1] == "]"
78
+ end = glob.find("]", j)
79
+ if end < 0 or "[:" in glob[i + 1:end + 1]:
80
+ # Git matches nothing for an unclosed `[` and reads `[:digit:]` as a POSIX class: refused
81
+ raise re.error("a class Git reads otherwise")
82
+ else:
83
+ inner = glob[i + 1:end].replace("\\", "\\\\")
84
+ negated = inner[:1] in ("!", "^") # Git reads both as a negated class
85
+ body = inner[negated:]
86
+ if body[:1] in ("^", "["): # a literal right after the negation
87
+ body = "\\" + body
88
+ for mark in "&~|": # literals, never the set operations a later Python may read there
89
+ body = body.replace(mark, "\\" + mark)
90
+ out.append("(?!/)[%s%s]" % ("^" if negated else "", body))
91
+ i = end
92
+ else:
93
+ out.append(re.escape(c))
94
+ i += 1
95
+ with warnings.catch_warnings(): # a FutureWarning (`--` in a class) would print the script's own path
96
+ warnings.simplefilter("error")
97
+ try:
98
+ _GLOBS[key] = re.compile("(?s:%s)\\Z" % "".join(out))
99
+ except Warning:
100
+ raise re.error("a class Python may read as a set operation")
101
+ return _GLOBS[key]
102
+
103
+
104
+ def _readable_glob(glob: str) -> bool:
105
+ """Whether every form `_ignored` tries can be read: a reversed range (`[9-0]`) is refused on every Python."""
106
+ try:
107
+ for form in (glob, glob.rstrip("/")):
108
+ _glob(form) # every form, whether `_ignored` tries it for this line or not
109
+ except re.error:
110
+ return False
111
+ return True
112
+
113
+
114
+ def _line(glob: str, where: str) -> str:
115
+ """An ignore line as it is matched: a leading `/` is the root (it changes nothing); a `!` exception is refused, since a
116
+ file it would bring back is left out here (it fails open), and so is a glob that cannot be read."""
117
+ if glob.startswith("!"):
118
+ raise CheckError("%s is an exception (`!`), which the check does not read: name the files to leave out" % where)
119
+ # `//` and `\` mean something else to Git (no match, an escape): refused, never read as a wider line
120
+ if "//" in glob or "\\" in glob:
121
+ raise CheckError("%s holds `\\` or `//`: write the path from the root with single `/`" % where)
122
+ glob = glob[1:] if glob.startswith("/") else glob
123
+ if not glob or not _readable_glob(glob):
124
+ raise CheckError("%s is not a glob it can read" % where)
125
+ return glob
126
+
127
+
128
+ def ignore_globs(root: Path, extra: Iterable[str]) -> List[str]:
129
+ globs = [_line(g.rstrip(" "), "an --exclude glob") for g in extra]
130
+ ignore = root / IGNORE_FILE
131
+ try:
132
+ present = ignore.is_file()
133
+ except OSError:
134
+ raise CheckError("the folder cannot be read")
135
+ if present:
136
+ try:
137
+ data = ignore.read_bytes().decode("utf-8-sig")
138
+ except (OSError, UnicodeDecodeError):
139
+ raise CheckError("%s cannot be read as UTF-8" % IGNORE_FILE)
140
+ # as Git splits it: at LF, a trailing CR dropped, trailing spaces trimmed; a tab or a leading space is the line's
141
+ for number, line in enumerate(data.split("\n"), 1):
142
+ glob = (line[:-1] if line.endswith("\r") else line).rstrip(" ")
143
+ if glob and not glob.startswith("#"):
144
+ globs.append(_line(glob, "line %d of %s" % (number, IGNORE_FILE)))
145
+ return globs
146
+
147
+
148
+ def _takes_folder(glob: str) -> bool:
149
+ """A line that names a folder takes all it holds: one ending in `/`, or a path with no wildcard (`vendor/lib`). A
150
+ wildcard line takes only what it matches: `vectors/*` the files in `vectors`, never `vectors/live/config.json`."""
151
+ return glob.endswith("/") or not any(c in glob for c in "*?[")
152
+
153
+
154
+ def _under(path: str, glob: str) -> bool:
155
+ """Whether a folder above the path matches the line, read as a folder: `src/**/` takes `src/pkg/x`, never
156
+ `src/main.py` (joining the line with `/**` would read `**/` as no folder at all)."""
157
+ folder = _glob(glob.rstrip("/"))
158
+ return any(folder.match(path[:i]) for i, c in enumerate(path) if c == "/")
159
+
160
+
161
+ def _ignored(path: str, globs: List[str], root: Optional[Path] = None) -> bool:
162
+ """Whether a line leaves the path out. A folder (a nested repository, which Git lists as `vendor/sub/`, or a
163
+ submodule, listed as `vendor/sub`) is left out only by a line that names it as a folder (`vendor/sub/`,
164
+ `vendor/sub`, or a wildcard folder line such as `vendor/*/`, which takes every one it reaches): a wildcard line
165
+ without the `/` takes files, so `vendor/*` reports a nested repository until it is checked in its own tree and
166
+ named. A line ending in `/` never takes a file of that name."""
167
+ on_disk = path.rstrip("/")
168
+ name = _name_bytes(on_disk) # matched as Git matches: byte by byte, the bytes on disk
169
+ known: List[bool] = []
170
+
171
+ def folder() -> bool:
172
+ if not known:
173
+ known.append(path.endswith("/") or (root is not None and os.path.isdir(str(root / on_disk))
174
+ and not os.path.islink(str(root / on_disk))))
175
+ return known[0]
176
+
177
+ for g in globs:
178
+ if _takes_folder(g):
179
+ if _under(name, g):
180
+ return True
181
+ if _glob(g.rstrip("/")).match(name) and (not g.endswith("/") or folder()):
182
+ return True
183
+ elif _glob(g).match(name) and not folder():
184
+ return True
185
+ return False
@@ -14,7 +14,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
14
14
  compatibility: Python 3.9+ or Node.js 20+ for the kits (Node.js 22+ for a Linux service's instance lock); the probe needs Python 3.9+. launchd steps are macOS-only; Linux services install a systemd user unit and Windows ones a per-user Scheduled Task with the kits' writers. Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
15
15
  metadata:
16
16
  author: PassionCode.ai
17
- version: "0.57.0"
17
+ version: "0.59.0"
18
18
  contract-version: "0.1.0"
19
19
  extension: "fabric-service/0.1"
20
20
  extension-commit: "3830a3353923e20f826d466610e1c479f120bec0"
@@ -63,6 +63,7 @@ Answer before writing code, and record the answers in the project README:
63
63
  | Port | one number; run the claim check below before choosing it |
64
64
  | Who calls it | the named agents or people, and through which surface (Step 1) |
65
65
  | What it stores | the store, where it lives (Step 2, rule 5), and what survives uninstall |
66
+ | Data and uptime | kind U or O, kit folders, a durable store or `<id>-data`, uptime A or B; a new agent's `LICENSE` holder, transfer audit per release (creating-fabric-agents) |
66
67
  | What it tells the operator | the events worth a notification (Step 5) |
67
68
 
68
69
  ## Step 1: choose the surfaces
@@ -321,7 +322,8 @@ the well-known document must answer again with a new pid within about 15 seconds
321
322
 
322
323
  To hand an agent to another machine or an organization, pack it as one folder (contract DEC-0050, DEC-0051). Write the
323
324
  author's `fabric.package.json` (the descriptive fields; for an agent `resultSignal` and `evals` with the receipt of the
324
- suite as packed), then:
325
+ suite as packed), run `fabric_source_check.py check <folder> --every-file` (adapting-projects-to-fabric, Python) on the clean copy
326
+ the package reference describes, then:
325
327
 
326
328
  ```bash
327
329
  python3 <skill-dir>/scripts/fabric_package.py pack <folder> # or: node fabric-package.mjs, npx @passioncode-ai/fabric-agent-adapter pack
@@ -352,9 +354,9 @@ first commit:
352
354
 
353
355
  Whoever owns the repository, an agent with a page gets the dashboard rule block in its `AGENTS.md`
354
356
  (below). org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
355
- someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's
356
- choice, nothing about it is published or listed by the organization, and none of these files is
357
- required of it, though the verified MCP quick start is still how anyone learns to drive it.
357
+ someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's choice, the organization
358
+ publishes and lists nothing about it, of these files one built new needs only a `LICENSE` naming its copyright holder (copied kit files keep
359
+ theirs), and the verified MCP quick start teaches anyone to drive it.
358
360
 
359
361
  ## Online services: the remote placement
360
362
 
@@ -417,20 +419,16 @@ this estate needs.
417
419
  ## Gotchas
418
420
 
419
421
  - `lsof` shows `*:<port>` or `0.0.0.0` → the bind is wrong even if the Host check works.
420
- - A health probe that only checks HTTP 200 accepts any program on the port. Compare
421
- `service.id` and `service.instance`: two services here once defaulted to ports
422
- another service already held.
423
- - `launchctl bootstrap` right after `bootout` fails with I/O error 5 until the unload
424
- finishes; wait, then retry.
422
+ - A health probe that only checks HTTP 200 accepts any program on the port: compare
423
+ `service.id` and `service.instance`.
424
+ - `launchctl bootstrap` right after `bootout` fails with I/O error 5: wait for the unload, then retry.
425
425
  - Under launchd `PATH` is bare. Pin the interpreter by absolute path; set `PATH`
426
426
  explicitly in the plist; never copy the whole shell environment into it.
427
- - A plist pointing at a worktree or a checkout breaks the day it moves. Point it at a
428
- release.
429
- - `http.server.HTTPServer` asks the resolver for its FQDN between `bind()` and
430
- `listen()`. With a slow resolver (a macOS CI runner, a Mac offline) the port is bound
431
- but silent: connects time out instead of being refused. Use `fs.LoopbackHTTPServer`.
432
- - `logging.basicConfig` called twice: the second call is a no-op, so a server's own
433
- log file stays empty. Configure logging once, in the entry point.
427
+ - A plist pointing at a worktree or checkout breaks when it moves: point it at a release.
428
+ - `http.server.HTTPServer` resolves its FQDN between `bind()` and `listen()`: with a slow
429
+ resolver (a macOS CI runner, a Mac offline) the port is bound but silent. Use `fs.LoopbackHTTPServer`.
430
+ - `logging.basicConfig` called twice: the second is a no-op, the server's log file
431
+ stays empty. Configure logging once, in the entry point.
434
432
  - Hand-made `.plist.bak` files in `~/Library/LaunchAgents` are never cleaned up; keep
435
433
  backups elsewhere.
436
434
 
@@ -42,6 +42,28 @@ python3 scripts/fabric_package.py pack <folder> # a dry run: the packa
42
42
  python3 scripts/fabric_package.py pack <folder> --apply # writes fabric.package.json when nothing is wrong
43
43
  ```
44
44
 
45
+ Run `adapting-projects-to-fabric/scripts/fabric_source_check.py check <folder> --every-file` on the folder you pack first (a floor under your own
46
+ review, not a secret scanner): a package carries
47
+ the source, and the instance (the owner's paths, keys, strings) stays out of it (`references/source-and-instance.md` of
48
+ that skill). `--every-file` reads what `pack` reads, files Git ignores included; without it the check reads only what Git
49
+ would commit. A path named in `.fabric-source-ignore` or `--exclude` is still carried by `pack`, unread, so name only synthetic files
50
+ there, and check a nested repository named there with `--every-file` in its own tree. Keep the signing key file
51
+ outside the folder. Pack a clean copy: what Git would commit (`git ls-files --cached --others --exclude-standard`) plus the nested
52
+ trees checked on their own, copied without their `.git` and with each committed symbolic link replaced or removed by where its resolved
53
+ target is (`pack` refuses a link): a file or folder Git would commit in the tree being packed, or in a nested tree checked
54
+ on its own, is copied in its place (a folder's committed files, each link inside it by the same rule; a file the ignore
55
+ file names needs an ignore line of its own at the new path); a link whose target is a folder already being copied on
56
+ the way to it (an ancestor, or a folder a link above it led into) is removed, so the copy ends; a target that leaves
57
+ that tree or lies in what `.gitignore` keeps out, such as an instance folder in the checkout, is removed, since it is
58
+ the instance or another project. Source the agent shares from outside the tree (a monorepo's common code, linked or
59
+ imported by a relative path) is copied into the agent's tree at the path it is reached by and committed before
60
+ packing. List each link removed and review it: a missing file is not seen by `pack` or `verify`. Leave out dependency
61
+ folders (`node_modules`, virtual environments, which hold install paths and binaries), what `.gitignore` keeps out as
62
+ instance (the real configuration, an instance folder in the checkout) and the files `pack` refuses by name (a
63
+ credential-free `.npmrc` that `.fabric-source-ignore` names for the check included). `pack` does not read
64
+ `.gitignore`, and an account id or a host is found only through the owner's list. The check is Python only (3.9+); a Node-only machine packs without it, and `pack` itself carries the kit's token files
65
+ and a private `*.jwk` by their names: keep them in the instance.
66
+
45
67
  It refuses to list what must not ship, and names each: a symlink, a special file, `.git`, `__pycache__`, `.DS_Store`,
46
68
  `*.pyc`, and files that are credentials by name (`.env` and `.env.*` but `.env.example|sample|template`, `*.pem`, `*.key`,
47
69
  `*.p12`, `*.pfx`, `*.jks`, `*.keystore`, `id_rsa`-style keys, `.npmrc`, `.netrc`, `.pypirc`). Remove them or pack a clean
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fabric-service-kit",
3
- "version": "0.57.0",
3
+ "version": "0.59.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
  {
@@ -4318,6 +4318,186 @@
4318
4318
  "fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
4319
4319
  "fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
4320
4320
  "share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
4321
+ },
4322
+ "0.58.0": {
4323
+ "fabric_jsvalue.py": "12888a2c934a885d35999ac9782eaedc8d04946d9775213344a235a8814d3d28",
4324
+ "fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
4325
+ "fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
4326
+ "fabric_secrets.py": "1d0191f0ec108fdc68345056b8c574b1f691509d1c353e41d98bfd58a59e8d3c",
4327
+ "fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
4328
+ "fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
4329
+ "fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
4330
+ "fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
4331
+ "fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
4332
+ "fabric_estate.py": "b3cd56dcb86e04d252980322ac8054da1aaef24ac64690f7aa80e7c765ee6f60",
4333
+ "fabric_mandate.py": "050c43d0166a7dfba82e29c4e978f05c364cf6da06d0a781bd023a566777c17b",
4334
+ "fabric_delegation.py": "716f0ff7ac01095f54b24970beac1962128e8ff33026b243fc0467876142ab03",
4335
+ "fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
4336
+ "fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
4337
+ "fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
4338
+ "fabric_package.py": "ff533f1b3f92b4ea0f532fcec50b68c610d47549930eef8ad5247692a64dfb3c",
4339
+ "fabric_protocols.py": "42529a7df4d753544d2e37a18ec81266d6e1e80477cb8558a3ab521416d45791",
4340
+ "fabric_actions.py": "ae21d1199614c4f74d1dcd1e0a87107a9d52434cd9e2d64aa24a7b1e196fb723",
4341
+ "fabric_connections.py": "7de55cb7d808f756d650effabfaa12027d40a5bd5d2f6d448c32969d47529ecb",
4342
+ "fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
4343
+ "fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
4344
+ "fabric_lock.py": "ba2399d2137c7a7791b7133b9db9cca36693f4eb7e30955b25a75ade814e95ba",
4345
+ "fabric_usage.py": "5cc2e03ccc5b06e81987ed7bc3eead7a7e6cbf1a7cb8e05c6c64315eed9d91b6",
4346
+ "fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
4347
+ "fabric_descriptor.py": "a6d7a4a38186f7ef527d16218bbf96e39e255f9547be668adcfbec063dabb7ed",
4348
+ "fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
4349
+ "fabric_auth.py": "71115d18c67b7b806cb57b64b77e01ad97fc0e63e6f49ee850f778123617d905",
4350
+ "fabric_events.py": "cd94b186e2a8f8cb4ad2b6bd44d83ec5021933075e28c5d655f387f16dff42b8",
4351
+ "fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
4352
+ "fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
4353
+ "fabric_service.py": "a2e813469a47d4d3eea996628635c8903007498bdb6a1ae032a7a5ce6f49fe2f",
4354
+ "fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
4355
+ "fabric_interop_rules.py": "82c079a4d9421ea2cd10c1dfa8e1c447b80d5e0dcc8d52d52415bf028e34c91e",
4356
+ "fabric_interop.py": "8f7e43bf01fa5e969d7d819f4b0188b9acfd846f6f85a37ea139e550d61e4e6c",
4357
+ "fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
4358
+ "fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
4359
+ "fabric_board_link.py": "e8d2310e5f53c75e4bf642c3d980b2faba692731eaf56ec245cea05446e38bef",
4360
+ "fabric_board_slots.py": "a3c705cded822103703a6c2d734bcd505fcd1d6c0293150c77dfa266fd416d53",
4361
+ "fabric_board_session.py": "9032c04fbafb63fd93809b494518aaa039215abef973ad181d960350beb56a9f",
4362
+ "fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
4363
+ "fabric_board.py": "34c1200860040aa61cdd35f911f70d34d64b4a5cd91f892328d1169e4a89d8cd",
4364
+ "fabric_http.py": "80e22ae0bba91de13a94d64b595108b39c58f16f7b4137f3a48bdd9503f424fe",
4365
+ "fabric_openrouter.py": "d06a40f83f2fb8bd441ab3485ae882f1fbdd9cd7a9c1ec175eb47cb8ae2cce81",
4366
+ "fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
4367
+ "fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
4368
+ "fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
4369
+ "fabric-secrets.mjs": "6ad8ab18ac899ecc2384e8ec6a3fb689b92be3b9a5ce3a59b92a37d52da5d766",
4370
+ "fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
4371
+ "fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
4372
+ "fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
4373
+ "fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
4374
+ "fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
4375
+ "fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
4376
+ "fabric-mandate.mjs": "deaab2bfb929d1bd35ae1bad71e7b9ad9902cc23b8eb51819ed592fd80eddf95",
4377
+ "fabric-delegation.mjs": "04eaf6cd1786d4309a253a27dbdb05e27b408cb511aa41b1633a96634b775e65",
4378
+ "fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
4379
+ "fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
4380
+ "fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
4381
+ "fabric-package.mjs": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88",
4382
+ "fabric-protocols.mjs": "5fd8765fdd3e507cc85c108492682bf5cbb4ae08873245c50374fb01d33c75e4",
4383
+ "fabric-actions.mjs": "06e42f5e24c96311d5417d0d1effa93479ef7128a7ab139903b2bcf59253f6be",
4384
+ "fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
4385
+ "fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
4386
+ "fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
4387
+ "fabric-lock.mjs": "ac219ab0a326d5c43c7c4c255e0c3843d4794de795a60e3af661e7d5350ea052",
4388
+ "fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
4389
+ "fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
4390
+ "fabric-descriptor.mjs": "243bbce42989f67c4c5ec6b7659e201b3f9a1c08c71d429d88ec934d870f5104",
4391
+ "fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
4392
+ "fabric-auth.mjs": "782c2aa732f8320f604194660117640a7ea145e12b8155c04e88cef7c516a5b3",
4393
+ "fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
4394
+ "fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
4395
+ "fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
4396
+ "fabric-service.mjs": "6aa673657427211b491a9b63f94b9847a7e6aa3ee1b7353e1af11f46469803a3",
4397
+ "fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
4398
+ "fabric-interop-rules.mjs": "e4f8b133138e27353a7976c0360bd1653c90daed43b3f843f85a19f14f5e9221",
4399
+ "fabric-interop.mjs": "ee7e6d32be1f42e6d21a943b8886652352408c49c6404d9749b8a02dc3901e92",
4400
+ "fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
4401
+ "fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
4402
+ "fabric-board-link.mjs": "b1ac329b65b679193b94b81fe735922e14427ed540ba910b1a815ce3447e3220",
4403
+ "fabric-board-slots.mjs": "43eb7ceb6c62989a4eb4b2089d1808e2c3cf8f873fcf9216fcb4c285e5b39eb4",
4404
+ "fabric-board-session.mjs": "04aa920b24a379157c55ebe3de516eb8b46bd0b0db50e4b883528d4ede7ed978",
4405
+ "fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
4406
+ "fabric-board.mjs": "0d75c3e27a0b791bdbc35b2e93b3299e5831d3ab3003ce441e6268397e32b448",
4407
+ "fabric-http.mjs": "9f899829073470b9c2b449605a9371b773848eec4e3a0122ed4ccde5eddcdb4b",
4408
+ "fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
4409
+ "fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
4410
+ "share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
4411
+ },
4412
+ "0.59.0": {
4413
+ "fabric_jsvalue.py": "12888a2c934a885d35999ac9782eaedc8d04946d9775213344a235a8814d3d28",
4414
+ "fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
4415
+ "fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
4416
+ "fabric_secrets.py": "1d0191f0ec108fdc68345056b8c574b1f691509d1c353e41d98bfd58a59e8d3c",
4417
+ "fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
4418
+ "fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
4419
+ "fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
4420
+ "fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
4421
+ "fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
4422
+ "fabric_estate.py": "b3cd56dcb86e04d252980322ac8054da1aaef24ac64690f7aa80e7c765ee6f60",
4423
+ "fabric_mandate.py": "050c43d0166a7dfba82e29c4e978f05c364cf6da06d0a781bd023a566777c17b",
4424
+ "fabric_delegation.py": "716f0ff7ac01095f54b24970beac1962128e8ff33026b243fc0467876142ab03",
4425
+ "fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
4426
+ "fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
4427
+ "fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
4428
+ "fabric_package.py": "ff533f1b3f92b4ea0f532fcec50b68c610d47549930eef8ad5247692a64dfb3c",
4429
+ "fabric_protocols.py": "42529a7df4d753544d2e37a18ec81266d6e1e80477cb8558a3ab521416d45791",
4430
+ "fabric_actions.py": "ae21d1199614c4f74d1dcd1e0a87107a9d52434cd9e2d64aa24a7b1e196fb723",
4431
+ "fabric_connections.py": "7de55cb7d808f756d650effabfaa12027d40a5bd5d2f6d448c32969d47529ecb",
4432
+ "fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
4433
+ "fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
4434
+ "fabric_lock.py": "ba2399d2137c7a7791b7133b9db9cca36693f4eb7e30955b25a75ade814e95ba",
4435
+ "fabric_usage.py": "5cc2e03ccc5b06e81987ed7bc3eead7a7e6cbf1a7cb8e05c6c64315eed9d91b6",
4436
+ "fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
4437
+ "fabric_descriptor.py": "a6d7a4a38186f7ef527d16218bbf96e39e255f9547be668adcfbec063dabb7ed",
4438
+ "fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
4439
+ "fabric_auth.py": "71115d18c67b7b806cb57b64b77e01ad97fc0e63e6f49ee850f778123617d905",
4440
+ "fabric_events.py": "cd94b186e2a8f8cb4ad2b6bd44d83ec5021933075e28c5d655f387f16dff42b8",
4441
+ "fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
4442
+ "fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
4443
+ "fabric_service.py": "a2e813469a47d4d3eea996628635c8903007498bdb6a1ae032a7a5ce6f49fe2f",
4444
+ "fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
4445
+ "fabric_interop_rules.py": "82c079a4d9421ea2cd10c1dfa8e1c447b80d5e0dcc8d52d52415bf028e34c91e",
4446
+ "fabric_interop.py": "8f7e43bf01fa5e969d7d819f4b0188b9acfd846f6f85a37ea139e550d61e4e6c",
4447
+ "fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
4448
+ "fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
4449
+ "fabric_board_link.py": "e8d2310e5f53c75e4bf642c3d980b2faba692731eaf56ec245cea05446e38bef",
4450
+ "fabric_board_slots.py": "a3c705cded822103703a6c2d734bcd505fcd1d6c0293150c77dfa266fd416d53",
4451
+ "fabric_board_session.py": "9032c04fbafb63fd93809b494518aaa039215abef973ad181d960350beb56a9f",
4452
+ "fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
4453
+ "fabric_board.py": "34c1200860040aa61cdd35f911f70d34d64b4a5cd91f892328d1169e4a89d8cd",
4454
+ "fabric_http.py": "80e22ae0bba91de13a94d64b595108b39c58f16f7b4137f3a48bdd9503f424fe",
4455
+ "fabric_openrouter.py": "d06a40f83f2fb8bd441ab3485ae882f1fbdd9cd7a9c1ec175eb47cb8ae2cce81",
4456
+ "fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
4457
+ "fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
4458
+ "fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
4459
+ "fabric-secrets.mjs": "6ad8ab18ac899ecc2384e8ec6a3fb689b92be3b9a5ce3a59b92a37d52da5d766",
4460
+ "fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
4461
+ "fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
4462
+ "fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
4463
+ "fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
4464
+ "fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
4465
+ "fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
4466
+ "fabric-mandate.mjs": "deaab2bfb929d1bd35ae1bad71e7b9ad9902cc23b8eb51819ed592fd80eddf95",
4467
+ "fabric-delegation.mjs": "04eaf6cd1786d4309a253a27dbdb05e27b408cb511aa41b1633a96634b775e65",
4468
+ "fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
4469
+ "fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
4470
+ "fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
4471
+ "fabric-package.mjs": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88",
4472
+ "fabric-protocols.mjs": "5fd8765fdd3e507cc85c108492682bf5cbb4ae08873245c50374fb01d33c75e4",
4473
+ "fabric-actions.mjs": "06e42f5e24c96311d5417d0d1effa93479ef7128a7ab139903b2bcf59253f6be",
4474
+ "fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
4475
+ "fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
4476
+ "fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
4477
+ "fabric-lock.mjs": "ac219ab0a326d5c43c7c4c255e0c3843d4794de795a60e3af661e7d5350ea052",
4478
+ "fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
4479
+ "fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
4480
+ "fabric-descriptor.mjs": "243bbce42989f67c4c5ec6b7659e201b3f9a1c08c71d429d88ec934d870f5104",
4481
+ "fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
4482
+ "fabric-auth.mjs": "782c2aa732f8320f604194660117640a7ea145e12b8155c04e88cef7c516a5b3",
4483
+ "fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
4484
+ "fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
4485
+ "fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
4486
+ "fabric-service.mjs": "6aa673657427211b491a9b63f94b9847a7e6aa3ee1b7353e1af11f46469803a3",
4487
+ "fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
4488
+ "fabric-interop-rules.mjs": "e4f8b133138e27353a7976c0360bd1653c90daed43b3f843f85a19f14f5e9221",
4489
+ "fabric-interop.mjs": "ee7e6d32be1f42e6d21a943b8886652352408c49c6404d9749b8a02dc3901e92",
4490
+ "fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
4491
+ "fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
4492
+ "fabric-board-link.mjs": "b1ac329b65b679193b94b81fe735922e14427ed540ba910b1a815ce3447e3220",
4493
+ "fabric-board-slots.mjs": "43eb7ceb6c62989a4eb4b2089d1808e2c3cf8f873fcf9216fcb4c285e5b39eb4",
4494
+ "fabric-board-session.mjs": "04aa920b24a379157c55ebe3de516eb8b46bd0b0db50e4b883528d4ede7ed978",
4495
+ "fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
4496
+ "fabric-board.mjs": "0d75c3e27a0b791bdbc35b2e93b3299e5831d3ab3003ce441e6268397e32b448",
4497
+ "fabric-http.mjs": "9f899829073470b9c2b449605a9371b773848eec4e3a0122ed4ccde5eddcdb4b",
4498
+ "fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
4499
+ "fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
4500
+ "share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
4321
4501
  }
4322
4502
  },
4323
4503
  "data": {