hatchkit 0.1.39 → 0.1.41
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/adopt.d.ts.map +1 -1
- package/dist/adopt.js +311 -77
- package/dist/adopt.js.map +1 -1
- package/dist/deploy/coolify-app.d.ts +9 -9
- package/dist/deploy/coolify-app.d.ts.map +1 -1
- package/dist/deploy/coolify-app.js +14 -19
- package/dist/deploy/coolify-app.js.map +1 -1
- package/dist/deploy/coolify.d.ts.map +1 -1
- package/dist/deploy/coolify.js +6 -2
- package/dist/deploy/coolify.js.map +1 -1
- package/dist/deploy/keys.d.ts +7 -2
- package/dist/deploy/keys.d.ts.map +1 -1
- package/dist/deploy/keys.js +27 -7
- package/dist/deploy/keys.js.map +1 -1
- package/dist/deploy/pages.d.ts +41 -0
- package/dist/deploy/pages.d.ts.map +1 -1
- package/dist/deploy/pages.js +360 -13
- package/dist/deploy/pages.js.map +1 -1
- package/dist/deploy/regen-infra.js +4 -0
- package/dist/deploy/regen-infra.js.map +1 -1
- package/dist/deploy/rollback.d.ts.map +1 -1
- package/dist/deploy/rollback.js +94 -22
- package/dist/deploy/rollback.js.map +1 -1
- package/dist/deploy/sync.d.ts +10 -7
- package/dist/deploy/sync.d.ts.map +1 -1
- package/dist/deploy/sync.js +13 -9
- package/dist/deploy/sync.js.map +1 -1
- package/dist/index.js +269 -23
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +144 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +1980 -0
- package/dist/inventory.js.map +1 -0
- package/dist/overview.d.ts +101 -0
- package/dist/overview.d.ts.map +1 -0
- package/dist/overview.js +852 -0
- package/dist/overview.js.map +1 -0
- package/dist/prompts.d.ts +22 -7
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +240 -40
- package/dist/prompts.js.map +1 -1
- package/dist/scaffold/app.js +1 -1
- package/dist/scaffold/app.js.map +1 -1
- package/dist/scaffold/infra.d.ts.map +1 -1
- package/dist/scaffold/infra.js +8 -2
- package/dist/scaffold/infra.js.map +1 -1
- package/dist/scaffold/manifest.d.ts +6 -0
- package/dist/scaffold/manifest.d.ts.map +1 -1
- package/dist/scaffold/manifest.js +1 -0
- package/dist/scaffold/manifest.js.map +1 -1
- package/dist/scaffold/pages-heuristics.d.ts +17 -0
- package/dist/scaffold/pages-heuristics.d.ts.map +1 -0
- package/dist/scaffold/pages-heuristics.js +344 -0
- package/dist/scaffold/pages-heuristics.js.map +1 -0
- package/dist/scaffold/pages-mode.d.ts +10 -0
- package/dist/scaffold/pages-mode.d.ts.map +1 -0
- package/dist/scaffold/pages-mode.js +109 -0
- package/dist/scaffold/pages-mode.js.map +1 -0
- package/dist/scaffold/surfaces.d.ts.map +1 -1
- package/dist/scaffold/surfaces.js +12 -1
- package/dist/scaffold/surfaces.js.map +1 -1
- package/dist/utils/cloudflare-api.d.ts +19 -0
- package/dist/utils/cloudflare-api.d.ts.map +1 -1
- package/dist/utils/cloudflare-api.js +16 -0
- package/dist/utils/cloudflare-api.js.map +1 -1
- package/dist/utils/coolify-api.d.ts +20 -0
- package/dist/utils/coolify-api.d.ts.map +1 -1
- package/dist/utils/coolify-api.js +51 -0
- package/dist/utils/coolify-api.js.map +1 -1
- package/dist/utils/coolify-server-ips.d.ts +6 -12
- package/dist/utils/coolify-server-ips.d.ts.map +1 -1
- package/dist/utils/coolify-server-ips.js +26 -81
- package/dist/utils/coolify-server-ips.js.map +1 -1
- package/dist/utils/run-ledger.d.ts +20 -0
- package/dist/utils/run-ledger.d.ts.map +1 -1
- package/dist/utils/run-ledger.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -166,6 +166,36 @@ async function main() {
|
|
|
166
166
|
await runDoctor({ json: isJson });
|
|
167
167
|
break;
|
|
168
168
|
}
|
|
169
|
+
case "overview": {
|
|
170
|
+
if (args.includes("--help"))
|
|
171
|
+
return printHelp("overview");
|
|
172
|
+
const { runOverview } = await import("./overview.js");
|
|
173
|
+
await runOverview({ json: isJson, all: args.includes("--all") });
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case "inventory": {
|
|
177
|
+
if (args.includes("--help"))
|
|
178
|
+
return printHelp("inventory");
|
|
179
|
+
const { runInventory } = await import("./inventory.js");
|
|
180
|
+
const nameFlag = flagValue("--name");
|
|
181
|
+
const domainFlag = flagValue("--domain");
|
|
182
|
+
const repoFlag = flagValue("--repo");
|
|
183
|
+
const inputOverride = {};
|
|
184
|
+
if (nameFlag)
|
|
185
|
+
inputOverride.name = nameFlag;
|
|
186
|
+
if (domainFlag)
|
|
187
|
+
inputOverride.domain = domainFlag;
|
|
188
|
+
if (repoFlag)
|
|
189
|
+
inputOverride.repo = repoFlag;
|
|
190
|
+
await runInventory(resolve("."), {
|
|
191
|
+
json: isJson,
|
|
192
|
+
yes: args.includes("--yes") || args.includes("-y"),
|
|
193
|
+
save: args.includes("--save"),
|
|
194
|
+
noSave: args.includes("--no-save"),
|
|
195
|
+
input: Object.keys(inputOverride).length > 0 ? inputOverride : undefined,
|
|
196
|
+
});
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
169
199
|
case "provision": {
|
|
170
200
|
const sub = args[1];
|
|
171
201
|
if (sub === "s3") {
|
|
@@ -212,6 +242,14 @@ async function main() {
|
|
|
212
242
|
if (command === "pages") {
|
|
213
243
|
console.log(chalk.yellow(" Note: `hatchkit pages` has been renamed to `hatchkit gh-pages`."));
|
|
214
244
|
}
|
|
245
|
+
if (args.includes("--undo")) {
|
|
246
|
+
const { runPagesUndo } = await import("./deploy/pages.js");
|
|
247
|
+
await runPagesUndo(resolve("."), {
|
|
248
|
+
dryRun: args.includes("--dry-run"),
|
|
249
|
+
yes: args.includes("--yes") || args.includes("-y"),
|
|
250
|
+
});
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
215
253
|
const { runPagesSetup } = await import("./deploy/pages.js");
|
|
216
254
|
await runPagesSetup(resolve("."));
|
|
217
255
|
break;
|
|
@@ -828,17 +866,22 @@ async function handleCreate() {
|
|
|
828
866
|
if (forceNoInstall)
|
|
829
867
|
config.installDeps = false;
|
|
830
868
|
// Ensure needed providers are configured (lazy prompting).
|
|
831
|
-
// Coolify + Hetzner
|
|
832
|
-
//
|
|
833
|
-
if (config.
|
|
869
|
+
// Coolify + Hetzner only matter for the coolify deployment mode.
|
|
870
|
+
// gh-pages skips them entirely (no server, no Docker registry).
|
|
871
|
+
if (config.deploymentMode === "coolify" &&
|
|
872
|
+
(config.deployTarget === "existing" || config.runDeployment)) {
|
|
834
873
|
await ensureCoolify();
|
|
835
874
|
}
|
|
836
875
|
// GitHub is checked here so auth failures surface before scaffold
|
|
837
|
-
// (not deep inside `setupGitHub` after files are on disk).
|
|
838
|
-
|
|
876
|
+
// (not deep inside `setupGitHub` after files are on disk). Pages
|
|
877
|
+
// also needs GitHub auth for the API calls that enable Pages and
|
|
878
|
+
// set the cname — so we require it whenever gh-pages is involved.
|
|
879
|
+
if (config.createGithubRepo || config.deploymentMode === "gh-pages") {
|
|
839
880
|
await ensureGitHub();
|
|
840
881
|
}
|
|
841
|
-
if (config.
|
|
882
|
+
if (config.deploymentMode === "coolify" &&
|
|
883
|
+
config.deployTarget === "new" &&
|
|
884
|
+
config.runDeployment) {
|
|
842
885
|
await ensureHetzner();
|
|
843
886
|
}
|
|
844
887
|
if (config.features.includes("s3") &&
|
|
@@ -872,9 +915,14 @@ async function handleCreate() {
|
|
|
872
915
|
console.log(chalk.bold("\n ── Summary ───────────────────────────────────────────────\n"));
|
|
873
916
|
console.log(` Project: ${chalk.cyan(config.name)}`);
|
|
874
917
|
console.log(` Domain: ${chalk.cyan(config.domain)}`);
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
918
|
+
if (config.deploymentMode === "gh-pages") {
|
|
919
|
+
console.log(` Deploy to: ${chalk.cyan("GitHub Pages (static)")}`);
|
|
920
|
+
}
|
|
921
|
+
else if (config.deploymentMode === "scaffold-only") {
|
|
922
|
+
console.log(` Deploy to: ${chalk.dim("scaffold only (no deploy)")}`);
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
console.log(` Deploy to: ${config.deployTarget === "existing" ? `existing server (${config.serverIpv4 ?? config.serverIp ?? "?"}${config.serverIpv6 ? ` · ${config.serverIpv6}` : ""})` : `new Hetzner ${config.serverSize}`}`);
|
|
878
926
|
}
|
|
879
927
|
console.log(` Features: ${config.features.length > 0 ? config.features.join(", ") : "none"}`);
|
|
880
928
|
console.log(` ML: ${config.mlServices.length > 0 ? config.mlServices.join(", ") : "none"}`);
|
|
@@ -1010,10 +1058,19 @@ async function handleCreate() {
|
|
|
1010
1058
|
}
|
|
1011
1059
|
}
|
|
1012
1060
|
if (config.dryRun) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1061
|
+
// Coolify mode previews the Terraform tfvars + Coolify env that
|
|
1062
|
+
// would be written. gh-pages and scaffold-only have nothing
|
|
1063
|
+
// equivalent — Pages reads no env, scaffold-only writes no infra.
|
|
1064
|
+
if (config.deploymentMode === "coolify") {
|
|
1065
|
+
scaffoldInfra(config, INFRA_ROOT, {
|
|
1066
|
+
serverPort: scaffoldResult?.ports.server,
|
|
1067
|
+
clientPort: scaffoldResult?.ports.client,
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
else if (config.deploymentMode === "gh-pages") {
|
|
1071
|
+
console.log(chalk.dim(" · gh-pages mode — would write `.github/workflows/gh-pages.yml`, patch `next.config`,\n" +
|
|
1072
|
+
" write CNAME, enable Pages, configure DNS, and wait for the Let's Encrypt cert."));
|
|
1073
|
+
}
|
|
1017
1074
|
console.log(chalk.green("\n ✓ Dry run complete. No changes were made.\n"));
|
|
1018
1075
|
return;
|
|
1019
1076
|
}
|
|
@@ -1063,8 +1120,10 @@ async function handleCreate() {
|
|
|
1063
1120
|
if (infraResult.coolifyEnvPath) {
|
|
1064
1121
|
ledger?.record({ kind: "coolifyEnv", path: infraResult.coolifyEnvPath });
|
|
1065
1122
|
}
|
|
1066
|
-
// Step 5: Terraform (DNS + optionally server)
|
|
1067
|
-
|
|
1123
|
+
// Step 5: Terraform (DNS + optionally server). Coolify-only —
|
|
1124
|
+
// gh-pages handles its own DNS via `runPagesSetupProgrammatic`
|
|
1125
|
+
// a few steps down, and `scaffold-only` skips deploy entirely.
|
|
1126
|
+
if (config.runDeployment && config.deploymentMode === "coolify") {
|
|
1068
1127
|
const tfResult = await runTerraform(config, INFRA_ROOT);
|
|
1069
1128
|
if (tfResult.applied) {
|
|
1070
1129
|
ledger?.record({
|
|
@@ -1074,8 +1133,9 @@ async function handleCreate() {
|
|
|
1074
1133
|
});
|
|
1075
1134
|
}
|
|
1076
1135
|
}
|
|
1077
|
-
// Step 6: Coolify setup
|
|
1078
|
-
|
|
1136
|
+
// Step 6: Coolify setup. Only runs in coolify mode; gh-pages has
|
|
1137
|
+
// no Coolify app to provision (the site lives on GitHub's CDN).
|
|
1138
|
+
if (config.runDeployment && config.deploymentMode === "coolify") {
|
|
1079
1139
|
const coolifyResult = await runCoolifySetup(config, {
|
|
1080
1140
|
repoUrl: repoUrl ?? undefined,
|
|
1081
1141
|
serverPort: scaffoldResult?.ports.server,
|
|
@@ -1113,7 +1173,11 @@ async function handleCreate() {
|
|
|
1113
1173
|
// print the manual command instead of failing the whole flow.
|
|
1114
1174
|
if (scaffoldResult?.dotenvx) {
|
|
1115
1175
|
try {
|
|
1116
|
-
|
|
1176
|
+
// App name matches the project name (the dockercompose
|
|
1177
|
+
// wrapper). The candidate-list fallback in
|
|
1178
|
+
// `pushProjectKeyToCoolify` still catches legacy `-web`
|
|
1179
|
+
// projects.
|
|
1180
|
+
await pushProjectKeyToCoolify(config.name, { appName: config.name });
|
|
1117
1181
|
}
|
|
1118
1182
|
catch (err) {
|
|
1119
1183
|
console.log(chalk.yellow(` Couldn't auto-push dotenvx key: ${err.message}`));
|
|
@@ -1148,6 +1212,63 @@ async function handleCreate() {
|
|
|
1148
1212
|
}
|
|
1149
1213
|
}
|
|
1150
1214
|
}
|
|
1215
|
+
// Step 6.25 (gh-pages only): run Pages setup. Writes the
|
|
1216
|
+
// .github/workflows/gh-pages.yml + CNAME file locally and wires
|
|
1217
|
+
// the remote side (enable Pages, register cname, configure DNS,
|
|
1218
|
+
// poll for the Let's Encrypt cert, flip https_enforced). Must
|
|
1219
|
+
// happen BEFORE push so the new files land in the first push and
|
|
1220
|
+
// the workflow runs immediately.
|
|
1221
|
+
if (config.deploymentMode === "gh-pages" &&
|
|
1222
|
+
config.scaffoldRepo &&
|
|
1223
|
+
config.runDeployment &&
|
|
1224
|
+
repoUrl) {
|
|
1225
|
+
const { runPagesSetupProgrammatic } = await import("./deploy/pages.js");
|
|
1226
|
+
const { exec: bashExec } = await import("./utils/exec.js");
|
|
1227
|
+
// The scaffold's `pruneToClientOnly` rewrites the root build
|
|
1228
|
+
// script to `pnpm --filter @starter/shared run build && pnpm
|
|
1229
|
+
// --filter @starter/client run build` — runs from the repo
|
|
1230
|
+
// root, outputs to `packages/client/out/` (after the Pages-
|
|
1231
|
+
// mode Next config patch sets `output: "export"`).
|
|
1232
|
+
const detected = {
|
|
1233
|
+
kind: "node-build",
|
|
1234
|
+
publishDir: "packages/client/out",
|
|
1235
|
+
packageManager: "pnpm",
|
|
1236
|
+
buildScript: "build",
|
|
1237
|
+
workDir: "",
|
|
1238
|
+
};
|
|
1239
|
+
const slug = repoUrl.replace(/^https?:\/\/github\.com\//, "");
|
|
1240
|
+
try {
|
|
1241
|
+
const { pageUrl } = await runPagesSetupProgrammatic(appDir, {
|
|
1242
|
+
detected,
|
|
1243
|
+
domain: config.domain,
|
|
1244
|
+
});
|
|
1245
|
+
ledger?.record({
|
|
1246
|
+
kind: "ghPages",
|
|
1247
|
+
repo: slug,
|
|
1248
|
+
projectDir: appDir,
|
|
1249
|
+
cname: config.domain,
|
|
1250
|
+
});
|
|
1251
|
+
// Commit the workflow + CNAME file before the push step
|
|
1252
|
+
// below picks up the staged changes. Empty diffs (e.g. re-
|
|
1253
|
+
// running on an idempotent state) just produce a no-op commit.
|
|
1254
|
+
await bashExec("git", ["add", "-A"], { cwd: appDir, silent: true });
|
|
1255
|
+
const status = await bashExec("git", ["status", "--porcelain"], {
|
|
1256
|
+
cwd: appDir,
|
|
1257
|
+
silent: true,
|
|
1258
|
+
});
|
|
1259
|
+
if (status.stdout.trim()) {
|
|
1260
|
+
await bashExec("git", ["commit", "-m", "ci: GitHub Pages setup"], {
|
|
1261
|
+
cwd: appDir,
|
|
1262
|
+
silent: true,
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
console.log(chalk.green(` ✓ GitHub Pages will publish at ${pageUrl}`));
|
|
1266
|
+
}
|
|
1267
|
+
catch (err) {
|
|
1268
|
+
console.log(chalk.yellow(` Couldn't auto-wire GitHub Pages: ${err.message}`));
|
|
1269
|
+
console.log(chalk.dim(` Run \`hatchkit gh-pages\` from ${appDir} once the issue is resolved.`));
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1151
1272
|
// Step 6.5: push the working branch to origin. Done AFTER Coolify
|
|
1152
1273
|
// wiring + Actions-secret upserts so the workflow's first run
|
|
1153
1274
|
// already has the secrets it needs to deploy. setupGitHub above
|
|
@@ -1220,6 +1341,9 @@ async function handleCreate() {
|
|
|
1220
1341
|
if (config.surfaces !== "client-only") {
|
|
1221
1342
|
console.log(` API: ${chalk.cyan(`https://${config.domain}/api`)}`);
|
|
1222
1343
|
}
|
|
1344
|
+
if (config.deploymentMode === "gh-pages") {
|
|
1345
|
+
console.log(chalk.dim(` Hosting: GitHub Pages — first build kicks off on push, https cert provisions over the next few minutes.`));
|
|
1346
|
+
}
|
|
1223
1347
|
console.log(` App dir: ${chalk.dim(appDir)}`);
|
|
1224
1348
|
console.log(` Config: ${chalk.dim(getConfigPath())}`);
|
|
1225
1349
|
if (config.scaffoldRepo) {
|
|
@@ -1369,13 +1493,20 @@ function printHelp(topic) {
|
|
|
1369
1493
|
hatchkit create [--dry-run]
|
|
1370
1494
|
|
|
1371
1495
|
${chalk.bold("What it does (interactively):")}
|
|
1372
|
-
1. Prompts for project name, domain,
|
|
1496
|
+
1. Prompts for project name, domain, surfaces, deployment mode, features, ML
|
|
1373
1497
|
2. Copies the starter template and strips unselected features
|
|
1374
1498
|
3. Assigns unique ports per project (server, client, native HMR)
|
|
1375
1499
|
4. Runs \`pnpm install\` (if pnpm is present and you opt in)
|
|
1376
1500
|
5. Initializes git, optionally creates a GitHub repo
|
|
1377
|
-
6. Generates Terraform tfvars + Coolify .env
|
|
1378
|
-
7.
|
|
1501
|
+
6. Generates Terraform tfvars + Coolify .env (Coolify mode)
|
|
1502
|
+
7. Deploys: Terraform → Coolify → ML ${chalk.dim("OR")} GitHub Pages setup
|
|
1503
|
+
|
|
1504
|
+
${chalk.bold("Deployment modes:")}
|
|
1505
|
+
${chalk.cyan("coolify")} Full-stack on Hetzner — DB, providers, Docker. Default.
|
|
1506
|
+
${chalk.cyan("gh-pages")} Static-only on GitHub Pages. Only offered when surfaces
|
|
1507
|
+
is ${chalk.dim("client-only")}; the scaffold's Next config is patched to
|
|
1508
|
+
${chalk.dim('`output: "export"`')} and the gh-pages workflow is written.
|
|
1509
|
+
${chalk.cyan("scaffold-only")} Write files, skip deploy. Pick this to defer setup.
|
|
1379
1510
|
|
|
1380
1511
|
${chalk.bold("Options:")}
|
|
1381
1512
|
--dry-run Show the plan without writing anything
|
|
@@ -1463,6 +1594,7 @@ function printHelp(topic) {
|
|
|
1463
1594
|
|
|
1464
1595
|
${chalk.bold("Usage:")}
|
|
1465
1596
|
cd <project-dir> && hatchkit gh-pages
|
|
1597
|
+
cd <project-dir> && hatchkit gh-pages --undo [--dry-run] [--yes]
|
|
1466
1598
|
|
|
1467
1599
|
${chalk.bold("What it does:")}
|
|
1468
1600
|
1. Reads the repo via \`gh repo view\` (must be a GitHub repo you own).
|
|
@@ -1484,6 +1616,17 @@ function printHelp(topic) {
|
|
|
1484
1616
|
${chalk.bold("After running:")}
|
|
1485
1617
|
git add -A && git commit -m "ci: deploy to GitHub Pages" && git push
|
|
1486
1618
|
|
|
1619
|
+
${chalk.bold("Undo (--undo):")}
|
|
1620
|
+
Reverses what the command put in place:
|
|
1621
|
+
- Disables Pages via ${chalk.dim("DELETE /repos/<owner>/<repo>/pages")} (clears the cname too).
|
|
1622
|
+
- Deletes Cloudflare records that point at GitHub's Pages IPs / ${chalk.dim("<user>.github.io")}
|
|
1623
|
+
for the registered domain (only when a Cloudflare token is configured + the
|
|
1624
|
+
zone is in this account).
|
|
1625
|
+
- Removes ${chalk.cyan(".github/workflows/gh-pages.yml")} (only the file hatchkit writes
|
|
1626
|
+
— hand-written Pages workflows are left untouched).
|
|
1627
|
+
- Removes any ${chalk.cyan("CNAME")} files whose content matches the registered domain.
|
|
1628
|
+
${chalk.dim("--dry-run")} prints the plan without changing anything. ${chalk.dim("--yes")} skips the confirm.
|
|
1629
|
+
|
|
1487
1630
|
${chalk.bold("Notes:")}
|
|
1488
1631
|
- Private repos need a paid GitHub plan for Pages. Free-tier repos
|
|
1489
1632
|
must be made public first.
|
|
@@ -1523,6 +1666,105 @@ function printHelp(topic) {
|
|
|
1523
1666
|
stored (Coolify /version, Hetzner /servers, Cloudflare /tokens/verify,
|
|
1524
1667
|
Resend /domains, …). Reports ok / fail / not-configured per provider
|
|
1525
1668
|
and exits non-zero if any check fails. Safe to run repeatedly.
|
|
1669
|
+
`);
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
if (topic === "overview") {
|
|
1673
|
+
console.log(`
|
|
1674
|
+
${chalk.bold("hatchkit overview")} — fleet-level view of every configured provider
|
|
1675
|
+
|
|
1676
|
+
Distinct from ${chalk.cyan("status")} (which providers do I have credentials for?),
|
|
1677
|
+
${chalk.cyan("doctor")} (are those credentials valid?), and ${chalk.cyan("inventory")} (what does THIS
|
|
1678
|
+
project have?). ${chalk.cyan("overview")} answers "what does my whole hatchkit
|
|
1679
|
+
footprint look like, across every configured provider?" — no name or
|
|
1680
|
+
domain filter, just a roll-up of top-level resources.
|
|
1681
|
+
|
|
1682
|
+
${chalk.bold("What it lists:")}
|
|
1683
|
+
· Coolify applications, projects, databases
|
|
1684
|
+
· Cloudflare DNS zones
|
|
1685
|
+
· R2 buckets (whole account)
|
|
1686
|
+
· Hetzner S3 / AWS S3 credential presence (bucket listing not implemented)
|
|
1687
|
+
· Resend verified domains
|
|
1688
|
+
· GlitchTip projects in the configured org
|
|
1689
|
+
· OpenPanel projects
|
|
1690
|
+
· Stripe webhook endpoints (test + live)
|
|
1691
|
+
|
|
1692
|
+
${chalk.bold("Cross-references:")}
|
|
1693
|
+
After listing every provider, ${chalk.cyan("overview")} cross-references the
|
|
1694
|
+
raw data to flag fleet-level inconsistencies — the kind of bitrot
|
|
1695
|
+
that a single-provider lens can't see:
|
|
1696
|
+
|
|
1697
|
+
· Coolify app deploys from a repo \`gh\` can't find (deleted/renamed)
|
|
1698
|
+
· App fqdn references an apex with no Cloudflare zone
|
|
1699
|
+
· R2 bucket follows the \`<project>-<role>\` convention but has no
|
|
1700
|
+
matching Coolify app (orphan from a destroyed project)
|
|
1701
|
+
· GlitchTip / OpenPanel project with no Coolify app counterpart
|
|
1702
|
+
· Cloudflare zone with no Coolify app pointing into it
|
|
1703
|
+
|
|
1704
|
+
${chalk.bold("Flags:")}
|
|
1705
|
+
--all Print every resource per provider (default: 6-line preview)
|
|
1706
|
+
--json Machine-readable OverviewReport (non-interactive)
|
|
1707
|
+
|
|
1708
|
+
Read-only — every call is a GET. Safe to run repeatedly.
|
|
1709
|
+
`);
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
if (topic === "inventory") {
|
|
1713
|
+
console.log(`
|
|
1714
|
+
${chalk.bold("hatchkit inventory")} — survey what already exists for this project
|
|
1715
|
+
|
|
1716
|
+
Inverse of ${chalk.cyan("doctor")}: instead of "are my credentials valid?", asks
|
|
1717
|
+
"given THIS project (cwd / name / domain / repo), what resources
|
|
1718
|
+
already exist across every configured provider — and is anything
|
|
1719
|
+
out of sync?"
|
|
1720
|
+
|
|
1721
|
+
${chalk.bold("Inference (cwd → identity):")}
|
|
1722
|
+
· .hatchkit.json — name, domain
|
|
1723
|
+
· package.json — name, description
|
|
1724
|
+
· git remote — GitHub owner/repo
|
|
1725
|
+
· CNAME file — gh-pages custom domain
|
|
1726
|
+
· .env.production dotenvx — encryption state
|
|
1727
|
+
· .github/workflows/ — gh-pages / Coolify deploy workflows
|
|
1728
|
+
|
|
1729
|
+
Asks interactively for anything that couldn't be inferred. Confirms
|
|
1730
|
+
inferred values unless ${chalk.cyan("--yes")} is passed.
|
|
1731
|
+
|
|
1732
|
+
${chalk.bold("Scans (parallel, read-only):")}
|
|
1733
|
+
· Coolify — projects, applications (with fqdn + git source)
|
|
1734
|
+
· DNS — Cloudflare zone + relevant records (apex/www/api/s3/…)
|
|
1735
|
+
· R2 — buckets (manifest + naming-convention candidates) + CORS
|
|
1736
|
+
· GitHub — repo visibility, Pages status, relevant repo secrets
|
|
1737
|
+
· Resend — verified-domain match
|
|
1738
|
+
· GlitchTip — projects in the configured org
|
|
1739
|
+
· OpenPanel — projects
|
|
1740
|
+
· Stripe — webhook endpoints whose URL contains the project domain
|
|
1741
|
+
|
|
1742
|
+
${chalk.bold("Drift detection (cross-references):")}
|
|
1743
|
+
· Coolify app fqdn vs DNS A record (and the linked server's public IP)
|
|
1744
|
+
· Coolify app git source vs local git remote (renamed repo gotcha)
|
|
1745
|
+
· Manifest s3Buckets entries vs live R2 buckets
|
|
1746
|
+
· Bucket CORS — manifest origins vs live policy
|
|
1747
|
+
· gh-pages workflow on disk vs Pages enabled on repo (and CNAME ↔ Pages cname)
|
|
1748
|
+
· dotenvx encrypted locally but no DOTENV_PRIVATE_KEY_PRODUCTION secret in GH
|
|
1749
|
+
|
|
1750
|
+
${chalk.bold("Flags:")}
|
|
1751
|
+
--name <project> Override inferred project name
|
|
1752
|
+
--domain <domain> Override inferred domain
|
|
1753
|
+
--repo <owner/name> Override inferred GitHub repo
|
|
1754
|
+
--yes, -y Skip confirm-inferred-value prompts
|
|
1755
|
+
--save Write a minimal .hatchkit.json without prompting
|
|
1756
|
+
--no-save Suppress the end-of-run save prompt
|
|
1757
|
+
--json Machine-readable InventoryReport (non-interactive)
|
|
1758
|
+
|
|
1759
|
+
${chalk.bold("Persisting identity:")}
|
|
1760
|
+
After an interactive run, when ${chalk.cyan(".hatchkit.json")} doesn't yet exist
|
|
1761
|
+
and both name + domain are inferred, hatchkit offers to write a
|
|
1762
|
+
minimal manifest. The manifest carries the right schema for every
|
|
1763
|
+
other command (adopt, update, sync, keys), with conservative defaults
|
|
1764
|
+
for fields inventory can't infer (features=[], s3Provider="none",
|
|
1765
|
+
deployTarget="existing", ports={server:3000,client:5173}). Run
|
|
1766
|
+
${chalk.cyan("hatchkit adopt --resume")} afterwards to flesh out the rest via
|
|
1767
|
+
the adopt stepper.
|
|
1526
1768
|
`);
|
|
1527
1769
|
return;
|
|
1528
1770
|
}
|
|
@@ -1958,6 +2200,8 @@ function printHelp(topic) {
|
|
|
1958
2200
|
setup One-time onboarding — wires up all credentials (alias: init)
|
|
1959
2201
|
status Show what's configured and what's next
|
|
1960
2202
|
doctor Health-check every provider with contextual fix hints
|
|
2203
|
+
inventory Survey what already exists for this project (and flag drift)
|
|
2204
|
+
overview Fleet-level survey — every resource across all configured providers
|
|
1961
2205
|
explain One-page mental model of the CLI
|
|
1962
2206
|
|
|
1963
2207
|
${chalk.bold("Projects:")}
|
|
@@ -1983,8 +2227,10 @@ function printHelp(topic) {
|
|
|
1983
2227
|
config reset Clear ALL CLI config (providers, tokens, ML registry, ports)
|
|
1984
2228
|
|
|
1985
2229
|
${chalk.bold("For agents / scripts:")}
|
|
1986
|
-
status --json
|
|
1987
|
-
doctor --json
|
|
2230
|
+
status --json StatusSnapshot as JSON
|
|
2231
|
+
doctor --json Per-provider health with fix hints as JSON
|
|
2232
|
+
inventory --json InventoryReport — resources found per provider + drift
|
|
2233
|
+
overview --json OverviewReport — fleet-level resource counts + names
|
|
1988
2234
|
completion <shell> Print a zsh/bash/fish completion script
|
|
1989
2235
|
|
|
1990
2236
|
${chalk.bold("Options:")}
|