@passioncode-ai/fabric-agent-adapter 0.82.0 → 0.83.1

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 (114) hide show
  1. package/CHANGELOG.md +122 -3
  2. package/README.md +6 -3
  3. package/SECURITY.md +4 -3
  4. package/package.json +1 -1
  5. package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  6. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +7 -2
  7. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/agents-database.md +10 -0
  8. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +4 -2
  9. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +18 -4
  10. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +16 -0
  11. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +31 -15
  12. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_secrets.py +37 -12
  13. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +21 -148
  14. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check_cli.py +164 -0
  15. package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +15 -8
  16. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +3 -3
  17. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.css +1 -1
  18. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.js +2 -2
  19. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/kit.json +9 -4
  20. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/README.md +76 -0
  21. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/config.example.json +28 -0
  22. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/data_agent.py +366 -0
  23. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +32 -17
  24. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/config.example.json +4 -0
  25. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support-agent.mjs +115 -0
  26. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support_agent.py +63 -11
  27. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/a2a.md +98 -0
  28. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/agents-database.md +10 -0
  29. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +20 -9
  30. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/connectors.md +2 -2
  31. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +14 -2
  32. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +5 -1
  33. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +8 -5
  34. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/ops-digest.md +75 -0
  35. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +7 -5
  36. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +25 -12
  37. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/remote-placement.md +10 -1
  38. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/surfaces-and-auth.md +70 -0
  39. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_a2a.py +158 -0
  40. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a-server.mjs +274 -0
  41. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a.mjs +1 -1
  42. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +4 -2
  43. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-fixture.mjs +14 -2
  44. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-link.mjs +54 -11
  45. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +45 -31
  46. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-slots.mjs +16 -6
  47. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-connections.mjs +26 -5
  48. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +3 -2
  49. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +18 -12
  50. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-events.mjs +6 -1
  51. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-holds.mjs +21 -7
  52. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-http.mjs +3 -5
  53. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-hub.mjs +6 -2
  54. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +20 -1
  55. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +5 -0
  56. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-lock.mjs +15 -2
  57. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-mandate.mjs +5 -0
  58. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-openrouter.mjs +24 -3
  59. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-ops-digest.mjs +455 -0
  60. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +17 -2
  61. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-paths.mjs +18 -5
  62. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-peer-owner.mjs +55 -19
  63. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-secrets.mjs +15 -2
  64. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-socket-proof.mjs +180 -0
  65. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +5 -12
  66. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-tailnet-identity.mjs +468 -0
  67. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a.py +2 -1
  68. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a_server.py +404 -0
  69. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +6 -2
  70. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_fixture.py +16 -3
  71. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_link.py +97 -9
  72. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +35 -48
  73. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_slots.py +15 -7
  74. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_connections.py +27 -6
  75. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +18 -13
  76. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +23 -14
  77. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_events.py +3 -1
  78. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_holds.py +31 -7
  79. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +18 -16
  80. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_hub.py +9 -5
  81. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +55 -96
  82. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +98 -1
  83. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_lock.py +15 -6
  84. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_openrouter.py +43 -1
  85. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_ops_digest.py +647 -0
  86. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +16 -4
  87. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_paths.py +20 -5
  88. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_peer_owner.py +93 -93
  89. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py +37 -12
  90. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_socket_proof.py +390 -0
  91. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +3 -8
  92. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_tailnet_identity.py +492 -0
  93. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +365 -53
  94. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample-remote-service.mjs +2 -1
  95. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +16 -7
  96. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/tailnet-groups.schema.json +69 -0
  97. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +5 -4
  98. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agent-templates.md +16 -0
  99. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-database.md +10 -0
  100. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-that-answer-people.md +14 -9
  101. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/models-and-providers.md +4 -2
  102. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +18 -4
  103. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md +2 -1
  104. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/admit.py +162 -0
  105. package/plugins/fabric-agent-adapter/skills/observing-products/SKILL.md +1 -1
  106. package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
  107. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/agents-database.md +10 -0
  108. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +14 -9
  109. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +7 -4
  110. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +10 -5
  111. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +2 -0
  112. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +16 -5
  113. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +79 -8
  114. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_paths.py +147 -45
@@ -38,7 +38,7 @@ unset the walk is used). A committed symbolic link is checked by the path it nam
38
38
  its first 8 KiB) is not read; it is listed. Globs in ROOT's `.fabric-source-ignore` (one per line, `#`
39
39
  comments) and `--exclude` leave matching paths out. The owner's list is `--instance-strings`, else the file
40
40
  `FABRIC_INSTANCE_STRINGS` names, else `passioncode-fabric/instance-strings.txt` in the user's configuration folder
41
- (`$XDG_CONFIG_HOME` when it is absolute, else `~/.config`; `%APPDATA%` on Windows) when it exists: one string per
41
+ (`$XDG_CONFIG_HOME` when it is absolute, else `~/.config`; `%APPDATA%` when absolute on Windows) when it exists: one string per
42
42
  line, a line starting with `#` a comment, `home: <name>` declaring the owner's home name (never neutral), strings
43
43
  shorter than three characters ignored, matched without regard to case. A list inside ROOT
44
44
  is refused: it would travel with the source. The summary says whether Git chose the files or every file was read.
@@ -95,6 +95,9 @@ def _refuse_sibling(step: str, missing: BaseException) -> NoReturn:
95
95
 
96
96
  _WROTE_BYTECODE, sys.dont_write_bytecode = sys.dont_write_bytecode, True # a copy inside the checked tree adds no .pyc
97
97
  _STEP = "fabric_secrets" # the sibling being loaded, so a refusal names it whatever the exception says
