@passioncode-ai/fabric-agent-adapter 0.49.2 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/README.md +7 -2
- package/package.json +1 -1
- package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +29 -11
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +53 -3
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/operator-channel-and-repository.md +11 -3
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +3 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +4 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +50 -14
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +38 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.css +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.js +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/kit.json +14 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md +9 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +53 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard.md +13 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +10 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +99 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +5 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/remote-placement.md +6 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/settings-backups.md +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/surfaces-and-auth.md +5 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +47 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +325 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/dashboard_kit.py +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +246 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +4 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-backups.mjs +3 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +22 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +45 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +45 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +2 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +302 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +7 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_backups.py +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +22 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +58 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +154 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +45 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +9 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +202 -18
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +29 -11
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +53 -3
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/operator-channel-and-repository.md +11 -3
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +41 -9
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +2 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.50.0 (2026-10-26)
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Pinned to the contract release `v0.1.0+2026-10-25` (`52c7d798`)** (FAA-117, contract CT-124). The release carries
|
|
8
|
+
DEC-0112 to DEC-0121; only DEC-0121 changes kit code, the others bind a node, an organization server or Project
|
|
9
|
+
Observatory and only regenerate vendored vectors (connections gains seven cases, the protocol register
|
|
10
|
+
`fabric-actions/0.1`). `2f020a6` stays supported. The brief is
|
|
11
|
+
`docs/evidence/specs/2026-10-26-faa-117-118-repin-52c7d79-brief.md` (decisions D1 to D17).
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Dashboard action parity (DEC-0121).** Every button a dashboard offers the operator is an agent action too.
|
|
16
|
+
- `fabric_actions.py` / `fabric-actions.mjs` port the contract's `actionDecision`, `actionsView` and the FAC-SEM-142
|
|
17
|
+
rules, checked against `fixtures/vectors/actions.json` (65 cases) and the semantic fixtures.
|
|
18
|
+
- A service declares its actions with `McpToolServer.add_tool(..., action=, target=, human_step_url=)` and
|
|
19
|
+
publishes them with `build_well_known(actions=)` / `buildWellKnown({actions})` as `extensions/actions/0.1`; a
|
|
20
|
+
declaration the contract would flag is refused.
|
|
21
|
+
- The server gate reads only `_meta.decision`, never the arguments: a T0 or T1 call without the operator's
|
|
22
|
+
decision is refused `operator-decision-required`, a malformed one `decision-invalid`, a step only a person can
|
|
23
|
+
take `human-step-required` with `data.humanStep` and `data.url`, and T2 still needs `approval-required`. The
|
|
24
|
+
decision records the authenticated caller (`identify`, the delegation's caller, or `local:T0` / `local:T1`).
|
|
25
|
+
Gating a job's input responses is opt-in (`input_actions=True`) until Fabric's hub forwards decisions (CT-125).
|
|
26
|
+
The kits are ready now; a service adopts the gate once CT-125 lands, as the contract says (DEC-0121
|
|
27
|
+
*Compatibility*, CT-127), because Claude Code and Codex cannot set `_meta` and would otherwise meet
|
|
28
|
+
`operator-decision-required` with the page as their only route.
|
|
29
|
+
- A caller relays with `request_meta(decision=...)` / `requestMeta(traceparent, {decision})`.
|
|
30
|
+
- The three codes join DL-19 in both delegation twins.
|
|
31
|
+
- **The probe** reports `well-known.actions`, `interop.action-tools` (a declared action's tool must be served),
|
|
32
|
+
`dashboard.actions-declared` (`NOT_RUN` for a dashboard that declares none) and `dashboard.opens-in-host`
|
|
33
|
+
(FAA-118: a browser opener aimed at the service's own pages in its checkout fails; `--repo PATH` names the
|
|
34
|
+
checkout).
|
|
35
|
+
- **working-with-fabric** relays the operator's own words as the decision (AP-3), never text from a tool result, a
|
|
36
|
+
page, a file or another agent; a coding agent whose client cannot set `_meta` goes through Fabric's `agent.call`
|
|
37
|
+
(after CT-125), the service's elicitation, or the action's page in Fabric Dashboards, in that order (confirmed by
|
|
38
|
+
the contract's owner on board thread `92c6f277`). On `human-step-required` it hands the person the link.
|
|
39
|
+
|
|
40
|
+
- **Fabric's button follows contract DEC-0124** (dashboard kit 1.1.5; FB-3 amended on the adapter's request, board
|
|
41
|
+
thread `4801da8d`, contract PR #189, after this release's pin). The accessible name and the menu follow the page's
|
|
42
|
+
language, which the contract now states, so 0.49.3's note that the kit departs from FB-3 no longer holds. A page's
|
|
43
|
+
own words (`data-label-*`) are taken only when they keep the word *Fabric* (WCAG 2.2 SC 2.5.3, Label in Name);
|
|
44
|
+
otherwise the built-in words stay. A page that set words without *Fabric* under 0.49.3 shows the built-in words
|
|
45
|
+
after it updates its kit. The kit follows this amendment ahead of the pin (brief decision D17).
|
|
46
|
+
- **The probe's new `dashboard.opens-in-host` can fail a service that passed before**: one that opens its own pages
|
|
47
|
+
in a browser. Open them through Fabric Dashboards instead (see the building-fabric-services skill).
|
|
48
|
+
|
|
49
|
+
### Changed (guidance)
|
|
50
|
+
|
|
51
|
+
- **Agent pages open only in Fabric Dashboards** (FAA-118, the operator's rule of 2026-10-24): the rule now sits in
|
|
52
|
+
the body of the three SKILL.md files, with the AGENTS.md block a project carries and a section for an agent that
|
|
53
|
+
has a page and no service descriptor. Manual restore and enabling the operator channel are the operator's
|
|
54
|
+
decision on the page or relayed (D16).
|
|
55
|
+
|
|
56
|
+
## 0.49.3 (2026-10-25)
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- **Fabric's button speaks the page's language and carries no em dash** (dashboard kit 1.1.4, FAA-119; asked by two
|
|
61
|
+
services on the board). The button wrote U+2014 into the console message a person reads when it is not shown, and
|
|
62
|
+
its four strings were English only, so a Russian dashboard could not mount it without editing a vendored file that
|
|
63
|
+
`dashboard_kit.py install` then refuses as changed. The message now uses a colon. The button reads the page's `lang`
|
|
64
|
+
and shows Russian on a Russian page, English otherwise; `data-label-chat`, `data-label-session` (with `{name}`),
|
|
65
|
+
`data-label-open` and `data-label-absent` set a page's own words. The strings were checked with Copylot in the
|
|
66
|
+
PassionCode.ai site voice (en-US report `239ff62c-e0af-4b8b-96e1-0c45c5eca02a`, ru-RU report
|
|
67
|
+
`be37143c-aff1-4502-b9d0-f8b1be56bbf3`, no findings). A test fails on U+2014 in any kit asset, and the WebKit test
|
|
68
|
+
opens a Russian page and a page with its own words. The button's accessible name follows the page too (*Открыть Fabric*, or
|
|
69
|
+
`data-label-open`), which departs from the pinned contract's FB-3 ("Open Fabric" in every case) the way Fabric
|
|
70
|
+
Dashboards' own button already does; the amendment to allow a localized name is proposed to the contract (board
|
|
71
|
+
thread `4801da8d`).
|
|
72
|
+
- **A remote service registers where its code lives** (FAA-120, a finding from a service that patched its descriptor
|
|
73
|
+
after the kit wrote it). `registerRemote` and `register_remote` take `source` (`{repository}`), which the contract's
|
|
74
|
+
`service-descriptor.schema.json` describes and Fabric Dashboards reads. The descriptor check now validates `source`
|
|
75
|
+
in every descriptor: an object with `repository` only, a URI of RFC 3986 characters (the same answer as the
|
|
76
|
+
contract's ajv-formats `uri` on 112 cases, brackets only around an IP-literal host), a host and a port up to 65535
|
|
77
|
+
when it names an authority (Fabric reads it with `new URL` and drops a descriptor it cannot read), no
|
|
78
|
+
`user:password@`, and no value shaped like a credential; `None` and `null` mean no source in
|
|
79
|
+
both twins. A provider entry's `source.repository` (`fabric_provider.py`) refuses a password or a credential the same
|
|
80
|
+
way. The probe's data rule and the SessionStart notice no longer raise on a descriptor whose `source` is not an
|
|
81
|
+
object.
|
|
82
|
+
|
|
3
83
|
## 0.49.2 (2026-10-24)
|
|
4
84
|
|
|
5
85
|
### Fixed
|
package/README.md
CHANGED
|
@@ -85,6 +85,7 @@ a service (a descriptor names it as `source.repository`) whose dashboard has no
|
|
|
85
85
|
anywhere else; the [kit reference](plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md)
|
|
86
86
|
has the upgrade steps, which the agent may take itself.
|
|
87
87
|
|
|
88
|
+
Agent pages open only in Fabric Dashboards (the operator's rule, carried in the builder skills since 0.50.0).
|
|
88
89
|
Dashboard handoffs use the host-generated `open_link` as the primary action. The
|
|
89
90
|
three builder skills carry the same [consumer procedure](plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md)
|
|
90
91
|
and a portable `scripts/check_dashboard_link.py` gate for generated renderers.
|
|
@@ -98,7 +99,11 @@ is the MCP tool of its name, long work is a job with a stable id (`fabric.job.ge
|
|
|
98
99
|
`fabric.job.cancel`), a question for a person is an elicitation, and every call carries
|
|
99
100
|
one W3C trace. The kits ship the helpers (`scripts/fabric_interop.py`,
|
|
100
101
|
`scripts/fabric-interop.mjs`), the sample service serves a job end to end, and the probe
|
|
101
|
-
checks the interop rules.
|
|
102
|
+
checks the interop rules. Every dashboard button has a tool (`fabric-actions/0.1`, contract DEC-0121): the
|
|
103
|
+
operator's agent relays their decision in `_meta.decision` (a service adopts the gate once Fabric's hub forwards
|
|
104
|
+
decisions, CT-125; Claude Code and Codex, which cannot set `_meta`, relay through Fabric's `agent.call`, the service's
|
|
105
|
+
own elicitation or the action's page), and the refusals `operator-decision-required`,
|
|
106
|
+
`decision-invalid` and `human-step-required` say what is missing. A job a provider limit paused or stopped, and an effect nobody saw, carry their own codes
|
|
102
107
|
(`quota-exhausted`, `outcome-unknown`) and keys per caller (contract DEC-0087). The estate kits check share grants into
|
|
103
108
|
an organization (`scripts/fabric_share.py`, DEC-0086) and the estate ceremonies: a revision's record rules, a headless
|
|
104
109
|
machine's enroll token, the nearby pairing by a comparison code and the hub's single use of a token
|
|
@@ -275,7 +280,7 @@ Scaffolding is non-destructive by default. It writes only the paths declared in
|
|
|
275
280
|
The default pin for new bundles is [`fabric-contract.lock.json`](fabric-contract.lock.json):
|
|
276
281
|
|
|
277
282
|
- version: `0.1.0`
|
|
278
|
-
- commit: `
|
|
283
|
+
- commit: `52c7d798b9f4eb784eb4e29e717038a8912c759d`
|
|
279
284
|
|
|
280
285
|
Every other mention of the default contract revision (this section, the skills' metadata, the
|
|
281
286
|
skill card, `adapt_project.py`) must equal it; `python3 test/validate.py` fails a tree
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passioncode-ai/fabric-agent-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
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.
|
|
6
|
+
"version": "0.50.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "PassionCode.ai",
|
|
9
9
|
"url": "https://passioncode.ai/"
|
|
@@ -5,9 +5,9 @@ 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.
|
|
8
|
+
version: "0.50.0"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
|
-
contract-commit: "
|
|
10
|
+
contract-commit: "52c7d798b9f4eb784eb4e29e717038a8912c759d"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Adapting projects to Fabric
|
|
@@ -76,7 +76,7 @@ Use exactly:
|
|
|
76
76
|
|
|
77
77
|
- contract version `0.1.0`;
|
|
78
78
|
- repository `https://github.com/passioncode-ai/fabric-agent-contract`;
|
|
79
|
-
- commit `
|
|
79
|
+
- commit `52c7d798b9f4eb784eb4e29e717038a8912c759d`.
|
|
80
80
|
|
|
81
81
|
This is the default for new bundles. When checking an issued legacy bundle, preserve its
|
|
82
82
|
explicit supported lock and use that exact clean checkout. Never silently upgrade or
|
|
@@ -218,9 +218,9 @@ not a connected provider.
|
|
|
218
218
|
If the agent has a human stop (a choice, an approval, a confirmation) or emits `notify: true`
|
|
219
219
|
events, propose its operator channel (`fabric-operator-channel/0.1`, OC-11) to the operator once,
|
|
220
220
|
now, as [the operator channel and the repository standard](references/operator-channel-and-repository.md)
|
|
221
|
-
describes: the operator's four steps, the answer recorded and never asked again,
|
|
222
|
-
token and enabling the channel
|
|
223
|
-
skips this step.
|
|
221
|
+
describes: the operator's four steps, the answer recorded and never asked again, the bot and the
|
|
222
|
+
token left to the person, and enabling the channel to the operator's decision (on the page or
|
|
223
|
+
relayed, DEC-0121). An agent with neither stops nor notify events skips this step.
|
|
224
224
|
|
|
225
225
|
## The repository around the provider
|
|
226
226
|
|
|
@@ -245,8 +245,26 @@ Report:
|
|
|
245
245
|
Never summarize the outcome as “Fabric-compatible” unless all required admission gates
|
|
246
246
|
passed against a real provider and the project binding is valid.
|
|
247
247
|
|
|
248
|
-
##
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
## Agent pages open only in Fabric Dashboards
|
|
249
|
+
|
|
250
|
+
The operator's rule (2026-10-24) for every page of an agent or a local Fabric service, in any host: a
|
|
251
|
+
link in an answer or a notification, `<tool> dashboard`, a completion message, a page the agent opens
|
|
252
|
+
itself. Hand over the `open_link` the `fabric-dashboards` MCP's `link` tool returns for the exact
|
|
253
|
+
registered service key (`list_services`, never guessed) and the page's path; open it only with the
|
|
254
|
+
host's `open` and `fallback=never`. Never `open <url>`, `open -a Safari`, `xdg-open`,
|
|
255
|
+
`webbrowser.open` or `shell.openExternal` aimed at the agent's pages. A browser only on a confirmed
|
|
256
|
+
`not_installed` host; an installed host that failed to open is reported, never replaced by HTTP.
|
|
257
|
+
Links carry no token or login code.
|
|
258
|
+
|
|
259
|
+
The agent's `AGENTS.md` carries this as its dashboard rule block, and its checks run the adapter's link gate
|
|
260
|
+
(`scripts/check_dashboard_link.py` from this skill's own directory; it is not copied into the project) on every
|
|
261
|
+
completion and notification action before delivery:
|
|
262
|
+
[dashboard links](references/dashboard-links.md) has the block, the gate's input and the remote case.
|
|
263
|
+
Headless agents need no dashboard.
|
|
264
|
+
|
|
265
|
+
## An agent with a page and no service descriptor
|
|
266
|
+
|
|
267
|
+
A local page no service descriptor names cannot open in Fabric Dashboards. Never open it in a browser
|
|
268
|
+
instead: make the agent a `fabric-service/0.1` service with `building-fabric-services` (the descriptor,
|
|
269
|
+
the well-known document, the login-code routes and a declared dashboard), and until that lands file a
|
|
270
|
+
board row for it and say the page waits on it.
|
|
@@ -5,6 +5,19 @@ an existing Fabric service dashboard to a person. This also applies to generated
|
|
|
5
5
|
agent instructions and completion messages. Headless agents need no dashboard;
|
|
6
6
|
API origins, external docs and OAuth links are not dashboard actions.
|
|
7
7
|
|
|
8
|
+
The rule (the operator's, 2026-10-24): every page of an agent or a local Fabric
|
|
9
|
+
service opens only in Fabric Dashboards, never in Safari, Dia, Chrome or any
|
|
10
|
+
other browser, unless Fabric Dashboards is confirmed not installed.
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
- Resolve, then hand over
|
|
15
|
+
- The block a generated AGENTS.md carries
|
|
16
|
+
- An agent with a page and no service descriptor
|
|
17
|
+
- Device and channel
|
|
18
|
+
- Portable output gate
|
|
19
|
+
- Degradation
|
|
20
|
+
|
|
8
21
|
## Resolve, then hand over
|
|
9
22
|
|
|
10
23
|
1. Discover the target computer and exact registered `service.id.instance`.
|
|
@@ -20,9 +33,12 @@ API origins, external docs and OAuth links are not dashboard actions.
|
|
|
20
33
|
compatible host resolver or report the limitation, not presumed absence.
|
|
21
34
|
4. A request for a link opens nothing. A request to open uses the host `open`
|
|
22
35
|
tool/approved opener, which starts or focuses the existing Fabric Dashboards
|
|
23
|
-
and reuses the service view
|
|
24
|
-
permits one only for confirmed absence. Discover schema support
|
|
25
|
-
`open` implementations may silently fall back on failure and are
|
|
36
|
+
and reuses the service view, with `fallback=never`: it forbids a browser.
|
|
37
|
+
`if_absent` permits one only for confirmed absence. Discover schema support
|
|
38
|
+
first: older `open` implementations may silently fall back on failure and are
|
|
39
|
+
not strict. Never
|
|
40
|
+
`open <url>`, `open -a Safari`, `xdg-open`, `start <url>`, `webbrowser.open`
|
|
41
|
+
or `shell.openExternal` aimed at the service's pages.
|
|
26
42
|
5. An installed host that failed to open is **not absent**. Report the failure;
|
|
27
43
|
do not silently launch HTTP in a browser, a second server, `npm run dev`, or
|
|
28
44
|
another standalone dashboard. A stopped backend stays on its host status/Start
|
|
@@ -31,6 +47,40 @@ API origins, external docs and OAuth links are not dashboard actions.
|
|
|
31
47
|
not page readiness, a successful login, or a completed job. Links carry no
|
|
32
48
|
bearer tokens or login codes. The host handles dashboard authentication.
|
|
33
49
|
|
|
50
|
+
## The block a generated AGENTS.md carries
|
|
51
|
+
|
|
52
|
+
An agent with a page carries this block in its `AGENTS.md` (its `CLAUDE.md` reads
|
|
53
|
+
it through `@AGENTS.md`), beside its browser rule, and its completion and
|
|
54
|
+
notification renderers follow it:
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
## Pages open only in Fabric Dashboards
|
|
58
|
+
|
|
59
|
+
Every page of this agent (its dashboard, a job, an approval, a report) opens in
|
|
60
|
+
Fabric Dashboards, never in a browser. Resolve the registered service key with
|
|
61
|
+
the fabric-dashboards MCP (`list_services`, never guessed) and hand over the
|
|
62
|
+
`open_link` its `link` tool returns for the page's path; to open it, call the
|
|
63
|
+
host's `open` with `fallback=never`. Never `open <url>`, `open -a Safari`,
|
|
64
|
+
`xdg-open`, `start <url>`, `webbrowser.open` or `shell.openExternal` on this
|
|
65
|
+
agent's pages. A browser only when `host_status` confirms `not_installed`; an
|
|
66
|
+
installed host that failed to open is reported, not replaced by HTTP. Links
|
|
67
|
+
carry no token or login code. Every completion and notification action passes
|
|
68
|
+
the Fabric Agent Adapter's link gate before delivery: `check_dashboard_link.py`
|
|
69
|
+
in its `building-fabric-services/scripts`, run from that skill's directory;
|
|
70
|
+
where it cannot run, the action is compared by hand and the check is NOT_RUN.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The project's checks run the gate below on each renderer's action, and a test
|
|
74
|
+
holds a raw-HTTP primary action beside an available host refused.
|
|
75
|
+
|
|
76
|
+
## An agent with a page and no service descriptor
|
|
77
|
+
|
|
78
|
+
Fabric Dashboards opens only a page a service descriptor names. An agent that
|
|
79
|
+
serves a local page without one cannot be opened there, and a browser is not the
|
|
80
|
+
workaround. Make it a `fabric-service/0.1` service (`building-fabric-services`:
|
|
81
|
+
the descriptor, the well-known document, the login-code routes and a declared
|
|
82
|
+
dashboard); until that lands, file a board row for it and say the page waits on it.
|
|
83
|
+
|
|
34
84
|
## Device and channel
|
|
35
85
|
|
|
36
86
|
A custom scheme opens on the device receiving it. A phone's localhost is not the
|
|
@@ -26,9 +26,12 @@ bare `/command` in a group is not addressed), and a channel nobody receives on i
|
|
|
26
26
|
|
|
27
27
|
Record the answer (`accepted`, `declined` or `later`) in the agent's state and in its conformance
|
|
28
28
|
report, and do not ask again; an agent that serves `surfaces.operatorChannel` also shows it as
|
|
29
|
-
`proposal` in its status document. Creating the bot
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
`proposal` in its status document. Creating the bot and putting its token into the secret store
|
|
30
|
+
stay the person's (a `secret` step, contract DEC-0121 AP-4): never create the bot, or ask for or
|
|
31
|
+
store the token's value. Enabling the channel, and removing a webhook found on its token, are the
|
|
32
|
+
operator's decision, taken by them on the agent's page or relayed to its agent in their own words
|
|
33
|
+
with `_meta.decision` (OC-1, OC-2, DEC-0121 AP-2); never enable it on your own account. The default
|
|
34
|
+
pin carries DEC-0034 and DEC-0035: a served status document is
|
|
32
35
|
checked against the pinned `schemas/operator-channel-status.schema.json`; the channel's runtime
|
|
33
36
|
rules have no probe rule here yet, so report them `NOT_RUN`. An agent with neither stops nor notify
|
|
34
37
|
events skips this.
|
|
@@ -49,6 +52,11 @@ For a PassionCode.ai repository, the agent's repository follows the organization
|
|
|
49
52
|
- an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
|
|
50
53
|
and a `CLAUDE.md` whose first line is `@AGENTS.md`.
|
|
51
54
|
|
|
55
|
+
Whoever owns the repository, an agent with a page gets the dashboard rule block in its `AGENTS.md`
|
|
56
|
+
(pages open only in Fabric Dashboards; the block is in `references/dashboard-links.md`), and its
|
|
57
|
+
checks run the adapter's link gate (`scripts/check_dashboard_link.py` from the skill's own directory, not a copy in
|
|
58
|
+
the project).
|
|
59
|
+
|
|
52
60
|
org-index `scripts/check_format.py` reports 0 findings for it before it is called done. An agent
|
|
53
61
|
someone builds for themselves is not a PassionCode.ai repository: its licence is its owner's
|
|
54
62
|
choice, nothing about it is published or listed by the organization, and none of these files is
|
package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md
CHANGED
|
@@ -53,7 +53,9 @@ It refuses, with one sentence that never quotes a value:
|
|
|
53
53
|
1024 characters), a shell-string command, more than 32 arguments or one over 1024 characters, more than 64 env
|
|
54
54
|
names, a `source.repository` that is not an absolute URI, an `installedAt` that is not an RFC 3339 date-time,
|
|
55
55
|
unknown fields. The patterns are the schema's, copied (`test_provider.SchemaParityTests` compares them with a
|
|
56
|
-
contract checkout at the pin when `FABRIC_CONTRACT_NEW` names one)
|
|
56
|
+
contract checkout at the pin when `FABRIC_CONTRACT_NEW` names one);
|
|
57
|
+
- a `source.repository` with a `user:password@` or a value shaped like a credential: it names the repository, never
|
|
58
|
+
reaches it (since 0.49.3, as the service kit's descriptor check).
|
|
57
59
|
|
|
58
60
|
`validate` also checks that the file is named `<id>.json` (FAC-SEM-014, slug half).
|
|
59
61
|
From Python: `write_provider_entry(entry)`, `remove_provider_entry(id)`,
|
package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py
CHANGED
|
@@ -22,10 +22,10 @@ import fabric_secrets as _secrets # noqa: E402 - the contract's credential dete
|
|
|
22
22
|
|
|
23
23
|
CONTRACT_VERSION = "0.1.0"
|
|
24
24
|
CONTRACT_REPOSITORY = "https://github.com/passioncode-ai/fabric-agent-contract"
|
|
25
|
-
CONTRACT_COMMIT = "
|
|
25
|
+
CONTRACT_COMMIT = "52c7d798b9f4eb784eb4e29e717038a8912c759d"
|
|
26
26
|
# #region supported-contract-revisions — docs: README.md#contract-pin
|
|
27
27
|
# Only this declaration may name a legacy revision in live files.
|
|
28
|
-
SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "87d384ffce2757081b7ab227e5dad3dddd08328f", "0a4ce773c4b9e6bec40c6387eb3f988af7152c0e", "e742016dc396aa31e5a13a893489e43f15786c69", "bfa9a67aa3c6cb808c093e7d83cd59799f1ee7d0", "c55f2d5a11c9a0197e958e87212fca7ae1154769", "07042181bfae2a481aa0da1a5651f24c76669c48", "d8146cfb0a1c74fc6432284c4356bb0c864b37b5", "82e163665e156a08aa3b92e4711015582c06162c", "1636fa97262f4a26e2086572d540c2ee701e0010", "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
|
|
28
|
+
SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "2f020a696453ae299b123517f14957f6a1354379", "87d384ffce2757081b7ab227e5dad3dddd08328f", "0a4ce773c4b9e6bec40c6387eb3f988af7152c0e", "e742016dc396aa31e5a13a893489e43f15786c69", "bfa9a67aa3c6cb808c093e7d83cd59799f1ee7d0", "c55f2d5a11c9a0197e958e87212fca7ae1154769", "07042181bfae2a481aa0da1a5651f24c76669c48", "d8146cfb0a1c74fc6432284c4356bb0c864b37b5", "82e163665e156a08aa3b92e4711015582c06162c", "1636fa97262f4a26e2086572d540c2ee701e0010", "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
|
|
29
29
|
# #endregion supported-contract-revisions
|
|
30
30
|
INTEROP_KEY = "https://fabric.passioncode.ai/agent-contract/extensions/interop/0.1"
|
|
31
31
|
MCP_REVISION = "2026-07-28"
|
package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py
CHANGED
|
@@ -47,6 +47,7 @@ ENV_MAX = 64
|
|
|
47
47
|
# Whole-value patterns end in \Z, never `$` (R1: `$` also matches before a trailing newline).
|
|
48
48
|
_ID = re.compile(r"^[a-z][a-z0-9-]{1,62}\Z")
|
|
49
49
|
_ENV_NAME = re.compile(r"^[A-Z_][A-Z0-9_]*\Z")
|
|
50
|
+
_URI_PASSWORD = re.compile(r"^[A-Za-z][A-Za-z0-9+.-]*://[^/?#@]*:[^/?#@]*@")
|
|
50
51
|
_SECRET_REF = re.compile(r"^secret-ref:([A-Za-z0-9][A-Za-z0-9._/:@-]{0,255})\Z")
|
|
51
52
|
_LOCAL_PATH = re.compile(LOCAL_PATH_PATTERN)
|
|
52
53
|
_URL = re.compile(URL_PATTERN)
|
|
@@ -131,6 +132,9 @@ def validate_provider_entry(entry: Any) -> List[str]:
|
|
|
131
132
|
problems.append("source is {repository?}")
|
|
132
133
|
elif "repository" in source and not _is_uri(source["repository"]):
|
|
133
134
|
problems.append("source.repository is an absolute URI")
|
|
135
|
+
elif "repository" in source and (_URI_PASSWORD.match(source["repository"]) or _secrets.secret_argument(source["repository"])):
|
|
136
|
+
# FAA-120, as the service kit: a repository is named, never reached with a credential in its URI.
|
|
137
|
+
problems.append("source.repository carries no password or credential: it names the repository")
|
|
134
138
|
if not isinstance(entry["installedAt"], str) or not _TIMESTAMP.fullmatch(entry["installedAt"]):
|
|
135
139
|
problems.append("installedAt is an RFC 3339 date-time")
|
|
136
140
|
if not isinstance(entry["installedBy"], str) or not 1 <= len(entry["installedBy"]) <= 200:
|
|
@@ -14,10 +14,10 @@ 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.
|
|
17
|
+
version: "0.50.0"
|
|
18
18
|
contract-version: "0.1.0"
|
|
19
19
|
extension: "fabric-service/0.1"
|
|
20
|
-
extension-commit: "
|
|
20
|
+
extension-commit: "52c7d798b9f4eb784eb4e29e717038a8912c759d"
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
# Building Fabric services
|
|
@@ -70,9 +70,9 @@ Answer before writing code, and record the answers in the project README:
|
|
|
70
70
|
| Caller | Surface | Why |
|
|
71
71
|
|---|---|---|
|
|
72
72
|
| 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; stdio-per-session once left twenty stray servers on this machine |
|
|
73
|
-
| The operator or a script | a CLI: `<tool> service status\|start\|stop\|restart`, `<tool> dashboard
|
|
73
|
+
| 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 |
|
|
74
74
|
| A remote agent | A2A `1.0` over HTTPS, or MCP behind an authenticated gateway | loopback reachability is not authorization |
|
|
75
|
-
| The dashboard page | same-origin REST under `/api`, session cookie plus a custom request header | the CSRF defence all four existing dashboards use |
|
|
75
|
+
| 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 CSRF defence all four existing dashboards use |
|
|
76
76
|
|
|
77
77
|
Read [the surfaces and auth reference](references/surfaces-and-auth.md) when wiring
|
|
78
78
|
MCP registration into a client config, the login flow or the CSRF header.
|
|
@@ -248,6 +248,16 @@ and declare it with `extensions=fs.dashboard_extension()`. Read
|
|
|
248
248
|
declaration and how to upgrade a dashboard built before the kit: an upgrade you may make yourself,
|
|
249
249
|
which the plugin's session-start notice offers in a repository that owns the service.
|
|
250
250
|
|
|
251
|
+
**Every button has a tool** (`fabric-actions/0.1`, contract DEC-0121 AP-1). Each action the page lets a
|
|
252
|
+
person take is a tool on the MCP surface with the same effect, checks, audit record and an idempotency key,
|
|
253
|
+
registered with `server.add_tool(tool, handler, action={id, title, tool, gate})` (`gate` is `none`, `relayed`
|
|
254
|
+
or `human` with its `humanStep`) and declared with `build_well_known(actions=server.action_table())` under
|
|
255
|
+
`extensions/actions/0.1`. A `relayed` action called by the operator's own agent with their decision in
|
|
256
|
+
`_meta.decision` is the operator's act: never answer it "operator session required" or send them to the page.
|
|
257
|
+
A service adopts the relayed gate once Fabric's hub forwards decisions (CT-125), as the contract says; until then its tools
|
|
258
|
+
answer as today. Read the interop reference's *Actions* section for when to adopt, the gate, its hooks and its
|
|
259
|
+
refusals.
|
|
260
|
+
|
|
251
261
|
**Say what the agent uses** (`fabric-connections/0.1`, DEC-0096) so Fabric Dashboards draws its chain: the other
|
|
252
262
|
agents, models, connectors, stores and channels, each with its state, from
|
|
253
263
|
`connections_extension()` / `connectionsExtension()` in `fabric_connections`. Never a credential in it, never a call
|
|
@@ -283,10 +293,13 @@ python3 <skill-dir>/scripts/check_service.py <id>[.<instance>]
|
|
|
283
293
|
It reports `PASS`, `FAIL` or `NOT_RUN` per rule (descriptor, port claim, well-known
|
|
284
294
|
shape, identity, latency, Host/Origin/cross-site guards, loopback bind, token file,
|
|
285
295
|
events auth and shape, single-use login, state outside code, instance lock, plist,
|
|
286
|
-
launchd pid equals answering pid, `dashboard.declared` (ED-9),
|
|
296
|
+
launchd pid equals answering pid, `dashboard.declared` (ED-9), `well-known.actions` (DEC-0121),
|
|
297
|
+
`dashboard.actions-declared` (`NOT_RUN` when a dashboard declares no actions), `dashboard.opens-in-host` (no browser
|
|
298
|
+
opener aimed at the service's pages in its checkout; `--repo PATH` names the checkout, and a service installed
|
|
299
|
+
from a release folder needs it), and the `interop.*` rules:
|
|
287
300
|
manifest link, capability list, tools equal to the manifest, job tools, unknown job, trace
|
|
288
|
-
propagation, event trace pairs,
|
|
289
|
-
any `FAIL`. A `NOT_RUN` is not a pass;
|
|
301
|
+
propagation, event trace pairs, `interop.action-tools` (every declared action's tool is served), and
|
|
302
|
+
`board.responder` for a service that declares the project board) and exits 1 on any `FAIL`. A `NOT_RUN` is not a pass;
|
|
290
303
|
name it in the report. A service with a dashboard also runs the layout probe,
|
|
291
304
|
`node <skill-dir>/scripts/check_dashboard_layout.mjs --service <id>` (WebKit at 360, 480, 768
|
|
292
305
|
and 1280 px; needs Playwright, else `NOT_RUN` and exit 3: nothing was measured).
|
|
@@ -328,7 +341,8 @@ first commit:
|
|
|
328
341
|
- an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
|
|
329
342
|
and a `CLAUDE.md` whose first line is `@AGENTS.md`.
|
|
330
343
|
|
|
331
|
-
|
|
344
|
+
Whoever owns the repository, an agent with a page gets the dashboard rule block in its `AGENTS.md`
|
|
345
|
+
(below). org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
|
|
332
346
|
someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's
|
|
333
347
|
choice, nothing about it is published or listed by the organization, and none of these files is
|
|
334
348
|
required of it, though the verified MCP quick start is still how anyone learns to drive it.
|
|
@@ -357,8 +371,9 @@ things change and the kits implement each:
|
|
|
357
371
|
so the platform's copy is known rather than invisible. Without Project Observatory, record
|
|
358
372
|
where the key lives in the person's own secret manager.
|
|
359
373
|
3. **Register it on the operator's computer**, the installer side, run there:
|
|
360
|
-
`registerRemote({ id, name, origin, token })` / `register_remote(...)` writes the token file
|
|
361
|
-
(0600) and the descriptor; pass the token from a file or
|
|
374
|
+
`registerRemote({ id, name, origin, token, source })` / `register_remote(...)` writes the token file
|
|
375
|
+
(0600) and the descriptor, with `source.repository` naming the code's repository; pass the token from a file or
|
|
376
|
+
stdin, never in argv. Public names
|
|
362
377
|
only in public artifacts: a private service is registered by a local descriptor and nowhere
|
|
363
378
|
else.
|
|
364
379
|
4. **Verify** with the probe: `check_service.py <id>` checks TLS, the token-gated well-known
|
|
@@ -417,8 +432,29 @@ the probe table verbatim with every `NOT_RUN` explained; the lock and kill-9 res
|
|
|
417
432
|
with pids; and the one next command. Never call a service conformant while the probe
|
|
418
433
|
reports a `FAIL`.
|
|
419
434
|
|
|
420
|
-
##
|
|
435
|
+
## Agent pages open only in Fabric Dashboards
|
|
436
|
+
|
|
437
|
+
The operator's rule (2026-10-24) for every page of an agent or a local Fabric service, in any host: a link in an
|
|
438
|
+
answer or a notification, `<tool> dashboard`, a completion message, a page the agent opens itself.
|
|
439
|
+
|
|
440
|
+
1. Resolve the exact registered service key (`id.instance`) with the `fabric-dashboards` MCP (`list_services`);
|
|
441
|
+
never guess it from a project name or a port.
|
|
442
|
+
2. Hand over the `open_link` its `link` tool returns for the service and the page's path, as the primary action.
|
|
443
|
+
3. To open it, use the host's `open` with `fallback=never`. Never `open <url>`, `open -a Safari`, `xdg-open`,
|
|
444
|
+
`start <url>`, `webbrowser.open` or `shell.openExternal` aimed at the service's pages.
|
|
445
|
+
4. A browser only on a confirmed `not_installed` host (`host_status`); an installed host that failed to open is
|
|
446
|
+
reported, never replaced by HTTP.
|
|
447
|
+
5. Links carry no token or login code: the host signs in.
|
|
448
|
+
|
|
449
|
+
Run `scripts/check_dashboard_link.py` (from this skill's own directory; it is not copied into the project) on an
|
|
450
|
+
action before it is printed or opened, and have the project's checks run it for every completion and notification
|
|
451
|
+
renderer. The generated `AGENTS.md` carries the rule as its dashboard
|
|
452
|
+
rule block; [dashboard links](references/dashboard-links.md) has the block, the gate's input and the remote case. The
|
|
453
|
+
probe's `dashboard.opens-in-host` fails a tracked file that opens the dashboard with a browser opener. Headless agents
|
|
454
|
+
need no dashboard.
|
|
455
|
+
|
|
456
|
+
## An agent with a page and no service descriptor
|
|
421
457
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
and
|
|
458
|
+
A local page no service descriptor names cannot open in Fabric Dashboards. Never open it in a browser instead:
|
|
459
|
+
make the agent a `fabric-service/0.1` service (this skill: the descriptor, the well-known document, the login-code
|
|
460
|
+
routes and a declared dashboard), and until that lands file a board row for it and say the page waits on it.
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
/* fabric-dashboard-kit@1.1.
|
|
1
|
+
/* fabric-dashboard-kit@1.1.5 · 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>
|
|
5
5
|
One floating menu button: "Chat with Fabric about <service>" and, when the page knows its project, "Open a coding
|
|
6
|
-
session in <project>"
|
|
6
|
+
session in <project>", in Russian on a page whose lang is ru; data-label-chat and data-label-session ({name} is the
|
|
7
|
+
service or the project), data-label-open and data-label-absent set the page's own words. Its links carry ids only and open a view in Fabric, nothing more (FB-8). It hides itself
|
|
7
8
|
when the host draws its own (the fabric_button cookie, FB-6) and says how to get Fabric when Fabric is absent
|
|
8
9
|
(FB-5). No inline script, no eval, nothing from a CDN; the only address it names is passioncode.ai/fabric.
|
|
9
10
|
Do not edit in place; `dashboard_kit.py install` replaces it on an upgrade. */
|
|
10
11
|
(function (root) {
|
|
11
12
|
'use strict';
|
|
12
13
|
|
|
13
|
-
var VERSION = '1.1.
|
|
14
|
+
var VERSION = '1.1.5';
|
|
14
15
|
var COOKIE = 'fabric_button';
|
|
15
16
|
var GET_FABRIC = 'https://passioncode.ai/fabric';
|
|
16
17
|
var ABSENT_AFTER_MS = 1500;
|
|
@@ -155,16 +156,39 @@
|
|
|
155
156
|
return null;
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
// The words a person reads (FAA-119): English, or Russian on a page whose lang is ru; the Russian is written as
|
|
160
|
+
// escapes so the file reads the same whatever charset it is served with. Checked with Copylot (en-US and ru-RU).
|
|
161
|
+
var LABELS = {
|
|
162
|
+
en: { chat: 'Chat with Fabric about {name}', session: 'Open a coding session in {name}', open: 'Open Fabric',
|
|
163
|
+
absent: 'Fabric is not installed. Get it at' },
|
|
164
|
+
ru: { chat: '\u0427\u0430\u0442 \u0441 Fabric \u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0435 {name}', session: '\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0441\u0435\u0441\u0441\u0438\u044e \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 {name}', open: '\u041e\u0442\u043a\u0440\u044b\u0442\u044c Fabric',
|
|
165
|
+
absent: 'Fabric \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d. \u0421\u043a\u0430\u0447\u0430\u0439\u0442\u0435 \u0435\u0433\u043e \u043d\u0430' }
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/** The labels for a page language, each one replaced by a non-blank string the page gives (data-label-*) that keeps
|
|
169
|
+
the word Fabric: the visible label stays inside the name and each item (DEC-0124, WCAG 2.2 SC 2.5.3). */
|
|
170
|
+
function labelsFor(lang, overrides) {
|
|
171
|
+
var base = LABELS[/^ru(-|$)/i.test(String(lang || '')) ? 'ru' : 'en'];
|
|
172
|
+
var out = {};
|
|
173
|
+
Object.keys(base).forEach(function (k) {
|
|
174
|
+
var own = overrides ? overrides[k] : undefined;
|
|
175
|
+
out[k] = typeof own === 'string' && own.trim() && own.indexOf('Fabric') >= 0 ? own.trim() : base[k];
|
|
176
|
+
});
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
function named(label, name) { return label.split('{name}').join(name); }
|
|
180
|
+
|
|
158
181
|
/** The menu items for a page: the chat link always, the session link when the page knows its project. */
|
|
159
|
-
function menuItems(options) {
|
|
182
|
+
function menuItems(options, labels) {
|
|
183
|
+
var words = labels || labelsFor();
|
|
160
184
|
var items = [];
|
|
161
185
|
var chat = { action: 'chat', service: options.service };
|
|
162
186
|
if (options.project) chat.project = options.project;
|
|
163
|
-
items.push({ label:
|
|
187
|
+
items.push({ label: named(words.chat, options.serviceName || options.service), href: buildFabricLink(chat) });
|
|
164
188
|
if (options.project) {
|
|
165
189
|
var session = { action: 'session.new', project: options.project };
|
|
166
190
|
if (options.runner) session.runner = options.runner;
|
|
167
|
-
items.push({ label:
|
|
191
|
+
items.push({ label: named(words.session, options.projectName || options.project), href: buildFabricLink(session) });
|
|
168
192
|
}
|
|
169
193
|
return items;
|
|
170
194
|
}
|
|
@@ -197,11 +221,12 @@
|
|
|
197
221
|
function mount(doc, options) {
|
|
198
222
|
var cookie = parseButtonCookie(doc.cookie);
|
|
199
223
|
if (cookie && cookie.owner === 'host') return null;
|
|
200
|
-
var
|
|
224
|
+
var words = labelsFor(options.lang !== undefined ? options.lang : doc.documentElement.getAttribute('lang'), options.labels);
|
|
225
|
+
var items = menuItems(options, words);
|
|
201
226
|
var absent = options.fabric === 'absent' || (cookie && cookie.fabric === 'absent');
|
|
202
227
|
|
|
203
228
|
var wrap = el(doc, 'div', { 'class': 'fd-fabric-button-wrap' });
|
|
204
|
-
var button = el(doc, 'button', { type: 'button', 'class': 'fd-fabric-button', 'aria-haspopup': 'menu', 'aria-expanded': 'false', 'aria-label':
|
|
229
|
+
var button = el(doc, 'button', { type: 'button', 'class': 'fd-fabric-button', 'aria-haspopup': 'menu', 'aria-expanded': 'false', 'aria-label': words.open });
|
|
205
230
|
button.appendChild(mark(doc));
|
|
206
231
|
button.appendChild(el(doc, 'span', { 'class': 'fd-fabric-button-label', 'aria-hidden': 'true' }, 'Fabric'));
|
|
207
232
|
var menu = el(doc, 'div', { role: 'menu', 'class': 'fd-fabric-button-menu', 'aria-label': 'Fabric' });
|
|
@@ -212,7 +237,7 @@
|
|
|
212
237
|
return a;
|
|
213
238
|
});
|
|
214
239
|
var note = el(doc, 'p', { 'class': 'fd-fabric-button-note', role: 'note' });
|
|
215
|
-
note.appendChild(doc.createTextNode(
|
|
240
|
+
note.appendChild(doc.createTextNode(words.absent + ' '));
|
|
216
241
|
// The way to get Fabric is a menu item too, so the arrows reach it whenever the note shows.
|
|
217
242
|
var get = el(doc, 'a', { role: 'menuitem', href: GET_FABRIC, target: '_blank', rel: 'noopener', tabindex: '-1', 'class': 'fd-fabric-button-get' }, 'passioncode.ai/fabric');
|
|
218
243
|
note.appendChild(get);
|
|
@@ -274,18 +299,19 @@
|
|
|
274
299
|
|
|
275
300
|
function optionsFrom(script) {
|
|
276
301
|
var d = script.dataset;
|
|
277
|
-
return { service: d.service, serviceName: d.serviceName, project: d.project, projectName: d.projectName, runner: d.runner, fabric: d.fabric
|
|
302
|
+
return { service: d.service, serviceName: d.serviceName, project: d.project, projectName: d.projectName, runner: d.runner, fabric: d.fabric,
|
|
303
|
+
labels: { chat: d.labelChat, session: d.labelSession, open: d.labelOpen, absent: d.labelAbsent } };
|
|
278
304
|
}
|
|
279
305
|
|
|
280
306
|
var api = { version: VERSION, parseFabricLink: parseFabricLink, buildFabricLink: buildFabricLink, parseButtonCookie: parseButtonCookie,
|
|
281
|
-
menuItems: menuItems, credentialName: credentialName, secretValue: secretValue, nameWords: nameWords, mount: mount };
|
|
307
|
+
menuItems: menuItems, labelsFor: labelsFor, credentialName: credentialName, secretValue: secretValue, nameWords: nameWords, mount: mount };
|
|
282
308
|
root.FabricButton = api;
|
|
283
309
|
|
|
284
310
|
var doc = root.document;
|
|
285
311
|
var script = doc && doc.currentScript;
|
|
286
312
|
if (script && script.dataset && script.dataset.service) {
|
|
287
313
|
var start = function () {
|
|
288
|
-
try { mount(doc, optionsFrom(script)); } catch (err) { if (root.console) root.console.warn('fabric-button: not shown
|
|
314
|
+
try { mount(doc, optionsFrom(script)); } catch (err) { if (root.console) root.console.warn('fabric-button: not shown: ' + err.message); }
|
|
289
315
|
};
|
|
290
316
|
if (doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', start); else start();
|
|
291
317
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* fabric-dashboard-kit@1.1.
|
|
1
|
+
/* fabric-dashboard-kit@1.1.5 · 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.
|
|
1
|
+
/* fabric-dashboard-kit@1.1.5 · 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.
|
|
11
|
+
var VERSION = '1.1.5';
|
|
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 */
|