appilot-mcp 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.claude-plugin/plugin.json +43 -0
  2. package/.codex-plugin/plugin.json +37 -0
  3. package/.mcp.json +19 -0
  4. package/README.md +268 -6
  5. package/dist/appilot-configurator.mcpb +0 -0
  6. package/dist/client.d.ts +140 -0
  7. package/dist/client.js +252 -0
  8. package/dist/config.d.ts +64 -0
  9. package/dist/config.js +78 -0
  10. package/dist/contract/bundleSnapshot.d.ts +12 -0
  11. package/dist/contract/bundleSnapshot.js +65 -0
  12. package/dist/contract/healthContract.d.ts +19 -0
  13. package/dist/contract/healthContract.js +297 -0
  14. package/dist/contract/index.d.ts +3 -0
  15. package/dist/contract/index.js +3 -0
  16. package/dist/contract/types.d.ts +86 -0
  17. package/dist/contract/types.js +9 -0
  18. package/dist/index.bundle.js +70081 -0
  19. package/dist/index.d.ts +20 -0
  20. package/dist/index.js +50 -0
  21. package/dist/manifest.d.ts +93 -0
  22. package/dist/manifest.js +147 -0
  23. package/dist/redaction.d.ts +30 -0
  24. package/dist/redaction.js +33 -0
  25. package/dist/remote/consent.d.ts +29 -0
  26. package/dist/remote/consent.js +99 -0
  27. package/dist/remote/httpServer.d.ts +20 -0
  28. package/dist/remote/httpServer.js +125 -0
  29. package/dist/remote/oauth.d.ts +74 -0
  30. package/dist/remote/oauth.js +288 -0
  31. package/dist/remote/tokens.d.ts +28 -0
  32. package/dist/remote/tokens.js +50 -0
  33. package/dist/scaffold.d.ts +37 -0
  34. package/dist/scaffold.js +203 -0
  35. package/dist/server.d.ts +15 -0
  36. package/dist/server.js +382 -0
  37. package/dist/soak.d.ts +32 -0
  38. package/dist/soak.js +51 -0
  39. package/dist/verify.d.ts +40 -0
  40. package/dist/verify.js +149 -0
  41. package/dist/version.d.ts +14 -0
  42. package/dist/version.js +14 -0
  43. package/mcpb/manifest.json +67 -0
  44. package/package.json +70 -16
  45. package/skills/app-configurator/SKILL.md +198 -0
  46. package/skills/app-configurator/agents/openai.yaml +13 -0
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "app-configurator",
3
+ "version": "0.1.1",
4
+ "description": "Set up, configure, audit, repair, back up, restore, and verify an Appilot app: provision the app, its domains and widget key, scaffold the host integration, and keep the content model correct. Uses the Appilot MCP server and the app-configurator skill.",
5
+ "author": {
6
+ "name": "Appilot",
7
+ "url": "https://appilot.space"
8
+ },
9
+ "homepage": "https://docs.appilot.space/docs/developers/configure-with-ai/overview",
10
+ "skills": "./skills/",
11
+ "mcpServers": {
12
+ "appilot": {
13
+ "command": "node",
14
+ "args": [
15
+ "${CLAUDE_PLUGIN_ROOT}/dist/index.bundle.js"
16
+ ],
17
+ "env": {
18
+ "APPILOT_BASE_URL": "${user_config.base_url}",
19
+ "APPILOT_PAT": "${user_config.pat}",
20
+ "APPILOT_APP_ID": "${user_config.app_id}"
21
+ }
22
+ }
23
+ },
24
+ "userConfig": {
25
+ "base_url": {
26
+ "type": "string",
27
+ "title": "Appilot backend URL",
28
+ "description": "Cloud or on-premise Appilot backend URL.",
29
+ "required": true
30
+ },
31
+ "pat": {
32
+ "type": "string",
33
+ "title": "Appilot service token",
34
+ "description": "Scoped appilot_pat service token. config:read for audits, config:write for changes, provision:write to create apps, domains, and widget keys.",
35
+ "sensitive": true
36
+ },
37
+ "app_id": {
38
+ "type": "string",
39
+ "title": "Default Appilot app ID",
40
+ "description": "Optional default app ID used when a tool call does not provide one."
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "app-configurator",
3
+ "version": "0.1.1",
4
+ "description": "Set up, configure, audit, repair, back up, restore, and verify an Appilot app: provision the app, its domains and widget key, scaffold the host integration, and keep the content model correct. Uses the Appilot MCP server and the app-configurator skill.",
5
+ "author": {
6
+ "name": "Appilot",
7
+ "url": "https://appilot.space"
8
+ },
9
+ "homepage": "https://docs.appilot.space/docs/developers/configure-with-ai/overview",
10
+ "license": "ISC",
11
+ "keywords": [
12
+ "appilot",
13
+ "mcp",
14
+ "agent-skill",
15
+ "configuration"
16
+ ],
17
+ "skills": "./skills/",
18
+ "mcpServers": "./.mcp.json",
19
+ "interface": {
20
+ "displayName": "Appilot Configurator",
21
+ "shortDescription": "Configure and validate Appilot apps with AI",
22
+ "longDescription": "Connect an AI agent to an Appilot instance to inspect, validate, repair, back up, restore, clone, and live-test its content-model configuration.",
23
+ "developerName": "Appilot",
24
+ "category": "Engineering",
25
+ "capabilities": [
26
+ "Interactive",
27
+ "Read",
28
+ "Write"
29
+ ],
30
+ "websiteURL": "https://appilot.space",
31
+ "defaultPrompt": [
32
+ "Audit my Appilot app configuration",
33
+ "Fix the critical Appilot config findings",
34
+ "Back up and validate my Appilot app"
35
+ ]
36
+ }
37
+ }
package/.mcp.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "mcpServers": {
3
+ "appilot": {
4
+ "command": "node",
5
+ "args": [
6
+ "./dist/index.bundle.js"
7
+ ],
8
+ "cwd": ".",
9
+ "env_vars": [
10
+ "APPILOT_BASE_URL",
11
+ "APPILOT_PAT",
12
+ "APPILOT_APP_ID",
13
+ "APPILOT_SOAK_STORAGE_STATE"
14
+ ],
15
+ "startup_timeout_sec": 10,
16
+ "tool_timeout_sec": 60
17
+ }
18
+ }
19
+ }
package/README.md CHANGED
@@ -1,11 +1,273 @@
1
1
  # appilot-mcp
