helipod-mcp 0.5.9 → 0.5.11
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/README.md +3 -3
- package/dist/http.js +79 -31
- package/dist/index.js +79 -31
- package/dist/stdio.js +79 -31
- package/package.json +1 -1
- package/skills/helipod/SKILL.md +2 -2
- package/skills/helipod-vps/SKILL.md +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use [Helipod](https://helipod.io) from AI coding tools: deploy apps and databases, run VPS servers, buy domains and edit DNS, and send email, by asking in plain language.
|
|
4
4
|
|
|
5
|
-
- **MCP server** (`helipod-mcp`):
|
|
5
|
+
- **MCP server** (`helipod-mcp`): 238 tools over the Container, Database, VPS, Domain, Email, Account and AI APIs. Works in Claude Code, Codex, Cursor, VS Code, Windsurf, Claude Desktop and any MCP client.
|
|
6
6
|
- **Skills** (`skills/`): step-by-step playbooks so the agent deploys, prices and confirms the way Helipod expects (price first, confirm before deleting, read build logs on failure).
|
|
7
7
|
|
|
8
8
|
Step-by-step guides for each tool (Claude Code, Codex, Cursor, VS Code, Gemini CLI, opencode, Windsurf, Claude Desktop, Cline and others): **https://docs.helipod.io/docs/helipod/ai-agents**
|
|
@@ -193,10 +193,10 @@ Pick your agents when asked (or pass `-a codex cursor gemini-cli opencode -y`).
|
|
|
193
193
|
|---|---|
|
|
194
194
|
| Containers | projects and environments (create, rename, delete), shared project variables, services (create from image, Git or Dockerfile, rename), deploy, deployments and build logs, restart, variables, domains and their ports, scale, volumes (with resize), function code, secret files, logs, metrics, templates, multi-service stacks |
|
|
195
195
|
| Databases | managed Postgres: create, connection string, branches per environment, compute settings, plan upgrades, usage, live stats, credit balance, tables, read-only queries, SQL classification and confirmed writes |
|
|
196
|
-
| VPS | plans, create, rename, power, reinstall, password, login details, terminate, renew, upgrade options and upgrade, auto-renew, metrics, firewall, port forwarding, SSH keys and SSH on/off, snapshots, backups |
|
|
196
|
+
| VPS | plans, create, rename, power, reinstall, password, login details, terminate, renew, upgrade options and upgrade (create, renew and upgrade each with a dry run), auto-renew, metrics, firewall, port forwarding, SSH keys and SSH on/off, snapshots, backups |
|
|
197
197
|
| Domains | search, prices, owner details, register, transfer, renew (each with a dry run), DNS records, nameservers, child nameservers, WHOIS privacy |
|
|
198
198
|
| Email | status, plan (buy, renew, change, inbox add-ons, with dry runs), send, sent log, sending domains, templates, contacts, segments, topics, broadcasts, suppressions, webhooks, inbox |
|
|
199
|
-
| Account | wallet and transactions, invoices, monthly statements, notifications, project activity, support tickets |
|
|
199
|
+
| Account | wallet and transactions, deposits (top-up payment links), invoices, monthly statements, notifications, project activity, support tickets |
|
|
200
200
|
| AI | models and prices, balance, usage, metrics, AI keys (list, revoke), top-up and auto top-up |
|
|
201
201
|
| Other | `helipod_api_get` to read any other documented endpoint (read only) |
|
|
202
202
|
|
package/dist/http.js
CHANGED
|
@@ -36281,6 +36281,10 @@ var period = exports_external.enum(["monthly", "3month", "6month", "yearly"]);
|
|
|
36281
36281
|
var pointId = exports_external.string().describe("Restore point id (from list_vps_restore_points)");
|
|
36282
36282
|
var backupPath = exports_external.string().max(1024).describe("Absolute path inside the backup, e.g. /etc/nginx or /var/www/index.html");
|
|
36283
36283
|
var migrationId = exports_external.string().describe("Migration id (from list_vps_migrations)");
|
|
36284
|
+
var dryRun2 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
|
|
36285
|
+
var idempotencyKey = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real call. Generated when omitted; pass the same value when retrying so it is never charged twice");
|
|
36286
|
+
var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
|
|
36287
|
+
var dryQuery = (d) => ({ dryRun: d ? "true" : undefined });
|
|
36284
36288
|
var vpsTools = [
|
|
36285
36289
|
defineTool({
|
|
36286
36290
|
name: "list_vps_plans",
|
|
@@ -36310,7 +36314,7 @@ var vpsTools = [
|
|
|
36310
36314
|
name: "create_vps",
|
|
36311
36315
|
title: "Create VPS",
|
|
36312
36316
|
kind: "write",
|
|
36313
|
-
description:
|
|
36317
|
+
description: `Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Call with dryRun=true first, show the price and the wallet after it, and get the user's explicit OK. ${DRY} autoRenew=false creates it with auto-renew off (renew it with renew_vps). Provisioning takes a few minutes: poll get_vps.`,
|
|
36314
36318
|
input: {
|
|
36315
36319
|
projectId: exports_external.string().uuid(),
|
|
36316
36320
|
planId: exports_external.string(),
|
|
@@ -36318,9 +36322,12 @@ var vpsTools = [
|
|
|
36318
36322
|
osType: exports_external.string().describe("From the plan's osTemplates, e.g. ubuntu"),
|
|
36319
36323
|
osVersion: exports_external.string().describe("e.g. 24.04"),
|
|
36320
36324
|
rootPassword: password,
|
|
36321
|
-
billingPeriod: period.optional().describe("Default monthly")
|
|
36325
|
+
billingPeriod: period.optional().describe("Default monthly"),
|
|
36326
|
+
autoRenew: exports_external.boolean().optional().describe("false = no automatic renewal from the wallet (default true)"),
|
|
36327
|
+
dryRun: dryRun2,
|
|
36328
|
+
idempotencyKey
|
|
36322
36329
|
},
|
|
36323
|
-
run: (
|
|
36330
|
+
run: ({ dryRun: dryRun3, idempotencyKey: idempotencyKey2, ...body }, api2) => api2.api("POST", "/vps", { body, query: dryQuery(dryRun3), idempotencyKey: purchaseKey(dryRun3, idempotencyKey2) })
|
|
36324
36331
|
}),
|
|
36325
36332
|
defineTool({
|
|
36326
36333
|
name: "vps_power",
|
|
@@ -36374,17 +36381,17 @@ var vpsTools = [
|
|
|
36374
36381
|
name: "renew_vps",
|
|
36375
36382
|
title: "Renew VPS",
|
|
36376
36383
|
kind: "write",
|
|
36377
|
-
description:
|
|
36378
|
-
input: { vpsId, billingPeriod: period },
|
|
36379
|
-
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod } })
|
|
36384
|
+
description: `Extend a server's paid period. COSTS MONEY: call with dryRun=true first, show the price and the wallet after it, and get the user's OK. ${DRY}`,
|
|
36385
|
+
input: { vpsId, billingPeriod: period, dryRun: dryRun2, idempotencyKey },
|
|
36386
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
|
|
36380
36387
|
}),
|
|
36381
36388
|
defineTool({
|
|
36382
36389
|
name: "upgrade_vps",
|
|
36383
36390
|
title: "Upgrade VPS plan",
|
|
36384
36391
|
kind: "write",
|
|
36385
|
-
description:
|
|
36386
|
-
input: { vpsId, planId: exports_external.string() },
|
|
36387
|
-
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId } })
|
|
36392
|
+
description: `Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options. Call with dryRun=true first and get the user's OK. ${DRY} Servers with providerCode 2 only.`,
|
|
36393
|
+
input: { vpsId, planId: exports_external.string(), dryRun: dryRun2, idempotencyKey },
|
|
36394
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
|
|
36388
36395
|
}),
|
|
36389
36396
|
defineTool({
|
|
36390
36397
|
name: "get_vps_upgrade_options",
|
|
@@ -36411,11 +36418,20 @@ var vpsTools = [
|
|
|
36411
36418
|
input: { vpsId, range: exports_external.enum(["1h", "6h", "24h", "7d", "30d"]).optional() },
|
|
36412
36419
|
run: (a, api2) => api2.api("GET", `/vps/${a.vpsId}/metrics`, { query: { range: a.range } })
|
|
36413
36420
|
}),
|
|
36421
|
+
defineTool({
|
|
36422
|
+
name: "set_vps_auto_renew",
|
|
36423
|
+
title: "Set VPS auto-renew",
|
|
36424
|
+
kind: "write",
|
|
36425
|
+
idempotent: true,
|
|
36426
|
+
description: "Turn auto-renew on or off (sets the value, safe to repeat). With it off, the server is only renewed by renew_vps and expires otherwise.",
|
|
36427
|
+
input: { vpsId, autoRenew: exports_external.boolean() },
|
|
36428
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew`, { body: { autoRenew: a.autoRenew } })
|
|
36429
|
+
}),
|
|
36414
36430
|
defineTool({
|
|
36415
36431
|
name: "toggle_vps_auto_renew",
|
|
36416
36432
|
title: "Toggle VPS auto-renew",
|
|
36417
36433
|
kind: "write",
|
|
36418
|
-
description: "Flip auto-renew
|
|
36434
|
+
description: "Flip auto-renew to the other value; the response gives the new one. Prefer set_vps_auto_renew to reach a given state.",
|
|
36419
36435
|
input: { vpsId },
|
|
36420
36436
|
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew-toggle`)
|
|
36421
36437
|
}),
|
|
@@ -36744,8 +36760,8 @@ var vpsTools = [
|
|
|
36744
36760
|
// src/tools/domains.ts
|
|
36745
36761
|
var domainId = exports_external.number().int().positive().describe("Domain id (number, from list_my_domains)");
|
|
36746
36762
|
var hostId = exports_external.number().int().positive().describe("Child nameserver id (from list_child_nameservers)");
|
|
36747
|
-
var
|
|
36748
|
-
var
|
|
36763
|
+
var dryRun3 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
|
|
36764
|
+
var DRY2 = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
|
|
36749
36765
|
var record3 = exports_external.object({
|
|
36750
36766
|
name: exports_external.string().describe('"@" for the domain itself, or the part before the domain, e.g. "www" or "_dmarc.mail". A full name like "www.example.com" is accepted and stored as "www"'),
|
|
36751
36767
|
type: exports_external.enum(["A", "AAAA", "MX", "MXE", "CNAME", "SPF", "TXT"]),
|
|
@@ -36821,12 +36837,12 @@ var domainTools = [
|
|
|
36821
36837
|
name: "register_domain",
|
|
36822
36838
|
title: "Register domain",
|
|
36823
36839
|
kind: "write",
|
|
36824
|
-
description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${
|
|
36840
|
+
description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY2} The dry run includes WHOIS privacy when whoisProtection is true.`,
|
|
36825
36841
|
input: {
|
|
36826
36842
|
name: exports_external.string().describe("Full domain, e.g. tokosaya.id"),
|
|
36827
36843
|
nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(),
|
|
36828
36844
|
whoisProtection: exports_external.boolean().optional(),
|
|
36829
|
-
dryRun:
|
|
36845
|
+
dryRun: dryRun3
|
|
36830
36846
|
},
|
|
36831
36847
|
run: (a, api2) => api2.api("POST", "/domains/register", { body: { name: a.name, period: 1, nameservers: a.nameservers, whoisProtection: a.whoisProtection }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
36832
36848
|
}),
|
|
@@ -36834,16 +36850,16 @@ var domainTools = [
|
|
|
36834
36850
|
name: "transfer_domain",
|
|
36835
36851
|
title: "Transfer domain in",
|
|
36836
36852
|
kind: "write",
|
|
36837
|
-
description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${
|
|
36838
|
-
input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun:
|
|
36853
|
+
description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY2}`,
|
|
36854
|
+
input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun3 },
|
|
36839
36855
|
run: (a, api2) => api2.api("POST", "/domains/transfer", { body: { name: a.name, authCode: a.authCode, period: 1, nameservers: a.nameservers }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
36840
36856
|
}),
|
|
36841
36857
|
defineTool({
|
|
36842
36858
|
name: "renew_domain",
|
|
36843
36859
|
title: "Renew domain",
|
|
36844
36860
|
kind: "write",
|
|
36845
|
-
description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${
|
|
36846
|
-
input: { domainId, dryRun:
|
|
36861
|
+
description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY2}`,
|
|
36862
|
+
input: { domainId, dryRun: dryRun3 },
|
|
36847
36863
|
run: (a, api2) => api2.api("POST", `/domains/${a.domainId}/renew`, { body: { period: 1 }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
36848
36864
|
}),
|
|
36849
36865
|
defineTool({
|
|
@@ -37006,7 +37022,7 @@ var message = exports_external.object({
|
|
|
37006
37022
|
variables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
37007
37023
|
idempotencyKey: exports_external.string().max(128).optional()
|
|
37008
37024
|
});
|
|
37009
|
-
var
|
|
37025
|
+
var dryRun4 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
|
|
37010
37026
|
var idem2 = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real purchase. Generated when omitted; pass the same value when retrying so it is never charged twice");
|
|
37011
37027
|
var MONEY = "COSTS MONEY from the wallet. dryRun=true returns the charge (IDR) and walletBalanceAfter without charging or changing anything.";
|
|
37012
37028
|
var planCode = exports_external.string().min(1).max(50).describe("Plan code from list_email_plans");
|
|
@@ -37042,7 +37058,7 @@ var emailTools = [
|
|
|
37042
37058
|
title: "Buy email plan",
|
|
37043
37059
|
kind: "write",
|
|
37044
37060
|
description: `Start an email plan for the project (a free plan activates without charge). Works for a project with no email yet: the real call also sets email up (result.setup). ${MONEY} The dry run's mode says purchase, activate_free or already_active.`,
|
|
37045
|
-
input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun:
|
|
37061
|
+
input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun4, idempotencyKey: idem2 },
|
|
37046
37062
|
run: (a, api2) => api2.email("POST", "/plan", {
|
|
37047
37063
|
project: a.projectId,
|
|
37048
37064
|
body: { planCode: a.planCode, months: a.months },
|
|
@@ -37055,7 +37071,7 @@ var emailTools = [
|
|
|
37055
37071
|
title: "Renew email plan",
|
|
37056
37072
|
kind: "write",
|
|
37057
37073
|
description: `Renew the project's ended or expiring email plan (409 PLAN_ACTIVE while one is still running and cannot be extended). Repeats the last plan and cycle unless planCode / months are given. ${MONEY}`,
|
|
37058
|
-
input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun:
|
|
37074
|
+
input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun4, idempotencyKey: idem2 },
|
|
37059
37075
|
run: (a, api2) => api2.email("POST", "/plan/renew", {
|
|
37060
37076
|
project: a.projectId,
|
|
37061
37077
|
body: { planCode: a.planCode, months: a.months },
|
|
@@ -37068,7 +37084,7 @@ var emailTools = [
|
|
|
37068
37084
|
title: "Change email plan",
|
|
37069
37085
|
kind: "write",
|
|
37070
37086
|
description: `Move the active email plan to another plan. An upgrade applies now and charges the prorated difference to the same end date; a downgrade is scheduled for the next cycle (no refund). ${MONEY}`,
|
|
37071
|
-
input: { projectId, planCode, dryRun:
|
|
37087
|
+
input: { projectId, planCode, dryRun: dryRun4, idempotencyKey: idem2 },
|
|
37072
37088
|
run: (a, api2) => api2.email("POST", "/plan/change", {
|
|
37073
37089
|
project: a.projectId,
|
|
37074
37090
|
body: { planCode: a.planCode },
|
|
@@ -37085,7 +37101,7 @@ var emailTools = [
|
|
|
37085
37101
|
projectId,
|
|
37086
37102
|
extraMailboxes: exports_external.number().int().min(0).max(50).describe("Total extra mailboxes wanted (0-50)"),
|
|
37087
37103
|
extraStorageGb: exports_external.number().int().min(0).max(1000).describe("Total extra inbox storage in GB (0-1000)"),
|
|
37088
|
-
dryRun:
|
|
37104
|
+
dryRun: dryRun4,
|
|
37089
37105
|
idempotencyKey: idem2
|
|
37090
37106
|
},
|
|
37091
37107
|
run: (a, api2) => api2.email("POST", "/plan/addons", {
|
|
@@ -37655,7 +37671,7 @@ var emailTools = [
|
|
|
37655
37671
|
// src/tools/databases.ts
|
|
37656
37672
|
var databaseId = exports_external.string().uuid().describe("Database id (from list_databases)");
|
|
37657
37673
|
var environmentId = exports_external.string().uuid().describe("Environment id (from list_projects). Omit for the primary (production) branch.");
|
|
37658
|
-
var
|
|
37674
|
+
var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
|
|
37659
37675
|
var databaseTools = [
|
|
37660
37676
|
defineTool({
|
|
37661
37677
|
name: "list_database_plans",
|
|
@@ -37749,7 +37765,7 @@ var databaseTools = [
|
|
|
37749
37765
|
minCu: exports_external.number().min(0.25).max(10).optional(),
|
|
37750
37766
|
maxCu: exports_external.number().min(0.25).max(10).optional(),
|
|
37751
37767
|
suspendTimeoutSeconds: exports_external.number().int().min(0).max(604800).optional().describe("0 = never suspend, or 60-604800"),
|
|
37752
|
-
dryRun:
|
|
37768
|
+
dryRun: dryRun5
|
|
37753
37769
|
},
|
|
37754
37770
|
run: (a, api2) => api2.api("PATCH", `/databases/${a.databaseId}/settings`, {
|
|
37755
37771
|
body: { minCu: a.minCu, maxCu: a.maxCu, suspendTimeoutSeconds: a.suspendTimeoutSeconds },
|
|
@@ -37761,7 +37777,7 @@ var databaseTools = [
|
|
|
37761
37777
|
title: "Upgrade database plan / top up",
|
|
37762
37778
|
kind: "write",
|
|
37763
37779
|
description: "Buy a paid plan or top up credits. COSTS MONEY from the wallet: call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
|
|
37764
|
-
input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun:
|
|
37780
|
+
input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun5, idempotencyKey: exports_external.string().max(128).optional() },
|
|
37765
37781
|
run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
37766
37782
|
}),
|
|
37767
37783
|
defineTool({
|
|
@@ -37887,6 +37903,38 @@ var accountTools = [
|
|
|
37887
37903
|
input: { limit: limit2, offset },
|
|
37888
37904
|
run: (a, api2) => api2.api("GET", "/account/wallet/transactions", { query: { limit: a.limit, offset: a.offset } })
|
|
37889
37905
|
}),
|
|
37906
|
+
defineTool({
|
|
37907
|
+
name: "create_deposit",
|
|
37908
|
+
title: "Create deposit (top-up)",
|
|
37909
|
+
kind: "write",
|
|
37910
|
+
description: `Create a wallet top-up of amount IDR (whole number, 10000-50000000) and its payment link. Nothing is charged: give the user paymentUrl to pay (QRIS or bank virtual account, listed in paymentMethods; the link lasts 3 hours); never try to pay it yourself. Limits: one unpaid deposit at a time (409 DEPOSIT_PENDING: details.deposit is the unpaid one, send its paymentUrl instead), 10 minutes between deposits (429 DEPOSIT_COOLDOWN, details.nextAllowedAt), 10 a day (429 DEPOSIT_DAILY_LIMIT). dryRun=true creates nothing and says whether it would be accepted now. Then follow it with get_deposit. ${ACCOUNT_KEY}`,
|
|
37911
|
+
input: {
|
|
37912
|
+
amount: exports_external.number().int().min(1e4).max(50000000).describe("IDR, whole number"),
|
|
37913
|
+
dryRun: exports_external.boolean().optional().describe("true = only check (methods, whether it would be accepted now); nothing is created"),
|
|
37914
|
+
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key. Generated when omitted; pass the same value when retrying so only one deposit is created")
|
|
37915
|
+
},
|
|
37916
|
+
run: (a, api2) => api2.api("POST", "/account/deposits", {
|
|
37917
|
+
body: { amount: a.amount },
|
|
37918
|
+
query: { dryRun: a.dryRun ? "true" : undefined },
|
|
37919
|
+
idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey)
|
|
37920
|
+
})
|
|
37921
|
+
}),
|
|
37922
|
+
defineTool({
|
|
37923
|
+
name: "get_deposit",
|
|
37924
|
+
title: "Get deposit",
|
|
37925
|
+
kind: "read",
|
|
37926
|
+
description: `One deposit: status pending (paymentUrl to pay), paid (paidAt; the wallet was credited) or expired (not paid in time). ${ACCOUNT_KEY}`,
|
|
37927
|
+
input: { depositId: uid2("Deposit") },
|
|
37928
|
+
run: (a, api2) => api2.api("GET", `/account/deposits/${a.depositId}`)
|
|
37929
|
+
}),
|
|
37930
|
+
defineTool({
|
|
37931
|
+
name: "list_deposits",
|
|
37932
|
+
title: "List deposits",
|
|
37933
|
+
kind: "read",
|
|
37934
|
+
description: `Deposits (wallet top-ups), newest first, with status and amount. ${ACCOUNT_KEY}`,
|
|
37935
|
+
input: { limit: limit2, offset },
|
|
37936
|
+
run: (a, api2) => api2.api("GET", "/account/deposits", { query: { limit: a.limit, offset: a.offset } })
|
|
37937
|
+
}),
|
|
37890
37938
|
defineTool({
|
|
37891
37939
|
name: "list_invoices",
|
|
37892
37940
|
title: "List invoices",
|
|
@@ -38019,7 +38067,7 @@ var accountTools = [
|
|
|
38019
38067
|
var projectId2 = exports_external.string().uuid().optional().describe("Limit to one project (a project key is always limited to its own)");
|
|
38020
38068
|
var keyId = exports_external.string().uuid().optional().describe("Limit to one AI key (id from list_ai_keys)");
|
|
38021
38069
|
var model = exports_external.string().max(100).optional().describe("Limit to one model id");
|
|
38022
|
-
var
|
|
38070
|
+
var dryRun6 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
|
|
38023
38071
|
var ACCOUNT_KEY2 = "Needs a whole-account key (a key limited to one project gets 403 ACCOUNT_KEY_REQUIRED).";
|
|
38024
38072
|
var imageProject = exports_external.string().uuid().optional().describe("Project the images belong to (required with a whole-account key; a project key uses its own)");
|
|
38025
38073
|
var INLINE_MAX_BYTES = 1500000;
|
|
@@ -38104,7 +38152,7 @@ var aiTools = [
|
|
|
38104
38152
|
n: exports_external.number().int().min(1).max(10).optional().describe("Images in this request (up to the model's maxN; default 1)"),
|
|
38105
38153
|
images: exports_external.array(exports_external.string().url().startsWith("https://")).max(10).optional().describe("Reference images as https:// links (only models with refImages > 0)"),
|
|
38106
38154
|
projectId: imageProject,
|
|
38107
|
-
dryRun:
|
|
38155
|
+
dryRun: dryRun6,
|
|
38108
38156
|
wait: exports_external.number().int().min(0).max(55).optional().describe("Seconds to wait for the result (default 50; 0 = return the id at once)"),
|
|
38109
38157
|
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real request. Generated when omitted; pass the same value when retrying so it is never charged twice")
|
|
38110
38158
|
},
|
|
@@ -38154,7 +38202,7 @@ var aiTools = [
|
|
|
38154
38202
|
input: {
|
|
38155
38203
|
packId: exports_external.string().min(1).max(64).optional(),
|
|
38156
38204
|
amount: exports_external.number().min(1).max(1e9).optional().describe("IDR"),
|
|
38157
|
-
dryRun:
|
|
38205
|
+
dryRun: dryRun6,
|
|
38158
38206
|
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real top-up. Generated when omitted; pass the same value when retrying so it is never charged twice")
|
|
38159
38207
|
},
|
|
38160
38208
|
run: (a, api2) => {
|
|
@@ -38177,7 +38225,7 @@ var aiTools = [
|
|
|
38177
38225
|
enabled: exports_external.boolean(),
|
|
38178
38226
|
threshold: exports_external.number().min(1).optional().describe("Top up when the AI balance is below this (IDR)"),
|
|
38179
38227
|
amount: exports_external.number().min(1).optional().describe("How much to top up each time (IDR)"),
|
|
38180
|
-
dryRun:
|
|
38228
|
+
dryRun: dryRun6
|
|
38181
38229
|
},
|
|
38182
38230
|
run: (a, api2) => api2.api("PUT", "/ai/auto-topup", {
|
|
38183
38231
|
body: { enabled: a.enabled, threshold: a.threshold, amount: a.amount },
|
|
@@ -38213,7 +38261,7 @@ var generalTools = [
|
|
|
38213
38261
|
];
|
|
38214
38262
|
|
|
38215
38263
|
// src/server.ts
|
|
38216
|
-
var VERSION = "0.5.
|
|
38264
|
+
var VERSION = "0.5.11";
|
|
38217
38265
|
var INSTRUCTIONS = `Helipod is a cloud platform (Indonesia): containers, managed Postgres databases, VPS, domains, email and an AI gateway, billed from a prepaid wallet in Rupiah.
|
|
38218
38266
|
- Start with list_projects to find projects and environment ids. Account tools (get_wallet, invoices, statements, notifications, get_project_activity, support tickets) cover the account; AI tools (list_ai_models, get_ai_overview, get_ai_usage, AI keys, top-ups, generate_ai_image) cover the AI gateway.
|
|
38219
38267
|
- Anything that costs money (create/deploy/scale services, resize volumes, upgrade a database plan, create/renew/upgrade VPS, VPS backups, register/renew/transfer domains, email plans and inbox add-ons, AI top-ups and auto top-up, generating images): call it with dryRun=true first (or the price lookup when it has no dry run), tell the user the cost in IDR and the wallet balance after, and wait for an explicit yes before the real call. Never treat silence or an earlier general OK as a yes.
|
package/dist/index.js
CHANGED
|
@@ -35426,6 +35426,10 @@ var period = exports_external.enum(["monthly", "3month", "6month", "yearly"]);
|
|
|
35426
35426
|
var pointId = exports_external.string().describe("Restore point id (from list_vps_restore_points)");
|
|
35427
35427
|
var backupPath = exports_external.string().max(1024).describe("Absolute path inside the backup, e.g. /etc/nginx or /var/www/index.html");
|
|
35428
35428
|
var migrationId = exports_external.string().describe("Migration id (from list_vps_migrations)");
|
|
35429
|
+
var dryRun2 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
|
|
35430
|
+
var idempotencyKey = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real call. Generated when omitted; pass the same value when retrying so it is never charged twice");
|
|
35431
|
+
var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
|
|
35432
|
+
var dryQuery = (d) => ({ dryRun: d ? "true" : undefined });
|
|
35429
35433
|
var vpsTools = [
|
|
35430
35434
|
defineTool({
|
|
35431
35435
|
name: "list_vps_plans",
|
|
@@ -35455,7 +35459,7 @@ var vpsTools = [
|
|
|
35455
35459
|
name: "create_vps",
|
|
35456
35460
|
title: "Create VPS",
|
|
35457
35461
|
kind: "write",
|
|
35458
|
-
description:
|
|
35462
|
+
description: `Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Call with dryRun=true first, show the price and the wallet after it, and get the user's explicit OK. ${DRY} autoRenew=false creates it with auto-renew off (renew it with renew_vps). Provisioning takes a few minutes: poll get_vps.`,
|
|
35459
35463
|
input: {
|
|
35460
35464
|
projectId: exports_external.string().uuid(),
|
|
35461
35465
|
planId: exports_external.string(),
|
|
@@ -35463,9 +35467,12 @@ var vpsTools = [
|
|
|
35463
35467
|
osType: exports_external.string().describe("From the plan's osTemplates, e.g. ubuntu"),
|
|
35464
35468
|
osVersion: exports_external.string().describe("e.g. 24.04"),
|
|
35465
35469
|
rootPassword: password,
|
|
35466
|
-
billingPeriod: period.optional().describe("Default monthly")
|
|
35470
|
+
billingPeriod: period.optional().describe("Default monthly"),
|
|
35471
|
+
autoRenew: exports_external.boolean().optional().describe("false = no automatic renewal from the wallet (default true)"),
|
|
35472
|
+
dryRun: dryRun2,
|
|
35473
|
+
idempotencyKey
|
|
35467
35474
|
},
|
|
35468
|
-
run: (
|
|
35475
|
+
run: ({ dryRun: dryRun3, idempotencyKey: idempotencyKey2, ...body }, api2) => api2.api("POST", "/vps", { body, query: dryQuery(dryRun3), idempotencyKey: purchaseKey(dryRun3, idempotencyKey2) })
|
|
35469
35476
|
}),
|
|
35470
35477
|
defineTool({
|
|
35471
35478
|
name: "vps_power",
|
|
@@ -35519,17 +35526,17 @@ var vpsTools = [
|
|
|
35519
35526
|
name: "renew_vps",
|
|
35520
35527
|
title: "Renew VPS",
|
|
35521
35528
|
kind: "write",
|
|
35522
|
-
description:
|
|
35523
|
-
input: { vpsId, billingPeriod: period },
|
|
35524
|
-
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod } })
|
|
35529
|
+
description: `Extend a server's paid period. COSTS MONEY: call with dryRun=true first, show the price and the wallet after it, and get the user's OK. ${DRY}`,
|
|
35530
|
+
input: { vpsId, billingPeriod: period, dryRun: dryRun2, idempotencyKey },
|
|
35531
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
|
|
35525
35532
|
}),
|
|
35526
35533
|
defineTool({
|
|
35527
35534
|
name: "upgrade_vps",
|
|
35528
35535
|
title: "Upgrade VPS plan",
|
|
35529
35536
|
kind: "write",
|
|
35530
|
-
description:
|
|
35531
|
-
input: { vpsId, planId: exports_external.string() },
|
|
35532
|
-
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId } })
|
|
35537
|
+
description: `Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options. Call with dryRun=true first and get the user's OK. ${DRY} Servers with providerCode 2 only.`,
|
|
35538
|
+
input: { vpsId, planId: exports_external.string(), dryRun: dryRun2, idempotencyKey },
|
|
35539
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
|
|
35533
35540
|
}),
|
|
35534
35541
|
defineTool({
|
|
35535
35542
|
name: "get_vps_upgrade_options",
|
|
@@ -35556,11 +35563,20 @@ var vpsTools = [
|
|
|
35556
35563
|
input: { vpsId, range: exports_external.enum(["1h", "6h", "24h", "7d", "30d"]).optional() },
|
|
35557
35564
|
run: (a, api2) => api2.api("GET", `/vps/${a.vpsId}/metrics`, { query: { range: a.range } })
|
|
35558
35565
|
}),
|
|
35566
|
+
defineTool({
|
|
35567
|
+
name: "set_vps_auto_renew",
|
|
35568
|
+
title: "Set VPS auto-renew",
|
|
35569
|
+
kind: "write",
|
|
35570
|
+
idempotent: true,
|
|
35571
|
+
description: "Turn auto-renew on or off (sets the value, safe to repeat). With it off, the server is only renewed by renew_vps and expires otherwise.",
|
|
35572
|
+
input: { vpsId, autoRenew: exports_external.boolean() },
|
|
35573
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew`, { body: { autoRenew: a.autoRenew } })
|
|
35574
|
+
}),
|
|
35559
35575
|
defineTool({
|
|
35560
35576
|
name: "toggle_vps_auto_renew",
|
|
35561
35577
|
title: "Toggle VPS auto-renew",
|
|
35562
35578
|
kind: "write",
|
|
35563
|
-
description: "Flip auto-renew
|
|
35579
|
+
description: "Flip auto-renew to the other value; the response gives the new one. Prefer set_vps_auto_renew to reach a given state.",
|
|
35564
35580
|
input: { vpsId },
|
|
35565
35581
|
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew-toggle`)
|
|
35566
35582
|
}),
|
|
@@ -35889,8 +35905,8 @@ var vpsTools = [
|
|
|
35889
35905
|
// src/tools/domains.ts
|
|
35890
35906
|
var domainId = exports_external.number().int().positive().describe("Domain id (number, from list_my_domains)");
|
|
35891
35907
|
var hostId = exports_external.number().int().positive().describe("Child nameserver id (from list_child_nameservers)");
|
|
35892
|
-
var
|
|
35893
|
-
var
|
|
35908
|
+
var dryRun3 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
|
|
35909
|
+
var DRY2 = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
|
|
35894
35910
|
var record3 = exports_external.object({
|
|
35895
35911
|
name: exports_external.string().describe('"@" for the domain itself, or the part before the domain, e.g. "www" or "_dmarc.mail". A full name like "www.example.com" is accepted and stored as "www"'),
|
|
35896
35912
|
type: exports_external.enum(["A", "AAAA", "MX", "MXE", "CNAME", "SPF", "TXT"]),
|
|
@@ -35966,12 +35982,12 @@ var domainTools = [
|
|
|
35966
35982
|
name: "register_domain",
|
|
35967
35983
|
title: "Register domain",
|
|
35968
35984
|
kind: "write",
|
|
35969
|
-
description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${
|
|
35985
|
+
description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY2} The dry run includes WHOIS privacy when whoisProtection is true.`,
|
|
35970
35986
|
input: {
|
|
35971
35987
|
name: exports_external.string().describe("Full domain, e.g. tokosaya.id"),
|
|
35972
35988
|
nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(),
|
|
35973
35989
|
whoisProtection: exports_external.boolean().optional(),
|
|
35974
|
-
dryRun:
|
|
35990
|
+
dryRun: dryRun3
|
|
35975
35991
|
},
|
|
35976
35992
|
run: (a, api2) => api2.api("POST", "/domains/register", { body: { name: a.name, period: 1, nameservers: a.nameservers, whoisProtection: a.whoisProtection }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
35977
35993
|
}),
|
|
@@ -35979,16 +35995,16 @@ var domainTools = [
|
|
|
35979
35995
|
name: "transfer_domain",
|
|
35980
35996
|
title: "Transfer domain in",
|
|
35981
35997
|
kind: "write",
|
|
35982
|
-
description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${
|
|
35983
|
-
input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun:
|
|
35998
|
+
description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY2}`,
|
|
35999
|
+
input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun3 },
|
|
35984
36000
|
run: (a, api2) => api2.api("POST", "/domains/transfer", { body: { name: a.name, authCode: a.authCode, period: 1, nameservers: a.nameservers }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
35985
36001
|
}),
|
|
35986
36002
|
defineTool({
|
|
35987
36003
|
name: "renew_domain",
|
|
35988
36004
|
title: "Renew domain",
|
|
35989
36005
|
kind: "write",
|
|
35990
|
-
description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${
|
|
35991
|
-
input: { domainId, dryRun:
|
|
36006
|
+
description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY2}`,
|
|
36007
|
+
input: { domainId, dryRun: dryRun3 },
|
|
35992
36008
|
run: (a, api2) => api2.api("POST", `/domains/${a.domainId}/renew`, { body: { period: 1 }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
35993
36009
|
}),
|
|
35994
36010
|
defineTool({
|
|
@@ -36151,7 +36167,7 @@ var message = exports_external.object({
|
|
|
36151
36167
|
variables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
36152
36168
|
idempotencyKey: exports_external.string().max(128).optional()
|
|
36153
36169
|
});
|
|
36154
|
-
var
|
|
36170
|
+
var dryRun4 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
|
|
36155
36171
|
var idem2 = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real purchase. Generated when omitted; pass the same value when retrying so it is never charged twice");
|
|
36156
36172
|
var MONEY = "COSTS MONEY from the wallet. dryRun=true returns the charge (IDR) and walletBalanceAfter without charging or changing anything.";
|
|
36157
36173
|
var planCode = exports_external.string().min(1).max(50).describe("Plan code from list_email_plans");
|
|
@@ -36187,7 +36203,7 @@ var emailTools = [
|
|
|
36187
36203
|
title: "Buy email plan",
|
|
36188
36204
|
kind: "write",
|
|
36189
36205
|
description: `Start an email plan for the project (a free plan activates without charge). Works for a project with no email yet: the real call also sets email up (result.setup). ${MONEY} The dry run's mode says purchase, activate_free or already_active.`,
|
|
36190
|
-
input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun:
|
|
36206
|
+
input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun4, idempotencyKey: idem2 },
|
|
36191
36207
|
run: (a, api2) => api2.email("POST", "/plan", {
|
|
36192
36208
|
project: a.projectId,
|
|
36193
36209
|
body: { planCode: a.planCode, months: a.months },
|
|
@@ -36200,7 +36216,7 @@ var emailTools = [
|
|
|
36200
36216
|
title: "Renew email plan",
|
|
36201
36217
|
kind: "write",
|
|
36202
36218
|
description: `Renew the project's ended or expiring email plan (409 PLAN_ACTIVE while one is still running and cannot be extended). Repeats the last plan and cycle unless planCode / months are given. ${MONEY}`,
|
|
36203
|
-
input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun:
|
|
36219
|
+
input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun4, idempotencyKey: idem2 },
|
|
36204
36220
|
run: (a, api2) => api2.email("POST", "/plan/renew", {
|
|
36205
36221
|
project: a.projectId,
|
|
36206
36222
|
body: { planCode: a.planCode, months: a.months },
|
|
@@ -36213,7 +36229,7 @@ var emailTools = [
|
|
|
36213
36229
|
title: "Change email plan",
|
|
36214
36230
|
kind: "write",
|
|
36215
36231
|
description: `Move the active email plan to another plan. An upgrade applies now and charges the prorated difference to the same end date; a downgrade is scheduled for the next cycle (no refund). ${MONEY}`,
|
|
36216
|
-
input: { projectId, planCode, dryRun:
|
|
36232
|
+
input: { projectId, planCode, dryRun: dryRun4, idempotencyKey: idem2 },
|
|
36217
36233
|
run: (a, api2) => api2.email("POST", "/plan/change", {
|
|
36218
36234
|
project: a.projectId,
|
|
36219
36235
|
body: { planCode: a.planCode },
|
|
@@ -36230,7 +36246,7 @@ var emailTools = [
|
|
|
36230
36246
|
projectId,
|
|
36231
36247
|
extraMailboxes: exports_external.number().int().min(0).max(50).describe("Total extra mailboxes wanted (0-50)"),
|
|
36232
36248
|
extraStorageGb: exports_external.number().int().min(0).max(1000).describe("Total extra inbox storage in GB (0-1000)"),
|
|
36233
|
-
dryRun:
|
|
36249
|
+
dryRun: dryRun4,
|
|
36234
36250
|
idempotencyKey: idem2
|
|
36235
36251
|
},
|
|
36236
36252
|
run: (a, api2) => api2.email("POST", "/plan/addons", {
|
|
@@ -36800,7 +36816,7 @@ var emailTools = [
|
|
|
36800
36816
|
// src/tools/databases.ts
|
|
36801
36817
|
var databaseId = exports_external.string().uuid().describe("Database id (from list_databases)");
|
|
36802
36818
|
var environmentId = exports_external.string().uuid().describe("Environment id (from list_projects). Omit for the primary (production) branch.");
|
|
36803
|
-
var
|
|
36819
|
+
var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
|
|
36804
36820
|
var databaseTools = [
|
|
36805
36821
|
defineTool({
|
|
36806
36822
|
name: "list_database_plans",
|
|
@@ -36894,7 +36910,7 @@ var databaseTools = [
|
|
|
36894
36910
|
minCu: exports_external.number().min(0.25).max(10).optional(),
|
|
36895
36911
|
maxCu: exports_external.number().min(0.25).max(10).optional(),
|
|
36896
36912
|
suspendTimeoutSeconds: exports_external.number().int().min(0).max(604800).optional().describe("0 = never suspend, or 60-604800"),
|
|
36897
|
-
dryRun:
|
|
36913
|
+
dryRun: dryRun5
|
|
36898
36914
|
},
|
|
36899
36915
|
run: (a, api2) => api2.api("PATCH", `/databases/${a.databaseId}/settings`, {
|
|
36900
36916
|
body: { minCu: a.minCu, maxCu: a.maxCu, suspendTimeoutSeconds: a.suspendTimeoutSeconds },
|
|
@@ -36906,7 +36922,7 @@ var databaseTools = [
|
|
|
36906
36922
|
title: "Upgrade database plan / top up",
|
|
36907
36923
|
kind: "write",
|
|
36908
36924
|
description: "Buy a paid plan or top up credits. COSTS MONEY from the wallet: call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
|
|
36909
|
-
input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun:
|
|
36925
|
+
input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun5, idempotencyKey: exports_external.string().max(128).optional() },
|
|
36910
36926
|
run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
36911
36927
|
}),
|
|
36912
36928
|
defineTool({
|
|
@@ -37032,6 +37048,38 @@ var accountTools = [
|
|
|
37032
37048
|
input: { limit: limit2, offset },
|
|
37033
37049
|
run: (a, api2) => api2.api("GET", "/account/wallet/transactions", { query: { limit: a.limit, offset: a.offset } })
|
|
37034
37050
|
}),
|
|
37051
|
+
defineTool({
|
|
37052
|
+
name: "create_deposit",
|
|
37053
|
+
title: "Create deposit (top-up)",
|
|
37054
|
+
kind: "write",
|
|
37055
|
+
description: `Create a wallet top-up of amount IDR (whole number, 10000-50000000) and its payment link. Nothing is charged: give the user paymentUrl to pay (QRIS or bank virtual account, listed in paymentMethods; the link lasts 3 hours); never try to pay it yourself. Limits: one unpaid deposit at a time (409 DEPOSIT_PENDING: details.deposit is the unpaid one, send its paymentUrl instead), 10 minutes between deposits (429 DEPOSIT_COOLDOWN, details.nextAllowedAt), 10 a day (429 DEPOSIT_DAILY_LIMIT). dryRun=true creates nothing and says whether it would be accepted now. Then follow it with get_deposit. ${ACCOUNT_KEY}`,
|
|
37056
|
+
input: {
|
|
37057
|
+
amount: exports_external.number().int().min(1e4).max(50000000).describe("IDR, whole number"),
|
|
37058
|
+
dryRun: exports_external.boolean().optional().describe("true = only check (methods, whether it would be accepted now); nothing is created"),
|
|
37059
|
+
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key. Generated when omitted; pass the same value when retrying so only one deposit is created")
|
|
37060
|
+
},
|
|
37061
|
+
run: (a, api2) => api2.api("POST", "/account/deposits", {
|
|
37062
|
+
body: { amount: a.amount },
|
|
37063
|
+
query: { dryRun: a.dryRun ? "true" : undefined },
|
|
37064
|
+
idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey)
|
|
37065
|
+
})
|
|
37066
|
+
}),
|
|
37067
|
+
defineTool({
|
|
37068
|
+
name: "get_deposit",
|
|
37069
|
+
title: "Get deposit",
|
|
37070
|
+
kind: "read",
|
|
37071
|
+
description: `One deposit: status pending (paymentUrl to pay), paid (paidAt; the wallet was credited) or expired (not paid in time). ${ACCOUNT_KEY}`,
|
|
37072
|
+
input: { depositId: uid2("Deposit") },
|
|
37073
|
+
run: (a, api2) => api2.api("GET", `/account/deposits/${a.depositId}`)
|
|
37074
|
+
}),
|
|
37075
|
+
defineTool({
|
|
37076
|
+
name: "list_deposits",
|
|
37077
|
+
title: "List deposits",
|
|
37078
|
+
kind: "read",
|
|
37079
|
+
description: `Deposits (wallet top-ups), newest first, with status and amount. ${ACCOUNT_KEY}`,
|
|
37080
|
+
input: { limit: limit2, offset },
|
|
37081
|
+
run: (a, api2) => api2.api("GET", "/account/deposits", { query: { limit: a.limit, offset: a.offset } })
|
|
37082
|
+
}),
|
|
37035
37083
|
defineTool({
|
|
37036
37084
|
name: "list_invoices",
|
|
37037
37085
|
title: "List invoices",
|
|
@@ -37164,7 +37212,7 @@ var accountTools = [
|
|
|
37164
37212
|
var projectId2 = exports_external.string().uuid().optional().describe("Limit to one project (a project key is always limited to its own)");
|
|
37165
37213
|
var keyId = exports_external.string().uuid().optional().describe("Limit to one AI key (id from list_ai_keys)");
|
|
37166
37214
|
var model = exports_external.string().max(100).optional().describe("Limit to one model id");
|
|
37167
|
-
var
|
|
37215
|
+
var dryRun6 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
|
|
37168
37216
|
var ACCOUNT_KEY2 = "Needs a whole-account key (a key limited to one project gets 403 ACCOUNT_KEY_REQUIRED).";
|
|
37169
37217
|
var imageProject = exports_external.string().uuid().optional().describe("Project the images belong to (required with a whole-account key; a project key uses its own)");
|
|
37170
37218
|
var INLINE_MAX_BYTES = 1500000;
|
|
@@ -37249,7 +37297,7 @@ var aiTools = [
|
|
|
37249
37297
|
n: exports_external.number().int().min(1).max(10).optional().describe("Images in this request (up to the model's maxN; default 1)"),
|
|
37250
37298
|
images: exports_external.array(exports_external.string().url().startsWith("https://")).max(10).optional().describe("Reference images as https:// links (only models with refImages > 0)"),
|
|
37251
37299
|
projectId: imageProject,
|
|
37252
|
-
dryRun:
|
|
37300
|
+
dryRun: dryRun6,
|
|
37253
37301
|
wait: exports_external.number().int().min(0).max(55).optional().describe("Seconds to wait for the result (default 50; 0 = return the id at once)"),
|
|
37254
37302
|
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real request. Generated when omitted; pass the same value when retrying so it is never charged twice")
|
|
37255
37303
|
},
|
|
@@ -37299,7 +37347,7 @@ var aiTools = [
|
|
|
37299
37347
|
input: {
|
|
37300
37348
|
packId: exports_external.string().min(1).max(64).optional(),
|
|
37301
37349
|
amount: exports_external.number().min(1).max(1e9).optional().describe("IDR"),
|
|
37302
|
-
dryRun:
|
|
37350
|
+
dryRun: dryRun6,
|
|
37303
37351
|
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real top-up. Generated when omitted; pass the same value when retrying so it is never charged twice")
|
|
37304
37352
|
},
|
|
37305
37353
|
run: (a, api2) => {
|
|
@@ -37322,7 +37370,7 @@ var aiTools = [
|
|
|
37322
37370
|
enabled: exports_external.boolean(),
|
|
37323
37371
|
threshold: exports_external.number().min(1).optional().describe("Top up when the AI balance is below this (IDR)"),
|
|
37324
37372
|
amount: exports_external.number().min(1).optional().describe("How much to top up each time (IDR)"),
|
|
37325
|
-
dryRun:
|
|
37373
|
+
dryRun: dryRun6
|
|
37326
37374
|
},
|
|
37327
37375
|
run: (a, api2) => api2.api("PUT", "/ai/auto-topup", {
|
|
37328
37376
|
body: { enabled: a.enabled, threshold: a.threshold, amount: a.amount },
|
|
@@ -37358,7 +37406,7 @@ var generalTools = [
|
|
|
37358
37406
|
];
|
|
37359
37407
|
|
|
37360
37408
|
// src/server.ts
|
|
37361
|
-
var VERSION = "0.5.
|
|
37409
|
+
var VERSION = "0.5.11";
|
|
37362
37410
|
var INSTRUCTIONS = `Helipod is a cloud platform (Indonesia): containers, managed Postgres databases, VPS, domains, email and an AI gateway, billed from a prepaid wallet in Rupiah.
|
|
37363
37411
|
- Start with list_projects to find projects and environment ids. Account tools (get_wallet, invoices, statements, notifications, get_project_activity, support tickets) cover the account; AI tools (list_ai_models, get_ai_overview, get_ai_usage, AI keys, top-ups, generate_ai_image) cover the AI gateway.
|
|
37364
37412
|
- Anything that costs money (create/deploy/scale services, resize volumes, upgrade a database plan, create/renew/upgrade VPS, VPS backups, register/renew/transfer domains, email plans and inbox add-ons, AI top-ups and auto top-up, generating images): call it with dryRun=true first (or the price lookup when it has no dry run), tell the user the cost in IDR and the wallet balance after, and wait for an explicit yes before the real call. Never treat silence or an earlier general OK as a yes.
|
package/dist/stdio.js
CHANGED
|
@@ -35533,6 +35533,10 @@ var period = exports_external.enum(["monthly", "3month", "6month", "yearly"]);
|
|
|
35533
35533
|
var pointId = exports_external.string().describe("Restore point id (from list_vps_restore_points)");
|
|
35534
35534
|
var backupPath = exports_external.string().max(1024).describe("Absolute path inside the backup, e.g. /etc/nginx or /var/www/index.html");
|
|
35535
35535
|
var migrationId = exports_external.string().describe("Migration id (from list_vps_migrations)");
|
|
35536
|
+
var dryRun2 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
|
|
35537
|
+
var idempotencyKey = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real call. Generated when omitted; pass the same value when retrying so it is never charged twice");
|
|
35538
|
+
var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
|
|
35539
|
+
var dryQuery = (d) => ({ dryRun: d ? "true" : undefined });
|
|
35536
35540
|
var vpsTools = [
|
|
35537
35541
|
defineTool({
|
|
35538
35542
|
name: "list_vps_plans",
|
|
@@ -35562,7 +35566,7 @@ var vpsTools = [
|
|
|
35562
35566
|
name: "create_vps",
|
|
35563
35567
|
title: "Create VPS",
|
|
35564
35568
|
kind: "write",
|
|
35565
|
-
description:
|
|
35569
|
+
description: `Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Call with dryRun=true first, show the price and the wallet after it, and get the user's explicit OK. ${DRY} autoRenew=false creates it with auto-renew off (renew it with renew_vps). Provisioning takes a few minutes: poll get_vps.`,
|
|
35566
35570
|
input: {
|
|
35567
35571
|
projectId: exports_external.string().uuid(),
|
|
35568
35572
|
planId: exports_external.string(),
|
|
@@ -35570,9 +35574,12 @@ var vpsTools = [
|
|
|
35570
35574
|
osType: exports_external.string().describe("From the plan's osTemplates, e.g. ubuntu"),
|
|
35571
35575
|
osVersion: exports_external.string().describe("e.g. 24.04"),
|
|
35572
35576
|
rootPassword: password,
|
|
35573
|
-
billingPeriod: period.optional().describe("Default monthly")
|
|
35577
|
+
billingPeriod: period.optional().describe("Default monthly"),
|
|
35578
|
+
autoRenew: exports_external.boolean().optional().describe("false = no automatic renewal from the wallet (default true)"),
|
|
35579
|
+
dryRun: dryRun2,
|
|
35580
|
+
idempotencyKey
|
|
35574
35581
|
},
|
|
35575
|
-
run: (
|
|
35582
|
+
run: ({ dryRun: dryRun3, idempotencyKey: idempotencyKey2, ...body }, api2) => api2.api("POST", "/vps", { body, query: dryQuery(dryRun3), idempotencyKey: purchaseKey(dryRun3, idempotencyKey2) })
|
|
35576
35583
|
}),
|
|
35577
35584
|
defineTool({
|
|
35578
35585
|
name: "vps_power",
|
|
@@ -35626,17 +35633,17 @@ var vpsTools = [
|
|
|
35626
35633
|
name: "renew_vps",
|
|
35627
35634
|
title: "Renew VPS",
|
|
35628
35635
|
kind: "write",
|
|
35629
|
-
description:
|
|
35630
|
-
input: { vpsId, billingPeriod: period },
|
|
35631
|
-
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod } })
|
|
35636
|
+
description: `Extend a server's paid period. COSTS MONEY: call with dryRun=true first, show the price and the wallet after it, and get the user's OK. ${DRY}`,
|
|
35637
|
+
input: { vpsId, billingPeriod: period, dryRun: dryRun2, idempotencyKey },
|
|
35638
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
|
|
35632
35639
|
}),
|
|
35633
35640
|
defineTool({
|
|
35634
35641
|
name: "upgrade_vps",
|
|
35635
35642
|
title: "Upgrade VPS plan",
|
|
35636
35643
|
kind: "write",
|
|
35637
|
-
description:
|
|
35638
|
-
input: { vpsId, planId: exports_external.string() },
|
|
35639
|
-
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId } })
|
|
35644
|
+
description: `Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options. Call with dryRun=true first and get the user's OK. ${DRY} Servers with providerCode 2 only.`,
|
|
35645
|
+
input: { vpsId, planId: exports_external.string(), dryRun: dryRun2, idempotencyKey },
|
|
35646
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
|
|
35640
35647
|
}),
|
|
35641
35648
|
defineTool({
|
|
35642
35649
|
name: "get_vps_upgrade_options",
|
|
@@ -35663,11 +35670,20 @@ var vpsTools = [
|
|
|
35663
35670
|
input: { vpsId, range: exports_external.enum(["1h", "6h", "24h", "7d", "30d"]).optional() },
|
|
35664
35671
|
run: (a, api2) => api2.api("GET", `/vps/${a.vpsId}/metrics`, { query: { range: a.range } })
|
|
35665
35672
|
}),
|
|
35673
|
+
defineTool({
|
|
35674
|
+
name: "set_vps_auto_renew",
|
|
35675
|
+
title: "Set VPS auto-renew",
|
|
35676
|
+
kind: "write",
|
|
35677
|
+
idempotent: true,
|
|
35678
|
+
description: "Turn auto-renew on or off (sets the value, safe to repeat). With it off, the server is only renewed by renew_vps and expires otherwise.",
|
|
35679
|
+
input: { vpsId, autoRenew: exports_external.boolean() },
|
|
35680
|
+
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew`, { body: { autoRenew: a.autoRenew } })
|
|
35681
|
+
}),
|
|
35666
35682
|
defineTool({
|
|
35667
35683
|
name: "toggle_vps_auto_renew",
|
|
35668
35684
|
title: "Toggle VPS auto-renew",
|
|
35669
35685
|
kind: "write",
|
|
35670
|
-
description: "Flip auto-renew
|
|
35686
|
+
description: "Flip auto-renew to the other value; the response gives the new one. Prefer set_vps_auto_renew to reach a given state.",
|
|
35671
35687
|
input: { vpsId },
|
|
35672
35688
|
run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew-toggle`)
|
|
35673
35689
|
}),
|
|
@@ -35996,8 +36012,8 @@ var vpsTools = [
|
|
|
35996
36012
|
// src/tools/domains.ts
|
|
35997
36013
|
var domainId = exports_external.number().int().positive().describe("Domain id (number, from list_my_domains)");
|
|
35998
36014
|
var hostId = exports_external.number().int().positive().describe("Child nameserver id (from list_child_nameservers)");
|
|
35999
|
-
var
|
|
36000
|
-
var
|
|
36015
|
+
var dryRun3 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
|
|
36016
|
+
var DRY2 = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
|
|
36001
36017
|
var record3 = exports_external.object({
|
|
36002
36018
|
name: exports_external.string().describe('"@" for the domain itself, or the part before the domain, e.g. "www" or "_dmarc.mail". A full name like "www.example.com" is accepted and stored as "www"'),
|
|
36003
36019
|
type: exports_external.enum(["A", "AAAA", "MX", "MXE", "CNAME", "SPF", "TXT"]),
|
|
@@ -36073,12 +36089,12 @@ var domainTools = [
|
|
|
36073
36089
|
name: "register_domain",
|
|
36074
36090
|
title: "Register domain",
|
|
36075
36091
|
kind: "write",
|
|
36076
|
-
description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${
|
|
36092
|
+
description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY2} The dry run includes WHOIS privacy when whoisProtection is true.`,
|
|
36077
36093
|
input: {
|
|
36078
36094
|
name: exports_external.string().describe("Full domain, e.g. tokosaya.id"),
|
|
36079
36095
|
nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(),
|
|
36080
36096
|
whoisProtection: exports_external.boolean().optional(),
|
|
36081
|
-
dryRun:
|
|
36097
|
+
dryRun: dryRun3
|
|
36082
36098
|
},
|
|
36083
36099
|
run: (a, api2) => api2.api("POST", "/domains/register", { body: { name: a.name, period: 1, nameservers: a.nameservers, whoisProtection: a.whoisProtection }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
36084
36100
|
}),
|
|
@@ -36086,16 +36102,16 @@ var domainTools = [
|
|
|
36086
36102
|
name: "transfer_domain",
|
|
36087
36103
|
title: "Transfer domain in",
|
|
36088
36104
|
kind: "write",
|
|
36089
|
-
description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${
|
|
36090
|
-
input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun:
|
|
36105
|
+
description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY2}`,
|
|
36106
|
+
input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun3 },
|
|
36091
36107
|
run: (a, api2) => api2.api("POST", "/domains/transfer", { body: { name: a.name, authCode: a.authCode, period: 1, nameservers: a.nameservers }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
36092
36108
|
}),
|
|
36093
36109
|
defineTool({
|
|
36094
36110
|
name: "renew_domain",
|
|
36095
36111
|
title: "Renew domain",
|
|
36096
36112
|
kind: "write",
|
|
36097
|
-
description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${
|
|
36098
|
-
input: { domainId, dryRun:
|
|
36113
|
+
description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY2}`,
|
|
36114
|
+
input: { domainId, dryRun: dryRun3 },
|
|
36099
36115
|
run: (a, api2) => api2.api("POST", `/domains/${a.domainId}/renew`, { body: { period: 1 }, query: { dryRun: a.dryRun ? "true" : undefined } })
|
|
36100
36116
|
}),
|
|
36101
36117
|
defineTool({
|
|
@@ -36258,7 +36274,7 @@ var message = exports_external.object({
|
|
|
36258
36274
|
variables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
36259
36275
|
idempotencyKey: exports_external.string().max(128).optional()
|
|
36260
36276
|
});
|
|
36261
|
-
var
|
|
36277
|
+
var dryRun4 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
|
|
36262
36278
|
var idem2 = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real purchase. Generated when omitted; pass the same value when retrying so it is never charged twice");
|
|
36263
36279
|
var MONEY = "COSTS MONEY from the wallet. dryRun=true returns the charge (IDR) and walletBalanceAfter without charging or changing anything.";
|
|
36264
36280
|
var planCode = exports_external.string().min(1).max(50).describe("Plan code from list_email_plans");
|
|
@@ -36294,7 +36310,7 @@ var emailTools = [
|
|
|
36294
36310
|
title: "Buy email plan",
|
|
36295
36311
|
kind: "write",
|
|
36296
36312
|
description: `Start an email plan for the project (a free plan activates without charge). Works for a project with no email yet: the real call also sets email up (result.setup). ${MONEY} The dry run's mode says purchase, activate_free or already_active.`,
|
|
36297
|
-
input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun:
|
|
36313
|
+
input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun4, idempotencyKey: idem2 },
|
|
36298
36314
|
run: (a, api2) => api2.email("POST", "/plan", {
|
|
36299
36315
|
project: a.projectId,
|
|
36300
36316
|
body: { planCode: a.planCode, months: a.months },
|
|
@@ -36307,7 +36323,7 @@ var emailTools = [
|
|
|
36307
36323
|
title: "Renew email plan",
|
|
36308
36324
|
kind: "write",
|
|
36309
36325
|
description: `Renew the project's ended or expiring email plan (409 PLAN_ACTIVE while one is still running and cannot be extended). Repeats the last plan and cycle unless planCode / months are given. ${MONEY}`,
|
|
36310
|
-
input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun:
|
|
36326
|
+
input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun4, idempotencyKey: idem2 },
|
|
36311
36327
|
run: (a, api2) => api2.email("POST", "/plan/renew", {
|
|
36312
36328
|
project: a.projectId,
|
|
36313
36329
|
body: { planCode: a.planCode, months: a.months },
|
|
@@ -36320,7 +36336,7 @@ var emailTools = [
|
|
|
36320
36336
|
title: "Change email plan",
|
|
36321
36337
|
kind: "write",
|
|
36322
36338
|
description: `Move the active email plan to another plan. An upgrade applies now and charges the prorated difference to the same end date; a downgrade is scheduled for the next cycle (no refund). ${MONEY}`,
|
|
36323
|
-
input: { projectId, planCode, dryRun:
|
|
36339
|
+
input: { projectId, planCode, dryRun: dryRun4, idempotencyKey: idem2 },
|
|
36324
36340
|
run: (a, api2) => api2.email("POST", "/plan/change", {
|
|
36325
36341
|
project: a.projectId,
|
|
36326
36342
|
body: { planCode: a.planCode },
|
|
@@ -36337,7 +36353,7 @@ var emailTools = [
|
|
|
36337
36353
|
projectId,
|
|
36338
36354
|
extraMailboxes: exports_external.number().int().min(0).max(50).describe("Total extra mailboxes wanted (0-50)"),
|
|
36339
36355
|
extraStorageGb: exports_external.number().int().min(0).max(1000).describe("Total extra inbox storage in GB (0-1000)"),
|
|
36340
|
-
dryRun:
|
|
36356
|
+
dryRun: dryRun4,
|
|
36341
36357
|
idempotencyKey: idem2
|
|
36342
36358
|
},
|
|
36343
36359
|
run: (a, api2) => api2.email("POST", "/plan/addons", {
|
|
@@ -36907,7 +36923,7 @@ var emailTools = [
|
|
|
36907
36923
|
// src/tools/databases.ts
|
|
36908
36924
|
var databaseId = exports_external.string().uuid().describe("Database id (from list_databases)");
|
|
36909
36925
|
var environmentId = exports_external.string().uuid().describe("Environment id (from list_projects). Omit for the primary (production) branch.");
|
|
36910
|
-
var
|
|
36926
|
+
var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
|
|
36911
36927
|
var databaseTools = [
|
|
36912
36928
|
defineTool({
|
|
36913
36929
|
name: "list_database_plans",
|
|
@@ -37001,7 +37017,7 @@ var databaseTools = [
|
|
|
37001
37017
|
minCu: exports_external.number().min(0.25).max(10).optional(),
|
|
37002
37018
|
maxCu: exports_external.number().min(0.25).max(10).optional(),
|
|
37003
37019
|
suspendTimeoutSeconds: exports_external.number().int().min(0).max(604800).optional().describe("0 = never suspend, or 60-604800"),
|
|
37004
|
-
dryRun:
|
|
37020
|
+
dryRun: dryRun5
|
|
37005
37021
|
},
|
|
37006
37022
|
run: (a, api2) => api2.api("PATCH", `/databases/${a.databaseId}/settings`, {
|
|
37007
37023
|
body: { minCu: a.minCu, maxCu: a.maxCu, suspendTimeoutSeconds: a.suspendTimeoutSeconds },
|
|
@@ -37013,7 +37029,7 @@ var databaseTools = [
|
|
|
37013
37029
|
title: "Upgrade database plan / top up",
|
|
37014
37030
|
kind: "write",
|
|
37015
37031
|
description: "Buy a paid plan or top up credits. COSTS MONEY from the wallet: call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
|
|
37016
|
-
input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun:
|
|
37032
|
+
input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun5, idempotencyKey: exports_external.string().max(128).optional() },
|
|
37017
37033
|
run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
37018
37034
|
}),
|
|
37019
37035
|
defineTool({
|
|
@@ -37139,6 +37155,38 @@ var accountTools = [
|
|
|
37139
37155
|
input: { limit: limit2, offset },
|
|
37140
37156
|
run: (a, api2) => api2.api("GET", "/account/wallet/transactions", { query: { limit: a.limit, offset: a.offset } })
|
|
37141
37157
|
}),
|
|
37158
|
+
defineTool({
|
|
37159
|
+
name: "create_deposit",
|
|
37160
|
+
title: "Create deposit (top-up)",
|
|
37161
|
+
kind: "write",
|
|
37162
|
+
description: `Create a wallet top-up of amount IDR (whole number, 10000-50000000) and its payment link. Nothing is charged: give the user paymentUrl to pay (QRIS or bank virtual account, listed in paymentMethods; the link lasts 3 hours); never try to pay it yourself. Limits: one unpaid deposit at a time (409 DEPOSIT_PENDING: details.deposit is the unpaid one, send its paymentUrl instead), 10 minutes between deposits (429 DEPOSIT_COOLDOWN, details.nextAllowedAt), 10 a day (429 DEPOSIT_DAILY_LIMIT). dryRun=true creates nothing and says whether it would be accepted now. Then follow it with get_deposit. ${ACCOUNT_KEY}`,
|
|
37163
|
+
input: {
|
|
37164
|
+
amount: exports_external.number().int().min(1e4).max(50000000).describe("IDR, whole number"),
|
|
37165
|
+
dryRun: exports_external.boolean().optional().describe("true = only check (methods, whether it would be accepted now); nothing is created"),
|
|
37166
|
+
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key. Generated when omitted; pass the same value when retrying so only one deposit is created")
|
|
37167
|
+
},
|
|
37168
|
+
run: (a, api2) => api2.api("POST", "/account/deposits", {
|
|
37169
|
+
body: { amount: a.amount },
|
|
37170
|
+
query: { dryRun: a.dryRun ? "true" : undefined },
|
|
37171
|
+
idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey)
|
|
37172
|
+
})
|
|
37173
|
+
}),
|
|
37174
|
+
defineTool({
|
|
37175
|
+
name: "get_deposit",
|
|
37176
|
+
title: "Get deposit",
|
|
37177
|
+
kind: "read",
|
|
37178
|
+
description: `One deposit: status pending (paymentUrl to pay), paid (paidAt; the wallet was credited) or expired (not paid in time). ${ACCOUNT_KEY}`,
|
|
37179
|
+
input: { depositId: uid2("Deposit") },
|
|
37180
|
+
run: (a, api2) => api2.api("GET", `/account/deposits/${a.depositId}`)
|
|
37181
|
+
}),
|
|
37182
|
+
defineTool({
|
|
37183
|
+
name: "list_deposits",
|
|
37184
|
+
title: "List deposits",
|
|
37185
|
+
kind: "read",
|
|
37186
|
+
description: `Deposits (wallet top-ups), newest first, with status and amount. ${ACCOUNT_KEY}`,
|
|
37187
|
+
input: { limit: limit2, offset },
|
|
37188
|
+
run: (a, api2) => api2.api("GET", "/account/deposits", { query: { limit: a.limit, offset: a.offset } })
|
|
37189
|
+
}),
|
|
37142
37190
|
defineTool({
|
|
37143
37191
|
name: "list_invoices",
|
|
37144
37192
|
title: "List invoices",
|
|
@@ -37271,7 +37319,7 @@ var accountTools = [
|
|
|
37271
37319
|
var projectId2 = exports_external.string().uuid().optional().describe("Limit to one project (a project key is always limited to its own)");
|
|
37272
37320
|
var keyId = exports_external.string().uuid().optional().describe("Limit to one AI key (id from list_ai_keys)");
|
|
37273
37321
|
var model = exports_external.string().max(100).optional().describe("Limit to one model id");
|
|
37274
|
-
var
|
|
37322
|
+
var dryRun6 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
|
|
37275
37323
|
var ACCOUNT_KEY2 = "Needs a whole-account key (a key limited to one project gets 403 ACCOUNT_KEY_REQUIRED).";
|
|
37276
37324
|
var imageProject = exports_external.string().uuid().optional().describe("Project the images belong to (required with a whole-account key; a project key uses its own)");
|
|
37277
37325
|
var INLINE_MAX_BYTES = 1500000;
|
|
@@ -37356,7 +37404,7 @@ var aiTools = [
|
|
|
37356
37404
|
n: exports_external.number().int().min(1).max(10).optional().describe("Images in this request (up to the model's maxN; default 1)"),
|
|
37357
37405
|
images: exports_external.array(exports_external.string().url().startsWith("https://")).max(10).optional().describe("Reference images as https:// links (only models with refImages > 0)"),
|
|
37358
37406
|
projectId: imageProject,
|
|
37359
|
-
dryRun:
|
|
37407
|
+
dryRun: dryRun6,
|
|
37360
37408
|
wait: exports_external.number().int().min(0).max(55).optional().describe("Seconds to wait for the result (default 50; 0 = return the id at once)"),
|
|
37361
37409
|
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real request. Generated when omitted; pass the same value when retrying so it is never charged twice")
|
|
37362
37410
|
},
|
|
@@ -37406,7 +37454,7 @@ var aiTools = [
|
|
|
37406
37454
|
input: {
|
|
37407
37455
|
packId: exports_external.string().min(1).max(64).optional(),
|
|
37408
37456
|
amount: exports_external.number().min(1).max(1e9).optional().describe("IDR"),
|
|
37409
|
-
dryRun:
|
|
37457
|
+
dryRun: dryRun6,
|
|
37410
37458
|
idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real top-up. Generated when omitted; pass the same value when retrying so it is never charged twice")
|
|
37411
37459
|
},
|
|
37412
37460
|
run: (a, api2) => {
|
|
@@ -37429,7 +37477,7 @@ var aiTools = [
|
|
|
37429
37477
|
enabled: exports_external.boolean(),
|
|
37430
37478
|
threshold: exports_external.number().min(1).optional().describe("Top up when the AI balance is below this (IDR)"),
|
|
37431
37479
|
amount: exports_external.number().min(1).optional().describe("How much to top up each time (IDR)"),
|
|
37432
|
-
dryRun:
|
|
37480
|
+
dryRun: dryRun6
|
|
37433
37481
|
},
|
|
37434
37482
|
run: (a, api2) => api2.api("PUT", "/ai/auto-topup", {
|
|
37435
37483
|
body: { enabled: a.enabled, threshold: a.threshold, amount: a.amount },
|
|
@@ -37465,7 +37513,7 @@ var generalTools = [
|
|
|
37465
37513
|
];
|
|
37466
37514
|
|
|
37467
37515
|
// src/server.ts
|
|
37468
|
-
var VERSION = "0.5.
|
|
37516
|
+
var VERSION = "0.5.11";
|
|
37469
37517
|
var INSTRUCTIONS = `Helipod is a cloud platform (Indonesia): containers, managed Postgres databases, VPS, domains, email and an AI gateway, billed from a prepaid wallet in Rupiah.
|
|
37470
37518
|
- Start with list_projects to find projects and environment ids. Account tools (get_wallet, invoices, statements, notifications, get_project_activity, support tickets) cover the account; AI tools (list_ai_models, get_ai_overview, get_ai_usage, AI keys, top-ups, generate_ai_image) cover the AI gateway.
|
|
37471
37519
|
- Anything that costs money (create/deploy/scale services, resize volumes, upgrade a database plan, create/renew/upgrade VPS, VPS backups, register/renew/transfer domains, email plans and inbox add-ons, AI top-ups and auto top-up, generating images): call it with dryRun=true first (or the price lookup when it has no dry run), tell the user the cost in IDR and the wallet balance after, and wait for an explicit yes before the real call. Never treat silence or an earlier general OK as a yes.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helipod-mcp",
|
|
3
3
|
"mcpName": "io.helipod/helipod",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.11",
|
|
5
5
|
"description": "MCP server for Helipod: containers, databases, VPS, domains and email from Claude Code, Codex, Cursor and other AI tools.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
package/skills/helipod/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: Use when the user mentions Helipod (helipod.io) or wants to work wi
|
|
|
5
5
|
|
|
6
6
|
# Helipod
|
|
7
7
|
|
|
8
|
-
Helipod is a cloud platform in Indonesia: containers (deploy apps from Git, images or Dockerfiles), managed Postgres databases, VPS, domain registration and DNS, email sending, and an AI gateway. Everything is billed from a prepaid wallet in Rupiah (IDR).
|
|
8
|
+
Helipod is a cloud platform in Indonesia: containers (deploy apps from Git, images or Dockerfiles), managed Postgres databases, VPS, domain registration and DNS, email sending, and an AI gateway. Everything is billed from a prepaid wallet in Rupiah (IDR). When the wallet is too low, `create_deposit` makes a top-up payment link: give the user its `paymentUrl` to pay (QRIS or bank virtual account) and check it with `get_deposit`; never pay it yourself.
|
|
9
9
|
|
|
10
10
|
## Access
|
|
11
11
|
|
|
@@ -19,7 +19,7 @@ Helipod is a cloud platform in Indonesia: containers (deploy apps from Git, imag
|
|
|
19
19
|
|
|
20
20
|
## Rules (always)
|
|
21
21
|
|
|
22
|
-
1. **Money**: before anything that costs money (create/deploy/scale a service, create/renew/upgrade a VPS, register/renew/transfer a domain, deploy a template), get the price first (`dryRun: true` on container calls
|
|
22
|
+
1. **Money**: before anything that costs money (create/deploy/scale a service, create/renew/upgrade a VPS, register/renew/transfer a domain, deploy a template), get the price first (`dryRun: true` on container, VPS and domain calls; `domain_prices` and `list_vps_plans` to compare), tell the user the amount in IDR, and wait for a clear yes.
|
|
23
23
|
2. **Destructive**: before deleting a service/volume, terminating/reinstalling a VPS, rolling back a snapshot or replacing DNS records, say exactly what will be lost and wait for a clear yes.
|
|
24
24
|
3. **Background work**: deploys and VPS actions are asynchronous. Poll (`get_deployment`, `get_vps`) every few seconds until they settle; on a failed deploy read `get_deployment_logs` and explain the cause.
|
|
25
25
|
4. **Secrets**: never print passwords, API keys or secret variable values unless the user asks. Store app secrets as secret variables, never in code.
|
|
@@ -11,9 +11,9 @@ Follow the `helipod` skill rules. A VPS is prepaid per period: creating one char
|
|
|
11
11
|
|
|
12
12
|
1. `list_vps_plans` → present 2 or 3 fitting plans with CPU, RAM, disk and `effectiveMonthly` (IDR). Note `osTemplates`.
|
|
13
13
|
2. `list_projects` → the project it belongs to (required).
|
|
14
|
-
3. Confirm with the user: plan, OS, billing period (`monthly`, `3month`, `6month`, `yearly`) and
|
|
14
|
+
3. `create_vps` with `dryRun: true` → the exact `price` and `walletBalanceAfter` (nothing is charged). Confirm with the user: plan, OS, billing period (`monthly`, `3month`, `6month`, `yearly`) and that price. Pass `autoRenew: false` if they want to renew by hand.
|
|
15
15
|
4. Generate a strong password (12 to 30 chars, upper, lower, digit, one of `!@#$%^*`) unless the user gives one; do not print it back unless asked.
|
|
16
|
-
5. `create_vps` → poll `get_vps` every 10 s until `running` (a few minutes) or `error` (`lastJobError` says why; a failed create is refunded).
|
|
16
|
+
5. `create_vps` again without `dryRun`, with an `idempotencyKey` (reuse it if you retry) → poll `get_vps` every 10 s until `running` (a few minutes) or `error` (`lastJobError` says why; a failed create is refunded).
|
|
17
17
|
6. Login: `get_vps_credentials` (Linux: `ssh root@<ip>`; Windows: RDP as Administrator, port 3389).
|
|
18
18
|
|
|
19
19
|
## Manage
|
|
@@ -22,7 +22,7 @@ Follow the `helipod` skill rules. A VPS is prepaid per period: creating one char
|
|
|
22
22
|
- Firewall: `list_firewall_rules`, `add_firewall_rules` (e.g. `{ "Protocol": "TCP", "Port": "443", "CidrBlock": "0.0.0.0/0", "Action": "ACCEPT" }`), `remove_firewall_rules` by id. Never remove the SSH rule (port 22) without asking.
|
|
23
23
|
- Snapshots: `create_snapshot` before risky changes; `apply_snapshot` rolls the disk back (data after it is lost: confirm).
|
|
24
24
|
- Reinstall: `reinstall_vps` wipes the disk: confirm explicitly.
|
|
25
|
-
- Billing: `
|
|
25
|
+
- Billing: `renew_vps` / `upgrade_vps` with `dryRun: true` first (price and wallet after), wait for the user's yes, then the real call with an `idempotencyKey`. `get_vps_renewal_options` lists every period; `upgrade_vps` charges the difference and restarts. Auto-renew: `set_vps_auto_renew` (on or off; `toggle_vps_auto_renew` flips it).
|
|
26
26
|
- Terminate: `terminate_vps` is permanent, no refund: confirm explicitly.
|
|
27
27
|
|
|
28
28
|
## Migrate a server to Helipod (free)
|