helipod-mcp 0.5.11 → 0.5.12
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/dist/http.js +22 -10
- package/dist/index.js +22 -10
- package/dist/stdio.js +22 -10
- package/package.json +1 -1
- package/skills/helipod/SKILL.md +2 -2
- package/skills/helipod-deploy/SKILL.md +9 -4
package/dist/http.js
CHANGED
|
@@ -35730,7 +35730,7 @@ function purchaseKey(dryRun, given) {
|
|
|
35730
35730
|
|
|
35731
35731
|
// src/tools/containers.ts
|
|
35732
35732
|
var id = (what) => exports_external.string().uuid().describe(`${what} id (UUID)`);
|
|
35733
|
-
var dryRun = exports_external.boolean().optional().describe("true: return the
|
|
35733
|
+
var dryRun = exports_external.boolean().optional().describe("true: return the cost only (dailyCost list price, walletDailyCost what the wallet pays after the bundle), change nothing");
|
|
35734
35734
|
var idem = exports_external.string().min(8).max(128).optional().describe("Idempotency-Key: a retry with the same value returns the first result instead of doing it twice");
|
|
35735
35735
|
var C = "/containers";
|
|
35736
35736
|
var port = exports_external.object({
|
|
@@ -35739,6 +35739,14 @@ var port = exports_external.object({
|
|
|
35739
35739
|
public: exports_external.boolean().optional().describe("http ports: give it a *.helipod.app domain (default true)")
|
|
35740
35740
|
});
|
|
35741
35741
|
var containerTools = [
|
|
35742
|
+
defineTool({
|
|
35743
|
+
name: "get_bundle",
|
|
35744
|
+
title: "Container bundle",
|
|
35745
|
+
kind: "read",
|
|
35746
|
+
description: "The account's container bundle: whether one is active, until when, quota used / max (vCPU, GB RAM, GB storage), what it covers and what it does not. Container services within the quota cost the wallet NOTHING: read this before telling the user to top up for a container deploy. The bundle covers containers only (not VPS, databases, email, domains or AI).",
|
|
35747
|
+
input: {},
|
|
35748
|
+
run: (_a3, api2) => api2.api("GET", `${C}/bundle`)
|
|
35749
|
+
}),
|
|
35742
35750
|
defineTool({
|
|
35743
35751
|
name: "list_projects",
|
|
35744
35752
|
title: "List projects",
|
|
@@ -35884,7 +35892,7 @@ var containerTools = [
|
|
|
35884
35892
|
name: "create_service",
|
|
35885
35893
|
title: "Create service",
|
|
35886
35894
|
kind: "write",
|
|
35887
|
-
description: "Create a service from a container image, a Git repository or an inline Dockerfile, and (unless deploy=false) start its first deploy. Private GitHub / GitLab repos work once connected (get_git_connections, list_git_repos); such services also redeploy on every push. A private repo that is not connected is refused (409 GITHUB_NOT_CONNECTED / GITLAB_NOT_CONNECTED / REPO_NOT_ACCESSIBLE, nothing created): give the user details.connectUrl and retry after they connect. COSTS MONEY once deployed
|
|
35895
|
+
description: "Create a service from a container image, a Git repository or an inline Dockerfile, and (unless deploy=false) start its first deploy. Private GitHub / GitLab repos work once connected (get_git_connections, list_git_repos); such services also redeploy on every push. A private repo that is not connected is refused (409 GITHUB_NOT_CONNECTED / GITLAB_NOT_CONNECTED / REPO_NOT_ACCESSIBLE, nothing created): give the user details.connectUrl and retry after they connect. COSTS MONEY once deployed, beyond the account's bundle: billed daily, from the bundle quota first and the wallet only beyond it. Call with dryRun=true first, tell the user what the wallet pays (walletDailyCost) and get their OK. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35888
35896
|
input: {
|
|
35889
35897
|
environmentId: id("Environment"),
|
|
35890
35898
|
name: exports_external.string().regex(/^[A-Za-z][A-Za-z0-9_-]{0,39}$/).describe("Service name (letters, digits, _ -; starts with a letter). Also used in references like ${{name.KEY}}"),
|
|
@@ -35966,7 +35974,7 @@ var containerTools = [
|
|
|
35966
35974
|
name: "deploy_service",
|
|
35967
35975
|
title: "Deploy service",
|
|
35968
35976
|
kind: "write",
|
|
35969
|
-
description: "Start a new deployment (Git services build the latest commit). COSTS MONEY: charges today's cost when the build starts
|
|
35977
|
+
description: "Start a new deployment (Git services build the latest commit). COSTS MONEY beyond the bundle: charges today's cost when the build starts (refunded if it fails), nothing within an active bundle's quota. dryRun=true returns the cost only. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance. The deployment runs in the background; get_deployment returns its status.",
|
|
35970
35978
|
input: { serviceId: id("Service"), dryRun, idempotencyKey: idem },
|
|
35971
35979
|
run: (a, api2) => api2.api("POST", `${C}/services/${a.serviceId}/deployments`, { query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
35972
35980
|
}),
|
|
@@ -36074,7 +36082,7 @@ var containerTools = [
|
|
|
36074
36082
|
title: "Scale service",
|
|
36075
36083
|
kind: "write",
|
|
36076
36084
|
idempotent: true,
|
|
36077
|
-
description: "Change replicas, CPU or RAM (applies right away). COSTS MONEY
|
|
36085
|
+
description: "Change replicas, CPU or RAM (applies right away). An increase COSTS MONEY unless the bundle covers it: call with dryRun=true first to show currentDailyCost vs newDailyCost and walletDailyCost, and get the user's OK. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
36078
36086
|
input: {
|
|
36079
36087
|
serviceId: id("Service"),
|
|
36080
36088
|
replicas: exports_external.number().int().min(1).max(20).optional(),
|
|
@@ -36122,7 +36130,7 @@ var containerTools = [
|
|
|
36122
36130
|
name: "resize_volume",
|
|
36123
36131
|
title: "Resize volume",
|
|
36124
36132
|
kind: "write",
|
|
36125
|
-
description: "Change a volume's size (1-50 GB; never below the space already used). A larger volume COSTS MONEY: it raises the daily cost, charged pro rata when the service is redeployed and in full from the next renewal. dryRun=true returns currentDailyCost and
|
|
36133
|
+
description: "Change a volume's size (1-50 GB; never below the space already used). A larger volume COSTS MONEY: it raises the daily cost, charged pro rata when the service is redeployed and in full from the next renewal. dryRun=true returns currentDailyCost, newDailyCost and walletDailyCost (IDR) without changing anything. Applies on the next deploy. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
36126
36134
|
input: {
|
|
36127
36135
|
serviceId: id("Service"),
|
|
36128
36136
|
volumeId: id("Volume"),
|
|
@@ -36244,20 +36252,21 @@ var containerTools = [
|
|
|
36244
36252
|
name: "deploy_template",
|
|
36245
36253
|
title: "Deploy template",
|
|
36246
36254
|
kind: "write",
|
|
36247
|
-
description: "Create every service of a template in an environment and deploy them. COSTS MONEY
|
|
36255
|
+
description: "Create every service of a template in an environment and deploy them. COSTS MONEY beyond the bundle: each service is billed daily (bundle quota first, then the wallet). Call with dryRun=true first. " + "Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
36248
36256
|
input: {
|
|
36249
36257
|
environmentId: id("Environment"),
|
|
36250
36258
|
slug: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/),
|
|
36251
36259
|
inputs: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Answers to the template's inputs"),
|
|
36260
|
+
dryRun,
|
|
36252
36261
|
idempotencyKey: idem
|
|
36253
36262
|
},
|
|
36254
|
-
run: (a, api2) => api2.api("POST", `${C}/environments/${a.environmentId}/templates/${a.slug}`, { body: { inputs: a.inputs }, idempotencyKey: a.idempotencyKey })
|
|
36263
|
+
run: (a, api2) => api2.api("POST", `${C}/environments/${a.environmentId}/templates/${a.slug}`, { body: { inputs: a.inputs }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
36255
36264
|
}),
|
|
36256
36265
|
defineTool({
|
|
36257
36266
|
name: "create_stack",
|
|
36258
36267
|
title: "Create stack from spec",
|
|
36259
36268
|
kind: "write",
|
|
36260
|
-
description: "Create several connected services from a template spec ({ version: 1, inputs?, services: [...] }) and deploy them in dependency order. COSTS MONEY: dryRun=true first. See https://docs.helipod.io/docs/container-api/templates-and-stacks",
|
|
36269
|
+
description: "Create several connected services from a template spec ({ version: 1, inputs?, services: [...] }) and deploy them in dependency order. COSTS MONEY beyond the bundle: billed daily (bundle quota first, then the wallet). dryRun=true first. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance. See https://docs.helipod.io/docs/container-api/templates-and-stacks",
|
|
36261
36270
|
input: {
|
|
36262
36271
|
environmentId: id("Environment"),
|
|
36263
36272
|
spec: exports_external.record(exports_external.string(), exports_external.any()).describe("Template spec object"),
|
|
@@ -37776,7 +37785,7 @@ var databaseTools = [
|
|
|
37776
37785
|
name: "upgrade_database",
|
|
37777
37786
|
title: "Upgrade database plan / top up",
|
|
37778
37787
|
kind: "write",
|
|
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.",
|
|
37788
|
+
description: "Buy a paid plan or top up credits. Only when the user asks for more than the FREE plan gives (never to get started: create_database is free). COSTS MONEY from the wallet (bundles do not cover it): call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
|
|
37780
37789
|
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() },
|
|
37781
37790
|
run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
37782
37791
|
}),
|
|
@@ -38261,10 +38270,13 @@ var generalTools = [
|
|
|
38261
38270
|
];
|
|
38262
38271
|
|
|
38263
38272
|
// src/server.ts
|
|
38264
|
-
var VERSION = "0.5.
|
|
38273
|
+
var VERSION = "0.5.12";
|
|
38265
38274
|
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.
|
|
38266
38275
|
- 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.
|
|
38267
38276
|
- 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.
|
|
38277
|
+
- What the user really pays: container services are paid from the account's bundle first (get_bundle: a monthly bundle with a CPU / RAM / storage quota) and from the wallet only beyond it. Container dry runs return walletDailyCost (what the wallet pays per day) and bundle.coversIt: when the bundle covers it, say it is included in their bundle and deploy after their OK; never ask for a top-up for something walletDailyCost says is 0, whatever the balance. Quote dailyCost only as the price without a bundle. The bundle covers containers only: VPS, domains, AI, paid database plans and paid email plans are paid from the wallet.
|
|
38278
|
+
- Free first: the managed Postgres database (create_database) starts on the FREE plan and email (buy_email_plan with the FREE plan) costs nothing. For a database, use create_database rather than running Postgres as a container service, and do not offer upgrade_database or a paid email plan unless the user asks for more or hits a limit.
|
|
38279
|
+
- Deploying an app, step by step: list_projects (pick the project and its production environmentId) -> get_bundle (is it covered?) -> if it needs Postgres: create_database, then get_database_connection and store DATABASE_URL with set_variables (secrets) after the service exists, or pass it in create_service variables -> create_service with dryRun=true (repo or image, port, variables) -> tell the user the cost (included in the bundle, or walletDailyCost per day) and wait for their OK -> create_service for real -> poll get_deployment until it settles, read get_deployment_logs if it failed -> give the user the *.helipod.app URL from get_service. Ask the user for secrets (API keys) only when the app needs them; never invent values.
|
|
38268
38280
|
- Destructive tools (delete, terminate, reinstall, rollback, restore a backup or an import, copy a backup onto another VPS, replace DNS, revoke a key, remove an SSH key or port forward, send a broadcast, execute_database_sql): confirm with the user and name what will be lost. Before execute_database_sql, run classify_database_sql.
|
|
38269
38281
|
- Email: when a project has no email yet (email_status shows activated false) or its plan ended, start it with buy_email_plan (dryRun first; the FREE plan costs nothing, still confirm). That also sets email up for the project; then add and verify a sending domain.
|
|
38270
38282
|
- Before replacing all DNS records (set_dns_records), read list_dns_records and keep every record the user still needs. Before sending or scheduling a broadcast, tell the user how many contacts it reaches. Before deleting or replacing code, files or variables, read the current value. After renaming a service, fix the variable references listed in warnings.
|
package/dist/index.js
CHANGED
|
@@ -34875,7 +34875,7 @@ function purchaseKey(dryRun, given) {
|
|
|
34875
34875
|
|
|
34876
34876
|
// src/tools/containers.ts
|
|
34877
34877
|
var id = (what) => exports_external.string().uuid().describe(`${what} id (UUID)`);
|
|
34878
|
-
var dryRun = exports_external.boolean().optional().describe("true: return the
|
|
34878
|
+
var dryRun = exports_external.boolean().optional().describe("true: return the cost only (dailyCost list price, walletDailyCost what the wallet pays after the bundle), change nothing");
|
|
34879
34879
|
var idem = exports_external.string().min(8).max(128).optional().describe("Idempotency-Key: a retry with the same value returns the first result instead of doing it twice");
|
|
34880
34880
|
var C = "/containers";
|
|
34881
34881
|
var port = exports_external.object({
|
|
@@ -34884,6 +34884,14 @@ var port = exports_external.object({
|
|
|
34884
34884
|
public: exports_external.boolean().optional().describe("http ports: give it a *.helipod.app domain (default true)")
|
|
34885
34885
|
});
|
|
34886
34886
|
var containerTools = [
|
|
34887
|
+
defineTool({
|
|
34888
|
+
name: "get_bundle",
|
|
34889
|
+
title: "Container bundle",
|
|
34890
|
+
kind: "read",
|
|
34891
|
+
description: "The account's container bundle: whether one is active, until when, quota used / max (vCPU, GB RAM, GB storage), what it covers and what it does not. Container services within the quota cost the wallet NOTHING: read this before telling the user to top up for a container deploy. The bundle covers containers only (not VPS, databases, email, domains or AI).",
|
|
34892
|
+
input: {},
|
|
34893
|
+
run: (_a3, api2) => api2.api("GET", `${C}/bundle`)
|
|
34894
|
+
}),
|
|
34887
34895
|
defineTool({
|
|
34888
34896
|
name: "list_projects",
|
|
34889
34897
|
title: "List projects",
|
|
@@ -35029,7 +35037,7 @@ var containerTools = [
|
|
|
35029
35037
|
name: "create_service",
|
|
35030
35038
|
title: "Create service",
|
|
35031
35039
|
kind: "write",
|
|
35032
|
-
description: "Create a service from a container image, a Git repository or an inline Dockerfile, and (unless deploy=false) start its first deploy. Private GitHub / GitLab repos work once connected (get_git_connections, list_git_repos); such services also redeploy on every push. A private repo that is not connected is refused (409 GITHUB_NOT_CONNECTED / GITLAB_NOT_CONNECTED / REPO_NOT_ACCESSIBLE, nothing created): give the user details.connectUrl and retry after they connect. COSTS MONEY once deployed
|
|
35040
|
+
description: "Create a service from a container image, a Git repository or an inline Dockerfile, and (unless deploy=false) start its first deploy. Private GitHub / GitLab repos work once connected (get_git_connections, list_git_repos); such services also redeploy on every push. A private repo that is not connected is refused (409 GITHUB_NOT_CONNECTED / GITLAB_NOT_CONNECTED / REPO_NOT_ACCESSIBLE, nothing created): give the user details.connectUrl and retry after they connect. COSTS MONEY once deployed, beyond the account's bundle: billed daily, from the bundle quota first and the wallet only beyond it. Call with dryRun=true first, tell the user what the wallet pays (walletDailyCost) and get their OK. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35033
35041
|
input: {
|
|
35034
35042
|
environmentId: id("Environment"),
|
|
35035
35043
|
name: exports_external.string().regex(/^[A-Za-z][A-Za-z0-9_-]{0,39}$/).describe("Service name (letters, digits, _ -; starts with a letter). Also used in references like ${{name.KEY}}"),
|
|
@@ -35111,7 +35119,7 @@ var containerTools = [
|
|
|
35111
35119
|
name: "deploy_service",
|
|
35112
35120
|
title: "Deploy service",
|
|
35113
35121
|
kind: "write",
|
|
35114
|
-
description: "Start a new deployment (Git services build the latest commit). COSTS MONEY: charges today's cost when the build starts
|
|
35122
|
+
description: "Start a new deployment (Git services build the latest commit). COSTS MONEY beyond the bundle: charges today's cost when the build starts (refunded if it fails), nothing within an active bundle's quota. dryRun=true returns the cost only. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance. The deployment runs in the background; get_deployment returns its status.",
|
|
35115
35123
|
input: { serviceId: id("Service"), dryRun, idempotencyKey: idem },
|
|
35116
35124
|
run: (a, api2) => api2.api("POST", `${C}/services/${a.serviceId}/deployments`, { query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
35117
35125
|
}),
|
|
@@ -35219,7 +35227,7 @@ var containerTools = [
|
|
|
35219
35227
|
title: "Scale service",
|
|
35220
35228
|
kind: "write",
|
|
35221
35229
|
idempotent: true,
|
|
35222
|
-
description: "Change replicas, CPU or RAM (applies right away). COSTS MONEY
|
|
35230
|
+
description: "Change replicas, CPU or RAM (applies right away). An increase COSTS MONEY unless the bundle covers it: call with dryRun=true first to show currentDailyCost vs newDailyCost and walletDailyCost, and get the user's OK. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35223
35231
|
input: {
|
|
35224
35232
|
serviceId: id("Service"),
|
|
35225
35233
|
replicas: exports_external.number().int().min(1).max(20).optional(),
|
|
@@ -35267,7 +35275,7 @@ var containerTools = [
|
|
|
35267
35275
|
name: "resize_volume",
|
|
35268
35276
|
title: "Resize volume",
|
|
35269
35277
|
kind: "write",
|
|
35270
|
-
description: "Change a volume's size (1-50 GB; never below the space already used). A larger volume COSTS MONEY: it raises the daily cost, charged pro rata when the service is redeployed and in full from the next renewal. dryRun=true returns currentDailyCost and
|
|
35278
|
+
description: "Change a volume's size (1-50 GB; never below the space already used). A larger volume COSTS MONEY: it raises the daily cost, charged pro rata when the service is redeployed and in full from the next renewal. dryRun=true returns currentDailyCost, newDailyCost and walletDailyCost (IDR) without changing anything. Applies on the next deploy. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35271
35279
|
input: {
|
|
35272
35280
|
serviceId: id("Service"),
|
|
35273
35281
|
volumeId: id("Volume"),
|
|
@@ -35389,20 +35397,21 @@ var containerTools = [
|
|
|
35389
35397
|
name: "deploy_template",
|
|
35390
35398
|
title: "Deploy template",
|
|
35391
35399
|
kind: "write",
|
|
35392
|
-
description: "Create every service of a template in an environment and deploy them. COSTS MONEY
|
|
35400
|
+
description: "Create every service of a template in an environment and deploy them. COSTS MONEY beyond the bundle: each service is billed daily (bundle quota first, then the wallet). Call with dryRun=true first. " + "Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35393
35401
|
input: {
|
|
35394
35402
|
environmentId: id("Environment"),
|
|
35395
35403
|
slug: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/),
|
|
35396
35404
|
inputs: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Answers to the template's inputs"),
|
|
35405
|
+
dryRun,
|
|
35397
35406
|
idempotencyKey: idem
|
|
35398
35407
|
},
|
|
35399
|
-
run: (a, api2) => api2.api("POST", `${C}/environments/${a.environmentId}/templates/${a.slug}`, { body: { inputs: a.inputs }, idempotencyKey: a.idempotencyKey })
|
|
35408
|
+
run: (a, api2) => api2.api("POST", `${C}/environments/${a.environmentId}/templates/${a.slug}`, { body: { inputs: a.inputs }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
35400
35409
|
}),
|
|
35401
35410
|
defineTool({
|
|
35402
35411
|
name: "create_stack",
|
|
35403
35412
|
title: "Create stack from spec",
|
|
35404
35413
|
kind: "write",
|
|
35405
|
-
description: "Create several connected services from a template spec ({ version: 1, inputs?, services: [...] }) and deploy them in dependency order. COSTS MONEY: dryRun=true first. See https://docs.helipod.io/docs/container-api/templates-and-stacks",
|
|
35414
|
+
description: "Create several connected services from a template spec ({ version: 1, inputs?, services: [...] }) and deploy them in dependency order. COSTS MONEY beyond the bundle: billed daily (bundle quota first, then the wallet). dryRun=true first. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance. See https://docs.helipod.io/docs/container-api/templates-and-stacks",
|
|
35406
35415
|
input: {
|
|
35407
35416
|
environmentId: id("Environment"),
|
|
35408
35417
|
spec: exports_external.record(exports_external.string(), exports_external.any()).describe("Template spec object"),
|
|
@@ -36921,7 +36930,7 @@ var databaseTools = [
|
|
|
36921
36930
|
name: "upgrade_database",
|
|
36922
36931
|
title: "Upgrade database plan / top up",
|
|
36923
36932
|
kind: "write",
|
|
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.",
|
|
36933
|
+
description: "Buy a paid plan or top up credits. Only when the user asks for more than the FREE plan gives (never to get started: create_database is free). COSTS MONEY from the wallet (bundles do not cover it): call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
|
|
36925
36934
|
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() },
|
|
36926
36935
|
run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
36927
36936
|
}),
|
|
@@ -37406,10 +37415,13 @@ var generalTools = [
|
|
|
37406
37415
|
];
|
|
37407
37416
|
|
|
37408
37417
|
// src/server.ts
|
|
37409
|
-
var VERSION = "0.5.
|
|
37418
|
+
var VERSION = "0.5.12";
|
|
37410
37419
|
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.
|
|
37411
37420
|
- 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.
|
|
37412
37421
|
- 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.
|
|
37422
|
+
- What the user really pays: container services are paid from the account's bundle first (get_bundle: a monthly bundle with a CPU / RAM / storage quota) and from the wallet only beyond it. Container dry runs return walletDailyCost (what the wallet pays per day) and bundle.coversIt: when the bundle covers it, say it is included in their bundle and deploy after their OK; never ask for a top-up for something walletDailyCost says is 0, whatever the balance. Quote dailyCost only as the price without a bundle. The bundle covers containers only: VPS, domains, AI, paid database plans and paid email plans are paid from the wallet.
|
|
37423
|
+
- Free first: the managed Postgres database (create_database) starts on the FREE plan and email (buy_email_plan with the FREE plan) costs nothing. For a database, use create_database rather than running Postgres as a container service, and do not offer upgrade_database or a paid email plan unless the user asks for more or hits a limit.
|
|
37424
|
+
- Deploying an app, step by step: list_projects (pick the project and its production environmentId) -> get_bundle (is it covered?) -> if it needs Postgres: create_database, then get_database_connection and store DATABASE_URL with set_variables (secrets) after the service exists, or pass it in create_service variables -> create_service with dryRun=true (repo or image, port, variables) -> tell the user the cost (included in the bundle, or walletDailyCost per day) and wait for their OK -> create_service for real -> poll get_deployment until it settles, read get_deployment_logs if it failed -> give the user the *.helipod.app URL from get_service. Ask the user for secrets (API keys) only when the app needs them; never invent values.
|
|
37413
37425
|
- Destructive tools (delete, terminate, reinstall, rollback, restore a backup or an import, copy a backup onto another VPS, replace DNS, revoke a key, remove an SSH key or port forward, send a broadcast, execute_database_sql): confirm with the user and name what will be lost. Before execute_database_sql, run classify_database_sql.
|
|
37414
37426
|
- Email: when a project has no email yet (email_status shows activated false) or its plan ended, start it with buy_email_plan (dryRun first; the FREE plan costs nothing, still confirm). That also sets email up for the project; then add and verify a sending domain.
|
|
37415
37427
|
- Before replacing all DNS records (set_dns_records), read list_dns_records and keep every record the user still needs. Before sending or scheduling a broadcast, tell the user how many contacts it reaches. Before deleting or replacing code, files or variables, read the current value. After renaming a service, fix the variable references listed in warnings.
|
package/dist/stdio.js
CHANGED
|
@@ -34982,7 +34982,7 @@ function purchaseKey(dryRun, given) {
|
|
|
34982
34982
|
|
|
34983
34983
|
// src/tools/containers.ts
|
|
34984
34984
|
var id = (what) => exports_external.string().uuid().describe(`${what} id (UUID)`);
|
|
34985
|
-
var dryRun = exports_external.boolean().optional().describe("true: return the
|
|
34985
|
+
var dryRun = exports_external.boolean().optional().describe("true: return the cost only (dailyCost list price, walletDailyCost what the wallet pays after the bundle), change nothing");
|
|
34986
34986
|
var idem = exports_external.string().min(8).max(128).optional().describe("Idempotency-Key: a retry with the same value returns the first result instead of doing it twice");
|
|
34987
34987
|
var C = "/containers";
|
|
34988
34988
|
var port = exports_external.object({
|
|
@@ -34991,6 +34991,14 @@ var port = exports_external.object({
|
|
|
34991
34991
|
public: exports_external.boolean().optional().describe("http ports: give it a *.helipod.app domain (default true)")
|
|
34992
34992
|
});
|
|
34993
34993
|
var containerTools = [
|
|
34994
|
+
defineTool({
|
|
34995
|
+
name: "get_bundle",
|
|
34996
|
+
title: "Container bundle",
|
|
34997
|
+
kind: "read",
|
|
34998
|
+
description: "The account's container bundle: whether one is active, until when, quota used / max (vCPU, GB RAM, GB storage), what it covers and what it does not. Container services within the quota cost the wallet NOTHING: read this before telling the user to top up for a container deploy. The bundle covers containers only (not VPS, databases, email, domains or AI).",
|
|
34999
|
+
input: {},
|
|
35000
|
+
run: (_a3, api2) => api2.api("GET", `${C}/bundle`)
|
|
35001
|
+
}),
|
|
34994
35002
|
defineTool({
|
|
34995
35003
|
name: "list_projects",
|
|
34996
35004
|
title: "List projects",
|
|
@@ -35136,7 +35144,7 @@ var containerTools = [
|
|
|
35136
35144
|
name: "create_service",
|
|
35137
35145
|
title: "Create service",
|
|
35138
35146
|
kind: "write",
|
|
35139
|
-
description: "Create a service from a container image, a Git repository or an inline Dockerfile, and (unless deploy=false) start its first deploy. Private GitHub / GitLab repos work once connected (get_git_connections, list_git_repos); such services also redeploy on every push. A private repo that is not connected is refused (409 GITHUB_NOT_CONNECTED / GITLAB_NOT_CONNECTED / REPO_NOT_ACCESSIBLE, nothing created): give the user details.connectUrl and retry after they connect. COSTS MONEY once deployed
|
|
35147
|
+
description: "Create a service from a container image, a Git repository or an inline Dockerfile, and (unless deploy=false) start its first deploy. Private GitHub / GitLab repos work once connected (get_git_connections, list_git_repos); such services also redeploy on every push. A private repo that is not connected is refused (409 GITHUB_NOT_CONNECTED / GITLAB_NOT_CONNECTED / REPO_NOT_ACCESSIBLE, nothing created): give the user details.connectUrl and retry after they connect. COSTS MONEY once deployed, beyond the account's bundle: billed daily, from the bundle quota first and the wallet only beyond it. Call with dryRun=true first, tell the user what the wallet pays (walletDailyCost) and get their OK. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35140
35148
|
input: {
|
|
35141
35149
|
environmentId: id("Environment"),
|
|
35142
35150
|
name: exports_external.string().regex(/^[A-Za-z][A-Za-z0-9_-]{0,39}$/).describe("Service name (letters, digits, _ -; starts with a letter). Also used in references like ${{name.KEY}}"),
|
|
@@ -35218,7 +35226,7 @@ var containerTools = [
|
|
|
35218
35226
|
name: "deploy_service",
|
|
35219
35227
|
title: "Deploy service",
|
|
35220
35228
|
kind: "write",
|
|
35221
|
-
description: "Start a new deployment (Git services build the latest commit). COSTS MONEY: charges today's cost when the build starts
|
|
35229
|
+
description: "Start a new deployment (Git services build the latest commit). COSTS MONEY beyond the bundle: charges today's cost when the build starts (refunded if it fails), nothing within an active bundle's quota. dryRun=true returns the cost only. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance. The deployment runs in the background; get_deployment returns its status.",
|
|
35222
35230
|
input: { serviceId: id("Service"), dryRun, idempotencyKey: idem },
|
|
35223
35231
|
run: (a, api2) => api2.api("POST", `${C}/services/${a.serviceId}/deployments`, { query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
35224
35232
|
}),
|
|
@@ -35326,7 +35334,7 @@ var containerTools = [
|
|
|
35326
35334
|
title: "Scale service",
|
|
35327
35335
|
kind: "write",
|
|
35328
35336
|
idempotent: true,
|
|
35329
|
-
description: "Change replicas, CPU or RAM (applies right away). COSTS MONEY
|
|
35337
|
+
description: "Change replicas, CPU or RAM (applies right away). An increase COSTS MONEY unless the bundle covers it: call with dryRun=true first to show currentDailyCost vs newDailyCost and walletDailyCost, and get the user's OK. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35330
35338
|
input: {
|
|
35331
35339
|
serviceId: id("Service"),
|
|
35332
35340
|
replicas: exports_external.number().int().min(1).max(20).optional(),
|
|
@@ -35374,7 +35382,7 @@ var containerTools = [
|
|
|
35374
35382
|
name: "resize_volume",
|
|
35375
35383
|
title: "Resize volume",
|
|
35376
35384
|
kind: "write",
|
|
35377
|
-
description: "Change a volume's size (1-50 GB; never below the space already used). A larger volume COSTS MONEY: it raises the daily cost, charged pro rata when the service is redeployed and in full from the next renewal. dryRun=true returns currentDailyCost and
|
|
35385
|
+
description: "Change a volume's size (1-50 GB; never below the space already used). A larger volume COSTS MONEY: it raises the daily cost, charged pro rata when the service is redeployed and in full from the next renewal. dryRun=true returns currentDailyCost, newDailyCost and walletDailyCost (IDR) without changing anything. Applies on the next deploy. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35378
35386
|
input: {
|
|
35379
35387
|
serviceId: id("Service"),
|
|
35380
35388
|
volumeId: id("Volume"),
|
|
@@ -35496,20 +35504,21 @@ var containerTools = [
|
|
|
35496
35504
|
name: "deploy_template",
|
|
35497
35505
|
title: "Deploy template",
|
|
35498
35506
|
kind: "write",
|
|
35499
|
-
description: "Create every service of a template in an environment and deploy them. COSTS MONEY
|
|
35507
|
+
description: "Create every service of a template in an environment and deploy them. COSTS MONEY beyond the bundle: each service is billed daily (bundle quota first, then the wallet). Call with dryRun=true first. " + "Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance.",
|
|
35500
35508
|
input: {
|
|
35501
35509
|
environmentId: id("Environment"),
|
|
35502
35510
|
slug: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/),
|
|
35503
35511
|
inputs: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Answers to the template's inputs"),
|
|
35512
|
+
dryRun,
|
|
35504
35513
|
idempotencyKey: idem
|
|
35505
35514
|
},
|
|
35506
|
-
run: (a, api2) => api2.api("POST", `${C}/environments/${a.environmentId}/templates/${a.slug}`, { body: { inputs: a.inputs }, idempotencyKey: a.idempotencyKey })
|
|
35515
|
+
run: (a, api2) => api2.api("POST", `${C}/environments/${a.environmentId}/templates/${a.slug}`, { body: { inputs: a.inputs }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
35507
35516
|
}),
|
|
35508
35517
|
defineTool({
|
|
35509
35518
|
name: "create_stack",
|
|
35510
35519
|
title: "Create stack from spec",
|
|
35511
35520
|
kind: "write",
|
|
35512
|
-
description: "Create several connected services from a template spec ({ version: 1, inputs?, services: [...] }) and deploy them in dependency order. COSTS MONEY: dryRun=true first. See https://docs.helipod.io/docs/container-api/templates-and-stacks",
|
|
35521
|
+
description: "Create several connected services from a template spec ({ version: 1, inputs?, services: [...] }) and deploy them in dependency order. COSTS MONEY beyond the bundle: billed daily (bundle quota first, then the wallet). dryRun=true first. Before saying a price, read walletDailyCost (what the wallet really pays per day) and bundle: with an active bundle that covers it (bundle.coversIt, walletDailyCost 0) nothing is charged and NO top-up is needed, even with a Rp 0 balance. See https://docs.helipod.io/docs/container-api/templates-and-stacks",
|
|
35513
35522
|
input: {
|
|
35514
35523
|
environmentId: id("Environment"),
|
|
35515
35524
|
spec: exports_external.record(exports_external.string(), exports_external.any()).describe("Template spec object"),
|
|
@@ -37028,7 +37037,7 @@ var databaseTools = [
|
|
|
37028
37037
|
name: "upgrade_database",
|
|
37029
37038
|
title: "Upgrade database plan / top up",
|
|
37030
37039
|
kind: "write",
|
|
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.",
|
|
37040
|
+
description: "Buy a paid plan or top up credits. Only when the user asks for more than the FREE plan gives (never to get started: create_database is free). COSTS MONEY from the wallet (bundles do not cover it): call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
|
|
37032
37041
|
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() },
|
|
37033
37042
|
run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
|
|
37034
37043
|
}),
|
|
@@ -37513,10 +37522,13 @@ var generalTools = [
|
|
|
37513
37522
|
];
|
|
37514
37523
|
|
|
37515
37524
|
// src/server.ts
|
|
37516
|
-
var VERSION = "0.5.
|
|
37525
|
+
var VERSION = "0.5.12";
|
|
37517
37526
|
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.
|
|
37518
37527
|
- 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.
|
|
37519
37528
|
- 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.
|
|
37529
|
+
- What the user really pays: container services are paid from the account's bundle first (get_bundle: a monthly bundle with a CPU / RAM / storage quota) and from the wallet only beyond it. Container dry runs return walletDailyCost (what the wallet pays per day) and bundle.coversIt: when the bundle covers it, say it is included in their bundle and deploy after their OK; never ask for a top-up for something walletDailyCost says is 0, whatever the balance. Quote dailyCost only as the price without a bundle. The bundle covers containers only: VPS, domains, AI, paid database plans and paid email plans are paid from the wallet.
|
|
37530
|
+
- Free first: the managed Postgres database (create_database) starts on the FREE plan and email (buy_email_plan with the FREE plan) costs nothing. For a database, use create_database rather than running Postgres as a container service, and do not offer upgrade_database or a paid email plan unless the user asks for more or hits a limit.
|
|
37531
|
+
- Deploying an app, step by step: list_projects (pick the project and its production environmentId) -> get_bundle (is it covered?) -> if it needs Postgres: create_database, then get_database_connection and store DATABASE_URL with set_variables (secrets) after the service exists, or pass it in create_service variables -> create_service with dryRun=true (repo or image, port, variables) -> tell the user the cost (included in the bundle, or walletDailyCost per day) and wait for their OK -> create_service for real -> poll get_deployment until it settles, read get_deployment_logs if it failed -> give the user the *.helipod.app URL from get_service. Ask the user for secrets (API keys) only when the app needs them; never invent values.
|
|
37520
37532
|
- Destructive tools (delete, terminate, reinstall, rollback, restore a backup or an import, copy a backup onto another VPS, replace DNS, revoke a key, remove an SSH key or port forward, send a broadcast, execute_database_sql): confirm with the user and name what will be lost. Before execute_database_sql, run classify_database_sql.
|
|
37521
37533
|
- Email: when a project has no email yet (email_status shows activated false) or its plan ended, start it with buy_email_plan (dryRun first; the FREE plan costs nothing, still confirm). That also sets email up for the project; then add and verify a sending domain.
|
|
37522
37534
|
- Before replacing all DNS records (set_dns_records), read list_dns_records and keep every record the user still needs. Before sending or scheduling a broadcast, tell the user how many contacts it reaches. Before deleting or replacing code, files or variables, read the current value. After renaming a service, fix the variable references listed in warnings.
|
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.12",
|
|
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). 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.
|
|
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), except container services covered by a monthly **bundle** (`get_bundle`): within its CPU / RAM / storage quota they cost the wallet nothing. The managed Postgres database and email both have a FREE plan. 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, 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.
|
|
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. For containers the amount is `walletDailyCost` (what the wallet pays after the bundle): when the dry run says `bundle.coversIt`, it is included in the bundle and needs no top-up, whatever the balance. Start with free options (FREE database and email plans); offer paid plans only when the user asks for more.
|
|
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,6 +11,7 @@ Follow the `helipod` skill rules (price first, confirm destructive actions, neve
|
|
|
11
11
|
|
|
12
12
|
`list_projects` → pick the project and environment (usually `production`). If several fit, ask the user.
|
|
13
13
|
`list_services` on that environment shows what already runs (avoid duplicate names; reuse an existing database).
|
|
14
|
+
`get_bundle` tells you whether the account has a container bundle: services within its quota cost the wallet nothing, so a Rp 0 balance is fine.
|
|
14
15
|
|
|
15
16
|
## 2. Choose the source
|
|
16
17
|
|
|
@@ -19,7 +20,8 @@ Follow the `helipod` skill rules (price first, confirm destructive actions, neve
|
|
|
19
20
|
| A published image (Docker Hub, GHCR, private registry) | `image` (fastest, no build). Private: `registryUsername` + `registryPassword` |
|
|
20
21
|
| A public Git repo | `repo` (+ `branch`, `rootDirectory` for monorepos). Built by Helipod: a `Dockerfile` in the folder is used, else **Helipack** detects Node/Bun, PHP and Python frameworks |
|
|
21
22
|
| Only local files | Push to a public Git repo first, or build and push an image. A short `dockerfile` can be sent inline |
|
|
22
|
-
|
|
|
23
|
+
| Needs Postgres | The managed database: `create_database` (FREE plan, nothing to pay), then `get_database_connection` → `DATABASE_URL` as a secret variable. Not a Postgres container |
|
|
24
|
+
| A known app or other database (MySQL, Redis, WordPress, n8n, ...) | `list_templates` → `deploy_template` (`dryRun: true` first) |
|
|
23
25
|
|
|
24
26
|
Private Git repos are connected in the dashboard (GitHub/GitLab integration), not through the API.
|
|
25
27
|
|
|
@@ -31,7 +33,10 @@ Before deploying a repo, check it quickly: the port the app listens on (prefer `
|
|
|
31
33
|
|
|
32
34
|
## 3. Create (price first)
|
|
33
35
|
|
|
34
|
-
1. `create_service` with `dryRun: true
|
|
36
|
+
1. `create_service` with `dryRun: true`. Read `walletDailyCost` and `bundle`:
|
|
37
|
+
- `bundle.coversIt: true` (`walletDailyCost: 0`): tell the user it is included in their bundle (say which, and until when), no charge and **no top-up needed**, and ask to go ahead.
|
|
38
|
+
- otherwise: tell the user `walletDailyCost` (IDR per day from the wallet; with a bundle it is only the part over the quota). Only if the wallet cannot cover it, offer `create_deposit`.
|
|
39
|
+
- `dailyCost` is the price without a bundle: never present it as what a bundle owner pays.
|
|
35
40
|
2. `create_service` again without `dryRun`, with an `idempotencyKey`. Give `ports: [{ "port": <app port> }]` so it gets an HTTPS `*.helipod.app` domain.
|
|
36
41
|
3. Poll `get_deployment` until `success` / `failed`. Builds take 1 to 5 minutes; images are faster.
|
|
37
42
|
4. On `success`: give the user the domain `url`. On `failed`: `get_deployment_logs`, find the real error (missing env var, wrong port, build error), fix, `deploy_service` again.
|
|
@@ -46,7 +51,7 @@ Before deploying a repo, check it quickly: the port the app listens on (prefer `
|
|
|
46
51
|
|
|
47
52
|
- Logs: `get_service_logs` (`filter: "error"`, `since: "1h"`); metrics: `get_service_metrics`.
|
|
48
53
|
- Restart without rebuild: `restart_service` (running services only).
|
|
49
|
-
- Scale: `scale_service` with `dryRun: true` first, show current vs new daily cost.
|
|
54
|
+
- Scale: `scale_service` with `dryRun: true` first, show current vs new daily cost and `walletDailyCost` (0 when the bundle still covers it).
|
|
50
55
|
- Custom domain: `add_service_domain` with `kind: "custom"`, then tell the user the DNS record from the response (`CNAME` to `cname.helipod.app`, or the `A` record for a root domain). If the domain is on Helipod, the `helipod-domains` skill can create the record.
|
|
51
56
|
- Persistent files: `add_volume` (mount path), then redeploy. Without a volume, files are lost on every deploy.
|
|
52
57
|
|
|
@@ -59,6 +64,6 @@ Before deploying a repo, check it quickly: the port the app listens on (prefer `
|
|
|
59
64
|
| Symptom | Fix |
|
|
60
65
|
|---|---|
|
|
61
66
|
| Health check / deploy timeout | App must listen on the exposed port, on `0.0.0.0`, not `localhost` |
|
|
62
|
-
| `402` | Plan limit (services, CPU, RAM, storage) or low balance: tell the user |
|
|
67
|
+
| `402` | Plan limit (services, CPU, RAM, storage) or low balance: tell the user. Check `get_bundle` first: within a bundle's quota the balance does not matter |
|
|
63
68
|
| Crash loop on start | Read runtime logs; usually a missing variable or failed DB connection |
|
|
64
69
|
| Monorepo built from root | Set `rootDirectory` (`update_service`) and redeploy |
|