hostinger-api-mcp 3.14.1 → 3.15.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 +14 -0
- package/README.md +9 -2
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/headless/steps/01-check-state/SKILL.md +3 -3
- package/src/core/runtime.js +50 -0
- package/src/core/runtime.ts +50 -0
- package/src/core/skills.js +3 -3
- package/src/core/skills.ts +3 -3
- package/src/core/tools/all.js +70 -0
- package/src/core/tools/all.ts +70 -0
- package/src/core/tools/hosting.js +70 -0
- package/src/core/tools/hosting.ts +70 -0
- 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/types.d.ts +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ 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.15.0 — 2026-10-27
|
|
8
|
+
|
|
9
|
+
### Operations
|
|
10
|
+
|
|
11
|
+
- Added `hosting_websites_trigger-event` — Trigger website event
|
|
12
|
+
|
|
13
|
+
### Also in this release
|
|
14
|
+
|
|
15
|
+
- Documentation: `README.md`
|
|
16
|
+
- Bundled agent skills: `skills/headless/steps/01-check-state/SKILL.md`
|
|
17
|
+
- Request handling and authentication: `src/core/runtime.js`, `src/core/runtime.ts`
|
|
18
|
+
- Other files: `src/core/skills.js`, `src/core/skills.ts`
|
|
19
|
+
- Build and packaging: `types.d.ts`
|
|
20
|
+
|
|
7
21
|
## v3.14.1 — 2026-10-27
|
|
8
22
|
|
|
9
23
|
### Operations
|
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ pnpm update -g @hostinger/mcp
|
|
|
69
69
|
|
|
70
70
|
This package installs the following MCP server commands:
|
|
71
71
|
|
|
72
|
-
- `hostinger-api-mcp` — unified server over every operation (
|
|
72
|
+
- `hostinger-api-mcp` — unified server over every operation (418 total)
|
|
73
73
|
- `hostinger-agency-hosting-mcp` — 42 operations for agency-hosting
|
|
74
74
|
- `hostinger-agent-readiness-mcp` — 3 operations for agent-readiness
|
|
75
75
|
- `hostinger-billing-mcp` — 9 operations for billing
|
|
@@ -77,7 +77,7 @@ This package installs the following MCP server commands:
|
|
|
77
77
|
- `hostinger-domains-mcp` — 42 operations for domains
|
|
78
78
|
- `hostinger-ecommerce-mcp` — 29 operations for ecommerce
|
|
79
79
|
- `hostinger-horizons-mcp` — 6 operations for horizons
|
|
80
|
-
- `hostinger-hosting-mcp` —
|
|
80
|
+
- `hostinger-hosting-mcp` — 68 operations for hosting
|
|
81
81
|
- `hostinger-mail-mcp` — 38 operations for mail
|
|
82
82
|
- `hostinger-reach-mcp` — 69 operations for reach
|
|
83
83
|
- `hostinger-vps-mcp` — 67 operations for vps
|
|
@@ -1828,6 +1828,13 @@ Check deployment status, build status, build logs and why a build failed (failur
|
|
|
1828
1828
|
- **Method**: `custom`
|
|
1829
1829
|
- **Path**: `custom`
|
|
1830
1830
|
|
|
1831
|
+
#### hosting_websites_trigger-event
|
|
1832
|
+
|
|
1833
|
+
Tell Hostinger that something happened on this website, so Hostinger services such as hPanel can react to it. `event.name` picks the event and decides which `event.params` it takes. The call changes nothing on the website.
|
|
1834
|
+
|
|
1835
|
+
- **Method**: `custom`
|
|
1836
|
+
- **Path**: `custom`
|
|
1837
|
+
|
|
1831
1838
|
#### hosting_cache_clear-website
|
|
1832
1839
|
|
|
1833
1840
|
Permanently clears all server-side cache for the website at once. Use it when content was
|
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.
|
|
9
|
+
"version": "3.15.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@hostinger/mcp",
|
|
14
|
-
"version": "3.
|
|
14
|
+
"version": "3.15.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -12,7 +12,7 @@ account can build. Your job here is to
|
|
|
12
12
|
This step owns all resume/position logic — no other stage second-guesses it.
|
|
13
13
|
|
|
14
14
|
Nothing in this step creates, provisions, deploys, or deletes anything. It reads and guides. Its
|
|
15
|
-
only writes to Hostinger are
|
|
15
|
+
only writes to Hostinger are unsent build reports (Step 0) and the tool-connected event (Step 3).
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -113,11 +113,11 @@ Query the Hostinger connector — the local files are never trusted on their own
|
|
|
113
113
|
(case-insensitive) and ignore the rest. No exact row ⇒ the domain is free. An exact row ⇒ the site
|
|
114
114
|
exists; note its `website_type` (`headless`, `wordpress`, `nodejs`, …) and `username`.
|
|
115
115
|
- **Tell hPanel the tool connected.** As soon as you have the exact row, call
|
|
116
|
-
`
|
|
116
|
+
`hosting_websites_trigger-event { "username": "<row username>", "domain": "<domain>", "event": { "name": "headless_onboarding.tool.connected", "params": { "tool": "<slug>" } } }`
|
|
117
117
|
once, where `<slug>` is the onboarding slug (see *State and artifacts* in the top-level `SKILL.md`).
|
|
118
118
|
This flips hPanel's onboarding card to "connected". Don't ask first and don't mention it. Skip the
|
|
119
119
|
call when there's no exact row or your tool has no onboarding slug. If
|
|
120
|
-
`
|
|
120
|
+
`hosting_websites_trigger-event` isn't in your connector, or the call returns an error,
|
|
121
121
|
ignore it: no retry, nothing said to the user, and the run carries on unchanged.
|
|
122
122
|
- **How far did it get?** If the site exists, check its setup via
|
|
123
123
|
`hosting_websites_list-setups { "domain": "<domain>" }` (exact-match the row the same way):
|
package/src/core/runtime.js
CHANGED
|
@@ -316,6 +316,8 @@ class MCPServer {
|
|
|
316
316
|
return await this.handleListJavascriptDeployments(params);
|
|
317
317
|
case 'hosting_show-js-deployment-logs':
|
|
318
318
|
return await this.handleShowJsDeploymentLogs(params);
|
|
319
|
+
case 'hosting_websites_trigger-event':
|
|
320
|
+
return await this.handleHostingTriggerWebsiteEvent(params);
|
|
319
321
|
case 'agency-hosting_deploy-node-static-website':
|
|
320
322
|
return await this.handleNodeStaticDeploy(params);
|
|
321
323
|
case 'agency-hosting_deploy-php-application':
|
|
@@ -2782,6 +2784,54 @@ class MCPServer {
|
|
|
2782
2784
|
};
|
|
2783
2785
|
}
|
|
2784
2786
|
|
|
2787
|
+
async handleHostingTriggerWebsiteEvent(params) {
|
|
2788
|
+
const { username, domain, event } = params;
|
|
2789
|
+
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
2790
|
+
const url = new URL(`api/hosting/v1/accounts/${encodeURIComponent(String(username))}/websites/${encodeURIComponent(String(domain))}/events/trigger`, baseUrl).toString();
|
|
2791
|
+
|
|
2792
|
+
try {
|
|
2793
|
+
const bearerToken = await this.getAuthToken();
|
|
2794
|
+
|
|
2795
|
+
const config = {
|
|
2796
|
+
method: 'post',
|
|
2797
|
+
url,
|
|
2798
|
+
headers: {
|
|
2799
|
+
...this.headers,
|
|
2800
|
+
'Authorization': `Bearer ${bearerToken}`,
|
|
2801
|
+
'Content-Type': 'application/json'
|
|
2802
|
+
},
|
|
2803
|
+
data: { event },
|
|
2804
|
+
timeout: 60000, // 60s
|
|
2805
|
+
validateStatus: function (status) {
|
|
2806
|
+
return status < 500;
|
|
2807
|
+
}
|
|
2808
|
+
};
|
|
2809
|
+
|
|
2810
|
+
const response = await axios(config);
|
|
2811
|
+
|
|
2812
|
+
if (response.status < 200 || response.status >= 300) {
|
|
2813
|
+
throw new ApiExecutionError(response.status, response.data);
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
return response.data;
|
|
2817
|
+
|
|
2818
|
+
} catch (error) {
|
|
2819
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2820
|
+
this.log('error', `Failed to trigger website event: ${errorMessage}`);
|
|
2821
|
+
|
|
2822
|
+
if (axios.isAxiosError(error)) {
|
|
2823
|
+
const responseData = error.response?.data;
|
|
2824
|
+
const responseStatus = error.response?.status;
|
|
2825
|
+
this.log('error', 'API Error Details:', {
|
|
2826
|
+
status: responseStatus,
|
|
2827
|
+
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
throw error;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2785
2835
|
agencyHosting_normalizePath(pathString) {
|
|
2786
2836
|
return pathString.replace(/\\/g, '/');
|
|
2787
2837
|
}
|
package/src/core/runtime.ts
CHANGED
|
@@ -347,6 +347,8 @@ class MCPServer {
|
|
|
347
347
|
return await this.handleListJavascriptDeployments(params);
|
|
348
348
|
case 'hosting_show-js-deployment-logs':
|
|
349
349
|
return await this.handleShowJsDeploymentLogs(params);
|
|
350
|
+
case 'hosting_websites_trigger-event':
|
|
351
|
+
return await this.handleHostingTriggerWebsiteEvent(params);
|
|
350
352
|
case 'agency-hosting_deploy-node-static-website':
|
|
351
353
|
return await this.handleNodeStaticDeploy(params);
|
|
352
354
|
case 'agency-hosting_deploy-php-application':
|
|
@@ -2819,6 +2821,54 @@ class MCPServer {
|
|
|
2819
2821
|
};
|
|
2820
2822
|
}
|
|
2821
2823
|
|
|
2824
|
+
private async handleHostingTriggerWebsiteEvent(params: Record<string, unknown>): Promise<unknown> {
|
|
2825
|
+
const { username, domain, event } = params;
|
|
2826
|
+
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
2827
|
+
const url = new URL(`api/hosting/v1/accounts/${encodeURIComponent(String(username))}/websites/${encodeURIComponent(String(domain))}/events/trigger`, baseUrl).toString();
|
|
2828
|
+
|
|
2829
|
+
try {
|
|
2830
|
+
const bearerToken = await this.getAuthToken();
|
|
2831
|
+
|
|
2832
|
+
const config: AxiosRequestConfig = {
|
|
2833
|
+
method: 'post',
|
|
2834
|
+
url,
|
|
2835
|
+
headers: {
|
|
2836
|
+
...this.headers,
|
|
2837
|
+
'Authorization': `Bearer ${bearerToken}`,
|
|
2838
|
+
'Content-Type': 'application/json'
|
|
2839
|
+
},
|
|
2840
|
+
data: { event },
|
|
2841
|
+
timeout: 60000, // 60s
|
|
2842
|
+
validateStatus: function (status: number): boolean {
|
|
2843
|
+
return status < 500;
|
|
2844
|
+
}
|
|
2845
|
+
};
|
|
2846
|
+
|
|
2847
|
+
const response = await axios(config);
|
|
2848
|
+
|
|
2849
|
+
if (response.status < 200 || response.status >= 300) {
|
|
2850
|
+
throw new ApiExecutionError(response.status, response.data);
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
return response.data;
|
|
2854
|
+
|
|
2855
|
+
} catch (error) {
|
|
2856
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2857
|
+
this.log('error', `Failed to trigger website event: ${errorMessage}`);
|
|
2858
|
+
|
|
2859
|
+
if (axios.isAxiosError(error)) {
|
|
2860
|
+
const responseData = error.response?.data;
|
|
2861
|
+
const responseStatus = error.response?.status;
|
|
2862
|
+
this.log('error', 'API Error Details:', {
|
|
2863
|
+
status: responseStatus,
|
|
2864
|
+
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
throw error;
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2822
2872
|
private agencyHosting_normalizePath(pathString: string): string {
|
|
2823
2873
|
return pathString.replace(/\\/g, '/');
|
|
2824
2874
|
}
|
package/src/core/skills.js
CHANGED
|
@@ -134,10 +134,10 @@ export default [
|
|
|
134
134
|
},
|
|
135
135
|
{
|
|
136
136
|
"uri": "skill://hostinger-headless/steps/01-check-state/SKILL.md",
|
|
137
|
-
"digest": "sha256:
|
|
138
|
-
"size":
|
|
137
|
+
"digest": "sha256:043f25855abf92f43728b6e442bbd8d580fd2a6bb0270cfa7ed95dd41050ee60",
|
|
138
|
+
"size": 13227,
|
|
139
139
|
"mimeType": "text/markdown",
|
|
140
|
-
"text": "---\nname: headless-step-check-state\ndescription: \"First build step of the Hostinger Headless flow, run right after connect. Works out where a given domain's build stands — is a run already in progress, does the site already exist, how far did it get — by reconciling the global workspace state under ~/.hostinger/<domain>/ with what the Hostinger connector reports, then guides the user to continue, switch or buy a domain, or start over. Owns all resume/position logic for the flow.\"\n---\n\n# headless-step-check-state\n\nThe first build step of the Hostinger Headless flow (orchestrated by `hostinger.com/headless/skill.md`),\nrun right after connect. Connect has already connected the Hostinger connector and confirmed the\naccount can build. Your job here is to\n**figure out where this domain stands and agree the route with the user** before any new work begins.\nThis step owns all resume/position logic — no other stage second-guesses it.\n\nNothing in this step creates, provisions, deploys, or deletes anything. It reads and guides. Its\nonly writes to Hostinger are reports: unsent build reports (Step 0) and the connected tool (Step 3).\n\n---\n\n## Where state lives\n\nEverything for a domain lives under the global site workspace **`~/.hostinger/<domain>/`** — one\ndirectory per domain, under the user's home (`~` = `$HOME`), **not** the current working directory.\nThe directory name **is** the domain, so `state.json` inside does not store it. Because it's global,\nthis domain's build is resumable from any directory.\n\n`state.json` shape (`stage` is the *next* stage to run; `provision` is the background task's status,\npresent only once provision has fired):\n\n```json\n{ \"tool\": \"claude-code\", \"stage\": \"content_model\",\n \"provision\": { \"status\": \"in_progress\", \"domain\": \"<domain>\", \"started_at\": \"…\" },\n \"updated_at\": \"…\" }\n```\n\n`provision.status` is `in_progress` or `completed` — but it is a **hint**: always re-verify it against\n`hosting_websites_list-setups` (Step 1) before trusting it to gate an instance-barrier step.\n\n---\n\n## Step 0 — Start the run log\n\nBefore anything else, start a run and the stage (see *Run log and build report* in the top-level\n`SKILL.md`):\n\n```bash\nnode <skill>/run-log.mjs <domain> run-start --tool <tool> --model <model> --skill <skill version>\nnode <skill>/run-log.mjs <domain> stage-start check_state\n```\n\nIf an earlier run on this domain never wrote its report, `run-start` closes it as `abandoned` first.\n\nThen send any build reports that haven't reached Hostinger yet (that abandoned run, or a send that\nfailed last time):\n\n```bash\nnode <skill>/run-log.mjs <domain> unsent\n```\n\nEach line is `<run> <path>`. For each, send the file through `hosting_headless_submit-build-report`\nand record the result, as described in *Run log and build report* in the top-level `SKILL.md`. One\ntry each. Don't ask first; if any went out, say so in one line (e.g. \"Sent 1 earlier build report to\nHostinger.\"). Don't let a failure hold up this step.\nCheck-state runs once per session. A follow-up after the final report, in the same session, doesn't\ncome back here: it calls `run-resume` (see *Run log and build report* in the top-level `SKILL.md`).\n\n## Step 1 — Is a build actively running right now? (check this before anything else)\n\nKeep two things separate, and **never guess from a timestamp.**\n\n- **A platform build actively in progress** — the only *reliable* \"running now\" signal. Check\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` and exact-match the domain row:\n - `status: running` → provisioning is underway **right now** (a background provision from an earlier\n turn). Don't start a second one — reconcile `state.json`'s `provision` to `in_progress`, and\n either resume prepare work while it finishes or poll it to `completed` at the barrier.\n - `status: failed` → not running; route to provision to self-heal (Step 3).\n - `completed` → provision is done; set `provision.status` to `completed`. Instance-barrier steps\n (content push, frontend data, deploy) are now unblocked.\n - no row → not provisioned yet; carry on.\n\n This check **is** the source of truth for `provision.status`: a stored `in_progress`/`completed` in\n `state.json` is only a hint until this reconciles it.\n\n- **A `.lock`** — `~/.hostinger/<domain>/.lock`. It only tells you *a previous run\n touched this domain*. **Do not judge it by age** — a 2-minute-old lock and a 3-year-old lock get the\n same treatment, and a lock on its own is **not** a reason to interrupt the user. Carry it into Step 5:\n if there's nothing to resume (new build), silently take it over; only if there's real state to resume\n do you also ask *is another Claude window building this domain right now?* (yes → stop; no → take\n over).\n\nOnce you're actively working this domain, **acquire/refresh the lock** (write the current timestamp),\nand **release it on a clean stop** (Step 6). The lock marks an active session, nothing more.\n\n---\n\n## Step 2 — Read the workspace directory\n\nCheck `~/.hostinger/<domain>/` by its exact path (for example `test -d ~/.hostinger/<domain>`), not\nby listing `~/.hostinger/`:\n\n- **Absent** → no history for this domain in the workspace.\n- **Present** → read `state.json` (its `stage`) and whatever artifacts exist (`brief.md`,\n `site.json`, …). Treat these as a hint, not the truth.\n\n---\n\n## Step 3 — Verify against reality (reality wins)\n\nQuery the Hostinger connector — the local files are never trusted on their own:\n\n- **Does the site exist?** `hosting_websites_list { \"domain\": \"<domain>\" }`. ⚠️ This filter is a\n **case-insensitive substring match**, not exact — so `test.com` would also return\n `shop.test.com`. **Compare each returned row's `domain` to your target for an *exact* match**\n (case-insensitive) and ignore the rest. No exact row ⇒ the domain is free. An exact row ⇒ the site\n exists; note its `website_type` (`headless`, `wordpress`, `nodejs`, …) and `username`.\n- **Tell hPanel the tool connected.** As soon as you have the exact row, call\n `hosting_headless_report-tool-connected { \"username\": \"<row username>\", \"domain\": \"<domain>\", \"tool\": \"<slug>\" }`\n once, where `<slug>` is the onboarding slug (see *State and artifacts* in the top-level `SKILL.md`).\n This flips hPanel's onboarding card to \"connected\". Don't ask first and don't mention it. Skip the\n call when there's no exact row or your tool has no onboarding slug. If\n `hosting_headless_report-tool-connected` isn't in your connector, or the call returns an error,\n ignore it: no retry, nothing said to the user, and the run carries on unchanged.\n- **How far did it get?** If the site exists, check its setup via\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` (exact-match the row the same way):\n - `status: completed` → provisioned; then, depending on backend, check whether a build has succeeded.\n - `status: running` → still provisioning (see Step 1).\n - `status: failed` → provisioning stopped before finishing → route to **provision** to self-heal.\n- **Is it an empty headless shell from onboarding?** A `headless` site whose setup is `completed` is\n **not necessarily finished** — onboarding now creates the website *before* the build runs, as an\n **empty headless shell** (`hosting_websites_start-setup { type: \"headless_empty\" }`): `website_type`\n is `headless` but **no backend is installed** (no WordPress at `/cms`, no store). Tell a shell from a\n real backend the same way provision does: within 24 h the setup `type` is `headless_empty`; otherwise\n `wordpress_installations_list { username, domain, ownership: \"all\" }` returns **no** `/cms` row and\n there is no `site.json` recording a backend. An empty shell means **provision has not chosen or\n installed a backend yet** — this is a **new build** that happens to already own its website, not a\n finished or resumable one. Route it to **refine** (choose the backend), then **provision** adopts the\n shell and swaps it to that backend (see the provision router's *Onboarding handoff*). Never create a\n second site, and never skip provision for it.\n\nReconcile with the local state. **When they disagree, reality wins** and the local `stage` is\ncorrected.\n\n---\n\n## Step 4 — Determine the position\n\nConnect always writes `state.json` (with `stage: check_state`) before this step runs, so local\nstate is never truly empty. **\"Local state: none\" here means only connect artifacts exist** —\n`connect.json` + a `state.json` still pointing at `check_state`/`refine`, and **no build artifacts\nyet** (`brief.md`, `site.json`, …).\n\n| Local state | Reality (domain) | Position |\n|---|---|---|\n| none (connect only) | free | **new build** — start from refine |\n| none (connect only) | empty headless shell (onboarding) | **new build on an existing shell** — start from refine; provision adopts the shell and swaps it to the chosen backend. Never create a second site, never skip provision |\n| none (connect only) | site exists with a real backend | **adopt / resume** — never create a second site; route to the first unfinished stage |\n| build artifacts present, incomplete | matches | **resume** at `state.json`'s `stage` |\n| build artifacts present | disagrees | **reality wins** — recompute the stage from what actually exists |\n| build artifacts present, all done | live site exists | **finished** — this is now an *iterate* request, not a build |\n\n---\n\n## Step 5 — Guide the user\n\nThe question to ask depends on whether there is actually **something to resume** — i.e. **build\nartifacts** (`brief.md`, `site.json`, …) or a **live site** for this domain. A stray `.lock` or\nconnect-only files are *not* something to resume.\n\n- **New build** (no build artifacts and either the domain is free **or** it holds only an empty\n headless shell from onboarding) → just proceed to refine. **Don't ask resume vs start over** —\n there's nothing to resume; both would land in the same place. An empty headless shell is not\n \"something to resume\": it has no backend and no content yet, so it's a fresh build that already owns\n its website — provision will adopt and swap it. Mention it in one line (\"Using the headless site\n onboarding already created for this domain.\"). If a leftover `.lock` is lying around, silently take\n it over and continue (optionally mention it in one line).\n- **Something to resume** (build artifacts exist, or a site exists for the domain **with a real backend\n installed** — WordPress at `/cms`, a store, or a deployed app) → tell the user plainly what's there\n and **ask them to choose**, regardless of how old it is (don't guess from timestamps):\n\n- **Resume** — pick up at the computed stage. (If a `.lock` is present in this case, also confirm no\n other window is actively building this domain right now.)\n- **Start over / redo a step** — only safe **before anything is provisioned**. If the site is already\n provisioned or live, you **cannot** re-create it: that is forward-only *iterate* (change content or\n design and redeploy), not a fresh build. Never re-provision or overwrite a live site; if the user\n insists on a clean slate for a live domain, explain the limitation and point them at hPanel.\n- **Switch domain** — they want a different domain. A new domain is a **new workspace**\n (`~/.hostinger/<other-domain>/`, with its own `src/`). Hand off to start that domain's run.\n- **Buy a domain** — they want a custom domain instead of the temporary one. Route to the domain\n purchase / `connect-domain` flow, then continue with the chosen domain.\n\nWhen the intent is ambiguous, **ask** — don't guess. A skipped tool result is ambiguous; see\n*When you ask the user* in the top-level `SKILL.md`.\n\n---\n\n## Step 6 — Record and route\n\nWrite `~/.hostinger/<domain>/state.json` with the agreed next `stage` and a fresh timestamp. If the\nroute isn't a new build, record it with `run-log.mjs <domain> run-update --kind resume` (or\n`redesign` / `iterate`), then end the stage with `stage-end check_state ok --note \"<the route>\"`.\nThen:\n\n- **Handing straight to the next stage in this same session?** Keep the `.lock` (you are still\n actively running) and continue.\n- **Stopping and returning control to the user?** (e.g. the next stage isn't built yet, or you are\n waiting on them.) **Delete the `.lock`** — the session is no longer active, and a leftover lock just\n forces an unnecessary \"is another window running?\" question on the next run. Always release the lock\n on a clean stop; it exists only to mark an actively-running session. A stop is still the end of a\n run: give the final report and save the build report (`09-done`).\n\n---\n\n## Invariants\n\n- **One directory per domain.** The domain is the key; state files never repeat it.\n- **Only this domain's workspace.** Never list `~/.hostinger/` or open another site's workspace (see\n *State and artifacts* in the top-level `SKILL.md`).\n- **Never create a second site** on a domain that already has one.\n- **Never re-provision or overwrite a live site.** Recovery is forward-only.\n- **Reality beats local state**, always.\n- **When ambiguous, ask.**\n"
|
|
140
|
+
"text": "---\nname: headless-step-check-state\ndescription: \"First build step of the Hostinger Headless flow, run right after connect. Works out where a given domain's build stands — is a run already in progress, does the site already exist, how far did it get — by reconciling the global workspace state under ~/.hostinger/<domain>/ with what the Hostinger connector reports, then guides the user to continue, switch or buy a domain, or start over. Owns all resume/position logic for the flow.\"\n---\n\n# headless-step-check-state\n\nThe first build step of the Hostinger Headless flow (orchestrated by `hostinger.com/headless/skill.md`),\nrun right after connect. Connect has already connected the Hostinger connector and confirmed the\naccount can build. Your job here is to\n**figure out where this domain stands and agree the route with the user** before any new work begins.\nThis step owns all resume/position logic — no other stage second-guesses it.\n\nNothing in this step creates, provisions, deploys, or deletes anything. It reads and guides. Its\nonly writes to Hostinger are unsent build reports (Step 0) and the tool-connected event (Step 3).\n\n---\n\n## Where state lives\n\nEverything for a domain lives under the global site workspace **`~/.hostinger/<domain>/`** — one\ndirectory per domain, under the user's home (`~` = `$HOME`), **not** the current working directory.\nThe directory name **is** the domain, so `state.json` inside does not store it. Because it's global,\nthis domain's build is resumable from any directory.\n\n`state.json` shape (`stage` is the *next* stage to run; `provision` is the background task's status,\npresent only once provision has fired):\n\n```json\n{ \"tool\": \"claude-code\", \"stage\": \"content_model\",\n \"provision\": { \"status\": \"in_progress\", \"domain\": \"<domain>\", \"started_at\": \"…\" },\n \"updated_at\": \"…\" }\n```\n\n`provision.status` is `in_progress` or `completed` — but it is a **hint**: always re-verify it against\n`hosting_websites_list-setups` (Step 1) before trusting it to gate an instance-barrier step.\n\n---\n\n## Step 0 — Start the run log\n\nBefore anything else, start a run and the stage (see *Run log and build report* in the top-level\n`SKILL.md`):\n\n```bash\nnode <skill>/run-log.mjs <domain> run-start --tool <tool> --model <model> --skill <skill version>\nnode <skill>/run-log.mjs <domain> stage-start check_state\n```\n\nIf an earlier run on this domain never wrote its report, `run-start` closes it as `abandoned` first.\n\nThen send any build reports that haven't reached Hostinger yet (that abandoned run, or a send that\nfailed last time):\n\n```bash\nnode <skill>/run-log.mjs <domain> unsent\n```\n\nEach line is `<run> <path>`. For each, send the file through `hosting_headless_submit-build-report`\nand record the result, as described in *Run log and build report* in the top-level `SKILL.md`. One\ntry each. Don't ask first; if any went out, say so in one line (e.g. \"Sent 1 earlier build report to\nHostinger.\"). Don't let a failure hold up this step.\nCheck-state runs once per session. A follow-up after the final report, in the same session, doesn't\ncome back here: it calls `run-resume` (see *Run log and build report* in the top-level `SKILL.md`).\n\n## Step 1 — Is a build actively running right now? (check this before anything else)\n\nKeep two things separate, and **never guess from a timestamp.**\n\n- **A platform build actively in progress** — the only *reliable* \"running now\" signal. Check\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` and exact-match the domain row:\n - `status: running` → provisioning is underway **right now** (a background provision from an earlier\n turn). Don't start a second one — reconcile `state.json`'s `provision` to `in_progress`, and\n either resume prepare work while it finishes or poll it to `completed` at the barrier.\n - `status: failed` → not running; route to provision to self-heal (Step 3).\n - `completed` → provision is done; set `provision.status` to `completed`. Instance-barrier steps\n (content push, frontend data, deploy) are now unblocked.\n - no row → not provisioned yet; carry on.\n\n This check **is** the source of truth for `provision.status`: a stored `in_progress`/`completed` in\n `state.json` is only a hint until this reconciles it.\n\n- **A `.lock`** — `~/.hostinger/<domain>/.lock`. It only tells you *a previous run\n touched this domain*. **Do not judge it by age** — a 2-minute-old lock and a 3-year-old lock get the\n same treatment, and a lock on its own is **not** a reason to interrupt the user. Carry it into Step 5:\n if there's nothing to resume (new build), silently take it over; only if there's real state to resume\n do you also ask *is another Claude window building this domain right now?* (yes → stop; no → take\n over).\n\nOnce you're actively working this domain, **acquire/refresh the lock** (write the current timestamp),\nand **release it on a clean stop** (Step 6). The lock marks an active session, nothing more.\n\n---\n\n## Step 2 — Read the workspace directory\n\nCheck `~/.hostinger/<domain>/` by its exact path (for example `test -d ~/.hostinger/<domain>`), not\nby listing `~/.hostinger/`:\n\n- **Absent** → no history for this domain in the workspace.\n- **Present** → read `state.json` (its `stage`) and whatever artifacts exist (`brief.md`,\n `site.json`, …). Treat these as a hint, not the truth.\n\n---\n\n## Step 3 — Verify against reality (reality wins)\n\nQuery the Hostinger connector — the local files are never trusted on their own:\n\n- **Does the site exist?** `hosting_websites_list { \"domain\": \"<domain>\" }`. ⚠️ This filter is a\n **case-insensitive substring match**, not exact — so `test.com` would also return\n `shop.test.com`. **Compare each returned row's `domain` to your target for an *exact* match**\n (case-insensitive) and ignore the rest. No exact row ⇒ the domain is free. An exact row ⇒ the site\n exists; note its `website_type` (`headless`, `wordpress`, `nodejs`, …) and `username`.\n- **Tell hPanel the tool connected.** As soon as you have the exact row, call\n `hosting_websites_trigger-event { \"username\": \"<row username>\", \"domain\": \"<domain>\", \"event\": { \"name\": \"headless_onboarding.tool.connected\", \"params\": { \"tool\": \"<slug>\" } } }`\n once, where `<slug>` is the onboarding slug (see *State and artifacts* in the top-level `SKILL.md`).\n This flips hPanel's onboarding card to \"connected\". Don't ask first and don't mention it. Skip the\n call when there's no exact row or your tool has no onboarding slug. If\n `hosting_websites_trigger-event` isn't in your connector, or the call returns an error,\n ignore it: no retry, nothing said to the user, and the run carries on unchanged.\n- **How far did it get?** If the site exists, check its setup via\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` (exact-match the row the same way):\n - `status: completed` → provisioned; then, depending on backend, check whether a build has succeeded.\n - `status: running` → still provisioning (see Step 1).\n - `status: failed` → provisioning stopped before finishing → route to **provision** to self-heal.\n- **Is it an empty headless shell from onboarding?** A `headless` site whose setup is `completed` is\n **not necessarily finished** — onboarding now creates the website *before* the build runs, as an\n **empty headless shell** (`hosting_websites_start-setup { type: \"headless_empty\" }`): `website_type`\n is `headless` but **no backend is installed** (no WordPress at `/cms`, no store). Tell a shell from a\n real backend the same way provision does: within 24 h the setup `type` is `headless_empty`; otherwise\n `wordpress_installations_list { username, domain, ownership: \"all\" }` returns **no** `/cms` row and\n there is no `site.json` recording a backend. An empty shell means **provision has not chosen or\n installed a backend yet** — this is a **new build** that happens to already own its website, not a\n finished or resumable one. Route it to **refine** (choose the backend), then **provision** adopts the\n shell and swaps it to that backend (see the provision router's *Onboarding handoff*). Never create a\n second site, and never skip provision for it.\n\nReconcile with the local state. **When they disagree, reality wins** and the local `stage` is\ncorrected.\n\n---\n\n## Step 4 — Determine the position\n\nConnect always writes `state.json` (with `stage: check_state`) before this step runs, so local\nstate is never truly empty. **\"Local state: none\" here means only connect artifacts exist** —\n`connect.json` + a `state.json` still pointing at `check_state`/`refine`, and **no build artifacts\nyet** (`brief.md`, `site.json`, …).\n\n| Local state | Reality (domain) | Position |\n|---|---|---|\n| none (connect only) | free | **new build** — start from refine |\n| none (connect only) | empty headless shell (onboarding) | **new build on an existing shell** — start from refine; provision adopts the shell and swaps it to the chosen backend. Never create a second site, never skip provision |\n| none (connect only) | site exists with a real backend | **adopt / resume** — never create a second site; route to the first unfinished stage |\n| build artifacts present, incomplete | matches | **resume** at `state.json`'s `stage` |\n| build artifacts present | disagrees | **reality wins** — recompute the stage from what actually exists |\n| build artifacts present, all done | live site exists | **finished** — this is now an *iterate* request, not a build |\n\n---\n\n## Step 5 — Guide the user\n\nThe question to ask depends on whether there is actually **something to resume** — i.e. **build\nartifacts** (`brief.md`, `site.json`, …) or a **live site** for this domain. A stray `.lock` or\nconnect-only files are *not* something to resume.\n\n- **New build** (no build artifacts and either the domain is free **or** it holds only an empty\n headless shell from onboarding) → just proceed to refine. **Don't ask resume vs start over** —\n there's nothing to resume; both would land in the same place. An empty headless shell is not\n \"something to resume\": it has no backend and no content yet, so it's a fresh build that already owns\n its website — provision will adopt and swap it. Mention it in one line (\"Using the headless site\n onboarding already created for this domain.\"). If a leftover `.lock` is lying around, silently take\n it over and continue (optionally mention it in one line).\n- **Something to resume** (build artifacts exist, or a site exists for the domain **with a real backend\n installed** — WordPress at `/cms`, a store, or a deployed app) → tell the user plainly what's there\n and **ask them to choose**, regardless of how old it is (don't guess from timestamps):\n\n- **Resume** — pick up at the computed stage. (If a `.lock` is present in this case, also confirm no\n other window is actively building this domain right now.)\n- **Start over / redo a step** — only safe **before anything is provisioned**. If the site is already\n provisioned or live, you **cannot** re-create it: that is forward-only *iterate* (change content or\n design and redeploy), not a fresh build. Never re-provision or overwrite a live site; if the user\n insists on a clean slate for a live domain, explain the limitation and point them at hPanel.\n- **Switch domain** — they want a different domain. A new domain is a **new workspace**\n (`~/.hostinger/<other-domain>/`, with its own `src/`). Hand off to start that domain's run.\n- **Buy a domain** — they want a custom domain instead of the temporary one. Route to the domain\n purchase / `connect-domain` flow, then continue with the chosen domain.\n\nWhen the intent is ambiguous, **ask** — don't guess. A skipped tool result is ambiguous; see\n*When you ask the user* in the top-level `SKILL.md`.\n\n---\n\n## Step 6 — Record and route\n\nWrite `~/.hostinger/<domain>/state.json` with the agreed next `stage` and a fresh timestamp. If the\nroute isn't a new build, record it with `run-log.mjs <domain> run-update --kind resume` (or\n`redesign` / `iterate`), then end the stage with `stage-end check_state ok --note \"<the route>\"`.\nThen:\n\n- **Handing straight to the next stage in this same session?** Keep the `.lock` (you are still\n actively running) and continue.\n- **Stopping and returning control to the user?** (e.g. the next stage isn't built yet, or you are\n waiting on them.) **Delete the `.lock`** — the session is no longer active, and a leftover lock just\n forces an unnecessary \"is another window running?\" question on the next run. Always release the lock\n on a clean stop; it exists only to mark an actively-running session. A stop is still the end of a\n run: give the final report and save the build report (`09-done`).\n\n---\n\n## Invariants\n\n- **One directory per domain.** The domain is the key; state files never repeat it.\n- **Only this domain's workspace.** Never list `~/.hostinger/` or open another site's workspace (see\n *State and artifacts* in the top-level `SKILL.md`).\n- **Never create a second site** on a domain that already has one.\n- **Never re-provision or overwrite a live site.** Recovery is forward-only.\n- **Reality beats local state**, always.\n- **When ambiguous, ask.**\n"
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"uri": "skill://hostinger-headless/steps/02-refine/SKILL.md",
|
package/src/core/skills.ts
CHANGED
|
@@ -148,10 +148,10 @@ const skills: Skill[] = [
|
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"uri": "skill://hostinger-headless/steps/01-check-state/SKILL.md",
|
|
151
|
-
"digest": "sha256:
|
|
152
|
-
"size":
|
|
151
|
+
"digest": "sha256:043f25855abf92f43728b6e442bbd8d580fd2a6bb0270cfa7ed95dd41050ee60",
|
|
152
|
+
"size": 13227,
|
|
153
153
|
"mimeType": "text/markdown",
|
|
154
|
-
"text": "---\nname: headless-step-check-state\ndescription: \"First build step of the Hostinger Headless flow, run right after connect. Works out where a given domain's build stands — is a run already in progress, does the site already exist, how far did it get — by reconciling the global workspace state under ~/.hostinger/<domain>/ with what the Hostinger connector reports, then guides the user to continue, switch or buy a domain, or start over. Owns all resume/position logic for the flow.\"\n---\n\n# headless-step-check-state\n\nThe first build step of the Hostinger Headless flow (orchestrated by `hostinger.com/headless/skill.md`),\nrun right after connect. Connect has already connected the Hostinger connector and confirmed the\naccount can build. Your job here is to\n**figure out where this domain stands and agree the route with the user** before any new work begins.\nThis step owns all resume/position logic — no other stage second-guesses it.\n\nNothing in this step creates, provisions, deploys, or deletes anything. It reads and guides. Its\nonly writes to Hostinger are reports: unsent build reports (Step 0) and the connected tool (Step 3).\n\n---\n\n## Where state lives\n\nEverything for a domain lives under the global site workspace **`~/.hostinger/<domain>/`** — one\ndirectory per domain, under the user's home (`~` = `$HOME`), **not** the current working directory.\nThe directory name **is** the domain, so `state.json` inside does not store it. Because it's global,\nthis domain's build is resumable from any directory.\n\n`state.json` shape (`stage` is the *next* stage to run; `provision` is the background task's status,\npresent only once provision has fired):\n\n```json\n{ \"tool\": \"claude-code\", \"stage\": \"content_model\",\n \"provision\": { \"status\": \"in_progress\", \"domain\": \"<domain>\", \"started_at\": \"…\" },\n \"updated_at\": \"…\" }\n```\n\n`provision.status` is `in_progress` or `completed` — but it is a **hint**: always re-verify it against\n`hosting_websites_list-setups` (Step 1) before trusting it to gate an instance-barrier step.\n\n---\n\n## Step 0 — Start the run log\n\nBefore anything else, start a run and the stage (see *Run log and build report* in the top-level\n`SKILL.md`):\n\n```bash\nnode <skill>/run-log.mjs <domain> run-start --tool <tool> --model <model> --skill <skill version>\nnode <skill>/run-log.mjs <domain> stage-start check_state\n```\n\nIf an earlier run on this domain never wrote its report, `run-start` closes it as `abandoned` first.\n\nThen send any build reports that haven't reached Hostinger yet (that abandoned run, or a send that\nfailed last time):\n\n```bash\nnode <skill>/run-log.mjs <domain> unsent\n```\n\nEach line is `<run> <path>`. For each, send the file through `hosting_headless_submit-build-report`\nand record the result, as described in *Run log and build report* in the top-level `SKILL.md`. One\ntry each. Don't ask first; if any went out, say so in one line (e.g. \"Sent 1 earlier build report to\nHostinger.\"). Don't let a failure hold up this step.\nCheck-state runs once per session. A follow-up after the final report, in the same session, doesn't\ncome back here: it calls `run-resume` (see *Run log and build report* in the top-level `SKILL.md`).\n\n## Step 1 — Is a build actively running right now? (check this before anything else)\n\nKeep two things separate, and **never guess from a timestamp.**\n\n- **A platform build actively in progress** — the only *reliable* \"running now\" signal. Check\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` and exact-match the domain row:\n - `status: running` → provisioning is underway **right now** (a background provision from an earlier\n turn). Don't start a second one — reconcile `state.json`'s `provision` to `in_progress`, and\n either resume prepare work while it finishes or poll it to `completed` at the barrier.\n - `status: failed` → not running; route to provision to self-heal (Step 3).\n - `completed` → provision is done; set `provision.status` to `completed`. Instance-barrier steps\n (content push, frontend data, deploy) are now unblocked.\n - no row → not provisioned yet; carry on.\n\n This check **is** the source of truth for `provision.status`: a stored `in_progress`/`completed` in\n `state.json` is only a hint until this reconciles it.\n\n- **A `.lock`** — `~/.hostinger/<domain>/.lock`. It only tells you *a previous run\n touched this domain*. **Do not judge it by age** — a 2-minute-old lock and a 3-year-old lock get the\n same treatment, and a lock on its own is **not** a reason to interrupt the user. Carry it into Step 5:\n if there's nothing to resume (new build), silently take it over; only if there's real state to resume\n do you also ask *is another Claude window building this domain right now?* (yes → stop; no → take\n over).\n\nOnce you're actively working this domain, **acquire/refresh the lock** (write the current timestamp),\nand **release it on a clean stop** (Step 6). The lock marks an active session, nothing more.\n\n---\n\n## Step 2 — Read the workspace directory\n\nCheck `~/.hostinger/<domain>/` by its exact path (for example `test -d ~/.hostinger/<domain>`), not\nby listing `~/.hostinger/`:\n\n- **Absent** → no history for this domain in the workspace.\n- **Present** → read `state.json` (its `stage`) and whatever artifacts exist (`brief.md`,\n `site.json`, …). Treat these as a hint, not the truth.\n\n---\n\n## Step 3 — Verify against reality (reality wins)\n\nQuery the Hostinger connector — the local files are never trusted on their own:\n\n- **Does the site exist?** `hosting_websites_list { \"domain\": \"<domain>\" }`. ⚠️ This filter is a\n **case-insensitive substring match**, not exact — so `test.com` would also return\n `shop.test.com`. **Compare each returned row's `domain` to your target for an *exact* match**\n (case-insensitive) and ignore the rest. No exact row ⇒ the domain is free. An exact row ⇒ the site\n exists; note its `website_type` (`headless`, `wordpress`, `nodejs`, …) and `username`.\n- **Tell hPanel the tool connected.** As soon as you have the exact row, call\n `hosting_headless_report-tool-connected { \"username\": \"<row username>\", \"domain\": \"<domain>\", \"tool\": \"<slug>\" }`\n once, where `<slug>` is the onboarding slug (see *State and artifacts* in the top-level `SKILL.md`).\n This flips hPanel's onboarding card to \"connected\". Don't ask first and don't mention it. Skip the\n call when there's no exact row or your tool has no onboarding slug. If\n `hosting_headless_report-tool-connected` isn't in your connector, or the call returns an error,\n ignore it: no retry, nothing said to the user, and the run carries on unchanged.\n- **How far did it get?** If the site exists, check its setup via\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` (exact-match the row the same way):\n - `status: completed` → provisioned; then, depending on backend, check whether a build has succeeded.\n - `status: running` → still provisioning (see Step 1).\n - `status: failed` → provisioning stopped before finishing → route to **provision** to self-heal.\n- **Is it an empty headless shell from onboarding?** A `headless` site whose setup is `completed` is\n **not necessarily finished** — onboarding now creates the website *before* the build runs, as an\n **empty headless shell** (`hosting_websites_start-setup { type: \"headless_empty\" }`): `website_type`\n is `headless` but **no backend is installed** (no WordPress at `/cms`, no store). Tell a shell from a\n real backend the same way provision does: within 24 h the setup `type` is `headless_empty`; otherwise\n `wordpress_installations_list { username, domain, ownership: \"all\" }` returns **no** `/cms` row and\n there is no `site.json` recording a backend. An empty shell means **provision has not chosen or\n installed a backend yet** — this is a **new build** that happens to already own its website, not a\n finished or resumable one. Route it to **refine** (choose the backend), then **provision** adopts the\n shell and swaps it to that backend (see the provision router's *Onboarding handoff*). Never create a\n second site, and never skip provision for it.\n\nReconcile with the local state. **When they disagree, reality wins** and the local `stage` is\ncorrected.\n\n---\n\n## Step 4 — Determine the position\n\nConnect always writes `state.json` (with `stage: check_state`) before this step runs, so local\nstate is never truly empty. **\"Local state: none\" here means only connect artifacts exist** —\n`connect.json` + a `state.json` still pointing at `check_state`/`refine`, and **no build artifacts\nyet** (`brief.md`, `site.json`, …).\n\n| Local state | Reality (domain) | Position |\n|---|---|---|\n| none (connect only) | free | **new build** — start from refine |\n| none (connect only) | empty headless shell (onboarding) | **new build on an existing shell** — start from refine; provision adopts the shell and swaps it to the chosen backend. Never create a second site, never skip provision |\n| none (connect only) | site exists with a real backend | **adopt / resume** — never create a second site; route to the first unfinished stage |\n| build artifacts present, incomplete | matches | **resume** at `state.json`'s `stage` |\n| build artifacts present | disagrees | **reality wins** — recompute the stage from what actually exists |\n| build artifacts present, all done | live site exists | **finished** — this is now an *iterate* request, not a build |\n\n---\n\n## Step 5 — Guide the user\n\nThe question to ask depends on whether there is actually **something to resume** — i.e. **build\nartifacts** (`brief.md`, `site.json`, …) or a **live site** for this domain. A stray `.lock` or\nconnect-only files are *not* something to resume.\n\n- **New build** (no build artifacts and either the domain is free **or** it holds only an empty\n headless shell from onboarding) → just proceed to refine. **Don't ask resume vs start over** —\n there's nothing to resume; both would land in the same place. An empty headless shell is not\n \"something to resume\": it has no backend and no content yet, so it's a fresh build that already owns\n its website — provision will adopt and swap it. Mention it in one line (\"Using the headless site\n onboarding already created for this domain.\"). If a leftover `.lock` is lying around, silently take\n it over and continue (optionally mention it in one line).\n- **Something to resume** (build artifacts exist, or a site exists for the domain **with a real backend\n installed** — WordPress at `/cms`, a store, or a deployed app) → tell the user plainly what's there\n and **ask them to choose**, regardless of how old it is (don't guess from timestamps):\n\n- **Resume** — pick up at the computed stage. (If a `.lock` is present in this case, also confirm no\n other window is actively building this domain right now.)\n- **Start over / redo a step** — only safe **before anything is provisioned**. If the site is already\n provisioned or live, you **cannot** re-create it: that is forward-only *iterate* (change content or\n design and redeploy), not a fresh build. Never re-provision or overwrite a live site; if the user\n insists on a clean slate for a live domain, explain the limitation and point them at hPanel.\n- **Switch domain** — they want a different domain. A new domain is a **new workspace**\n (`~/.hostinger/<other-domain>/`, with its own `src/`). Hand off to start that domain's run.\n- **Buy a domain** — they want a custom domain instead of the temporary one. Route to the domain\n purchase / `connect-domain` flow, then continue with the chosen domain.\n\nWhen the intent is ambiguous, **ask** — don't guess. A skipped tool result is ambiguous; see\n*When you ask the user* in the top-level `SKILL.md`.\n\n---\n\n## Step 6 — Record and route\n\nWrite `~/.hostinger/<domain>/state.json` with the agreed next `stage` and a fresh timestamp. If the\nroute isn't a new build, record it with `run-log.mjs <domain> run-update --kind resume` (or\n`redesign` / `iterate`), then end the stage with `stage-end check_state ok --note \"<the route>\"`.\nThen:\n\n- **Handing straight to the next stage in this same session?** Keep the `.lock` (you are still\n actively running) and continue.\n- **Stopping and returning control to the user?** (e.g. the next stage isn't built yet, or you are\n waiting on them.) **Delete the `.lock`** — the session is no longer active, and a leftover lock just\n forces an unnecessary \"is another window running?\" question on the next run. Always release the lock\n on a clean stop; it exists only to mark an actively-running session. A stop is still the end of a\n run: give the final report and save the build report (`09-done`).\n\n---\n\n## Invariants\n\n- **One directory per domain.** The domain is the key; state files never repeat it.\n- **Only this domain's workspace.** Never list `~/.hostinger/` or open another site's workspace (see\n *State and artifacts* in the top-level `SKILL.md`).\n- **Never create a second site** on a domain that already has one.\n- **Never re-provision or overwrite a live site.** Recovery is forward-only.\n- **Reality beats local state**, always.\n- **When ambiguous, ask.**\n"
|
|
154
|
+
"text": "---\nname: headless-step-check-state\ndescription: \"First build step of the Hostinger Headless flow, run right after connect. Works out where a given domain's build stands — is a run already in progress, does the site already exist, how far did it get — by reconciling the global workspace state under ~/.hostinger/<domain>/ with what the Hostinger connector reports, then guides the user to continue, switch or buy a domain, or start over. Owns all resume/position logic for the flow.\"\n---\n\n# headless-step-check-state\n\nThe first build step of the Hostinger Headless flow (orchestrated by `hostinger.com/headless/skill.md`),\nrun right after connect. Connect has already connected the Hostinger connector and confirmed the\naccount can build. Your job here is to\n**figure out where this domain stands and agree the route with the user** before any new work begins.\nThis step owns all resume/position logic — no other stage second-guesses it.\n\nNothing in this step creates, provisions, deploys, or deletes anything. It reads and guides. Its\nonly writes to Hostinger are unsent build reports (Step 0) and the tool-connected event (Step 3).\n\n---\n\n## Where state lives\n\nEverything for a domain lives under the global site workspace **`~/.hostinger/<domain>/`** — one\ndirectory per domain, under the user's home (`~` = `$HOME`), **not** the current working directory.\nThe directory name **is** the domain, so `state.json` inside does not store it. Because it's global,\nthis domain's build is resumable from any directory.\n\n`state.json` shape (`stage` is the *next* stage to run; `provision` is the background task's status,\npresent only once provision has fired):\n\n```json\n{ \"tool\": \"claude-code\", \"stage\": \"content_model\",\n \"provision\": { \"status\": \"in_progress\", \"domain\": \"<domain>\", \"started_at\": \"…\" },\n \"updated_at\": \"…\" }\n```\n\n`provision.status` is `in_progress` or `completed` — but it is a **hint**: always re-verify it against\n`hosting_websites_list-setups` (Step 1) before trusting it to gate an instance-barrier step.\n\n---\n\n## Step 0 — Start the run log\n\nBefore anything else, start a run and the stage (see *Run log and build report* in the top-level\n`SKILL.md`):\n\n```bash\nnode <skill>/run-log.mjs <domain> run-start --tool <tool> --model <model> --skill <skill version>\nnode <skill>/run-log.mjs <domain> stage-start check_state\n```\n\nIf an earlier run on this domain never wrote its report, `run-start` closes it as `abandoned` first.\n\nThen send any build reports that haven't reached Hostinger yet (that abandoned run, or a send that\nfailed last time):\n\n```bash\nnode <skill>/run-log.mjs <domain> unsent\n```\n\nEach line is `<run> <path>`. For each, send the file through `hosting_headless_submit-build-report`\nand record the result, as described in *Run log and build report* in the top-level `SKILL.md`. One\ntry each. Don't ask first; if any went out, say so in one line (e.g. \"Sent 1 earlier build report to\nHostinger.\"). Don't let a failure hold up this step.\nCheck-state runs once per session. A follow-up after the final report, in the same session, doesn't\ncome back here: it calls `run-resume` (see *Run log and build report* in the top-level `SKILL.md`).\n\n## Step 1 — Is a build actively running right now? (check this before anything else)\n\nKeep two things separate, and **never guess from a timestamp.**\n\n- **A platform build actively in progress** — the only *reliable* \"running now\" signal. Check\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` and exact-match the domain row:\n - `status: running` → provisioning is underway **right now** (a background provision from an earlier\n turn). Don't start a second one — reconcile `state.json`'s `provision` to `in_progress`, and\n either resume prepare work while it finishes or poll it to `completed` at the barrier.\n - `status: failed` → not running; route to provision to self-heal (Step 3).\n - `completed` → provision is done; set `provision.status` to `completed`. Instance-barrier steps\n (content push, frontend data, deploy) are now unblocked.\n - no row → not provisioned yet; carry on.\n\n This check **is** the source of truth for `provision.status`: a stored `in_progress`/`completed` in\n `state.json` is only a hint until this reconciles it.\n\n- **A `.lock`** — `~/.hostinger/<domain>/.lock`. It only tells you *a previous run\n touched this domain*. **Do not judge it by age** — a 2-minute-old lock and a 3-year-old lock get the\n same treatment, and a lock on its own is **not** a reason to interrupt the user. Carry it into Step 5:\n if there's nothing to resume (new build), silently take it over; only if there's real state to resume\n do you also ask *is another Claude window building this domain right now?* (yes → stop; no → take\n over).\n\nOnce you're actively working this domain, **acquire/refresh the lock** (write the current timestamp),\nand **release it on a clean stop** (Step 6). The lock marks an active session, nothing more.\n\n---\n\n## Step 2 — Read the workspace directory\n\nCheck `~/.hostinger/<domain>/` by its exact path (for example `test -d ~/.hostinger/<domain>`), not\nby listing `~/.hostinger/`:\n\n- **Absent** → no history for this domain in the workspace.\n- **Present** → read `state.json` (its `stage`) and whatever artifacts exist (`brief.md`,\n `site.json`, …). Treat these as a hint, not the truth.\n\n---\n\n## Step 3 — Verify against reality (reality wins)\n\nQuery the Hostinger connector — the local files are never trusted on their own:\n\n- **Does the site exist?** `hosting_websites_list { \"domain\": \"<domain>\" }`. ⚠️ This filter is a\n **case-insensitive substring match**, not exact — so `test.com` would also return\n `shop.test.com`. **Compare each returned row's `domain` to your target for an *exact* match**\n (case-insensitive) and ignore the rest. No exact row ⇒ the domain is free. An exact row ⇒ the site\n exists; note its `website_type` (`headless`, `wordpress`, `nodejs`, …) and `username`.\n- **Tell hPanel the tool connected.** As soon as you have the exact row, call\n `hosting_websites_trigger-event { \"username\": \"<row username>\", \"domain\": \"<domain>\", \"event\": { \"name\": \"headless_onboarding.tool.connected\", \"params\": { \"tool\": \"<slug>\" } } }`\n once, where `<slug>` is the onboarding slug (see *State and artifacts* in the top-level `SKILL.md`).\n This flips hPanel's onboarding card to \"connected\". Don't ask first and don't mention it. Skip the\n call when there's no exact row or your tool has no onboarding slug. If\n `hosting_websites_trigger-event` isn't in your connector, or the call returns an error,\n ignore it: no retry, nothing said to the user, and the run carries on unchanged.\n- **How far did it get?** If the site exists, check its setup via\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }` (exact-match the row the same way):\n - `status: completed` → provisioned; then, depending on backend, check whether a build has succeeded.\n - `status: running` → still provisioning (see Step 1).\n - `status: failed` → provisioning stopped before finishing → route to **provision** to self-heal.\n- **Is it an empty headless shell from onboarding?** A `headless` site whose setup is `completed` is\n **not necessarily finished** — onboarding now creates the website *before* the build runs, as an\n **empty headless shell** (`hosting_websites_start-setup { type: \"headless_empty\" }`): `website_type`\n is `headless` but **no backend is installed** (no WordPress at `/cms`, no store). Tell a shell from a\n real backend the same way provision does: within 24 h the setup `type` is `headless_empty`; otherwise\n `wordpress_installations_list { username, domain, ownership: \"all\" }` returns **no** `/cms` row and\n there is no `site.json` recording a backend. An empty shell means **provision has not chosen or\n installed a backend yet** — this is a **new build** that happens to already own its website, not a\n finished or resumable one. Route it to **refine** (choose the backend), then **provision** adopts the\n shell and swaps it to that backend (see the provision router's *Onboarding handoff*). Never create a\n second site, and never skip provision for it.\n\nReconcile with the local state. **When they disagree, reality wins** and the local `stage` is\ncorrected.\n\n---\n\n## Step 4 — Determine the position\n\nConnect always writes `state.json` (with `stage: check_state`) before this step runs, so local\nstate is never truly empty. **\"Local state: none\" here means only connect artifacts exist** —\n`connect.json` + a `state.json` still pointing at `check_state`/`refine`, and **no build artifacts\nyet** (`brief.md`, `site.json`, …).\n\n| Local state | Reality (domain) | Position |\n|---|---|---|\n| none (connect only) | free | **new build** — start from refine |\n| none (connect only) | empty headless shell (onboarding) | **new build on an existing shell** — start from refine; provision adopts the shell and swaps it to the chosen backend. Never create a second site, never skip provision |\n| none (connect only) | site exists with a real backend | **adopt / resume** — never create a second site; route to the first unfinished stage |\n| build artifacts present, incomplete | matches | **resume** at `state.json`'s `stage` |\n| build artifacts present | disagrees | **reality wins** — recompute the stage from what actually exists |\n| build artifacts present, all done | live site exists | **finished** — this is now an *iterate* request, not a build |\n\n---\n\n## Step 5 — Guide the user\n\nThe question to ask depends on whether there is actually **something to resume** — i.e. **build\nartifacts** (`brief.md`, `site.json`, …) or a **live site** for this domain. A stray `.lock` or\nconnect-only files are *not* something to resume.\n\n- **New build** (no build artifacts and either the domain is free **or** it holds only an empty\n headless shell from onboarding) → just proceed to refine. **Don't ask resume vs start over** —\n there's nothing to resume; both would land in the same place. An empty headless shell is not\n \"something to resume\": it has no backend and no content yet, so it's a fresh build that already owns\n its website — provision will adopt and swap it. Mention it in one line (\"Using the headless site\n onboarding already created for this domain.\"). If a leftover `.lock` is lying around, silently take\n it over and continue (optionally mention it in one line).\n- **Something to resume** (build artifacts exist, or a site exists for the domain **with a real backend\n installed** — WordPress at `/cms`, a store, or a deployed app) → tell the user plainly what's there\n and **ask them to choose**, regardless of how old it is (don't guess from timestamps):\n\n- **Resume** — pick up at the computed stage. (If a `.lock` is present in this case, also confirm no\n other window is actively building this domain right now.)\n- **Start over / redo a step** — only safe **before anything is provisioned**. If the site is already\n provisioned or live, you **cannot** re-create it: that is forward-only *iterate* (change content or\n design and redeploy), not a fresh build. Never re-provision or overwrite a live site; if the user\n insists on a clean slate for a live domain, explain the limitation and point them at hPanel.\n- **Switch domain** — they want a different domain. A new domain is a **new workspace**\n (`~/.hostinger/<other-domain>/`, with its own `src/`). Hand off to start that domain's run.\n- **Buy a domain** — they want a custom domain instead of the temporary one. Route to the domain\n purchase / `connect-domain` flow, then continue with the chosen domain.\n\nWhen the intent is ambiguous, **ask** — don't guess. A skipped tool result is ambiguous; see\n*When you ask the user* in the top-level `SKILL.md`.\n\n---\n\n## Step 6 — Record and route\n\nWrite `~/.hostinger/<domain>/state.json` with the agreed next `stage` and a fresh timestamp. If the\nroute isn't a new build, record it with `run-log.mjs <domain> run-update --kind resume` (or\n`redesign` / `iterate`), then end the stage with `stage-end check_state ok --note \"<the route>\"`.\nThen:\n\n- **Handing straight to the next stage in this same session?** Keep the `.lock` (you are still\n actively running) and continue.\n- **Stopping and returning control to the user?** (e.g. the next stage isn't built yet, or you are\n waiting on them.) **Delete the `.lock`** — the session is no longer active, and a leftover lock just\n forces an unnecessary \"is another window running?\" question on the next run. Always release the lock\n on a clean stop; it exists only to mark an actively-running session. A stop is still the end of a\n run: give the final report and save the build report (`09-done`).\n\n---\n\n## Invariants\n\n- **One directory per domain.** The domain is the key; state files never repeat it.\n- **Only this domain's workspace.** Never list `~/.hostinger/` or open another site's workspace (see\n *State and artifacts* in the top-level `SKILL.md`).\n- **Never create a second site** on a domain that already has one.\n- **Never re-provision or overwrite a live site.** Recovery is forward-only.\n- **Reality beats local state**, always.\n- **When ambiguous, ask.**\n"
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
157
|
"uri": "skill://hostinger-headless/steps/02-refine/SKILL.md",
|
package/src/core/tools/all.js
CHANGED
|
@@ -559,6 +559,76 @@ export default [
|
|
|
559
559
|
"group": "hosting",
|
|
560
560
|
"hidden": true
|
|
561
561
|
},
|
|
562
|
+
{
|
|
563
|
+
"name": "hosting_websites_trigger-event",
|
|
564
|
+
"title": "Trigger website event",
|
|
565
|
+
"annotations": {
|
|
566
|
+
"title": "Trigger website event",
|
|
567
|
+
"readOnlyHint": false,
|
|
568
|
+
"destructiveHint": false
|
|
569
|
+
},
|
|
570
|
+
"topic": "hosting",
|
|
571
|
+
"description": "Tell Hostinger that something happened on this website, so Hostinger services such as hPanel can react to it. `event.name` picks the event and decides which `event.params` it takes. The call changes nothing on the website.",
|
|
572
|
+
"method": "",
|
|
573
|
+
"path": "",
|
|
574
|
+
"inputSchema": {
|
|
575
|
+
"type": "object",
|
|
576
|
+
"properties": {
|
|
577
|
+
"username": {
|
|
578
|
+
"type": "string",
|
|
579
|
+
"description": "Username of the hosting account that owns the website, as hosting_websites_list returns it"
|
|
580
|
+
},
|
|
581
|
+
"domain": {
|
|
582
|
+
"type": "string",
|
|
583
|
+
"description": "Domain name of the website (e.g., example.com)"
|
|
584
|
+
},
|
|
585
|
+
"event": {
|
|
586
|
+
"type": "object",
|
|
587
|
+
"description": "Event to trigger; `name` decides which `params` it takes",
|
|
588
|
+
"properties": {
|
|
589
|
+
"name": {
|
|
590
|
+
"type": "string",
|
|
591
|
+
"description": "Name of the event. `headless_onboarding.tool.connected`: an AI coding tool started building this website with the Hostinger headless skill, so hPanel shows the tool as connected; send it once per skill run; takes `params.tool`.",
|
|
592
|
+
"enum": [
|
|
593
|
+
"headless_onboarding.tool.connected"
|
|
594
|
+
]
|
|
595
|
+
},
|
|
596
|
+
"params": {
|
|
597
|
+
"type": "object",
|
|
598
|
+
"description": "Fields of the event",
|
|
599
|
+
"properties": {
|
|
600
|
+
"tool": {
|
|
601
|
+
"type": "string",
|
|
602
|
+
"description": "Lowercase slug of the AI coding tool. Known slugs: `claude` (Claude Code), `cursor` (Cursor), `codex` (Codex), `vscode` (VS Code), `antigravity` (Antigravity) and `devin` (Devin). For any other tool, send its own slug.",
|
|
603
|
+
"pattern": "^[a-z0-9-]{1,32}$"
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
"required": [
|
|
607
|
+
"tool"
|
|
608
|
+
],
|
|
609
|
+
"additionalProperties": false
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
"required": [
|
|
613
|
+
"name",
|
|
614
|
+
"params"
|
|
615
|
+
],
|
|
616
|
+
"additionalProperties": false
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
"required": [
|
|
620
|
+
"username",
|
|
621
|
+
"domain",
|
|
622
|
+
"event"
|
|
623
|
+
]
|
|
624
|
+
},
|
|
625
|
+
"security": [],
|
|
626
|
+
"custom": true,
|
|
627
|
+
"templateFile": "trigger-website-event.template.js",
|
|
628
|
+
"templateFileTS": "trigger-website-event.template.ts",
|
|
629
|
+
"handlerMethod": "handleHostingTriggerWebsiteEvent",
|
|
630
|
+
"group": "hosting"
|
|
631
|
+
},
|
|
562
632
|
{
|
|
563
633
|
"name": "agency-hosting_datacenters_list",
|
|
564
634
|
"title": "List available datacenters",
|
package/src/core/tools/all.ts
CHANGED
|
@@ -575,6 +575,76 @@ const tools: OpenApiTool[] = [
|
|
|
575
575
|
"group": "hosting",
|
|
576
576
|
"hidden": true
|
|
577
577
|
},
|
|
578
|
+
{
|
|
579
|
+
"name": "hosting_websites_trigger-event",
|
|
580
|
+
"title": "Trigger website event",
|
|
581
|
+
"annotations": {
|
|
582
|
+
"title": "Trigger website event",
|
|
583
|
+
"readOnlyHint": false,
|
|
584
|
+
"destructiveHint": false
|
|
585
|
+
},
|
|
586
|
+
"topic": "hosting",
|
|
587
|
+
"description": "Tell Hostinger that something happened on this website, so Hostinger services such as hPanel can react to it. `event.name` picks the event and decides which `event.params` it takes. The call changes nothing on the website.",
|
|
588
|
+
"method": "",
|
|
589
|
+
"path": "",
|
|
590
|
+
"inputSchema": {
|
|
591
|
+
"type": "object",
|
|
592
|
+
"properties": {
|
|
593
|
+
"username": {
|
|
594
|
+
"type": "string",
|
|
595
|
+
"description": "Username of the hosting account that owns the website, as hosting_websites_list returns it"
|
|
596
|
+
},
|
|
597
|
+
"domain": {
|
|
598
|
+
"type": "string",
|
|
599
|
+
"description": "Domain name of the website (e.g., example.com)"
|
|
600
|
+
},
|
|
601
|
+
"event": {
|
|
602
|
+
"type": "object",
|
|
603
|
+
"description": "Event to trigger; `name` decides which `params` it takes",
|
|
604
|
+
"properties": {
|
|
605
|
+
"name": {
|
|
606
|
+
"type": "string",
|
|
607
|
+
"description": "Name of the event. `headless_onboarding.tool.connected`: an AI coding tool started building this website with the Hostinger headless skill, so hPanel shows the tool as connected; send it once per skill run; takes `params.tool`.",
|
|
608
|
+
"enum": [
|
|
609
|
+
"headless_onboarding.tool.connected"
|
|
610
|
+
]
|
|
611
|
+
},
|
|
612
|
+
"params": {
|
|
613
|
+
"type": "object",
|
|
614
|
+
"description": "Fields of the event",
|
|
615
|
+
"properties": {
|
|
616
|
+
"tool": {
|
|
617
|
+
"type": "string",
|
|
618
|
+
"description": "Lowercase slug of the AI coding tool. Known slugs: `claude` (Claude Code), `cursor` (Cursor), `codex` (Codex), `vscode` (VS Code), `antigravity` (Antigravity) and `devin` (Devin). For any other tool, send its own slug.",
|
|
619
|
+
"pattern": "^[a-z0-9-]{1,32}$"
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
"required": [
|
|
623
|
+
"tool"
|
|
624
|
+
],
|
|
625
|
+
"additionalProperties": false
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
"required": [
|
|
629
|
+
"name",
|
|
630
|
+
"params"
|
|
631
|
+
],
|
|
632
|
+
"additionalProperties": false
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
"required": [
|
|
636
|
+
"username",
|
|
637
|
+
"domain",
|
|
638
|
+
"event"
|
|
639
|
+
]
|
|
640
|
+
},
|
|
641
|
+
"security": [],
|
|
642
|
+
"custom": true,
|
|
643
|
+
"templateFile": "trigger-website-event.template.js",
|
|
644
|
+
"templateFileTS": "trigger-website-event.template.ts",
|
|
645
|
+
"handlerMethod": "handleHostingTriggerWebsiteEvent",
|
|
646
|
+
"group": "hosting"
|
|
647
|
+
},
|
|
578
648
|
{
|
|
579
649
|
"name": "agency-hosting_datacenters_list",
|
|
580
650
|
"title": "List available datacenters",
|
|
@@ -479,6 +479,76 @@ export default [
|
|
|
479
479
|
"group": "hosting",
|
|
480
480
|
"hidden": true
|
|
481
481
|
},
|
|
482
|
+
{
|
|
483
|
+
"name": "hosting_websites_trigger-event",
|
|
484
|
+
"title": "Trigger website event",
|
|
485
|
+
"annotations": {
|
|
486
|
+
"title": "Trigger website event",
|
|
487
|
+
"readOnlyHint": false,
|
|
488
|
+
"destructiveHint": false
|
|
489
|
+
},
|
|
490
|
+
"topic": "hosting",
|
|
491
|
+
"description": "Tell Hostinger that something happened on this website, so Hostinger services such as hPanel can react to it. `event.name` picks the event and decides which `event.params` it takes. The call changes nothing on the website.",
|
|
492
|
+
"method": "",
|
|
493
|
+
"path": "",
|
|
494
|
+
"inputSchema": {
|
|
495
|
+
"type": "object",
|
|
496
|
+
"properties": {
|
|
497
|
+
"username": {
|
|
498
|
+
"type": "string",
|
|
499
|
+
"description": "Username of the hosting account that owns the website, as hosting_websites_list returns it"
|
|
500
|
+
},
|
|
501
|
+
"domain": {
|
|
502
|
+
"type": "string",
|
|
503
|
+
"description": "Domain name of the website (e.g., example.com)"
|
|
504
|
+
},
|
|
505
|
+
"event": {
|
|
506
|
+
"type": "object",
|
|
507
|
+
"description": "Event to trigger; `name` decides which `params` it takes",
|
|
508
|
+
"properties": {
|
|
509
|
+
"name": {
|
|
510
|
+
"type": "string",
|
|
511
|
+
"description": "Name of the event. `headless_onboarding.tool.connected`: an AI coding tool started building this website with the Hostinger headless skill, so hPanel shows the tool as connected; send it once per skill run; takes `params.tool`.",
|
|
512
|
+
"enum": [
|
|
513
|
+
"headless_onboarding.tool.connected"
|
|
514
|
+
]
|
|
515
|
+
},
|
|
516
|
+
"params": {
|
|
517
|
+
"type": "object",
|
|
518
|
+
"description": "Fields of the event",
|
|
519
|
+
"properties": {
|
|
520
|
+
"tool": {
|
|
521
|
+
"type": "string",
|
|
522
|
+
"description": "Lowercase slug of the AI coding tool. Known slugs: `claude` (Claude Code), `cursor` (Cursor), `codex` (Codex), `vscode` (VS Code), `antigravity` (Antigravity) and `devin` (Devin). For any other tool, send its own slug.",
|
|
523
|
+
"pattern": "^[a-z0-9-]{1,32}$"
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
"required": [
|
|
527
|
+
"tool"
|
|
528
|
+
],
|
|
529
|
+
"additionalProperties": false
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"required": [
|
|
533
|
+
"name",
|
|
534
|
+
"params"
|
|
535
|
+
],
|
|
536
|
+
"additionalProperties": false
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
"required": [
|
|
540
|
+
"username",
|
|
541
|
+
"domain",
|
|
542
|
+
"event"
|
|
543
|
+
]
|
|
544
|
+
},
|
|
545
|
+
"security": [],
|
|
546
|
+
"custom": true,
|
|
547
|
+
"templateFile": "trigger-website-event.template.js",
|
|
548
|
+
"templateFileTS": "trigger-website-event.template.ts",
|
|
549
|
+
"handlerMethod": "handleHostingTriggerWebsiteEvent",
|
|
550
|
+
"group": "hosting"
|
|
551
|
+
},
|
|
482
552
|
{
|
|
483
553
|
"name": "hosting_cache_clear-website",
|
|
484
554
|
"title": "Clear website cache",
|
|
@@ -495,6 +495,76 @@ const tools: OpenApiTool[] = [
|
|
|
495
495
|
"group": "hosting",
|
|
496
496
|
"hidden": true
|
|
497
497
|
},
|
|
498
|
+
{
|
|
499
|
+
"name": "hosting_websites_trigger-event",
|
|
500
|
+
"title": "Trigger website event",
|
|
501
|
+
"annotations": {
|
|
502
|
+
"title": "Trigger website event",
|
|
503
|
+
"readOnlyHint": false,
|
|
504
|
+
"destructiveHint": false
|
|
505
|
+
},
|
|
506
|
+
"topic": "hosting",
|
|
507
|
+
"description": "Tell Hostinger that something happened on this website, so Hostinger services such as hPanel can react to it. `event.name` picks the event and decides which `event.params` it takes. The call changes nothing on the website.",
|
|
508
|
+
"method": "",
|
|
509
|
+
"path": "",
|
|
510
|
+
"inputSchema": {
|
|
511
|
+
"type": "object",
|
|
512
|
+
"properties": {
|
|
513
|
+
"username": {
|
|
514
|
+
"type": "string",
|
|
515
|
+
"description": "Username of the hosting account that owns the website, as hosting_websites_list returns it"
|
|
516
|
+
},
|
|
517
|
+
"domain": {
|
|
518
|
+
"type": "string",
|
|
519
|
+
"description": "Domain name of the website (e.g., example.com)"
|
|
520
|
+
},
|
|
521
|
+
"event": {
|
|
522
|
+
"type": "object",
|
|
523
|
+
"description": "Event to trigger; `name` decides which `params` it takes",
|
|
524
|
+
"properties": {
|
|
525
|
+
"name": {
|
|
526
|
+
"type": "string",
|
|
527
|
+
"description": "Name of the event. `headless_onboarding.tool.connected`: an AI coding tool started building this website with the Hostinger headless skill, so hPanel shows the tool as connected; send it once per skill run; takes `params.tool`.",
|
|
528
|
+
"enum": [
|
|
529
|
+
"headless_onboarding.tool.connected"
|
|
530
|
+
]
|
|
531
|
+
},
|
|
532
|
+
"params": {
|
|
533
|
+
"type": "object",
|
|
534
|
+
"description": "Fields of the event",
|
|
535
|
+
"properties": {
|
|
536
|
+
"tool": {
|
|
537
|
+
"type": "string",
|
|
538
|
+
"description": "Lowercase slug of the AI coding tool. Known slugs: `claude` (Claude Code), `cursor` (Cursor), `codex` (Codex), `vscode` (VS Code), `antigravity` (Antigravity) and `devin` (Devin). For any other tool, send its own slug.",
|
|
539
|
+
"pattern": "^[a-z0-9-]{1,32}$"
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
"required": [
|
|
543
|
+
"tool"
|
|
544
|
+
],
|
|
545
|
+
"additionalProperties": false
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
"required": [
|
|
549
|
+
"name",
|
|
550
|
+
"params"
|
|
551
|
+
],
|
|
552
|
+
"additionalProperties": false
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
"required": [
|
|
556
|
+
"username",
|
|
557
|
+
"domain",
|
|
558
|
+
"event"
|
|
559
|
+
]
|
|
560
|
+
},
|
|
561
|
+
"security": [],
|
|
562
|
+
"custom": true,
|
|
563
|
+
"templateFile": "trigger-website-event.template.js",
|
|
564
|
+
"templateFileTS": "trigger-website-event.template.ts",
|
|
565
|
+
"handlerMethod": "handleHostingTriggerWebsiteEvent",
|
|
566
|
+
"group": "hosting"
|
|
567
|
+
},
|
|
498
568
|
{
|
|
499
569
|
"name": "hosting_cache_clear-website",
|
|
500
570
|
"title": "Clear website cache",
|
|
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
|
|
|
4
4
|
import tools from '../core/tools/agency-hosting.js';
|
|
5
5
|
import instructions from '../core/instructions.agency-hosting.js';
|
|
6
6
|
|
|
7
|
-
startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.
|
|
7
|
+
startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.15.0', tools, instructions });
|
|
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
|
|
|
4
4
|
import tools from '../core/tools/agency-hosting.js';
|
|
5
5
|
import instructions from '../core/instructions.agency-hosting.js';
|
|
6
6
|
|
|
7
|
-
startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.
|
|
7
|
+
startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.15.0', tools, instructions });
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
import { startServer } from '../core/runtime.js';
|
|
4
4
|
import tools from '../core/tools/agent-readiness.js';
|
|
5
5
|
|
|
6
|
-
startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.
|
|
6
|
+
startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.15.0', tools });
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
import { startServer } from '../core/runtime.js';
|
|
4
4
|
import tools from '../core/tools/agent-readiness.js';
|
|
5
5
|
|
|
6
|
-
startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.
|
|
6
|
+
startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.15.0', tools });
|
package/src/servers/all.js
CHANGED
|
@@ -5,4 +5,4 @@ import tools from '../core/tools/all.js';
|
|
|
5
5
|
import instructions from '../core/instructions.js';
|
|
6
6
|
import skills from '../core/skills.js';
|
|
7
7
|
|
|
8
|
-
startServer({ name: 'hostinger-api-mcp', version: '3.
|
|
8
|
+
startServer({ name: 'hostinger-api-mcp', version: '3.15.0', tools, instructions, skills });
|
package/src/servers/all.ts
CHANGED
|
@@ -5,4 +5,4 @@ import tools from '../core/tools/all.js';
|
|
|
5
5
|
import instructions from '../core/instructions.js';
|
|
6
6
|
import skills from '../core/skills.js';
|
|
7
7
|
|
|
8
|
-
startServer({ name: 'hostinger-api-mcp', version: '3.
|
|
8
|
+
startServer({ name: 'hostinger-api-mcp', version: '3.15.0', tools, instructions, skills });
|
package/src/servers/billing.js
CHANGED
package/src/servers/billing.ts
CHANGED
package/src/servers/dns.js
CHANGED
package/src/servers/dns.ts
CHANGED
package/src/servers/domains.js
CHANGED
package/src/servers/domains.ts
CHANGED
package/src/servers/ecommerce.js
CHANGED
package/src/servers/ecommerce.ts
CHANGED
package/src/servers/horizons.js
CHANGED
package/src/servers/horizons.ts
CHANGED
package/src/servers/hosting.js
CHANGED
|
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
|
|
|
4
4
|
import tools from '../core/tools/hosting.js';
|
|
5
5
|
import instructions from '../core/instructions.js';
|
|
6
6
|
|
|
7
|
-
startServer({ name: 'hostinger-hosting-mcp', version: '3.
|
|
7
|
+
startServer({ name: 'hostinger-hosting-mcp', version: '3.15.0', tools, instructions });
|
package/src/servers/hosting.ts
CHANGED
|
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
|
|
|
4
4
|
import tools from '../core/tools/hosting.js';
|
|
5
5
|
import instructions from '../core/instructions.js';
|
|
6
6
|
|
|
7
|
-
startServer({ name: 'hostinger-hosting-mcp', version: '3.
|
|
7
|
+
startServer({ name: 'hostinger-hosting-mcp', version: '3.15.0', tools, instructions });
|
package/src/servers/mail.js
CHANGED
package/src/servers/mail.ts
CHANGED
package/src/servers/reach.js
CHANGED
package/src/servers/reach.ts
CHANGED
package/src/servers/vps.js
CHANGED
package/src/servers/vps.ts
CHANGED
package/src/servers/wordpress.js
CHANGED
package/src/servers/wordpress.ts
CHANGED
package/types.d.ts
CHANGED
|
@@ -287,6 +287,27 @@ export interface APITools {
|
|
|
287
287
|
response: any; // Response structure will depend on the API
|
|
288
288
|
};
|
|
289
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Tell Hostinger that something happened on this website, so Hostinger services such as hPanel can react to it. `event.name` picks the event and decides which `event.params` it takes. The call changes nothing on the website.
|
|
292
|
+
*/
|
|
293
|
+
"hosting_websites_trigger-event": {
|
|
294
|
+
params: {
|
|
295
|
+
/**
|
|
296
|
+
* Username of the hosting account that owns the website, as hosting_websites_list returns it
|
|
297
|
+
*/
|
|
298
|
+
username: string;
|
|
299
|
+
/**
|
|
300
|
+
* Domain name of the website (e.g., example.com)
|
|
301
|
+
*/
|
|
302
|
+
domain: string;
|
|
303
|
+
/**
|
|
304
|
+
* Event to trigger; `name` decides which `params` it takes
|
|
305
|
+
*/
|
|
306
|
+
event: object;
|
|
307
|
+
};
|
|
308
|
+
response: any; // Response structure will depend on the API
|
|
309
|
+
};
|
|
310
|
+
|
|
290
311
|
/**
|
|
291
312
|
* Lists the datacenters available for provisioning a new website on the given Agency Plan
|
|
292
313
|
hosting order.
|