2
2
 
3
- This version is a name reservation, not a working package. It contains no code.
3
+ MCP server that lets an AI agent **read, validate, fix, and soak** an Appilot
4
+ app's content-model configuration (Views, Controls, Forms, Action Plans,
5
+ Knowledge, Zones, Tools) against the **config health contract**.
4
6
 
5
- The published package will hold the MCP server that lets a coding agent configure an Appilot app.
7
+ It is the tool surface behind the `app-configurator` skill. The skill is the
8
+ instructions; this server is what actually touches your instance.
6
9
 
7
- Appilot adds intelligence to any web app: its AI helps people operate the app and
8
- act on their behalf, shipping as a browser extension or an embeddable widget.
9
- See https://appilot.space.
10
+ ## Why
10
11
 
11
- Maintained by BetterKnow GmbH.
12
+ Hand-authored app config drifts silently: a plan that opens an input but never
13
+ submits, an auto-generated selector like `#nc-vue-30`, half-translated labels, a
14
+ knowledge article that is really a procedure. This server encodes the contract
15
+ those failures violate and checks it locally (no round-trip, air-gapped-safe)
16
+ and against the live instance.
17
+
18
+ ## Cloud or on-premise
19
+
20
+ Endpoint-agnostic by design. The same binary talks to a cloud tenant or an
21
+ on-premise / sovereign instance purely by pointing `APPILOT_BASE_URL` at it. The
22
+ static gate runs locally from `appilot-shared`, so it works with no network.
23
+
24
+ ## Two transports, one tool surface
25
+
26
+ The same tools serve two shapes, and which one you need is decided by the client,
27
+ not by preference.
28
+
29
+ | Transport | Who launches it | Credential | Clients |
30
+ |-----------|-----------------|------------|---------|
31
+ | **stdio** (default) | The client, as a local child process | `APPILOT_PAT` in the process environment | Claude Code, Codex (CLI, IDE, app), Claude Desktop, Cursor, Antigravity |
32
+ | **HTTP** (`--http`) | You, as a deployed service | The caller's own service token, collected once through OAuth | ChatGPT, claude.ai, and any remote MCP client |
33
+
34
+ ChatGPT and claude.ai cannot start a process on your machine, and neither offers
35
+ a field for pasting an API key: they connect to a URL and authenticate with
36
+ OAuth. That is what HTTP mode is for. See
37
+ [Remote deployment](#remote-deployment).
38
+
39
+ ## Configuration
40
+
41
+ | Env | Required | Meaning |
42
+ |-----|----------|---------|
43
+ | `APPILOT_BASE_URL` | yes | Backend URL, e.g. `https://api.appilot.space` or `http://localhost:6001` |
44
+ | `APPILOT_PAT` | for writes / server echo | A scoped service token (`appilot_pat_…`), minted in the Backoffice (admin → service tokens). `config:read` to read/validate, `config:write` to apply. |
45
+ | `APPILOT_APP_ID` | optional | Default app id for tools that omit one |
46
+ | `APPILOT_SOAK_STORAGE_STATE` | optional | Path to a Playwright `storageState` JSON for an authenticated site session (soak) |
47
+
48
+ HTTP mode reads a different profile. It holds no Appilot credential of its own,
49
+ because each caller brings theirs.
50
+
51
+ | Env | Required | Meaning |
52
+ |-----|----------|---------|
53
+ | `APPILOT_BASE_URL` | yes | The one instance this deployment fronts. Never client-supplied: a caller-chosen URL would make the service an SSRF relay. |
54
+ | `APPILOT_MCP_PUBLIC_URL` | yes | The public origin clients reach, e.g. `https://mcp.appilot.space`. OAuth metadata and redirects are absolute. |
55
+ | `APPILOT_MCP_OAUTH_SECRET` | yes | At least 32 characters (`openssl rand -base64 32`). Everything the authorization server issues is sealed with it, so rotating it disconnects every client. |
56
+ | `PORT` | no | Defaults to 8080. Cloud Run supplies it. |
57
+ | `APPILOT_MCP_ALLOWED_HOSTS` | no | Extra `Host` values to accept, comma separated, for a proxy or a platform-assigned hostname. The public origin is always accepted. |
58
+
59
+ ## Tools
60
+
61
+ | Tool | Purpose |
62
+ |------|---------|
63
+ | `capabilities` | Version + migration level + schema versions of the connected instance (negotiate before configuring) |
64
+ | `read_config` | Normalized snapshot of an app's content model |
65
+ | `validate_config` | Severity-ranked findings against the health contract (local + server echo) |
66
+ | `update_action_plan` / `update_control` / `update_knowledge` | Guarded writes (server re-validates the trust boundary) |
67
+ | `export_config` / `import_config` | Whole-app ConfigBundle round trip, dry-run first, merge or replace |
68
+ | `soak_selectors` | Headless-browser check that each control selector resolves on the live page (needs Playwright) |
69
+
70
+ ## Distribution artifacts
71
+
72
+ One package carries a tool-neutral implementation plus thin discovery adapters:
73
+
74
+ | Artifact | Consumers |
75
+ |----------|-----------|
76
+ | `skills/app-configurator/SKILL.md` | Open Agent Skills clients, including Codex, Claude Code, Cursor, and Antigravity |
77
+ | `.mcp.json` | Codex plugin MCP declaration |
78
+ | `.codex-plugin/plugin.json` | Codex plugin |
79
+ | `.claude-plugin/plugin.json` | Claude Code plugin |
80
+ | `mcpb/manifest.json` | Claude Desktop bundle manifest, packed by `pnpm build:mcpb` |
81
+ | `Dockerfile` | The remote HTTP service |
82
+ | `dist/index.bundle.js` | Self-contained Node.js MCP server, both transports |
83
+
84
+ `pnpm pack` runs the build and includes all of these artifacts in the package.
85
+ The server can initialize and expose its tools before connection settings are
86
+ provided. Live tool calls return a clear configuration error until
87
+ `APPILOT_BASE_URL` is set.
88
+
89
+ ## Install from this repository
90
+
91
+ Build the self-contained server first:
92
+
93
+ ```bash
94
+ pnpm -C packages/tools/appilot-mcp build
95
+ ```
96
+
97
+ ### Codex
98
+
99
+ The repository is a Codex marketplace:
100
+
101
+ ```bash
102
+ codex plugin marketplace add /absolute/path/to/app-pilot
103
+ codex plugin add app-configurator@appilot
104
+ ```
105
+
106
+ The plugin installs both the skill and MCP server globally. Start a new Codex
107
+ task after installation so the new tool surface is loaded.
108
+
109
+ ### Claude Code
110
+
111
+ Add the repository marketplace and install `app-configurator@appilot`. Claude's
112
+ plugin settings prompt for the backend URL, service token, and optional app ID.
113
+ The token is declared sensitive so Claude stores it in secure credential
114
+ storage.
115
+
116
+ ### Claude Desktop
117
+
118
+ Build the double-clickable bundle and open it:
119
+
120
+ ```bash
121
+ pnpm -C packages/tools/appilot-mcp build
122
+ pnpm -C packages/tools/appilot-mcp build:mcpb
123
+ open packages/tools/appilot-mcp/dist/appilot-configurator.mcpb
124
+ ```
125
+
126
+ Desktop asks for the backend URL and the service token, storing the token in the
127
+ OS keychain. The bundle carries the server and its manifest and nothing else, so
128
+ it installs on a machine with no Node toolchain and no network.
129
+
130
+ ### ChatGPT and claude.ai
131
+
132
+ Neither can launch a local process, so both connect to a deployed HTTP endpoint.
133
+ Once one is running (see [Remote deployment](#remote-deployment)):
134
+
135
+ - **ChatGPT**: Settings, Apps, Advanced settings, turn on Developer mode, then
136
+ add a connector pointing at `https://<your-host>/mcp` with OAuth. The
137
+ authorization step asks for a service token, once.
138
+ - **claude.ai**: Settings, Connectors, Add custom connector, same URL.
139
+ - **Codex** can use the remote endpoint too, if you prefer one shared deployment
140
+ over a local process: add the URL to `~/.codex/config.toml` and run
141
+ `codex mcp login appilot`.
142
+
143
+ To reach a private instance from ChatGPT without exposing an endpoint at all,
144
+ run OpenAI's Secure MCP Tunnel against the local stdio server instead. The
145
+ tunnel client makes an outbound connection from inside your network, so the
146
+ instance needs no public listener.
147
+
148
+ ### Cursor
149
+
150
+ Copy `skills/app-configurator` to `~/.cursor/skills/app-configurator`, then add
151
+ the server to `~/.cursor/mcp.json`.
152
+
153
+ ### Antigravity
154
+
155
+ Copy `skills/app-configurator` to the applicable global skills directory:
156
+
157
+ - Antigravity IDE: `~/.gemini/config/skills/app-configurator`
158
+ - Antigravity CLI: `~/.gemini/antigravity-cli/skills/app-configurator`
159
+
160
+ Register the stdio server in `~/.gemini/config/mcp_config.json`.
161
+
162
+ ### Any stdio MCP client
163
+
164
+ Point the client at the built bundle:
165
+
166
+ ```json
167
+ {
168
+ "mcpServers": {
169
+ "appilot": {
170
+ "command": "node",
171
+ "args": ["/absolute/path/to/appilot-mcp/dist/index.bundle.js"],
172
+ "env": {
173
+ "APPILOT_BASE_URL": "https://api.appilot.space",
174
+ "APPILOT_PAT": "appilot_pat_...",
175
+ "APPILOT_APP_ID": "123"
176
+ }
177
+ }
178
+ }
179
+ }
180
+ ```
181
+
182
+ For registry-backed distribution, replace `node` and the bundle path with
183
+ `pnpm dlx appilot-mcp` after the package is published.
184
+
185
+ Do not commit service tokens. Use the MCP client's secure secret storage or
186
+ environment forwarding when available.
187
+
188
+ ## Remote deployment
189
+
190
+ HTTP mode is a small stateless web service. It stores nothing: no user table, no
191
+ client registry, no token store. The OAuth authorization server seals its own
192
+ state into the artifacts it issues (see `src/remote/tokens.ts`), so a second
193
+ instance behaves exactly like the first and a cold start loses nothing.
194
+
195
+ What it serves:
196
+
197
+ | Path | Purpose |
198
+ |------|---------|
199
+ | `POST /mcp` | The MCP endpoint, Streamable HTTP, bearer required |
200
+ | `/.well-known/oauth-protected-resource/mcp` | Points a client at the authorization server |
201
+ | `/.well-known/oauth-authorization-server` | Metadata, PKCE with S256, dynamic registration |
202
+ | `/authorize`, `/token`, `/register` | The OAuth 2.1 flow |
203
+ | `/consent` | The one page a person sees, where they paste a service token |
204
+ | `/health` (also `/healthz` off Cloud Run) | Liveness |
205
+
206
+ Run it:
207
+
208
+ ```bash
209
+ pnpm -C packages/tools/appilot-mcp build
210
+ docker build -t appilot-mcp packages/tools/appilot-mcp
211
+ docker run -p 8080:8080 \
212
+ -e APPILOT_BASE_URL=https://api.appilot.space \
213
+ -e APPILOT_MCP_PUBLIC_URL=https://mcp.appilot.space \
214
+ -e APPILOT_MCP_OAUTH_SECRET="$(openssl rand -base64 32)" \
215
+ appilot-mcp
216
+ ```
217
+
218
+ On Cloud Run, which is where the backend already runs, use the script rather
219
+ than a hand-typed `gcloud` line: it creates the signing secret, grants the
220
+ runtime service account access to it, and handles the two-pass problem where the
221
+ service URL only exists after the first deploy while `APPILOT_MCP_PUBLIC_URL`
222
+ has to name that exact origin.
223
+
224
+ ```bash
225
+ packages/tools/appilot-mcp/scripts/deploy-cloud-run.sh
226
+ ```
227
+
228
+ It deploys unauthenticated on purpose: the service does its own OAuth, and
229
+ platform-level IAM in front of it would block the discovery endpoints a client
230
+ has to read before it can authenticate at all. The upload carries the
231
+ `Dockerfile` and the one bundle, which is what `.gcloudignore` is for. Mapping
232
+ `mcp.appilot.space` onto the service is the last step, and the script prints it.
233
+
234
+ ### How access works
235
+
236
+ The service never sees an Appilot password and never mints a credential. A
237
+ person creates a scoped service token in the Backoffice, pastes it once on the
238
+ consent screen, and the service checks it against the instance
239
+ (`GET /config/whoami`) before granting anything. The granted scopes are the
240
+ intersection of what the client asked for and what that token actually carries,
241
+ so a `config:read` token cannot be talked into write access. The token is then
242
+ sealed inside the OAuth tokens the client holds; the client never sees it.
243
+
244
+ Revocation happens where the token was minted: revoke it in the Backoffice and
245
+ every connection built on it stops working on the next call. Rotating
246
+ `APPILOT_MCP_OAUTH_SECRET` invalidates every issued token at once.
247
+
248
+ The soak tool is deliberately unavailable in this mode: driving a real browser
249
+ belongs on the operator's machine over stdio, not in a shared web service.
250
+
251
+ ## Develop
252
+
253
+ ```bash
254
+ pnpm -C packages/tools/appilot-mcp build # tsc + the self-contained bundle
255
+ pnpm -C packages/tools/appilot-mcp build:mcpb # the Claude Desktop bundle
256
+ pnpm -C packages/tools/appilot-mcp test # vitest: contract, OAuth, HTTP end to end
257
+ pnpm -C packages/tools/appilot-mcp typecheck
258
+ ```
259
+
260
+ To exercise HTTP mode locally, point it at a dev backend and use a localhost
261
+ public origin, which is the one case where plaintext is accepted:
262
+
263
+ ```bash
264
+ APPILOT_BASE_URL=http://localhost:6001 \
265
+ APPILOT_MCP_PUBLIC_URL=http://localhost:8080 \
266
+ APPILOT_MCP_OAUTH_SECRET="$(openssl rand -base64 32)" \
267
+ node packages/tools/appilot-mcp/dist/index.bundle.js --http
268
+ ```
269
+
270
+ Live DOM soak needs Playwright: add it with pnpm and install Chromium.
271
+
272
+ See `docs/architecture/appilot-mcp.md` (server) and
273
+ `docs/content-model/config-health-contract.md` (the contract it enforces).
Binary file
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Thin HTTP client over the Appilot config API. Adds service-token auth, reads
3
+ * the content-model entities, and normalizes them into a `ConfigSnapshot` for
4
+ * the health contract. Also exposes the non-persisting server-side plan
5
+ * validation echo.
6
+ *
7
+ * Translation shapes vary by instance version (base + `*_i18n` map on newer
8
+ * builds, per-locale side-table rows on others), so the normalizers below read
9
+ * defensively and never throw on a missing field. Field mapping is verified
10
+ * against a live instance in the plan's Phase F.
11
+ */
12
+ import type { AppilotConnection } from './config.js';
13
+ import type { ConfigSnapshot } from './contract/types.js';
14
+ export interface Capabilities {
15
+ appVersion: string;
16
+ migrations: {
17
+ count: number;
18
+ latest: string | null;
19
+ latestExecutedAt: string | null;
20
+ } | null;
21
+ schemaVersions: Record<string, string>;
22
+ configurableEntities: string[];
23
+ /** Absent on instances that predate config portability; clients must refuse
24
+ * to import rather than guess. */
25
+ configPortability?: {
26
+ bundleFormatVersion: string;
27
+ maxImportBytes: number;
28
+ };
29
+ }
30
+ /** Mirrors the backend's ProvisioningService result. Secrets appear once. */
31
+ export interface ProvisionAppResponse {
32
+ dryRun: boolean;
33
+ app: {
34
+ id: number | null;
35
+ name: string;
36
+ action: 'created' | 'reused' | 'updated';
37
+ };
38
+ domains: Array<{
39
+ id: number | null;
40
+ domain: string;
41
+ action: 'created' | 'reused' | 'updated';
42
+ verification_status: string;
43
+ dns_record_name?: string;
44
+ dns_record_value?: string;
45
+ }>;
46
+ widgetKey?: {
47
+ id: number | null;
48
+ action: 'created' | 'reused' | 'updated';
49
+ keyPrefix?: string;
50
+ rawKey?: string;
51
+ rawSecret?: string;
52
+ };
53
+ integration: {
54
+ apiUrl: string | null;
55
+ scriptTag: string;
56
+ bootSnippet: string;
57
+ tokenEndpointHint: string;
58
+ };
59
+ }
60
+ export interface ProvisionAppRequest {
61
+ app: {
62
+ name: string;
63
+ description?: string;
64
+ };
65
+ domains?: Array<{
66
+ domain: string;
67
+ name?: string;
68
+ default_language?: string | null;
69
+ configured_languages?: string[];
70
+ }>;
71
+ widgetKey?: {
72
+ name?: string;
73
+ allowedDomains?: string[];
74
+ isTest?: boolean;
75
+ };
76
+ dryRun?: boolean;
77
+ }
78
+ export interface ProvisionedAppSummary {
79
+ id: number;
80
+ name: string;
81
+ domains: Array<{
82
+ id: number;
83
+ domain: string;
84
+ verification_status: string;
85
+ }>;
86
+ }
87
+ export declare class AppilotApiError extends Error {
88
+ readonly status: number;
89
+ readonly body?: unknown;
90
+ constructor(message: string, status: number, body?: unknown);
91
+ }
92
+ export declare class AppilotClient {
93
+ private readonly conn;
94
+ constructor(conn: AppilotConnection);
95
+ private request;
96
+ getCapabilities(): Promise<Capabilities>;
97
+ getHealth(): Promise<{
98
+ status: string;
99
+ version?: string;
100
+ }>;
101
+ listActionPlans(appId: number): Promise<Record<string, unknown>[]>;
102
+ listControls(appId: number): Promise<Record<string, unknown>[]>;
103
+ listForms(appId: number): Promise<Record<string, unknown>[]>;
104
+ listViews(appId: number): Promise<Record<string, unknown>[]>;
105
+ listKnowledge(appId: number): Promise<Record<string, unknown>[]>;
106
+ updateActionPlan(id: string, body: unknown): Promise<unknown>;
107
+ updateControl(id: string, body: unknown): Promise<unknown>;
108
+ createControl(body: unknown): Promise<unknown>;
109
+ updateForm(id: string, body: unknown): Promise<unknown>;
110
+ updateKnowledge(id: string, body: unknown): Promise<unknown>;
111
+ /** Server-side non-persisting plan validation (echoes the runtime trust boundary). */
112
+ validatePlan(appId: number, sections: unknown, formValues: unknown): Promise<{
113
+ ok: boolean;
114
+ errors: string[];
115
+ }>;
116
+ listProvisioned(): Promise<ProvisionedAppSummary[]>;
117
+ provisionApp(body: ProvisionAppRequest): Promise<ProvisionAppResponse>;
118
+ /**
119
+ * Domain check: does this hostname resolve to a tenant? Public endpoint, so
120
+ * it answers even for a read-only caller, which is what makes it usable as
121
+ * the first probe of `verify_integration`.
122
+ */
123
+ checkDomain(domain: string): Promise<Record<string, unknown>>;
124
+ /** Who this credential is: org, app narrowing, scopes. Never a secret. */
125
+ whoami(): Promise<Record<string, unknown>>;
126
+ exportConfig(appId: number): Promise<import('appilot-shared/config-bundle').ConfigBundle>;
127
+ importConfig(appId: number, body: {
128
+ mode: 'merge' | 'replace';
129
+ dryRun?: boolean;
130
+ bundle: unknown;
131
+ expectedCurrentHash?: string;
132
+ allowUnhealthy?: boolean;
133
+ }): Promise<import('appilot-shared/config-bundle').ImportDiff>;
134
+ /**
135
+ * Read the app's content-model config and normalize it into a ConfigSnapshot.
136
+ * `expectedLocales` defaults to de/en/es (Appilot's trilingual baseline) but
137
+ * can be overridden per call once the domain's configured_languages are known.
138
+ */
139
+ buildSnapshot(appId: number, expectedLocales?: string[]): Promise<ConfigSnapshot>;
140
+ }