appilot-mcp 0.1.1 → 0.3.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/LICENSE +15 -0
- package/README.md +102 -24
- package/dist/appilot-configurator.mcpb +0 -0
- package/dist/cli.d.ts +34 -0
- package/dist/cli.js +171 -0
- package/dist/client.d.ts +122 -3
- package/dist/client.js +306 -31
- package/dist/config.d.ts +14 -0
- package/dist/config.js +19 -0
- package/dist/contract/bundleSnapshot.js +8 -1
- package/dist/contract/healthContract.d.ts +1 -1
- package/dist/contract/healthContract.js +100 -10
- package/dist/contract/types.d.ts +37 -1
- package/dist/index.bundle.js +4487 -16520
- package/dist/index.js +7 -0
- package/dist/inspect.d.ts +88 -0
- package/dist/inspect.js +384 -0
- package/dist/manifest.d.ts +14 -2
- package/dist/manifest.js +31 -9
- package/dist/public-marketplace/.claude-plugin/marketplace.json +20 -0
- package/dist/public-marketplace/README.md +23 -0
- package/dist/public-marketplace/plugins/app-configurator/.claude-plugin/plugin.json +43 -0
- package/dist/public-marketplace/plugins/app-configurator/README.md +328 -0
- package/dist/public-marketplace/plugins/app-configurator/dist/index.bundle.js +57370 -0
- package/dist/public-marketplace/plugins/app-configurator/skills/app-configurator/SKILL.md +267 -0
- package/dist/public-marketplace/plugins/app-configurator/skills/app-configurator/agents/openai.yaml +13 -0
- package/dist/redaction.d.ts +18 -3
- package/dist/redaction.js +27 -3
- package/dist/remote/consent.d.ts +30 -20
- package/dist/remote/consent.js +114 -82
- package/dist/remote/consentMessages.d.ts +65 -0
- package/dist/remote/consentMessages.js +199 -0
- package/dist/remote/handoff.d.ts +10 -0
- package/dist/remote/handoff.js +44 -0
- package/dist/remote/httpServer.js +28 -5
- package/dist/remote/oauth.d.ts +39 -6
- package/dist/remote/oauth.js +281 -36
- package/dist/scaffold.d.ts +110 -1
- package/dist/scaffold.js +474 -39
- package/dist/server.js +425 -38
- package/dist/soak.js +21 -1
- package/dist/templates.d.ts +62 -0
- package/dist/templates.js +255 -0
- package/dist/verify.js +18 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/examples/app.appilot.json +212 -0
- package/mcpb/manifest.json +117 -15
- package/package.json +5 -3
- package/skills/app-configurator/SKILL.md +136 -25
package/mcpb/manifest.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "appilot-configurator",
|
|
4
4
|
"display_name": "Appilot Configurator",
|
|
5
|
-
"version": "0.
|
|
6
|
-
"description": "
|
|
7
|
-
"long_description": "Connects Claude Desktop to an Appilot instance, cloud or on-premise,
|
|
5
|
+
"version": "0.3.0",
|
|
6
|
+
"description": "Set up, configure, audit, repair, back up and restore an Appilot app's content model, and provision the app, its domains and its widget key.",
|
|
7
|
+
"long_description": "Connects Claude Desktop to an Appilot instance, cloud or on-premise. It provisions an app, its domains and its widget key, generates the host application's integration code, and verifies the result against the running page. It then reads the app's content-model configuration, audits it against the Appilot config health contract, repairs what it finds, and exports or restores a whole configuration bundle. The static gate runs locally, so an audit works without network access to anything but your own instance. Access is a scoped service token you create in the Backoffice and revoke there.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Appilot",
|
|
10
10
|
"url": "https://appilot.space"
|
|
@@ -12,13 +12,20 @@
|
|
|
12
12
|
"homepage": "https://appilot.space",
|
|
13
13
|
"documentation": "https://docs.appilot.space/docs/developers/configure-with-ai/overview",
|
|
14
14
|
"license": "ISC",
|
|
15
|
-
"keywords": [
|
|
15
|
+
"keywords": [
|
|
16
|
+
"appilot",
|
|
17
|
+
"configuration",
|
|
18
|
+
"content-model",
|
|
19
|
+
"audit"
|
|
20
|
+
],
|
|
16
21
|
"server": {
|
|
17
22
|
"type": "node",
|
|
18
23
|
"entry_point": "dist/index.bundle.js",
|
|
19
24
|
"mcp_config": {
|
|
20
25
|
"command": "node",
|
|
21
|
-
"args": [
|
|
26
|
+
"args": [
|
|
27
|
+
"${__dirname}/dist/index.bundle.js"
|
|
28
|
+
],
|
|
22
29
|
"env": {
|
|
23
30
|
"APPILOT_BASE_URL": "${user_config.base_url}",
|
|
24
31
|
"APPILOT_PAT": "${user_config.pat}",
|
|
@@ -27,15 +34,106 @@
|
|
|
27
34
|
}
|
|
28
35
|
},
|
|
29
36
|
"tools": [
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
37
|
+
{
|
|
38
|
+
"name": "capabilities",
|
|
39
|
+
"description": "Probe the instance for its version, migration level, and the closed vocabularies its entities accept."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "read_config",
|
|
43
|
+
"description": "Read an app's content-model configuration as a normalized snapshot."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "validate_config",
|
|
47
|
+
"description": "Audit a configuration against the Appilot config health contract."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "entity_template",
|
|
51
|
+
"description": "A valid skeleton for one entity kind, with the enums this instance accepts."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "create_entity",
|
|
55
|
+
"description": "Create a view, control, form, tool, zone, action plan, knowledge article or session template."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "update_entity",
|
|
59
|
+
"description": "Patch any of the eight entity kinds by its row id."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "delete_entity",
|
|
63
|
+
"description": "Delete an entity, refused when something still references it."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "validate_action_plan",
|
|
67
|
+
"description": "Check draft plan sections before writing them."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "export_config",
|
|
71
|
+
"description": "Export the whole configuration as a portable ConfigBundle."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "import_config",
|
|
75
|
+
"description": "Import a ConfigBundle, dry-run first, merge or replace."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "whoami",
|
|
79
|
+
"description": "Which organization, which app and which scopes this credential reaches."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "create_app",
|
|
83
|
+
"description": "Provision an app, its domains and a widget key, with the snippets to paste in."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "list_apps",
|
|
87
|
+
"description": "The apps this organization has provisioned, with their domains and verification status."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "list_widget_keys",
|
|
91
|
+
"description": "The organization's widget keys by label and prefix. No raw key, no secret."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "verify_domain",
|
|
95
|
+
"description": "Where a domain's DNS verification stands, and the TXT record it needs."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "integration_snippet",
|
|
99
|
+
"description": "The script tag and boot call for an app that already exists. Writes nothing."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "plan_manifest",
|
|
103
|
+
"description": "Preview what an appilot.app-manifest would change. Writes nothing."
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "apply_manifest",
|
|
107
|
+
"description": "Provision and configure an app from a previously planned manifest."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "scaffold_integration",
|
|
111
|
+
"description": "The host application's integration source: identity relay, widget boot, a client action."
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "verify_integration",
|
|
115
|
+
"description": "Load the running page and report what is actually true about the integration."
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "soak_selectors",
|
|
119
|
+
"description": "Check control selectors against a live page. Needs Playwright."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "inspect_page",
|
|
123
|
+
"description": "Read a page and rank locator candidates. Takes a URL or pasted markup."
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "scaffold_agent_first",
|
|
127
|
+
"description": "The artifacts one capability needs to be agent-operable."
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "report_feedback",
|
|
131
|
+
"description": "Report a gap or a defect in Appilot. Needs the feedback:write scope."
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "list_feedback",
|
|
135
|
+
"description": "This organization's reports, with status and occurrence count."
|
|
136
|
+
}
|
|
39
137
|
],
|
|
40
138
|
"user_config": {
|
|
41
139
|
"base_url": {
|
|
@@ -59,7 +157,11 @@
|
|
|
59
157
|
}
|
|
60
158
|
},
|
|
61
159
|
"compatibility": {
|
|
62
|
-
"platforms": [
|
|
160
|
+
"platforms": [
|
|
161
|
+
"darwin",
|
|
162
|
+
"win32",
|
|
163
|
+
"linux"
|
|
164
|
+
],
|
|
63
165
|
"runtimes": {
|
|
64
166
|
"node": ">=18.0.0"
|
|
65
167
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appilot-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Appilot MCP server: read, validate
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Appilot MCP server: provision an Appilot app, scaffold and verify the host integration, then read, validate and fix its content-model configuration against the config health contract. Endpoint-agnostic (cloud or on-premise), scoped-service-token auth, local static gate + optional live DOM soak.",
|
|
5
5
|
"homepage": "https://appilot.space",
|
|
6
6
|
"author": "BetterKnow GmbH",
|
|
7
7
|
"keywords": [
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
26
|
"skills",
|
|
27
|
+
"examples",
|
|
27
28
|
"mcpb",
|
|
28
29
|
".mcp.json",
|
|
29
30
|
".codex-plugin",
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
"test": "vitest run",
|
|
67
68
|
"test:watch": "vitest",
|
|
68
69
|
"start:http": "node dist/index.bundle.js --http",
|
|
69
|
-
"build:mcpb": "node scripts/build-mcpb.mjs"
|
|
70
|
+
"build:mcpb": "node scripts/build-mcpb.mjs",
|
|
71
|
+
"build:marketplace": "node scripts/build-public-marketplace.mjs"
|
|
70
72
|
}
|
|
71
73
|
}
|
|
@@ -15,10 +15,14 @@ well-formed configuration; every rule maps to a real failure mode.
|
|
|
15
15
|
|
|
16
16
|
## Two modes
|
|
17
17
|
|
|
18
|
-
- **Connected (Appilot MCP server available).**
|
|
19
|
-
`validate_config
|
|
20
|
-
`
|
|
21
|
-
|
|
18
|
+
- **Connected (Appilot MCP server available).** `capabilities`, `read_config` and
|
|
19
|
+
`validate_config` read and audit. `entity_template`, `create_entity`,
|
|
20
|
+
`update_entity`, `delete_entity` and `validate_action_plan` author, across all
|
|
21
|
+
eight entity kinds. `whoami`, `list_apps`, `list_widget_keys`, `create_app`,
|
|
22
|
+
`verify_domain`, `scaffold_integration` and `integration_snippet` set an app up
|
|
23
|
+
from nothing. `inspect_page` looks at the screen. `export_config` /
|
|
24
|
+
`import_config` back up, clone and promote. `soak_selectors` and
|
|
25
|
+
`verify_integration` prove it works on the real page. Prefer this mode. Always
|
|
22
26
|
start with `capabilities` so you configure against what THIS instance supports
|
|
23
27
|
(on-premise instances trail cloud).
|
|
24
28
|
- **Advisory (no MCP).** Ask the user to paste or export their config (or read it
|
|
@@ -36,21 +40,53 @@ runs in the customer's own repository, where you already are.
|
|
|
36
40
|
|
|
37
41
|
1. **`whoami`.** Which org does this credential reach, and which scopes does it
|
|
38
42
|
hold? A mistyped or revoked token should fail here, not as an opaque 401
|
|
39
|
-
halfway through. Provisioning needs `provision:write
|
|
43
|
+
halfway through. Provisioning needs `provision:write`, minted in the
|
|
44
|
+
Backoffice under Service tokens with the preset called **Set up
|
|
45
|
+
integrations**. Only an organization administrator can mint it, so a
|
|
46
|
+
configurator who is not one has to ask for it rather than work around it.
|
|
47
|
+
`list_apps` then answers what already exists, and `list_widget_keys` answers
|
|
48
|
+
which keys were already minted, so a re-run recognises them instead of asking
|
|
49
|
+
for more.
|
|
40
50
|
2. **`create_app`** with the app name and the domains it runs on. Idempotent, so
|
|
41
51
|
a re-run converges rather than creating a second app. It returns the script
|
|
42
52
|
tag, the boot snippet, and (once, at creation) the widget key and its secret.
|
|
53
|
+
A **live** key needs a verified domain: on a hostname nobody has proved they
|
|
54
|
+
control, the key comes back `blocked` with the TXT record to publish, while
|
|
55
|
+
the app and its domains are created anyway. Publish the record, then
|
|
56
|
+
`verify_domain` with `trigger`, or pass `isTest: true` for a test key that
|
|
57
|
+
claims nothing. The parameter is `isTest`, the same name the manifest and the
|
|
58
|
+
API use.
|
|
43
59
|
The **key** is publishable and belongs in the page. The **secret** is not: it
|
|
44
60
|
goes in the host's backend environment and must never reach a browser or a
|
|
45
61
|
client bundle. If you are connected over the remote transport the secret is
|
|
46
62
|
withheld on purpose; point the user at the Backoffice for it.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
|
|
64
|
+
**Working on the user's own machine.** There are two answers and they are not
|
|
65
|
+
equal. Preferred: have them run the app on a hostname that resolves to
|
|
66
|
+
127.0.0.1 (`myapp.lvh.me:3000`, or an `/etc/hosts` entry) and register THAT
|
|
67
|
+
hostname as a domain. The page then resolves its tenant by hostname, no widget
|
|
68
|
+
key is needed, verification is not required for that path, and the turn gets
|
|
69
|
+
the full app context: views, controls, forms, plans, knowledge. Fallback:
|
|
70
|
+
bare `localhost` with a `wk_test_` key, which gets the organization and the
|
|
71
|
+
user and NO app context, because no domain resolves. Never pass `localhost` or
|
|
72
|
+
`127.0.0.1` as a domain: they name every developer's machine, one row for the
|
|
73
|
+
whole platform, and the write is refused.
|
|
74
|
+
3. **`scaffold_integration`** for the host's framework, or `other` when the
|
|
75
|
+
backend is not Node (Django, Rails, PHP), which returns the raw exchange
|
|
76
|
+
instead. It returns file CONTENTS, which you write into the repository. The
|
|
77
|
+
identity relay is the piece that matters: `resolveUser` is the security
|
|
78
|
+
boundary and must derive the user from a credential the host already trusts,
|
|
79
|
+
never from the request body. The boot file reads the publishable key from the
|
|
80
|
+
framework's public variable (`NEXT_PUBLIC_APPILOT_WIDGET_KEY`,
|
|
81
|
+
`VITE_APPILOT_WIDGET_KEY`, `PUBLIC_APPILOT_WIDGET_KEY`); do not replace that
|
|
82
|
+
with a literal. `integration_snippet` returns the same two snippets later, for
|
|
83
|
+
an app that already exists, without a provisioning write.
|
|
51
84
|
4. **Make one capability agent-operable** before adding screens' worth of
|
|
52
85
|
configuration. The test for agent-first is whether a user could complete the
|
|
53
|
-
feature end to end through the assistant alone.
|
|
86
|
+
feature end to end through the assistant alone. `scaffold_agent_first` takes a
|
|
87
|
+
`shape`: `create` for an open/fill/submit flow, `navigate` for one step to a
|
|
88
|
+
screen, and `read` for a capability the agent answers, which gets no action
|
|
89
|
+
plan at all.
|
|
54
90
|
5. **`verify_integration`** against the running page. It answers what is actually
|
|
55
91
|
true: does the domain resolve to a tenant, does the relay refuse an
|
|
56
92
|
unauthenticated caller correctly, does the widget boot, is the console clean.
|
|
@@ -58,12 +94,23 @@ runs in the customer's own repository, where you already are.
|
|
|
58
94
|
6. Then configure the content model, below, and `validate_config`.
|
|
59
95
|
|
|
60
96
|
**A whole tenant as one file.** `plan_manifest` / `apply_manifest` take an
|
|
61
|
-
`appilot.app-manifest`: the app, its domains, its keys, and a ConfigBundle.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
97
|
+
`appilot.app-manifest`: the app, its domains, its keys, and a ConfigBundle. A
|
|
98
|
+
complete example ships with the server at `examples/app.appilot.json`; copy it
|
|
99
|
+
rather than assembling one from doc snippets. Keep it in the repository under
|
|
100
|
+
version control, so configuration is diffable in a pull request and promotable
|
|
101
|
+
from staging to production. `apply_manifest` requires the `planToken` that
|
|
102
|
+
`plan_manifest` returned for the same file, so you cannot apply something nobody
|
|
103
|
+
previewed, and an edit between the two calls fails rather than committing
|
|
104
|
+
silently.
|
|
105
|
+
|
|
106
|
+
Two things about an apply are worth saying out loud. Only the CONFIG half is
|
|
107
|
+
revisioned: every commit persists a `pre_restore` revision, so a wrong import is
|
|
108
|
+
undone by restoring it, while a domain or a key the provisioning half created is
|
|
109
|
+
undone by hand. And provisioning is asked for by the manifest, not by the tool:
|
|
110
|
+
an apply whose app, domains and keys already exist needs only `config:write`,
|
|
111
|
+
which is what lets a CI token keep content in sync without being able to claim
|
|
112
|
+
domains. Outside an MCP client, `appilot-mcp plan <file>` and
|
|
113
|
+
`appilot-mcp apply <file>` are the same two calls from a shell.
|
|
67
114
|
|
|
68
115
|
## The content model (what you configure)
|
|
69
116
|
|
|
@@ -116,20 +163,82 @@ sparingly, over-authoring is an anti-pattern); the live DOM is ground truth.**
|
|
|
116
163
|
as "Use when the user wants to…", localized. This is how the agent finds the
|
|
117
164
|
plan; a weak/mislocalized description breaks adoption.
|
|
118
165
|
|
|
166
|
+
## Authoring, one entity at a time
|
|
167
|
+
|
|
168
|
+
`entity_template(kind)` first, for any kind you have not written this session. It
|
|
169
|
+
returns a body with every container field present, the fields the write path
|
|
170
|
+
refuses without, and the closed enums THIS instance accepts. Two failures repeat
|
|
171
|
+
without it, and both are avoidable rather than diagnosable: a missing empty array
|
|
172
|
+
or object, which the import answers with a path you then have to decode; and a
|
|
173
|
+
guessed enum, because a brand-new app exports empty arrays and there was no
|
|
174
|
+
example to copy. Control locators are `id`, `class_text`, `aria`, `xpath`,
|
|
175
|
+
`semantic`. A CSS selector is not a kind, and a CSS selector list belongs under
|
|
176
|
+
`class_text`.
|
|
177
|
+
|
|
178
|
+
**Order is not a preference.** An entity that names another must come second.
|
|
179
|
+
|
|
180
|
+
1. Controls, with stable locators.
|
|
181
|
+
2. The form that names them as entry, submit and required fields.
|
|
182
|
+
3. The tool, with its credential in the same call when it reaches an
|
|
183
|
+
authenticated backend. A tool with no stored credential runs in the page, so a
|
|
184
|
+
session template whose preflight calls it is refused.
|
|
185
|
+
4. The action plan, after `validate_action_plan` on the draft sections.
|
|
186
|
+
5. The knowledge article, carrying meaning and not steps.
|
|
187
|
+
6. Activate what you created once `validate_config` is clean.
|
|
188
|
+
|
|
189
|
+
**Look at the page before you author a control.** `inspect_page` takes a `url`
|
|
190
|
+
and loads it, or `html` and scans markup the person pasted when there is no
|
|
191
|
+
browser on this connection. It ranks locator candidates by whether they survive
|
|
192
|
+
the next render and marks a framework-generated id as fragile, which is the
|
|
193
|
+
defect that passes every static check and then resolves to nothing. A scan of
|
|
194
|
+
pasted markup is not proof: run `soak_selectors` before you trust a locator.
|
|
195
|
+
|
|
196
|
+
**Building a capability, not just fixing one.** `scaffold_agent_first` returns
|
|
197
|
+
the four artifacts a capability needs so that a user could complete it through
|
|
198
|
+
the assistant alone: the tool, the client action when the operation belongs in
|
|
199
|
+
the page, the action plan that is the procedure, and the knowledge that carries
|
|
200
|
+
the meaning. That test, whether a user could finish end to end through the
|
|
201
|
+
assistant, is what agent-first means. A feature that only has a screen is not
|
|
202
|
+
done.
|
|
203
|
+
|
|
204
|
+
**A tool credential never travels over the remote transport.** `auth_secret` is
|
|
205
|
+
refused there, on purpose: a tool argument on that transport is already stored in
|
|
206
|
+
the conversation. Set it from a local stdio server, or in the Backoffice.
|
|
207
|
+
|
|
208
|
+
## Reporting what is missing
|
|
209
|
+
|
|
210
|
+
`report_feedback` records a gap or a defect in Appilot itself, or in this
|
|
211
|
+
configuration. Say plainly what it does before you call it, because the honest
|
|
212
|
+
version is short: the report is recorded and it is read, and a reply is part of a
|
|
213
|
+
support plan rather than something promised here. The tool's answer says which of
|
|
214
|
+
those applies to this organization.
|
|
215
|
+
|
|
216
|
+
Call `list_feedback` first, so a known problem gets a counter rather than a
|
|
217
|
+
duplicate. Show the person the exact title and body before sending. Never put
|
|
218
|
+
configuration contents, knowledge bodies, customer data or a secret in a report;
|
|
219
|
+
the machine context that matters (server version, failing tool, error code) is
|
|
220
|
+
attached for you.
|
|
221
|
+
|
|
119
222
|
## Workflow
|
|
120
223
|
|
|
121
224
|
1. **Discover.** Call `capabilities`. Note the version/migration level; degrade
|
|
122
225
|
gracefully if a field is absent.
|
|
123
226
|
2. **Read.** `read_config` for the target app. Understand the views, controls,
|
|
124
|
-
forms, plans, and knowledge.
|
|
227
|
+
forms, tools, zones, plans, and knowledge. If the result carries a `gaps`
|
|
228
|
+
array, an entity could not be read: say so, and do not report on it. Every
|
|
229
|
+
lint over a gap passed by default, which makes the rest of the report an
|
|
230
|
+
incomplete audit, not a clean one.
|
|
125
231
|
3. **Validate.** `validate_config`. It runs the contract locally and echoes the
|
|
126
232
|
server-side plan trust boundary. Read the severity-ranked findings.
|
|
127
233
|
4. **Explain.** Give the user a prioritized, plain-language report (critical →
|
|
128
234
|
low), each finding with the concrete fix. Do not dump raw tool output.
|
|
129
|
-
5. **Fix (with consent).**
|
|
130
|
-
`
|
|
131
|
-
|
|
132
|
-
|
|
235
|
+
5. **Fix (with consent).** Patch with `update_entity`, using the `kind` and the
|
|
236
|
+
row `id` `read_config` returns. Create with `create_entity`, after
|
|
237
|
+
`entity_template` for that kind. Remove with `delete_entity`, which refuses
|
|
238
|
+
and names the dependents when something still points at the entity; prefer
|
|
239
|
+
`is_active: false` when you mean retire rather than remove. Do not export and
|
|
240
|
+
re-import a bundle to change one thing. For a broken create flow the fix is
|
|
241
|
+
usually: add a `Form` (entry + submit + required field) → author the plan as
|
|
133
242
|
open → choose → `{{form:…}}` → submit → localize name/description/narratives →
|
|
134
243
|
give the control a stable selector.
|
|
135
244
|
6. **Re-validate.** `validate_config` again; confirm the findings are gone.
|
|
@@ -170,10 +279,12 @@ file is safe to save, commit, or share. `read_config` stays the reasoning view;
|
|
|
170
279
|
## Guardrails
|
|
171
280
|
|
|
172
281
|
- Confirm before writing. Show the diff you intend to apply; get a yes.
|
|
173
|
-
- Writing needs a `config:write` service token; reading
|
|
174
|
-
`config:read`; provisioning needs `provision:write`. If a call
|
|
175
|
-
|
|
176
|
-
|
|
282
|
+
- Writing needs a `config:write` service token; reading and validating need only
|
|
283
|
+
`config:read`; provisioning needs `provision:write`. If a call is refused on
|
|
284
|
+
scope, say which scope is missing and who grants it: `provision:write` comes
|
|
285
|
+
from an organization administrator in the Backoffice, under Service tokens
|
|
286
|
+
with the "Set up integrations" preset, or from the account approval screen. Do
|
|
287
|
+
not work around it.
|
|
177
288
|
- **Never put a widget secret in a file that ships to the browser.** It goes in
|
|
178
289
|
the server environment: no `NEXT_PUBLIC_` prefix, no `VITE_`, no committed
|
|
179
290
|
`.env`. The publishable key is different and belongs in the page.
|