98
+ #: The public names of fabric_secrets the check and its command line read (FAA-190 (2), (12)), checked when it loads.
99
+ SECRETS_NAMES = ("BEARER_INSIDE", "JS_SPACE", "NOT_LINE_END", "SCHEME", "credential_name", "has_jwt", "jwt_spans",
100
+ "long_secret", "raw_values", "secret_value", "url_parts")
98
101
  try:
99
102
  import fabric_secrets as _secrets # noqa: E402 - the contract's credential detector (DEC-0044), beside this file
100
103
  _STEP = "fabric_source_ignore"
@@ -105,12 +108,18 @@ try:
105
108
  _STEP = "fabric_source_homes"
106
109
  from fabric_source_homes import ( # noqa: E402,F401 - how a declared home is read, beside this file
107
110
  HOME_ENTRY, _NAME_JOIN, _NAME_START, _SEP, _dashed_home, _name_parts, _owner_homes, _spellings)
111
+ _STEP = "fabric_source_check_cli"
112
+ from fabric_source_check_cli import ( # noqa: E402,F401 - the command line, beside this file (FAA-190 (104))
113
+ LIST_NAME, _Parser, _absolute, _list_path, _print, default_list, run_check)
114
+ _STEP = "fabric_secrets"
115
+ for _name in SECRETS_NAMES: # a detector from another version fails here, never at the first file read (FAA-190 (89))
116
+ if not hasattr(_secrets, _name):
117
+ raise ImportError(_name)
108
118
  except Exception as _missing: # a copy without its siblings, or mixed from two versions: a refusal, never exit 1
109
119
  _refuse_sibling(_STEP, _missing)
110
120
  finally:
111
121
  sys.dont_write_bytecode = _WROTE_BYTECODE
112
122
 
113
- LIST_NAME = "instance-strings.txt"
114
123
  #: The folders `pack` refuses to carry (fabric_package.JUNK); --every-file leaves them and the files below unread.
115
124
  PACK_REFUSES = {".git", "__pycache__"}
116
125
  WALK_SKIP = {".git", "node_modules", "__pycache__", ".venv", "venv", ".tox", ".mypy_cache", ".pytest_cache"}
