hostinger-api-mcp 3.24.1 → 3.24.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/deploy-to-hosting/SKILL.md +2 -2
- package/skills/headless/SKILL.md +16 -0
- package/skills/headless/VERSION +3 -0
- package/skills/headless/connect/SKILL.md +15 -2
- package/skills/headless/connect/tools/antigravity.md +6 -4
- package/skills/headless/connect/tools/claude-code.md +2 -2
- package/skills/headless/connect/tools/codex.md +6 -4
- package/skills/headless/connect/tools/cursor.md +6 -4
- package/skills/headless/connect/tools/devin.md +6 -4
- package/skills/headless/connect/tools/vscode.md +6 -4
- package/skills/headless/run-log.mjs +2 -1
- package/skills/headless/steps/03-provision/SKILL.md +21 -0
- package/skills/headless/steps/03-provision/webapp/SKILL.md +7 -1
- package/skills/headless/steps/08-deploy/SKILL.md +13 -5
- package/src/core/runtime.js +3 -2
- package/src/core/runtime.ts +3 -2
- package/src/core/skills.js +39 -39
- package/src/core/skills.ts +39 -39
- package/src/servers/agency-hosting.js +1 -1
- package/src/servers/agency-hosting.ts +1 -1
- package/src/servers/agent-readiness.js +1 -1
- package/src/servers/agent-readiness.ts +1 -1
- package/src/servers/all.js +1 -1
- package/src/servers/all.ts +1 -1
- package/src/servers/billing.js +1 -1
- package/src/servers/billing.ts +1 -1
- package/src/servers/dns.js +1 -1
- package/src/servers/dns.ts +1 -1
- package/src/servers/domains.js +1 -1
- package/src/servers/domains.ts +1 -1
- package/src/servers/ecommerce.js +1 -1
- package/src/servers/ecommerce.ts +1 -1
- package/src/servers/horizons.js +1 -1
- package/src/servers/horizons.ts +1 -1
- package/src/servers/hosting.js +1 -1
- package/src/servers/hosting.ts +1 -1
- package/src/servers/mail.js +1 -1
- package/src/servers/mail.ts +1 -1
- package/src/servers/reach.js +1 -1
- package/src/servers/reach.ts +1 -1
- package/src/servers/vps.js +1 -1
- package/src/servers/vps.ts +1 -1
- package/src/servers/wordpress.js +1 -1
- package/src/servers/wordpress.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ Generated at release time from the diff between the previous and current
|
|
|
4
4
|
build. Tool changes are listed individually; everything else is summarised
|
|
5
5
|
by area.
|
|
6
6
|
|
|
7
|
+
## v3.24.3 — 2026-11-03
|
|
8
|
+
|
|
9
|
+
### Also in this release
|
|
10
|
+
|
|
11
|
+
- Bundled agent skills: `skills/deploy-to-hosting/SKILL.md`, `skills/headless/VERSION`
|
|
12
|
+
- Request handling and authentication: `src/core/runtime.js`, `src/core/runtime.ts`
|
|
13
|
+
- Other files: `src/core/skills.js`, `src/core/skills.ts`
|
|
14
|
+
|
|
15
|
+
## v3.24.2 — 2026-11-03
|
|
16
|
+
|
|
17
|
+
### Also in this release
|
|
18
|
+
|
|
19
|
+
- Dependencies: `package-lock.json`
|
|
20
|
+
- Bundled agent skills: 13 files
|
|
21
|
+
- Other files: `src/core/skills.js`, `src/core/skills.ts`
|
|
22
|
+
|
|
7
23
|
## v3.24.1 — 2026-10-30
|
|
8
24
|
|
|
9
25
|
### Also in this release
|
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/hostinger/api-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "3.24.
|
|
9
|
+
"version": "3.24.3",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@hostinger/mcp",
|
|
14
|
-
"version": "3.24.
|
|
14
|
+
"version": "3.24.3",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -11,7 +11,7 @@ Ship a project that already exists — on disk or in a Git repository — to a H
|
|
|
11
11
|
|
|
12
12
|
- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. "deploy static").
|
|
13
13
|
- Batch reads with `multi-execute`; values pass between steps as `$steps.<i>.<path>`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `wordpress` and `agency-hosting` as separate servers.
|
|
14
|
-
- Every deploy overwrites the website's current contents. Confirm the target domain with the user before the first deploy to a site that already has content.
|
|
14
|
+
- Every deploy overwrites the website's current contents. Confirm the target domain with the user before the first deploy to a site that already has content. A `.hostinger/site.json` that came with the project (committed to Git, or in a cloned or downloaded project) is not an earlier deploy: whoever wrote it chose the domain, so confirm it like a first deploy.
|
|
15
15
|
|
|
16
16
|
## 0. If a shell and the Hostinger CLI are available
|
|
17
17
|
|
|
@@ -29,7 +29,7 @@ Run it from the project directory: the link is written in the current directory,
|
|
|
29
29
|
|
|
30
30
|
## 1. Pick the target website
|
|
31
31
|
|
|
32
|
-
- `.hostinger/site.json` in the project: reuse its `domain` and `username` (or `website_uid`). The CLI's `hostinger up` and `hostinger link` write the same file and keep any fields you add while it names the same website (linking another website starts it afresh); its `type` may be `static`, `nodejs` or `php`.
|
|
32
|
+
- `.hostinger/site.json` in the project: reuse its `domain` and `username` (or `website_uid`) — only without asking when you or the user wrote it in an earlier deploy; if `git ls-files .hostinger/site.json` lists it, or the project was cloned or downloaded, name the domain and confirm it with the user first. The CLI's `hostinger up` and `hostinger link` write the same file and keep any fields you add while it names the same website (linking another website starts it afresh); its `type` may be `static`, `nodejs` or `php`.
|
|
33
33
|
- Otherwise look the domain up with `hosting_websites_list` (Shared and Cloud; substring match — take the exact entry) and `agency-hosting_websites_list-plan` (Agency; keep `details.uid`).
|
|
34
34
|
- No website yet:
|
|
35
35
|
- Shared and Cloud: take a domain (the user's own, or `hosting_domains_generate-free-subdomain`) and an `order_id` from `hosting_orders_list`, then `hosting_websites_create`. `datacenter_code` is needed only for the first website on a new plan (first entry of `hosting_datacenters_list`). Poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed` — uploads and deploys return 404 or 409 before that.
|
package/skills/headless/SKILL.md
CHANGED
|
@@ -141,6 +141,22 @@ written in English, sets the site's language (refine records it with the must-ha
|
|
|
141
141
|
stay in English. When the latest message gives nothing to go by (a "yes", a link, only pasted text),
|
|
142
142
|
keep the language you were using.
|
|
143
143
|
|
|
144
|
+
### One instance per headless site (the core invariant)
|
|
145
|
+
|
|
146
|
+
> **A headless site is exactly one website.** The frontend is served at the site root and WordPress
|
|
147
|
+
> lives at `/cms` **on the same domain** — one instance, one domain. There is **never** a second
|
|
148
|
+
> website for the same build and **never** a separate CMS domain: the CMS is always the `/cms` path of
|
|
149
|
+
> the site's own domain (`https://<domain>/cms`), not `cms.<domain>` or any other host. Provision
|
|
150
|
+
> therefore **adopts and swaps** an existing headless shell rather than creating another site, and a
|
|
151
|
+
> headless frontend is always deployed as a Node.js app (`type: "nodejs"`) — **never** `type: "static"`,
|
|
152
|
+
> which serves the build as plain files over the document root and overwrites `/cms`.
|
|
153
|
+
>
|
|
154
|
+
> The deleted legacy flow that put WordPress on a separate `cms.<domain>` subdomain with a
|
|
155
|
+
> `type:"static"` frontend on the main domain (two instances, recorded as `cms_domain` + `type:"static"`
|
|
156
|
+
> in `site.json`) is **gone and forbidden**. If you ever see `cms_domain` or `type:"static"` in a
|
|
157
|
+
> workspace's `site.json`, you are running a stale/legacy skill copy — stop and re-pull the latest skills
|
|
158
|
+
> (see `connect/SKILL.md`) before continuing.
|
|
159
|
+
|
|
144
160
|
### Background provision & the instance barrier
|
|
145
161
|
|
|
146
162
|
**Provision always runs — what it does depends on what the domain already has.** Often onboarding on
|
|
@@ -88,13 +88,24 @@ Credentials live in `~/.config/hostinger-mcp/credentials.json` (macOS/Linux) or
|
|
|
88
88
|
If the user's tool isn't listed yet, connect it the generic way (register the Hostinger MCP server
|
|
89
89
|
for that tool, or drive `npx --yes --package=@hostinger/mcp@latest hostinger-api-mcp` over stdio) and continue.
|
|
90
90
|
|
|
91
|
+
### Always pull the latest skills (never run a stale copy)
|
|
92
|
+
|
|
93
|
+
The tool route above downloads the `headless/` skill bundle. **Do this at the start of every run and let
|
|
94
|
+
it overwrite any existing local `headless/` folder** — a build must never run on a stale copy, because an
|
|
95
|
+
outdated local bundle is how a deleted, legacy flow (e.g. a separate CMS domain + a `type:"static"`
|
|
96
|
+
frontend — two instances) ends up executing. The bundle ships a `headless/VERSION` stamp
|
|
97
|
+
(`version=<semver>` on line 1, then `commit` and `built`). After pulling, read it; if you want to confirm
|
|
98
|
+
it is current, compare line 1 against the published marker at
|
|
99
|
+
`https://raw.githubusercontent.com/hostinger/api-mcp-server/main/skills/headless/VERSION` and re-pull if
|
|
100
|
+
they differ. Carry the version into `connect.json` (Phase 3) so the build report records which skills ran.
|
|
101
|
+
|
|
91
102
|
## Phase 3 - Write `connect.json`
|
|
92
103
|
|
|
93
104
|
Once the tool is connected, write `~/.hostinger/<domain>/connect.json` (create the folder if it is
|
|
94
105
|
missing) in exactly this shape:
|
|
95
106
|
|
|
96
107
|
```json
|
|
97
|
-
{ "tool": "claude-code", "mcp": "hostinger-api-mcp 3.8.0", "auth": "oauth_cached",
|
|
108
|
+
{ "tool": "claude-code", "mcp": "hostinger-api-mcp 3.8.0", "skills": "3.9.0", "auth": "oauth_cached",
|
|
98
109
|
"order_id": null, "plan": null, "connected_at": "2026-10-28T10:46:12Z" }
|
|
99
110
|
```
|
|
100
111
|
|
|
@@ -103,11 +114,13 @@ missing) in exactly this shape:
|
|
|
103
114
|
passed to `run-start --tool`.
|
|
104
115
|
- `mcp`: one string, `hostinger-api-mcp <version>`, with the `version` from the `mcp_ok` event.
|
|
105
116
|
Never an object.
|
|
117
|
+
- `skills`: the `version=` value from the `headless/VERSION` stamp you just pulled — the skills bundle
|
|
118
|
+
version this run used. `"unknown"` if the stamp is absent (an old bundle predating the stamp).
|
|
106
119
|
- `auth`: the `method` from the `logged_in` event (`api_token`, `oauth_cached` or `oauth`).
|
|
107
120
|
- `order_id`, `plan`: the active hosting plan's order id and name when you looked them up, else `null`.
|
|
108
121
|
- `connected_at`: the current time in UTC, ISO 8601 to the second.
|
|
109
122
|
|
|
110
|
-
Extra keys are allowed, but keep these
|
|
123
|
+
Extra keys are allowed, but keep these seven as above: the build report reads `tool`, `mcp` and `skills`.
|
|
111
124
|
|
|
112
125
|
## Phase 4 - Continue to Headless Hosting steps
|
|
113
126
|
|
|
@@ -41,9 +41,11 @@ quit and reopen Antigravity once so it loads:
|
|
|
41
41
|
|
|
42
42
|
## Step 2 — Install the skills and hand off
|
|
43
43
|
|
|
44
|
-
Download the Hostinger skills locally so your agent reads the files
|
|
45
|
-
|
|
46
|
-
`
|
|
44
|
+
Download the Hostinger skills locally **at the start of every run** so your agent then reads the files
|
|
45
|
+
directly with no further network calls during the run. **Always pull the latest and overwrite any
|
|
46
|
+
existing local `headless/` copy** so a run never uses a stale skill version. The repo is **public**, so
|
|
47
|
+
fetch just the skill folder over HTTPS — no git, no credentials (see `connect/tools/claude-code.md` for
|
|
48
|
+
the full tarball command):
|
|
47
49
|
|
|
48
50
|
```bash
|
|
49
51
|
# macOS/Linux — downloads only skills/headless from the public repo
|
|
@@ -51,4 +53,4 @@ curl -fsSL https://codeload.github.com/hostinger/api-mcp-server/tar.gz/refs/head
|
|
|
51
53
|
| tar -xz --strip-components=2 'api-mcp-server-main/skills/headless'
|
|
52
54
|
```
|
|
53
55
|
|
|
54
|
-
Then open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
56
|
+
Then read the `headless/VERSION` stamp (record it in `connect.json`), open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
@@ -26,7 +26,7 @@ process alive for the whole run, and use it wherever a stage calls the connector
|
|
|
26
26
|
|
|
27
27
|
## Step 2 — Install the skills and hand off
|
|
28
28
|
|
|
29
|
-
Download the Hostinger skills locally so your agent reads the files directly with no further network calls. The repo is **public**, so fetch just the skill folder over HTTPS — no git, no credentials:
|
|
29
|
+
Download the Hostinger skills locally **at the start of every run** so your agent then reads the files directly with no further network calls during the run. **Always pull the latest and let it overwrite any existing local `headless/` folder** — a build must never run on a stale skill copy (an outdated local copy is how a deleted, legacy flow ends up executing). The repo is **public**, so fetch just the skill folder over HTTPS — no git, no credentials:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
# macOS/Linux — downloads only skills/headless from the public repo
|
|
@@ -40,7 +40,7 @@ curl.exe -fsSL https://codeload.github.com/hostinger/api-mcp-server/tar.gz/refs/
|
|
|
40
40
|
tar -xf headless-skill.tar.gz --strip-components=2 'api-mcp-server-main/skills/headless'
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
This leaves a local `headless/` folder with the full skill set. If the tarball is ever blocked, browse the public folder at `https://github.com/hostinger/api-mcp-server/tree/main/skills/headless` and save each file under a local `headless/` directory, preserving the paths.
|
|
43
|
+
This leaves a local `headless/` folder with the full skill set. The bundle carries a `headless/VERSION` stamp (`version=<semver>`, commit, build date) — after pulling, read it and tell the user which skills version this run uses; it is the value to record in `connect.json`. If the tarball is ever blocked, browse the public folder at `https://github.com/hostinger/api-mcp-server/tree/main/skills/headless` and save each file under a local `headless/` directory, preserving the paths.
|
|
44
44
|
|
|
45
45
|
Then open `headless/SKILL.md` and follow the steps on it. That skill owns the rest of the run — it resolves the operations sequence.
|
|
46
46
|
|
|
@@ -35,9 +35,11 @@ args = ["-y", "@hostinger/mcp@latest"]
|
|
|
35
35
|
|
|
36
36
|
## Step 2 — Install the skills and hand off
|
|
37
37
|
|
|
38
|
-
Download the Hostinger skills locally so your agent reads the files
|
|
39
|
-
|
|
40
|
-
`
|
|
38
|
+
Download the Hostinger skills locally **at the start of every run** so your agent then reads the files
|
|
39
|
+
directly with no further network calls during the run. **Always pull the latest and overwrite any
|
|
40
|
+
existing local `headless/` copy** so a run never uses a stale skill version. The repo is **public**, so
|
|
41
|
+
fetch just the skill folder over HTTPS — no git, no credentials (see `connect/tools/claude-code.md` for
|
|
42
|
+
the full tarball command):
|
|
41
43
|
|
|
42
44
|
```bash
|
|
43
45
|
# macOS/Linux — downloads only skills/headless from the public repo
|
|
@@ -45,4 +47,4 @@ curl -fsSL https://codeload.github.com/hostinger/api-mcp-server/tar.gz/refs/head
|
|
|
45
47
|
| tar -xz --strip-components=2 'api-mcp-server-main/skills/headless'
|
|
46
48
|
```
|
|
47
49
|
|
|
48
|
-
Then open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
50
|
+
Then read the `headless/VERSION` stamp (record it in `connect.json`), open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
@@ -96,9 +96,11 @@ and speak newline-delimited JSON-RPC 2.0 over its stdin/stdout (`initialize` →
|
|
|
96
96
|
|
|
97
97
|
## Step 4 — Install the skills and hand off
|
|
98
98
|
|
|
99
|
-
Download the Hostinger skills locally so your agent reads the files
|
|
100
|
-
|
|
101
|
-
`
|
|
99
|
+
Download the Hostinger skills locally **at the start of every run** so your agent then reads the files
|
|
100
|
+
directly with no further network calls during the run. **Always pull the latest and overwrite any
|
|
101
|
+
existing local `headless/` copy** so a run never uses a stale skill version. The repo is **public**, so
|
|
102
|
+
fetch just the skill folder over HTTPS — no git, no credentials (see `connect/tools/claude-code.md` for
|
|
103
|
+
the full tarball command):
|
|
102
104
|
|
|
103
105
|
```bash
|
|
104
106
|
# macOS/Linux — downloads only skills/headless from the public repo
|
|
@@ -106,4 +108,4 @@ curl -fsSL https://codeload.github.com/hostinger/api-mcp-server/tar.gz/refs/head
|
|
|
106
108
|
| tar -xz --strip-components=2 'api-mcp-server-main/skills/headless'
|
|
107
109
|
```
|
|
108
110
|
|
|
109
|
-
Then open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
111
|
+
Then read the `headless/VERSION` stamp (record it in `connect.json`), open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
@@ -37,9 +37,11 @@ over stdio instead: spawn it and speak newline-delimited JSON-RPC 2.0 over its s
|
|
|
37
37
|
|
|
38
38
|
## Step 2 — Install the skills and hand off
|
|
39
39
|
|
|
40
|
-
Download the Hostinger skills locally so your agent reads the files
|
|
41
|
-
|
|
42
|
-
`
|
|
40
|
+
Download the Hostinger skills locally **at the start of every run** so your agent then reads the files
|
|
41
|
+
directly with no further network calls during the run. **Always pull the latest and overwrite any
|
|
42
|
+
existing local `headless/` copy** so a run never uses a stale skill version. The repo is **public**, so
|
|
43
|
+
fetch just the skill folder over HTTPS — no git, no credentials (see `connect/tools/claude-code.md` for
|
|
44
|
+
the full tarball command):
|
|
43
45
|
|
|
44
46
|
```bash
|
|
45
47
|
# macOS/Linux — downloads only skills/headless from the public repo
|
|
@@ -47,4 +49,4 @@ curl -fsSL https://codeload.github.com/hostinger/api-mcp-server/tar.gz/refs/head
|
|
|
47
49
|
| tar -xz --strip-components=2 'api-mcp-server-main/skills/headless'
|
|
48
50
|
```
|
|
49
51
|
|
|
50
|
-
Then open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
52
|
+
Then read the `headless/VERSION` stamp (record it in `connect.json`), open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
@@ -44,9 +44,11 @@ over stdio instead: spawn it and speak newline-delimited JSON-RPC 2.0 over its s
|
|
|
44
44
|
|
|
45
45
|
## Step 2 — Install the skills and hand off
|
|
46
46
|
|
|
47
|
-
Download the Hostinger skills locally so your agent reads the files
|
|
48
|
-
|
|
49
|
-
`
|
|
47
|
+
Download the Hostinger skills locally **at the start of every run** so your agent then reads the files
|
|
48
|
+
directly with no further network calls during the run. **Always pull the latest and overwrite any
|
|
49
|
+
existing local `headless/` copy** so a run never uses a stale skill version. The repo is **public**, so
|
|
50
|
+
fetch just the skill folder over HTTPS — no git, no credentials (see `connect/tools/claude-code.md` for
|
|
51
|
+
the full tarball command):
|
|
50
52
|
|
|
51
53
|
```bash
|
|
52
54
|
# macOS/Linux — downloads only skills/headless from the public repo
|
|
@@ -54,4 +56,4 @@ curl -fsSL https://codeload.github.com/hostinger/api-mcp-server/tar.gz/refs/head
|
|
|
54
56
|
| tar -xz --strip-components=2 'api-mcp-server-main/skills/headless'
|
|
55
57
|
```
|
|
56
58
|
|
|
57
|
-
Then open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
59
|
+
Then read the `headless/VERSION` stamp (record it in `connect.json`), open `headless/SKILL.md` and follow its steps. That skill owns the rest of the run.
|
|
@@ -129,7 +129,7 @@ function readConnect(ws) {
|
|
|
129
129
|
const mcp = connect.mcp ?? connect.mcp_version ?? null;
|
|
130
130
|
let version = mcp;
|
|
131
131
|
if (mcp !== null && typeof mcp !== 'string') version = mcp.name || mcp.version ? [mcp.name, mcp.version].filter(Boolean).join(' ') : JSON.stringify(mcp);
|
|
132
|
-
return { tool: typeof connect.tool === 'string' ? connect.tool : null, mcp: version };
|
|
132
|
+
return { tool: typeof connect.tool === 'string' ? connect.tool : null, mcp: version, skills: typeof connect.skills === 'string' ? connect.skills : null };
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
function runStart(ws, flags) {
|
|
@@ -348,6 +348,7 @@ function buildReport(ws, { outcome, tokens } = {}) {
|
|
|
348
348
|
os: process.platform,
|
|
349
349
|
node: process.version,
|
|
350
350
|
mcp: connect.mcp,
|
|
351
|
+
skills: connect.skills,
|
|
351
352
|
backend: site.backend ?? null,
|
|
352
353
|
frontend: frontendStack(state.frontend ?? {}, ws.dir),
|
|
353
354
|
outcome: resolvedOutcome,
|
|
@@ -77,6 +77,13 @@ So provision has **two entry shapes**, and each subfolder's *detect* step picks
|
|
|
77
77
|
> flow — it is only triggered by experimental actions — so it **ignores headless instances entirely**:
|
|
78
78
|
> it never detects, adopts or swaps an empty headless shell, and provisions its own **Node.js app host**
|
|
79
79
|
> exactly as `webapp/SKILL.md` defines today. The swap below is **WordPress and Ecommerce only**.
|
|
80
|
+
>
|
|
81
|
+
> **But webapp must still never create a second instance for this build.** Only route here when the
|
|
82
|
+
> brief's backend is `webapp` — a `wordpress` or `ecommerce` brief is never provisioned as a webapp. And
|
|
83
|
+
> if this build's domain already has a headless shell/site (an onboarding `headless_empty`, or any row
|
|
84
|
+
> from the single-instance gate below), **do not** spin up a separate Node.js host on another domain:
|
|
85
|
+
> stop and tell the user the domain already has a headless instance, rather than leaving the build split
|
|
86
|
+
> across two sites.
|
|
80
87
|
|
|
81
88
|
**How to tell an empty shell from an already-provisioned backend** (both read `website_type: headless`):
|
|
82
89
|
|
|
@@ -96,6 +103,20 @@ asynchronous — fire it, record `provision.in_progress`, and join at the barrie
|
|
|
96
103
|
|
|
97
104
|
## Step 2 — Fire (don't block)
|
|
98
105
|
|
|
106
|
+
> **Single-instance gate — pass all four before you create anything:**
|
|
107
|
+
> 1. **Exact-match the domain** in `hosting_websites_list` (substring filter — match the exact `domain`).
|
|
108
|
+
> 2. **A `headless` row already exists → adopt + swap it, never create.** Install the backend into that
|
|
109
|
+
> same site (WordPress → `wordpress_installations_install { type: "headless" }` into `/cms`; Ecommerce
|
|
110
|
+
> → adopt as the storefront host). **Do not call `start-setup` again** — that makes a second site.
|
|
111
|
+
> 3. **No row at all → create one fresh** with the backend's own `start-setup` type. Create it for **this
|
|
112
|
+
> build's domain only** — never provision the backend on a different domain than the frontend (the CMS
|
|
113
|
+
> is the `/cms` path of the same site, never a separate `cms.<domain>`).
|
|
114
|
+
> 4. **A row of a different kind** (`website_type` not `headless`) → **stop and tell the user**; never
|
|
115
|
+
> overwrite it.
|
|
116
|
+
>
|
|
117
|
+
> This is the whole of *Onboarding handoff* above, as one checklist. See it for how to tell an empty
|
|
118
|
+
> shell from an already-provisioned backend.
|
|
119
|
+
|
|
99
120
|
Follow the chosen subfolder's `SKILL.md` through **detect + create** (its "start-setup" call). The
|
|
100
121
|
moment the create is accepted and queued, record it in `state.json` and **return** — do not sit on the
|
|
101
122
|
readiness poll:
|
|
@@ -31,7 +31,13 @@ All website/database operations below go through the Hostinger connector (`execu
|
|
|
31
31
|
- `running` → skip to Step 2 (poll).
|
|
32
32
|
- `failed` → tell the user and point them at hPanel; do **not** call start-setup again on your own.
|
|
33
33
|
3. If the site exists but is a different kind (`website_type` is `headless`, or a WordPress/store site),
|
|
34
|
-
stop and tell the user — this domain already hosts something else.
|
|
34
|
+
stop and tell the user — this domain already hosts something else. **Never create a second site.**
|
|
35
|
+
4. **Only provision a webapp for a `webapp` brief.** A `wordpress` or `ecommerce` brief belongs in those
|
|
36
|
+
subfolders (installed into one headless instance's `/cms`) — never spin up a separate Node.js host for
|
|
37
|
+
it. And if this build already has a headless shell/site (a recorded `site.json → website_type:
|
|
38
|
+
"headless"`, or a `headless`/`headless_empty` row for the project's domain), do **not** create a new
|
|
39
|
+
website on another domain: stop and tell the user, so the build never ends up split across two
|
|
40
|
+
instances.
|
|
35
41
|
|
|
36
42
|
## Step 2 — Create (only when nothing exists)
|
|
37
43
|
|
|
@@ -124,7 +124,8 @@ build output must already be absent).
|
|
|
124
124
|
|
|
125
125
|
`hosting_deploy` uploads the source and triggers `npm install` + the build **end-to-end** — do not call
|
|
126
126
|
any upload or build operation yourself. It returns a build `buildUuid` at once. This **overwrites the
|
|
127
|
-
website root** (expected) and keeps `/cms` intact.
|
|
127
|
+
website root** (expected) and keeps `/cms` intact. `type` is always `"nodejs"` for a headless site —
|
|
128
|
+
**never `"static"`** (see the gate below). Passing `appType`/`outputDirectory` explicitly is what
|
|
128
129
|
keeps the root from serving 403 (see the rule above).
|
|
129
130
|
|
|
130
131
|
## Step 4 — Poll to completion
|
|
@@ -199,10 +200,17 @@ Update `state.json` (`stage` → `done`), then continue to Step 9 (done).
|
|
|
199
200
|
|
|
200
201
|
---
|
|
201
202
|
|
|
202
|
-
##
|
|
203
|
+
## Gate — a headless deploy is always `type: "nodejs"`, never `type: "static"`
|
|
204
|
+
|
|
205
|
+
**Hard rule, check it before every deploy:** a headless site is deployed **only** with
|
|
206
|
+
`type: "nodejs"` plus the explicit `appType`/`outputDirectory`/`nodeVersion`/`buildScript`/
|
|
207
|
+
`packageManager`/`rootDirectory` from the table. **Never pass `type: "static"` on a headless site** — it
|
|
208
|
+
serves the build as plain files over the document root and **overwrites `/cms`**, destroying the
|
|
209
|
+
WordPress backend (this is the "accidentally cleared cms" failure). A static-built SPA (Vite/React/Vue)
|
|
210
|
+
still deploys as `type: "nodejs"` with its `appType` — the framework's static output is served correctly
|
|
211
|
+
that way, and `/cms` is preserved. If a flow ever tells you to deploy a headless site as `type:
|
|
212
|
+
"static"`, you are on a stale/legacy skill copy — stop and re-pull the latest skills.
|
|
203
213
|
|
|
204
214
|
`hosting_deploy` on the local server is the only path: give its `path` either the source **directory** (it archives it for
|
|
205
215
|
you) or a local **archive** (`zip`, `tar`, `tar.gz`, `tgz`, `7z`) — there is no separate upload or build
|
|
206
|
-
step to fall back to.
|
|
207
|
-
`buildScript`/`packageManager`/`rootDirectory` from the table. Never deploy a headless site with
|
|
208
|
-
`type: "static"` — it serves the directory as plain files and can overwrite `/cms`.
|
|
216
|
+
step to fall back to.
|
package/src/core/runtime.js
CHANGED
|
@@ -385,9 +385,10 @@ class MCPServer {
|
|
|
385
385
|
const homes = path.dirname(home);
|
|
386
386
|
const otherHome = !inHome && path.dirname(homes) !== homes && inside(homes, real);
|
|
387
387
|
// Hidden directories at the top of the home (dotfiles, tool state) and the per-user app data (Library,
|
|
388
|
-
// AppData
|
|
388
|
+
// AppData, and ~/snap where Ubuntu's snap packages such as Firefox and Chromium keep their profiles) hold keychains,
|
|
389
|
+
// browser profiles and tokens; .claude-worktrees holds Claude Code's git worktrees.
|
|
389
390
|
const top = path.relative(fold(home), fold(real)).split(path.sep)[0];
|
|
390
|
-
const inHomeData = inHome && top !== '.claude-worktrees' && (top.startsWith('.') || top === fold('Library') || top === fold('AppData'));
|
|
391
|
+
const inHomeData = inHome && top !== '.claude-worktrees' && (top.startsWith('.') || top === fold('Library') || top === fold('AppData') || top === 'snap');
|
|
391
392
|
const inSystem = !inHome && this.systemDirectories.some((dir) => inside(dir, real));
|
|
392
393
|
// Windows drops trailing dots and spaces from a name: .ssh. is .ssh.
|
|
393
394
|
const inCredentials = real.split(path.sep).some((segment) => this.credentialDirectories.includes((process.platform === 'win32' ? segment.replace(/[. ]+$/, '') : segment).toLowerCase()));
|
package/src/core/runtime.ts
CHANGED
|
@@ -417,9 +417,10 @@ class MCPServer {
|
|
|
417
417
|
const homes = path.dirname(home);
|
|
418
418
|
const otherHome = !inHome && path.dirname(homes) !== homes && inside(homes, real);
|
|
419
419
|
// Hidden directories at the top of the home (dotfiles, tool state) and the per-user app data (Library,
|
|
420
|
-
// AppData
|
|
420
|
+
// AppData, and ~/snap where Ubuntu's snap packages such as Firefox and Chromium keep their profiles) hold keychains,
|
|
421
|
+
// browser profiles and tokens; .claude-worktrees holds Claude Code's git worktrees.
|
|
421
422
|
const top = path.relative(fold(home), fold(real)).split(path.sep)[0];
|
|
422
|
-
const inHomeData = inHome && top !== '.claude-worktrees' && (top.startsWith('.') || top === fold('Library') || top === fold('AppData'));
|
|
423
|
+
const inHomeData = inHome && top !== '.claude-worktrees' && (top.startsWith('.') || top === fold('Library') || top === fold('AppData') || top === 'snap');
|
|
423
424
|
const inSystem = !inHome && this.systemDirectories.some((dir: string) => inside(dir, real));
|
|
424
425
|
// Windows drops trailing dots and spaces from a name: .ssh. is .ssh.
|
|
425
426
|
const inCredentials = real.split(path.sep).some((segment: string) => this.credentialDirectories.includes((process.platform === 'win32' ? segment.replace(/[. ]+$/, '') : segment).toLowerCase()));
|