@passioncode-ai/fabric-agent-adapter 0.48.2 → 0.48.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.48.3 (2026-10-24)
4
+
5
+ ### Fixed
6
+
7
+ - **The Node package reader narrows the window for a folder swapped for a link** (FAA-91). `readFolder` checked a
8
+ folder by lstat before and after listing it; a swap that lasted while a file was read and was undone before the
9
+ closing check read that file through the link. It now checks the folder after each file it reads too, and refuses it
10
+ as a symlink. Node has no openat, so a swap made and undone between two checks is still not seen; the Python kit holds
11
+ each folder open and is not affected. `references/package.md` says so.
12
+
13
+ ### Documentation
14
+
15
+ - **The support recipe's limits name the file names that read as links** (FAA-84): `README.md`, `setup.py`,
16
+ `install.sh` end in country codes, so a reply naming such a file goes to a person unless a cited entry names it; the
17
+ country codes stay so that a host on them is still caught. A test pins both sides.
18
+
3
19
  ## 0.48.2 (2026-10-23)
4
20
 
5
21
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/fabric-agent-adapter",
3
- "version": "0.48.2",
3
+ "version": "0.48.3",
4
4
  "description": "Create Fabric-compatible agents, run them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract. This package is the installer CLI.",
5
5
  "scripts": {
6
6
  "test": "python3 test/validate.py && python3 -m unittest discover -s test -q && node --test 'test/*.test.mjs'"
@@ -3,7 +3,7 @@
3
3
  "name": "fabric-agent-adapter",
4
4
  "displayName": "Fabric Agent Adapter",
5
5
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
6
- "version": "0.48.2",
6
+ "version": "0.48.3",
7
7
  "author": {
8
8
  "name": "PassionCode.ai",
9
9
  "url": "https://passioncode.ai/"
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.48.2"
8
+ version: "0.48.3"
9
9
  contract-version: "0.1.0"
10
10
  contract-commit: "2f020a696453ae299b123517f14957f6a1354379"
11
11
  ---
@@ -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.48.2"
17
+ version: "0.48.3"
18
18
  contract-version: "0.1.0"
19
19
  extension: "fabric-service/0.1"
20
20
  extension-commit: "2f020a696453ae299b123517f14957f6a1354379"
@@ -31,7 +31,10 @@ country prefix may be left out).
31
31
  Limits: the check reads digits, links, addresses and phone numbers, not meaning. A promise written in words
32
32
  ("your full refund is approved", "fifty dollars", "thirty days") is not caught, and neither is a host whose
33
33
  top-level domain is not on the list. A word run into the next sentence without a space (`Settings.No`) can read as a
34
- link; that reply is handed to a person, which costs a review, never a wrong answer.
34
+ link; that reply is handed to a person, which costs a review, never a wrong answer. So does a file name whose extension
35
+ is also a country code (`README.md`, `setup.py`, `install.sh`: Moldova, Paraguay, Saint Helena): the list keeps those
36
+ codes, because a host on them must still be caught, so such a reply is handed to a person unless an entry it cites
37
+ names the same file.
35
38
 
36
39
  ## Install into a service
37
40
 
@@ -45,7 +45,10 @@ python3 scripts/fabric_package.py pack <folder> --apply # writes fabric.packag
45
45
  It refuses to list what must not ship, and names each: a symlink, a special file, `.git`, `__pycache__`, `.DS_Store`,
46
46
  `*.pyc`, and files that are credentials by name (`.env` and `.env.*` but `.env.example|sample|template`, `*.pem`, `*.key`,
47
47
  `*.p12`, `*.pfx`, `*.jks`, `*.keystore`, `id_rsa`-style keys, `.npmrc`, `.netrc`, `.pypirc`). Remove them or pack a clean
48
- copy. A member named twice in `fabric.package.json` is refused too.
48
+ copy. A member named twice in `fabric.package.json` is refused too. A folder swapped for a link while it is read is refused as a
49
+ symlink. The Python kit reads each folder through an open handle, so a swap cannot redirect it. The Node kit has no
50
+ such call: it checks each folder before listing it, after each file it reads and after listing it (FAA-91), and does not
51
+ see a swap made and undone between two of those checks. Pack from a folder only you can write to.
49
52
 
50
53
  Then every problem is listed, each with its `rule`: `schema` (the contract's schema), `FAC-SEM-064` (whole: its own
51
54
  digest, each path once, every named file listed with the same SHA-256), `FAC-SEM-065` (no secret), `FAC-SEM-066`
@@ -393,7 +393,8 @@ function readRegular(file) {
393
393
  * cache litter, and a file that is a credential by its name, each named, so the person can remove it or pack a clean
394
394
  * copy. A file or folder it cannot read is a refusal naming it (`Cannot read <path>: EACCES.`), never a crash (FAA-78,
395
395
  * finding 41); a file is opened without following a link and read only when it is a regular file (finding 42). Node
396
- * has no openat, so a folder swapped for a link mid-walk is checked by lstat before and after it is listed; the Python
396
+ * has no openat, so a folder swapped for a link mid-walk is checked by lstat before it is listed, after each file it
397
+ * reads and after it is listed (FAA-91); a swap made and undone between two of those checks is not seen. The Python
397
398
  * twin holds each folder open instead.
398
399
  */
399
400
  export function readFolder(folder) {
@@ -402,6 +403,11 @@ export function readFolder(folder) {
402
403
  if (!stat?.isDirectory()) throw new PackageError(`${folder} is not a folder.`);
403
404
  const files = new Map();
404
405
  const refused = [];
406
+ const moved = (dir, before) => {
407
+ let now;
408
+ try { now = fs.lstatSync(dir); } catch { return true; }
409
+ return !now.isDirectory() || now.ino !== before.ino || now.dev !== before.dev;
410
+ };
405
411
  const walk = (dir, rel) => {
406
412
  let entries;
407
413
  let before;
@@ -419,15 +425,15 @@ export function readFolder(folder) {
419
425
  if (!why) {
420
426
  let read;
421
427
  try { read = readRegular(path.join(dir, entry.name)); } catch (error) { throw unreadable(where, error); }
428
+ if (before && moved(dir, before)) { refused.push(`${rel} (a symlink)`); return; } // read through a swapped folder
422
429
  if (read.why === undefined) { files.set(where, read.bytes); continue; }
423
430
  why = read.why;
424
431
  }
425
432
  refused.push(`${where} (${why})`);
426
433
  }
427
434
  if (before) {
428
- let after;
429
- try { after = fs.lstatSync(dir); } catch (error) { throw unreadable(rel, error); }
430
- if (!after.isDirectory() || after.ino !== before.ino || after.dev !== before.dev) refused.push(`${rel} (a symlink)`);
435
+ try { fs.lstatSync(dir); } catch (error) { throw unreadable(rel, error); }
436
+ if (moved(dir, before)) refused.push(`${rel} (a symlink)`);
431
437
  }
432
438
  };
433
439
  walk(folder, '');
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fabric-service-kit",
3
- "version": "0.48.2",
3
+ "version": "0.48.3",
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
  {
@@ -535,7 +535,7 @@
535
535
  "fabric-package-schema",
536
536
  "fabric-secrets"
537
537
  ],
538
- "sha256": "49b757eefcc50e4c91585d5a5694bf3c83d0b03d112c1837f1c9f01466797ab5"
538
+ "sha256": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88"
539
539
  },
540
540
  {
541
541
  "module": "fabric-protocols",
@@ -2799,6 +2799,82 @@
2799
2799
  "fabric-openrouter.mjs": "f8f0168691f968b7cd85f36aa98e2167e88cc44f1104ff514fd0c7699b339253",
2800
2800
  "fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
2801
2801
  "share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
2802
+ },
2803
+ "0.48.3": {
2804
+ "fabric_jsvalue.py": "ff1bbfae999fb5683e59e31189143f1c85fa9d7780040b5a6952e060a143fc18",
2805
+ "fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
2806
+ "fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
2807
+ "fabric_secrets.py": "68a0b5e9e90c468ec9ac67b2f0fd1848e08813d6ec1ec14a2a47337eb1a98911",
2808
+ "fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
2809
+ "fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
2810
+ "fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
2811
+ "fabric_paths.py": "6c92af34292baddf7e10d3134d0a5b0bb23387741812a9f79c409cf1628bad23",
2812
+ "fabric_estate.py": "f493cd424dae6380d1caad91ffdca2764e51a49a6b1051a77731e3d8755277cd",
2813
+ "fabric_delegation.py": "6638a6039befe3b43f59018bab292e050931af21b544df1044cd5059572ded36",
2814
+ "fabric_share.py": "78506fe1a60829c525dbf6266a0aa0749a2164290ed94da8249a20b4e9be5605",
2815
+ "fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
2816
+ "fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
2817
+ "fabric_package.py": "ff533f1b3f92b4ea0f532fcec50b68c610d47549930eef8ad5247692a64dfb3c",
2818
+ "fabric_protocols.py": "28e693282cddff8e4396ae39ce6fa935e875e8ddac7375bea2c189dd6fb04970",
2819
+ "fabric_connections.py": "77d3ccf863c70e9209d21ef45c46a6db9c06237019bc20d54f8d3946873f3f11",
2820
+ "fabric_lock.py": "ba2399d2137c7a7791b7133b9db9cca36693f4eb7e30955b25a75ade814e95ba",
2821
+ "fabric_usage.py": "b1f335cab4625171a616cf68cf3fd892b18bdadcf4baec3a3a0fdcf12785c170",
2822
+ "fabric_backups.py": "02dc5eb66ec956723d3ddba07b6dd15f6a9726d901f4d60edfd02eee57880ceb",
2823
+ "fabric_descriptor.py": "daf16f95b1810283b1386488e006e940a0cdfa5c3c8d09386ac33d29d58df78d",
2824
+ "fabric_auth.py": "55794587750f6c9f40935b74295d83d7096cb4b26a0b36ac292334cd87d8f853",
2825
+ "fabric_events.py": "0086886c2c29dbd5c9d152be9d375c5bdf4a29f89b8095baff8490c2bcd64e47",
2826
+ "fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
2827
+ "fabric_supervisors.py": "a2dbf15aa1c4eb9de42996b413103fa4814afd2e4e66d1ab7315f30e06ab6841",
2828
+ "fabric_service.py": "103ab4026f96e127c01cd91e4ad44f6b0ce2424ff33f078ed106b39e773a10e3",
2829
+ "fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
2830
+ "fabric_interop_rules.py": "b5f0ecb1791b4c50c5ac151f987f692ed7001bc74841a8a161125ec775145e09",
2831
+ "fabric_interop.py": "f92fa111328fe3811438f733d8f0eb5295e6941c774123a78e26318bdcb35fc3",
2832
+ "fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
2833
+ "fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
2834
+ "fabric_board_link.py": "b5bff994cb615c6a4bb0d88d020e3a59504166180e1fa1aea3be93224f2aeccb",
2835
+ "fabric_board_session.py": "6fa205f7aa80f920cb910b52516086a54f0a06ce88c01dc6c39dbeabacc779d2",
2836
+ "fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
2837
+ "fabric_board.py": "0f075894b3e352194fcaf3fc7d08dee4e9f1c215e859769ed85fa5010e4a1599",
2838
+ "fabric_http.py": "5d7f72af3a73fdbe8c1f487ecf60cef23a7c46289eba542b03aff4706b8218d4",
2839
+ "fabric_openrouter.py": "2017d898c17001d7275f58267de653be7ecf6acd17ad8d9b24ecb7a43e5617d4",
2840
+ "fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
2841
+ "fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
2842
+ "fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
2843
+ "fabric-secrets.mjs": "6ad8ab18ac899ecc2384e8ec6a3fb689b92be3b9a5ce3a59b92a37d52da5d766",
2844
+ "fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
2845
+ "fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
2846
+ "fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
2847
+ "fabric-paths.mjs": "2201b4ed7126f260931b63e8386377b4317d8dcad419d109bf22c683e44b7f29",
2848
+ "fabric-estate.mjs": "b6610aaad9d00d8a7f42955627fb81725950841455bcedfc050abb0e079f0f93",
2849
+ "fabric-delegation.mjs": "dafde9ea327c88d92dc73d1400282b43dc6f84dd345a2d2b9b154304f65fc9d7",
2850
+ "fabric-share.mjs": "d40a7405d7bd4c3213ed691330b7549afe8e023db0753b582e249714e672010d",
2851
+ "fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
2852
+ "fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
2853
+ "fabric-package.mjs": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88",
2854
+ "fabric-protocols.mjs": "dc039ec3612d0090c9ba6a3a949b0b7df54712bead300dcb6cc8b9bc305cd183",
2855
+ "fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
2856
+ "fabric-lock.mjs": "ac219ab0a326d5c43c7c4c255e0c3843d4794de795a60e3af661e7d5350ea052",
2857
+ "fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
2858
+ "fabric-backups.mjs": "849acdc6eba0fff648c8158e61f6b68cccd7bedf0880f7d5ec03a6a20f1e7005",
2859
+ "fabric-descriptor.mjs": "95c0b7ff36c29d7c748f1fa6fb1767d6fe8c97fca7f1ee404559930abbd3cc84",
2860
+ "fabric-auth.mjs": "3ea6796470fcd829bc72a772fb7e4c37532a09d1e37ff41592dcf1a1a3074ca2",
2861
+ "fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
2862
+ "fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
2863
+ "fabric-supervisors.mjs": "9d61c1a17538a5e2453e33e5ae347337336748041bc054478a96dd51f3d4fc51",
2864
+ "fabric-service.mjs": "1b4639a10c530c1241d317c5e9cf3a10cd96e5d9af8b29fc1b1844de5c63bfec",
2865
+ "fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
2866
+ "fabric-interop-rules.mjs": "57a8081307956e8c381119b361fa5232a7bf791815be8e3541eb5492820e2ea5",
2867
+ "fabric-interop.mjs": "581983ec3e197e5c2340ad25e8468320c69a2459c90097c7fcf2699766b4fb26",
2868
+ "fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
2869
+ "fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
2870
+ "fabric-board-link.mjs": "4cd6d4e21713a38c8e56f7d59db3148638b8c27c08e774d96a0f6283c6049e0a",
2871
+ "fabric-board-session.mjs": "82961581c6a5b6872dec0c18c2afca77286ab86dae5d21300aeea9903ec785a4",
2872
+ "fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
2873
+ "fabric-board.mjs": "9e7bfe4d412521982224ef12bebfc2ff255eeeaf8d96cf40234ac18de68e7a59",
2874
+ "fabric-http.mjs": "f886bcb49c4353d039387ac9568738dda26697a94dd07fc6a6204500f0bc27bf",
2875
+ "fabric-openrouter.mjs": "f8f0168691f968b7cd85f36aa98e2167e88cc44f1104ff514fd0c7699b339253",
2876
+ "fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
2877
+ "share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
2802
2878
  }
2803
2879
  },
2804
2880
  "data": {
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.48.2"
8
+ version: "0.48.3"
9
9
  contract-version: "0.1.0"
10
10
  contract-commit: "2f020a696453ae299b123517f14957f6a1354379"
11
11
  ---
@@ -19,7 +19,7 @@ compatibility: >-
19
19
  this plugin; Codex/Kimi use command hooks; Hermes/OpenCode/OpenClaw/Pi use native context plugins after opt-in.
20
20
  metadata:
21
21
  author: PassionCode.ai
22
- version: "0.48.2"
22
+ version: "0.48.3"
23
23
  ---
24
24
 
25
25
  # Working with Fabric
@@ -57,7 +57,7 @@ SESSION_ID = re.compile(r"[A-Za-z0-9._:-]{1,128}")
57
57
  LOOPBACK_ORIGIN = re.compile(r"http://127\.0\.0\.1:(\d{1,5})")
58
58
 
59
59
 
60
- VERSION = "0.48.2"
60
+ VERSION = "0.48.3"
61
61
 
62
62
 
63
63
  HOSTS = {"claude": "Claude Code", "codex": "Codex", "kimi": "Kimi Code",