@@ -145,12 +154,12 @@ SYNTHETIC = ("example", "not-a-key", "notakey", "fake", "dummy", "placeholder",
145
154
  # This check's own key shapes, the detector's before DEC-0151 (unbounded, ASCII case-insensitive), apart from the shared
146
155
  # SECRET_VALUE for parity with the contract's tree check (KEY_SHAPE_SOURCES, `src/source-rules.ts` at the pin; CT-207,
147
156
  # FAA-258). Not the detector's `^bearer\s.+`, which reads a whole header value and would take prose that starts with the
148
- # word: a bearer token is looked for as `_BEARER_INSIDE` does, the word, then a run of 12 or more with a digit.
157
+ # word: a bearer token is looked for as `BEARER_INSIDE` does, the word, then a run of 12 or more with a digit.
149
158
  KEY_SHAPE_SOURCES = ("sk-[A-Za-z0-9_-]{16,}", "gh[pousr]_[A-Za-z0-9]{20,}", "xox[abposr]-[A-Za-z0-9-]{10,}", "AKIA[0-9A-Z]{16}", "glpat-[A-Za-z0-9_-]{20,}")
150
159
  PROVIDER_KEY = re.compile("(" + "|".join(KEY_SHAPE_SOURCES) + ")", re.IGNORECASE | re.ASCII)
151
160
  #: A URL parameter's value read as the contract's tree check reads it (`URL_SECRET_VALUE` in `src/source-rules.ts`): the
152
161
  #: detector's whole-value `^bearer` branch and this check's own unbounded shapes, never the shared DEC-0151 detector.
153
- URL_SECRET_VALUE = re.compile("(^bearer[" + _secrets.JS_SPACE + "]" + _secrets._NOT_LINE_END + "+|" + "|".join(KEY_SHAPE_SOURCES) + ")",
162
+ URL_SECRET_VALUE = re.compile("(^bearer[" + _secrets.JS_SPACE + "]" + _secrets.NOT_LINE_END + "+|" + "|".join(KEY_SHAPE_SOURCES) + ")",
154
163
  re.IGNORECASE | re.ASCII)
155
164
  #: Files that are credentials by their name: pack's own rule (fabric_package.SECRET_FILE and SECRET_FILE_OK, held
156
165
  #: equal by test_source_check), and the kit's token files (`service.token`, fabric_auth; `board.credential`, fabric_hub).
@@ -217,7 +226,7 @@ def _url_secret(url: str, depth: int = 0) -> bool:
217
226
  So: a password of 8 or more mixing letters and digits, or a query or fragment value that, percent-decoded, is shaped
218
227
  like a credential (a provider key, a JWT, a long random run under a credential's name)."""
219
228
  url = url.rstrip(".,;)]}")
220
- if not _secrets._SCHEME.match(url):
229
+ if not _secrets.SCHEME.match(url):
221
230
  return False
222
231
  password, query, fragment = _secrets.url_parts(url)
223
232
  # what says it is synthetic is the value, never the host or the path (`examples.blob.core.windows.net`)
@@ -233,7 +242,7 @@ def _url_secret(url: str, depth: int = 0) -> bool:
233
242
  return True
234
243
  if not _real(value):
235
244
  continue
236
- if (_secrets.credential_name(name) and _secrets._long_secret(value)) or \
245
+ if (_secrets.credential_name(name) and _secrets.long_secret(value)) or \
237
246
  _secrets.has_jwt(value) or URL_SECRET_VALUE.search(value):
238
247
  return True
239
248
  return False
@@ -293,13 +302,13 @@ def _secret_in(line: str) -> Optional[str]:
293
302
  # with a segment ending in `sk` (`task-management-service`) would read as a key.
294
303
  if any(True for _ in _provider_keys(line)):
295
304
  return "a value shaped like a provider key"
296
- if any(_real(m.group(0)) for m in _overlapping(_secrets._BEARER_INSIDE, line)):
305
+ if any(_real(m.group(0)) for m in _overlapping(_secrets.BEARER_INSIDE, line)):
297
306
  return "a bearer token"
298
307
  if "://" in line and any(_url_secret(m.group(0)) for m in URL.finditer(line)):
299
308
  return "a URL that carries a credential"
300
309
  for m in (_assigned(line) if ":" in line or "=" in line else ()):
301
310
  name, value = m.group(1), m.group(2)
302
- if _credential(name, value, _quoted(line, m)) and _secrets._long_secret(unquote(value)) and _real(unquote(value)):
311
+ if _credential(name, value, _quoted(line, m)) and _secrets.long_secret(unquote(value)) and _real(unquote(value)):
303
312
  return "a long random value assigned to a credential's name"
304
313
  return None
305
314
 
@@ -489,10 +498,10 @@ def _raw_secret_spans(text: str) -> List[Tuple[int, int]]:
489
498
  spans += [(a, b) for a, b in _secrets.jwt_spans(text) if _real(text[a:b])] # one pass (FAA-313)
490
499
  spans += [m.span(1) for m in JWK_PRIVATE.finditer(text) if _real(m.group(1))]
491
500
  spans += list(_provider_keys(text))
492
- spans += [m.span() for m in _overlapping(_secrets._BEARER_INSIDE, text) if _real(m.group(0))]
501
+ spans += [m.span() for m in _overlapping(_secrets.BEARER_INSIDE, text) if _real(m.group(0))]
493
502
  spans += [m.span() for m in URL.finditer(text) if _url_secret(m.group(0))]
494
503
  spans += [m.span(2) for m in _assigned(text)
495
- if _credential(m.group(1), m.group(2), _quoted(text, m)) and _secrets._long_secret(unquote(m.group(2)))
504
+ if _credential(m.group(1), m.group(2), _quoted(text, m)) and _secrets.long_secret(unquote(m.group(2)))
496
505
  and _real(unquote(m.group(2)))]
497
506
  return spans
498
507
 
@@ -814,145 +823,9 @@ def check(root: Path, entries: List[str], globs: List[str], every_file: bool = F
814
823
  "dependencies": [shown(d) for d in dependencies]}
815
824
 
816
825
 
817
- def _absolute(path: str, platform: str) -> bool:
818
- return bool(re.match(r"[A-Za-z]:[\\/]|[\\/]{2}", path)) if platform == "win32" else path.startswith("/")
819
-
820
-
821
- def default_list(platform: Optional[str] = None, env: Optional[Dict[str, str]] = None) -> Path:
822
- """Where the owner's list is kept when none is named: the user's configuration folder, never a project's."""
823
- platform = platform or sys.platform
824
- env = os.environ if env is None else env
825
- # a relative value is ignored, as the XDG spec says (a quoted `~/.config` is relative too): never "no list"
826
- if platform == "win32":
827
- appdata = env.get("APPDATA", "")
828
- base = Path(appdata) if appdata and _absolute(appdata, platform) else Path.home() / "AppData" / "Roaming"
829
- else:
830
- xdg = env.get("XDG_CONFIG_HOME", "")
831
- base = Path(xdg) if xdg.startswith("/") else Path.home() / ".config"
832
- return base / "passioncode-fabric" / LIST_NAME
833
-
834
-
835
- def _list_path(given: Optional[str]) -> Tuple[Optional[Path], bool]:
836
- """The owner's list and whether it must be read (a named or an existing default list; else no list)."""
837
- named = given or os.environ.get("FABRIC_INSTANCE_STRINGS")
838
- if named:
839
- try:
840
- return Path(named).expanduser(), True
841
- except (RuntimeError, KeyError): # `~user` with no such user (KeyError on 3.9): never "no finding"
842
- raise CheckError("the owner's list cannot be read")
843
- try:
844
- default = default_list()
845
- except (RuntimeError, KeyError): # no HOME and no account entry (a container's arbitrary user)
846
- raise CheckError("there is no home folder to find the owner's list in: set an absolute XDG_CONFIG_HOME (APPDATA on "
847
- "Windows) or name the list with --instance-strings")
848
- try:
849
- os.stat(str(default))
850
- except (FileNotFoundError, NotADirectoryError):
851
- # a link to a list, its folder or the configuration folder on a volume not mounted: there, but not reachable
852
- if any(os.path.lexists(str(p)) and not os.path.exists(str(p)) for p in (default, *default.parents)):
853
- raise CheckError("the owner's list in your configuration folder cannot be read")
854
- return None, False
855
- except OSError: # a list that is there but cannot be reached is not "no list": that would fail open
856
- raise CheckError("the owner's list cannot be read")
857
- return default, True
858
-
859
-
860
- class _Parser(argparse.ArgumentParser):
861
- def error(self, message: str) -> NoReturn: # argparse would repeat the arguments it could not read
862
- self.print_usage(sys.stderr)
863
- self.exit(2, "fabric_source_check.py: the arguments cannot be read; see the usage above\n")
864
-
865
-
866
- def _print(text: str) -> None:
867
- """Print a line the console may not encode (a name in another script, a byte Git kept as a surrogate)."""
868
- encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
869
- print(text.encode(encoding, "backslashreplace").decode(encoding, "replace"))
870
-
871
-
872
826
  def run(argv: Optional[List[str]] = None) -> int:
873
- parser = _Parser(prog="fabric_source_check.py", description="Find instance data in an agent's source.")
874
- sub = parser.add_subparsers(dest="command", required=True)
875
- cmd = sub.add_parser("check", help="read the source tree and report instance data without repeating it")
876
- cmd.add_argument("root", nargs="?", default=".")
877
- cmd.add_argument("--instance-strings", metavar="FILE")
878
- cmd.add_argument("--exclude", action="append", default=[], metavar="GLOB")
879
- cmd.add_argument("--json", action="store_true")
880
- cmd.add_argument("--every-file", action="store_true", help="read every file, not only what Git would commit")
881
- try:
882
- args = parser.parse_args(argv)
883
- except SystemExit as exc: # --help, or arguments argparse refused: the exit code, as the command line gives it
884
- return exc.code if isinstance(exc.code, int) else 2
885
- try:
886
- try:
887
- root = Path(args.root).expanduser().resolve()
888
- is_folder = root.is_dir()
889
- except (OSError, RuntimeError, KeyError): # a parent it cannot search, a symlink loop, `~` with no home
890
- raise CheckError("the folder cannot be read")
891
- if not is_folder:
892
- raise CheckError("ROOT is not a folder")
893
- list_path, _ = _list_path(args.instance_strings) # may raise CheckError
894
- entries: List[str] = []
895
- if list_path is not None:
896
- try:
897
- resolved = list_path.resolve()
898
- except (OSError, RuntimeError): # RuntimeError: Python 3.9's symlink loop
899
- raise CheckError("the owner's list cannot be read")
900
- if resolved == root or root in resolved.parents:
901
- raise CheckError("the owner's list is inside the tree it checks, where it would travel with the source; "
902
- "keep it in your configuration folder (passioncode-fabric/%s)" % LIST_NAME)
903
- try:
904
- present = resolved.is_file()
905
- except OSError:
906
- raise CheckError("the owner's list cannot be read")
907
- if not present:
908
- raise CheckError("the owner's list named by --instance-strings or FABRIC_INSTANCE_STRINGS does not exist or is "
909
- "not a regular file"
910
- if args.instance_strings or os.environ.get("FABRIC_INSTANCE_STRINGS")
911
- else "the owner's list in your configuration folder is not a file")
912
- try:
913
- entries = read_list(resolved)
914
- except UnicodeDecodeError:
915
- raise CheckError("the owner's list is not UTF-8")
916
- except OSError:
917
- raise CheckError("the owner's list cannot be read")
918
- result = check(root, entries, ignore_globs(root, args.exclude), args.every_file)
919
- except CheckError as exc:
920
- print("fabric_source_check.py: %s" % exc, file=sys.stderr)
921
- return 2
922
- if result.get("manifest"): # under fabric.source.json: its own report (fabric_source_tree.print_run)
923
- return sys.modules["fabric_source_tree"].print_run(result, entries, list_path is not None, args.json, _print)
924
- findings = result["findings"]
925
- skipped = result["skipped"]
926
- listed = sum(1 for e in entries if e and not e.startswith(HOME_ENTRY))
927
- homes = sum(1 for e in entries if e.startswith(HOME_ENTRY))
928
- # a binary file is not read either: it is complete only when every one is named as source in the ignore file
929
- complete = not (skipped["unreadable"] or skipped["binary"] or skipped["nested"]) # type: ignore[index]
930
- result.update({"instanceStrings": listed, "ownerHomes": homes, "instanceList": list_path is not None, "blocking": bool(findings),
931
- "complete": complete})
932
- if args.json:
933
- _print(json.dumps(result, indent=2))
934
- else:
935
- for f in findings: # type: ignore[union-attr]
936
- _print("%s:%s: %s (%s)" % (f["path"], f["line"], f["kind"], f["rule"]))
937
- for n in result["notRead"]: # type: ignore[union-attr]
938
- _print("%s: not read (%s)" % (n["path"], n["why"]))
939
- for folder in result["dependencies"]: # type: ignore[union-attr]
940
- _print("%s: not read (a dependency folder; --every-file reads it)" % folder)
941
- if skipped["dependencies"]: # type: ignore[index]
942
- _print("%d dependency folder(s) not read (node_modules, virtual environments, caches); --every-file reads "
943
- "them" % skipped["dependencies"]) # type: ignore[index]
944
- _print("%d file(s) read (%s), %d finding(s); not read: %d binary, %d unreadable, %d nested repository; "
945
- "%d left out by name; %d instance string(s) listed%s%s"
946
- % (result["files"], "what Git would commit" if result["mode"] == "git" else
947
- "every file, as pack reads them" if args.every_file else "every file but dependency folders, Git not used",
948
- len(findings), skipped["binary"], skipped["unreadable"], skipped["nested"], # type: ignore[index]
949
- skipped["excluded"], listed, # type: ignore[index]
950
- "" if listed or homes else (" (the owner's list holds no string of three or more characters)"
951
- if list_path else " (no owner's list)"),
952
- "; %d home name(s) declared" % homes if homes else ""))
953
- if findings:
954
- return 1
955
- return 0 if complete else 2
827
+ """The command line (fabric_source_check_cli.run_check), run with this module's check."""
828
+ return run_check(argv, check, read_list, ignore_globs, HOME_ENTRY)
956
829
 
957
830
 
958
831
  if __name__ == "__main__":
@@ -0,0 +1,164 @@
1
+ """The command line of fabric_source_check.py (FAA-190 (104)): where the owner's list is found, the arguments, and the
2
+ printed report. It sits beside fabric_source_check.py, which imports it inside its import guard and keeps the entry
3
+ point (`fabric_source_check.py check ...`, `fabric_source_check.run`); run the check by path, never this file.
4
+ Standard library only."""
5
+ from __future__ import annotations
6
+
7
+ import argparse
8
+ import json
9
+ import os
10
+ from pathlib import Path
11
+ import re
12
+ import sys
13
+ from typing import Callable, Dict, List, NoReturn, Optional, Tuple
14
+
15
+ from fabric_source_ignore import CheckError
16
+
17
+ LIST_NAME = "instance-strings.txt"
18
+
19
+
20
+ def _absolute(path: str, platform: str) -> bool:
21
+ return bool(re.match(r"[A-Za-z]:[\\/]|[\\/]{2}", path)) if platform == "win32" else path.startswith("/")
22
+
23
+
24
+ def default_list(platform: Optional[str] = None, env: Optional[Dict[str, str]] = None) -> Path:
25
+ """Where the owner's list is kept when none is named: the user's configuration folder, never a project's."""
26
+ platform = platform or sys.platform
27
+ env = os.environ if env is None else env
28
+ # a relative value is ignored, as the XDG spec says (a quoted `~/.config` is relative too): never "no list"
29
+ if platform == "win32":
30
+ appdata = env.get("APPDATA", "")
31
+ base = Path(appdata) if appdata and _absolute(appdata, platform) else Path.home() / "AppData" / "Roaming"
32
+ else:
33
+ xdg = env.get("XDG_CONFIG_HOME", "")
34
+ base = Path(xdg) if xdg.startswith("/") else Path.home() / ".config"
35
+ return base / "passioncode-fabric" / LIST_NAME
36
+
37
+
38
+ def _list_path(given: Optional[str]) -> Tuple[Optional[Path], bool]:
39
+ """The owner's list and whether it must be read (a named or an existing default list; else no list)."""
40
+ named = given or os.environ.get("FABRIC_INSTANCE_STRINGS")
41
+ if named:
42
+ try:
43
+ return Path(named).expanduser(), True
44
+ except (RuntimeError, KeyError): # `~user` with no such user (KeyError on 3.9): never "no finding"
45
+ raise CheckError("the owner's list cannot be read")
46
+ try:
47
+ default = default_list()
48
+ except (RuntimeError, KeyError): # no HOME and no account entry (a container's arbitrary user)
49
+ raise CheckError("there is no home folder to find the owner's list in: set an absolute XDG_CONFIG_HOME (APPDATA on "
50
+ "Windows) or name the list with --instance-strings")
51
+ try:
52
+ os.stat(str(default))
53
+ except (FileNotFoundError, NotADirectoryError):
54
+ # a link to a list, its folder or the configuration folder on a volume not mounted: there, but not reachable
55
+ if any(os.path.lexists(str(p)) and not os.path.exists(str(p)) for p in (default, *default.parents)):
56
+ raise CheckError("the owner's list in your configuration folder cannot be read")
57
+ return None, False
58
+ except OSError: # a list that is there but cannot be reached is not "no list": that would fail open
59
+ raise CheckError("the owner's list cannot be read")
60
+ return default, True
61
+
62
+
63
+ class _Parser(argparse.ArgumentParser):
64
+ def error(self, message: str) -> NoReturn: # argparse would repeat the arguments it could not read
65
+ self.print_usage(sys.stderr)
66
+ self.exit(2, "fabric_source_check.py: the arguments cannot be read; see the usage above\n")
67
+
68
+
69
+ def _print(text: str) -> None:
70
+ """Print a line the console may not encode (a name in another script, a byte Git kept as a surrogate)."""
71
+ encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
72
+ print(text.encode(encoding, "backslashreplace").decode(encoding, "replace"))
73
+
74
+
75
+ def run_check(argv: Optional[List[str]], check: Callable[..., Dict[str, object]], read_list: Callable[[Path], List[str]],
76
+ ignore_globs: Callable[[Path, List[str]], List[str]], home_entry: str) -> int:
77
+ """The command line, run with the check's own functions (fabric_source_check.run passes them)."""
78
+ parser = _Parser(prog="fabric_source_check.py", description="Find instance data in an agent's source.")
79
+ sub = parser.add_subparsers(dest="command", required=True)
80
+ cmd = sub.add_parser("check", help="read the source tree and report instance data without repeating it")
81
+ cmd.add_argument("root", nargs="?", default=".", help="the folder to check (default: the current folder)")
82
+ cmd.add_argument("--instance-strings", metavar="FILE", help="the owner's list (default: FABRIC_INSTANCE_STRINGS, else "
83
+ "passioncode-fabric/%s in the user's configuration folder)" % LIST_NAME)
84
+ cmd.add_argument("--exclude", action="append", default=[], metavar="GLOB",
85
+ help="leave matching paths out, as a .fabric-source-ignore line does (repeatable)")
86
+ cmd.add_argument("--json", action="store_true", help="one JSON object: mode, files, findings, notRead, skipped, "
87
+ "dependencies, instanceStrings, ownerHomes, instanceList, blocking, complete; exit 0 clean and "
88
+ "complete, 1 a finding, 2 a file not read or no run")
89
+ cmd.add_argument("--every-file", action="store_true", help="read every file, not only what Git would commit")
90
+ try:
91
+ args = parser.parse_args(argv)
92
+ except SystemExit as exc: # --help, or arguments argparse refused: the exit code, as the command line gives it
93
+ return exc.code if isinstance(exc.code, int) else 2
94
+ try:
95
+ try:
96
+ root = Path(args.root).expanduser().resolve()
97
+ is_folder = root.is_dir()
98
+ except (OSError, RuntimeError, KeyError): # a parent it cannot search, a symlink loop, `~` with no home
99
+ raise CheckError("the folder cannot be read")
100
+ if not is_folder:
101
+ raise CheckError("ROOT is not a folder")
102
+ list_path, _ = _list_path(args.instance_strings) # may raise CheckError
103
+ entries: List[str] = []
104
+ if list_path is not None:
105
+ try:
106
+ resolved = list_path.resolve()
107
+ except (OSError, RuntimeError): # RuntimeError: Python 3.9's symlink loop
108
+ raise CheckError("the owner's list cannot be read")
109
+ if resolved == root or root in resolved.parents:
110
+ raise CheckError("the owner's list is inside the tree it checks, where it would travel with the source; "
111
+ "keep it in your configuration folder (passioncode-fabric/%s)" % LIST_NAME)
112
+ try:
113
+ present = resolved.is_file()
114
+ except OSError:
115
+ raise CheckError("the owner's list cannot be read")
116
+ if not present:
117
+ raise CheckError("the owner's list named by --instance-strings or FABRIC_INSTANCE_STRINGS does not exist or is "
118
+ "not a regular file"
119
+ if args.instance_strings or os.environ.get("FABRIC_INSTANCE_STRINGS")
120
+ else "the owner's list in your configuration folder is not a file")
121
+ try:
122
+ entries = read_list(resolved)
123
+ except UnicodeDecodeError:
124
+ raise CheckError("the owner's list is not UTF-8")
125
+ except OSError:
126
+ raise CheckError("the owner's list cannot be read")
127
+ result = check(root, entries, ignore_globs(root, args.exclude), args.every_file)
128
+ except CheckError as exc:
129
+ print("fabric_source_check.py: %s" % exc, file=sys.stderr)
130
+ return 2
131
+ if result.get("manifest"): # under fabric.source.json: its own report (fabric_source_tree.print_run)
132
+ return sys.modules["fabric_source_tree"].print_run(result, entries, list_path is not None, args.json, _print)
133
+ findings = result["findings"]
134
+ skipped = result["skipped"]
135
+ listed = sum(1 for e in entries if e and not e.startswith(home_entry))
136
+ homes = sum(1 for e in entries if e.startswith(home_entry))
137
+ # a binary file is not read either: it is complete only when every one is named as source in the ignore file
138
+ complete = not (skipped["unreadable"] or skipped["binary"] or skipped["nested"]) # type: ignore[index]
139
+ result.update({"instanceStrings": listed, "ownerHomes": homes, "instanceList": list_path is not None, "blocking": bool(findings),
140
+ "complete": complete})
141
+ if args.json:
142
+ _print(json.dumps(result, indent=2))
143
+ else:
144
+ for f in findings: # type: ignore[union-attr]
145
+ _print("%s:%s: %s (%s)" % (f["path"], f["line"], f["kind"], f["rule"]))
146
+ for n in result["notRead"]: # type: ignore[union-attr]
147
+ _print("%s: not read (%s)" % (n["path"], n["why"]))
148
+ for folder in result["dependencies"]: # type: ignore[union-attr]
149
+ _print("%s: not read (a dependency folder; --every-file reads it)" % folder)
150
+ if skipped["dependencies"]: # type: ignore[index]
151
+ _print("%d dependency folder(s) not read (node_modules, virtual environments, caches); --every-file reads "
152
+ "them" % skipped["dependencies"]) # type: ignore[index]
153
+ _print("%d file(s) read (%s), %d finding(s); not read: %d binary, %d unreadable, %d nested repository; "
154
+ "%d left out by name; %d instance string(s) listed%s%s"
155
+ % (result["files"], "what Git would commit" if result["mode"] == "git" else
156
+ "every file, as pack reads them" if args.every_file else "every file but dependency folders, Git not used",
157
+ len(findings), skipped["binary"], skipped["unreadable"], skipped["nested"], # type: ignore[index]
158
+ skipped["excluded"], listed, # type: ignore[index]
159
+ "" if listed or homes else (" (the owner's list holds no string of three or more characters)"
160
+ if list_path else " (no owner's list)"),
161
+ "; %d home name(s) declared" % homes if homes else ""))
162
+ if findings:
163
+ return 1
164
+ return 0 if complete else 2
@@ -6,10 +6,10 @@ description: >-
6
6
  for a provider manifest (adapting-projects-to-fabric), a new agent (creating-fabric-agents) or a session
7
7
  (working-with-fabric).
8
8
  license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
9
- 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.
9
+ 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. On macOS 27.2 run kit services and the probe on Apple's /usr/bin/python3.
10
10
  metadata:
11
11
  author: PassionCode.ai
12
- version: "0.82.0"
12
+ version: "0.83.1"
13
13
  contract-version: "0.1.0"
14
14
  extension: "fabric-service/0.1"
15
15
  extension-commit: "93e7db164c7829e2783b4b7705c4113bb5201131"
@@ -52,7 +52,8 @@ to review one. Do not use it for:
52
52
 
53
53
  It is a service when at least one is true: other agents call it while the operator is
54
54
  away; it runs jobs longer than one request; it owns a store other tools read; the
55
- operator watches it on a page. Otherwise build a CLI and stop here.
55
+ operator watches it on a page; it is an uptime A agent that answers while the owner's computer is off (an online
56
+ service, the remote placement below). Otherwise build a CLI and stop here.
56
57
 
57
58
  Answer before writing code, and record the answers in the project README:
58
59
 
@@ -73,7 +74,7 @@ Answer before writing code, and record the answers in the project README:
73
74
  | Another agent on this machine | MCP `2026-07-28`, `streamable-http`, at `/mcp` on the service origin, token in a header | one process serves every session; `mcp-register` checks the listener (HA-2) first |
74
75
  | The operator or a script | a CLI: `<tool> service status\|start\|stop\|restart`, `<tool> dashboard` (opens only in Fabric Dashboards, below), `<tool> doctor --json` | every verb delegates to launchd and the well-known document, never to its own process table |
75
76
  | A remote agent | A2A `1.0` over HTTPS, or MCP behind an authenticated gateway | loopback reachability is not authorization |
76
- | An A2A peer on this computer | `surfaces.a2a` names a public Agent Card, served without the token; its interface URLs are loopback (DEC-0122); the kits read it, serving one is FAA-10 | the card carries no credential, process member or required Fabric extension |
77
+ | An A2A peer on this computer | `surfaces.a2a` names a public Agent Card, served without the token; its interface URLs are loopback (DEC-0122); the kits read it and serve one with the JSON-RPC binding ([the A2A reference](references/a2a.md)) | the card carries no credential, process member or required Fabric extension |
77
78
  | The dashboard page | same-origin REST under `/api`, session cookie plus a custom request header; every button on it also a tool on the MCP surface (Step 5) | the existing dashboards' CSRF defence |
78
79
 
79
80
  Read [the surfaces and auth reference](references/surfaces-and-auth.md) when wiring
@@ -105,6 +106,7 @@ When the service answers requests other Projects post to its Project on Fabric's
105
106
  responder in its own process: [`scripts/fabric_hub.py`](scripts/fabric_hub.py) finds the hub and joins,
106
107
  [`scripts/fabric_board.py`](scripts/fabric_board.py) enrolls, claims, accepts the exact digest, checkpoints before
107
108
  every step and stops on `fenced`, `lease_expired` or `authority_changed` (Node: `fabric-hub.mjs`, `fabric-board.mjs`).
109
+ It reads an operator's mandate on a request only when it opts in with `agent_id=` and `mandate_from=` (Node: `agentId`, `mandateFrom`).
108
110
  The request body is data behind the service's own input gate. A slot moved to another
109
111
  session stays moved until `take_back` (`takeBack`) or a move back once Fabric offers one: give it an operator action with its tool and gate
110
112
  `relayed` (the operator reaches it through an agent that relays their decision in `_meta`, the service's
@@ -172,7 +174,8 @@ spend ceiling before every attempt, receipts and a fallback). The draft-and-chec
172
174
  support agent on the kit: [`recipes/support-agent/`](recipes/support-agent/README.md); the whole agent:
173
175
  creating-fabric-agents' `references/agents-that-answer-people.md`. A service that is only a dashboard:
174
176
  [`recipes/dashboard-only/`](recipes/dashboard-only/README.md); a door that hands a key on sealed:
175
- [`recipes/key-delivery-door/`](recipes/key-delivery-door/README.md).
177
+ [`recipes/key-delivery-door/`](recipes/key-delivery-door/README.md); numbers computed by code from a read-only
178
+ source: [`recipes/data-agent/`](recipes/data-agent/README.md).
176
179
 
177
180
  Startup order is the part that goes wrong; keep it exactly:
178
181
 
@@ -228,7 +231,7 @@ The installer, not the service, owns the plist and the descriptor. Sequence:
228
231
  FileVault restart) install a LaunchDaemon instead: the plist in `/Library/LaunchDaemons` with `user_name`, as root
229
232
  (the lifecycle reference, *The system domain*).
230
233
  3. Uninstall: `launchd_uninstall` (waits for the unload, removes the plist, resets the
231
- override; `purge=True` removes data only when the operator asks), `remove_descriptor`.
234
+ override; `purge=True` removes data only when the operator asks, and a LaunchDaemon refuses it), `remove_descriptor`.
232
235
  4. After an upgrade, `prune_releases(releases_dir, current=…)` keeps the running release
233
236
  and the one before it.
234
237
 
@@ -268,6 +271,9 @@ agents, models, connectors, stores and channels, each with its state, from
268
271
  `connections_extension()` / `connectionsExtension()` in `fabric_connections`. Never a credential in it, never a call
269
272
  count (that is the usage report); details: [the wire reference](references/protocol.md).
270
273
 
274
+ A service counts its own log into hourly and daily **operations digests** (DEC-0153) with `fabric_ops_digest`
275
+ (`fabric-ops-digest.mjs`): [the ops digest reference](references/ops-digest.md).
276
+
271
277
  The events feed is a **view over the log you already keep** (a jobs table, a JSONL
272
278
  journal), not a second store. Each event is one sentence a person reads; set
273
279
  `notify: true` only when the operator must decide, or something failed or degrades their
@@ -355,7 +361,8 @@ Whoever owns the repository, an agent with a page gets the dashboard rule block
355
361
  (below). org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
356
362
  someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's choice, the organization
357
363
  publishes and lists nothing about it, of these files one built new needs only a `LICENSE` naming its copyright holder (copied kit files keep
358
- theirs), and the verified MCP quick start teaches anyone to drive it.
364
+ theirs), and the verified MCP quick start teaches anyone to drive it. The kit's files stay AGPL-3.0-only or the
365
+ PassionCode commercial licence there, in an agent built for a client too.
359
366
 
360
367
  ## Online services: the remote placement
361
368
 
@@ -421,7 +428,7 @@ this estate needs.
421
428
  explicitly in the plist; never copy the whole shell environment into it.
422
429
  - A plist pointing at a worktree or checkout breaks when it moves: point it at a release.
423
430
  - `http.server.HTTPServer` resolves its FQDN between `bind()` and `listen()`: with a slow
424
- resolver (a macOS CI runner, a Mac offline) the port is bound but silent. Use `fs.LoopbackHTTPServer`.
431
+ resolver (a macOS CI runner, a Mac offline) the port is bound but silent: connects time out. Use `fs.LoopbackHTTPServer`.
425
432
  - `logging.basicConfig` called twice: the second is a no-op and the log stays empty. Configure logging once, in
426
433
  the entry point.
427
434
  - Hand-made `.plist.bak` files in `~/Library/LaunchAgents` are never cleaned up; keep
@@ -1,4 +1,4 @@
1
- /* fabric-dashboard-kit@1.1.7 · Fabric's button (Fabric Agent Contract DEC-0040, FB-1…FB-8, FL-1…FL-6)
1
+ /* fabric-dashboard-kit@1.1.8 · Fabric's button (Fabric Agent Contract DEC-0040, FB-1…FB-8, FL-1…FL-6)
2
2
  Load it same-origin, after fabric-dashboard.css, with the service it opens Fabric on:
3
3
  <script src="fabric-button.js" defer data-service="example-agent.default" data-service-name="Example Agent"
4
4
  data-project="project-a" data-project-name="Project A"></script>
@@ -11,7 +11,7 @@
11
11
  (function (root) {
12
12
  'use strict';
13
13
 
14
- var VERSION = '1.1.7';
14
+ var VERSION = '1.1.8';
15
15
  var COOKIE = 'fabric_button';
16
16
  var GET_FABRIC = 'https://passioncode.ai/fabric';
17
17
  var ABSENT_AFTER_MS = 1500;
@@ -178,7 +178,7 @@
178
178
  }
179
179
 
180
180
  // The words a person reads (FAA-119): English, or Russian on a page whose lang is ru; the Russian is written as
181
- // escapes so the file reads the same whatever charset it is served with. Checked with Copylot (en-US and ru-RU).
181
+ // escapes so the file reads the same whatever charset it is served with. Checked with the copy checker (en-US and ru-RU).
182
182
  var LABELS = {
183
183
  en: { chat: 'Chat with Fabric about {name}', session: 'Open a coding session in {name}', open: 'Open Fabric',
184
184
  absent: 'Fabric is not installed. Get it at' },
@@ -1,4 +1,4 @@
1
- /* fabric-dashboard-kit@1.1.7 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
1
+ /* fabric-dashboard-kit@1.1.8 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
2
2
  A Fabric service dashboard shown inside a host pane: usable from 360 CSS px up, navigation always
3
3
  reachable, wide content in its own scroll box, one vertical scroller, appearance from the host.
4
4
  Copy this file next to fabric-dashboard.js; do not edit it in place. Put the page's own rules in
@@ -1,4 +1,4 @@
1
- /* fabric-dashboard-kit@1.1.7 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
1
+ /* fabric-dashboard-kit@1.1.8 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
2
2
  Load it same-origin with <script src="fabric-dashboard.js" defer></script>: no inline script, no
3
3
  eval, nothing from a CDN. It applies the host's appearance (ED-6, ED-8), folds navigation that
4
4
  does not fit into a More menu (ED-3), makes wide boxes keyboard-reachable (ED-4) and keeps
@@ -8,7 +8,7 @@
8
8
  'use strict';
9
9
 
10
10
  var KIT = 'fabric-dashboard-kit';
11
- var VERSION = '1.1.7';
11
+ var VERSION = '1.1.8';
12
12
  /* fabric-dashboard-kit:theme-ids:begin */
13
13
  var THEMES = ['graphite', 'fabric', 'paper', 'forest', 'ocean', 'ember', 'contrast'];
14
14
  /* fabric-dashboard-kit:theme-ids:end */
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fabric-dashboard-kit",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "profile": "fabric-dashboard/0.1",
5
5
  "decision": "DEC-0036",
6
6
  "declarationKey": "https://fabric.passioncode.ai/agent-contract/extensions/dashboard/0.1",
@@ -15,9 +15,9 @@
15
15
  "contrast"
16
16
  ],
17
17
  "files": {
18
- "fabric-dashboard.css": "549c1c1546bc9f8d2c1a579e7ad1035d009c94c3324b91d89d892afc437d5800",
19
- "fabric-dashboard.js": "fa966a8f3b228391c2dae33674887a0d44b102cb58e42dbe6331f08bab57b391",
20
- "fabric-button.js": "220c5c9719a9f6e35851d8748e2abb1d6606ffdb9fc43f3aff2b01f34637d925",
18
+ "fabric-dashboard.css": "0c58c2758e780f4f520d3ea45d69a7d4221ad97cdc7e6999da7c7511e6beb96c",
19
+ "fabric-dashboard.js": "dfaef6b91102817061f612aea1f4b5a85699a890d7f4fbda2780dc153f2e5da1",
20
+ "fabric-button.js": "11ebad0cc598b09f034666be377caf0edc0042e9e86d7d8e8292dda57789be7f",
21
21
  "themes.json": "511839f1a1a83eb37fadfd132791474497d5c28c5091700b9aa31ad076e359b5"
22
22
  },
23
23
  "history": {
@@ -64,6 +64,11 @@
64
64
  "fabric-dashboard.css": "549c1c1546bc9f8d2c1a579e7ad1035d009c94c3324b91d89d892afc437d5800",
65
65
  "fabric-dashboard.js": "fa966a8f3b228391c2dae33674887a0d44b102cb58e42dbe6331f08bab57b391",
66
66
  "fabric-button.js": "220c5c9719a9f6e35851d8748e2abb1d6606ffdb9fc43f3aff2b01f34637d925"
67
+ },
68
+ "1.1.8": {
69
+ "fabric-dashboard.css": "0c58c2758e780f4f520d3ea45d69a7d4221ad97cdc7e6999da7c7511e6beb96c",
70
+ "fabric-dashboard.js": "dfaef6b91102817061f612aea1f4b5a85699a890d7f4fbda2780dc153f2e5da1",
71
+ "fabric-button.js": "11ebad0cc598b09f034666be377caf0edc0042e9e86d7d8e8292dda57789be7f"
67
72
  }
68
73
  }
69
74
  }