hatchkit 0.1.1
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/config.d.ts +131 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +629 -0
- package/dist/config.js.map +1 -0
- package/dist/deploy/coolify.d.ts +4 -0
- package/dist/deploy/coolify.d.ts.map +1 -0
- package/dist/deploy/coolify.js +20 -0
- package/dist/deploy/coolify.js.map +1 -0
- package/dist/deploy/github.d.ts +4 -0
- package/dist/deploy/github.d.ts.map +1 -0
- package/dist/deploy/github.js +39 -0
- package/dist/deploy/github.js.map +1 -0
- package/dist/deploy/gpu.d.ts +4 -0
- package/dist/deploy/gpu.d.ts.map +1 -0
- package/dist/deploy/gpu.js +97 -0
- package/dist/deploy/gpu.js.map +1 -0
- package/dist/deploy/keys.d.ts +9 -0
- package/dist/deploy/keys.d.ts.map +1 -0
- package/dist/deploy/keys.js +73 -0
- package/dist/deploy/keys.js.map +1 -0
- package/dist/deploy/terraform.d.ts +4 -0
- package/dist/deploy/terraform.d.ts.map +1 -0
- package/dist/deploy/terraform.js +55 -0
- package/dist/deploy/terraform.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +599 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +52 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +313 -0
- package/dist/prompts.js.map +1 -0
- package/dist/provision/glitchtip.d.ts +6 -0
- package/dist/provision/glitchtip.d.ts.map +1 -0
- package/dist/provision/glitchtip.js +46 -0
- package/dist/provision/glitchtip.js.map +1 -0
- package/dist/provision/index.d.ts +9 -0
- package/dist/provision/index.d.ts.map +1 -0
- package/dist/provision/index.js +108 -0
- package/dist/provision/index.js.map +1 -0
- package/dist/provision/openpanel.d.ts +8 -0
- package/dist/provision/openpanel.d.ts.map +1 -0
- package/dist/provision/openpanel.js +66 -0
- package/dist/provision/openpanel.js.map +1 -0
- package/dist/provision/resend.d.ts +16 -0
- package/dist/provision/resend.d.ts.map +1 -0
- package/dist/provision/resend.js +43 -0
- package/dist/provision/resend.js.map +1 -0
- package/dist/scaffold/app.d.ts +13 -0
- package/dist/scaffold/app.d.ts.map +1 -0
- package/dist/scaffold/app.js +340 -0
- package/dist/scaffold/app.js.map +1 -0
- package/dist/scaffold/dotenvx.d.ts +30 -0
- package/dist/scaffold/dotenvx.d.ts.map +1 -0
- package/dist/scaffold/dotenvx.js +142 -0
- package/dist/scaffold/dotenvx.js.map +1 -0
- package/dist/scaffold/infra.d.ts +17 -0
- package/dist/scaffold/infra.d.ts.map +1 -0
- package/dist/scaffold/infra.js +200 -0
- package/dist/scaffold/infra.js.map +1 -0
- package/dist/scaffold/manifest.d.ts +50 -0
- package/dist/scaffold/manifest.d.ts.map +1 -0
- package/dist/scaffold/manifest.js +83 -0
- package/dist/scaffold/manifest.js.map +1 -0
- package/dist/scaffold/ml-client.d.ts +20 -0
- package/dist/scaffold/ml-client.d.ts.map +1 -0
- package/dist/scaffold/ml-client.js +38 -0
- package/dist/scaffold/ml-client.js.map +1 -0
- package/dist/scaffold/pkg-json.d.ts +20 -0
- package/dist/scaffold/pkg-json.d.ts.map +1 -0
- package/dist/scaffold/pkg-json.js +113 -0
- package/dist/scaffold/pkg-json.js.map +1 -0
- package/dist/scaffold/starter-files.d.ts +40 -0
- package/dist/scaffold/starter-files.d.ts.map +1 -0
- package/dist/scaffold/starter-files.js +197 -0
- package/dist/scaffold/starter-files.js.map +1 -0
- package/dist/scaffold/update.d.ts +8 -0
- package/dist/scaffold/update.d.ts.map +1 -0
- package/dist/scaffold/update.js +255 -0
- package/dist/scaffold/update.js.map +1 -0
- package/dist/templates/addons/analytics/middleware.ts.hbs +13 -0
- package/dist/templates/addons/analytics/sentry.ts.hbs +16 -0
- package/dist/templates/addons/storage/s3.ts.hbs +40 -0
- package/dist/templates/addons/storage/upload.ts.hbs +23 -0
- package/dist/templates/addons/stripe/checkout.ts.hbs +27 -0
- package/dist/templates/addons/stripe/client.ts.hbs +6 -0
- package/dist/templates/addons/stripe/webhook.ts.hbs +39 -0
- package/dist/templates/addons/websocket/redis.ts.hbs +25 -0
- package/dist/templates/addons/websocket/ws.ts.hbs +32 -0
- package/dist/templates/base/.dockerignore.hbs +7 -0
- package/dist/templates/base/Dockerfile.hbs +18 -0
- package/dist/templates/base/env.example.hbs +60 -0
- package/dist/templates/base/github-actions.yml.hbs +35 -0
- package/dist/templates/base/gitignore.hbs +5 -0
- package/dist/templates/base/package.json.hbs +36 -0
- package/dist/templates/base/src/auth/auth.ts.hbs +13 -0
- package/dist/templates/base/src/auth/routes.ts.hbs +19 -0
- package/dist/templates/base/src/config.ts.hbs +36 -0
- package/dist/templates/base/src/db.ts.hbs +12 -0
- package/dist/templates/base/src/index.ts.hbs +80 -0
- package/dist/templates/base/src/routes/health.ts.hbs +12 -0
- package/dist/templates/base/tsconfig.json.hbs +18 -0
- package/dist/templates/ml-clients/3d-extraction.ts.hbs +20 -0
- package/dist/templates/ml-clients/background-removal.ts.hbs +17 -0
- package/dist/templates/ml-clients/custom-hf.ts.hbs +20 -0
- package/dist/templates/ml-clients/image-recognition.ts.hbs +22 -0
- package/dist/templates/ml-clients/subtitles.ts.hbs +26 -0
- package/dist/utils/coolify-api.d.ts +45 -0
- package/dist/utils/coolify-api.d.ts.map +1 -0
- package/dist/utils/coolify-api.js +72 -0
- package/dist/utils/coolify-api.js.map +1 -0
- package/dist/utils/errors.d.ts +2 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +31 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/exec.d.ts +23 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +47 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/flags.d.ts +17 -0
- package/dist/utils/flags.d.ts.map +1 -0
- package/dist/utils/flags.js +116 -0
- package/dist/utils/flags.js.map +1 -0
- package/dist/utils/hf-api.d.ts +13 -0
- package/dist/utils/hf-api.d.ts.map +1 -0
- package/dist/utils/hf-api.js +30 -0
- package/dist/utils/hf-api.js.map +1 -0
- package/dist/utils/ports.d.ts +29 -0
- package/dist/utils/ports.d.ts.map +1 -0
- package/dist/utils/ports.js +86 -0
- package/dist/utils/ports.js.map +1 -0
- package/dist/utils/secrets.d.ts +25 -0
- package/dist/utils/secrets.d.ts.map +1 -0
- package/dist/utils/secrets.js +51 -0
- package/dist/utils/secrets.js.map +1 -0
- package/dist/utils/template.d.ts +7 -0
- package/dist/utils/template.d.ts.map +1 -0
- package/dist/utils/template.js +35 -0
- package/dist/utils/template.js.map +1 -0
- package/dist/utils/validate.d.ts +16 -0
- package/dist/utils/validate.d.ts.map +1 -0
- package/dist/utils/validate.js +60 -0
- package/dist/utils/validate.js.map +1 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +21 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +48 -0
- package/scripts/copy-templates.mjs +20 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* dotenvx integration for scaffolded projects.
|
|
3
|
+
*
|
|
4
|
+
* Flow at scaffold time:
|
|
5
|
+
* 1. For each key → value mapping the user supplied (prompts OR
|
|
6
|
+
* presets), call `dotenvx.set(key, value, { encrypt: true, path })`.
|
|
7
|
+
* On first call, dotenvx generates a fresh ECIES keypair and
|
|
8
|
+
* writes the public key into `.env.production` + the private
|
|
9
|
+
* key into `.env.keys`.
|
|
10
|
+
* 2. Read `.env.keys` to pull out DOTENV_PRIVATE_KEY_PRODUCTION and
|
|
11
|
+
* mirror it into the OS keychain under
|
|
12
|
+
* `dotenvx:<project-name>:production-private-key`.
|
|
13
|
+
* The on-disk .env.keys stays (gitignored) for local dev
|
|
14
|
+
* ergonomics; keychain is the canonical backup + source for
|
|
15
|
+
* re-deploy.
|
|
16
|
+
* 3. Values the user DIDN'T supply land as plaintext "CHANGE_ME_*"
|
|
17
|
+
* in .env.production — the user can encrypt them later with
|
|
18
|
+
* `dotenvx set KEY value -f .env.production`.
|
|
19
|
+
*
|
|
20
|
+
* None of the values (including the generated BETTER_AUTH_SECRET)
|
|
21
|
+
* are ever logged. The private key is printed once via a deferred
|
|
22
|
+
* hint pointing at `hatchkit keys show <project>`.
|
|
23
|
+
*/
|
|
24
|
+
import { randomBytes } from "node:crypto";
|
|
25
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
import { set as dotenvxSet } from "@dotenvx/dotenvx";
|
|
28
|
+
import chalk from "chalk";
|
|
29
|
+
import ora from "ora";
|
|
30
|
+
import { SECRET_KEYS, setSecret } from "../utils/secrets.js";
|
|
31
|
+
/** Set of keys that are always safe to auto-generate if the user
|
|
32
|
+
* doesn't supply them (cryptographically random, no external
|
|
33
|
+
* coordination needed). */
|
|
34
|
+
function autoGeneratedDefaults() {
|
|
35
|
+
return {
|
|
36
|
+
BETTER_AUTH_SECRET: randomBytes(32).toString("hex"),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Populate the starter's `packages/server/.env.production` with
|
|
40
|
+
* encrypted values + a plaintext CHANGE_ME_<KEY> for anything the
|
|
41
|
+
* user didn't supply. Mirrors the generated private key into the
|
|
42
|
+
* macOS Keychain (or equivalent) and returns it so callers can push
|
|
43
|
+
* it to Coolify / print a retrieval hint. */
|
|
44
|
+
export async function seedDotenvxProduction(outputDir, config, userValues) {
|
|
45
|
+
const envPath = join(outputDir, "packages/server/.env.production");
|
|
46
|
+
const envKeysPath = join(outputDir, "packages/server/.env.keys");
|
|
47
|
+
// Determine the full key → value map.
|
|
48
|
+
const defaults = autoGeneratedDefaults();
|
|
49
|
+
const merged = { ...defaults, ...userValues };
|
|
50
|
+
// Every key we want present in .env.production. Includes ones the
|
|
51
|
+
// user didn't supply — those get plaintext CHANGE_ME_<KEY> so
|
|
52
|
+
// misconfigured deploys fail loudly rather than silently running
|
|
53
|
+
// with empty secrets.
|
|
54
|
+
const allKeys = candidateKeys(config);
|
|
55
|
+
const encryptedKeys = [];
|
|
56
|
+
const placeholderKeys = [];
|
|
57
|
+
const spinner = ora("Seeding encrypted .env.production").start();
|
|
58
|
+
try {
|
|
59
|
+
for (const key of allKeys) {
|
|
60
|
+
const raw = merged[key];
|
|
61
|
+
const hasValue = raw !== undefined && raw.trim() !== "";
|
|
62
|
+
if (hasValue) {
|
|
63
|
+
dotenvxSet(key, raw, { path: envPath, encrypt: true });
|
|
64
|
+
encryptedKeys.push(key);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// CHANGE_ME_<KEY> is intentionally noisy — anything that tries
|
|
68
|
+
// to use it at runtime will either fail validation (zod /
|
|
69
|
+
// `getRequired`) or point clearly at its own source.
|
|
70
|
+
dotenvxSet(key, `CHANGE_ME_${key}`, { path: envPath, encrypt: false });
|
|
71
|
+
placeholderKeys.push(key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
spinner.succeed(`Seeded .env.production (${encryptedKeys.length} encrypted, ${placeholderKeys.length} placeholders)`);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
spinner.fail("Failed to seed .env.production");
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
// Pull the freshly-generated private key out of .env.keys.
|
|
81
|
+
const { privateKey, publicKey } = readKeypair(envKeysPath, envPath);
|
|
82
|
+
// Mirror the private key into the OS keychain. Keep .env.keys on
|
|
83
|
+
// disk too (gitignored) so local `pnpm start` works with no extra
|
|
84
|
+
// steps — the keychain copy is for re-deploy + recovery.
|
|
85
|
+
await setSecret(SECRET_KEYS.dotenvxPrivateKey(config.name), privateKey);
|
|
86
|
+
return { privateKey, publicKey, encryptedKeys, placeholderKeys };
|
|
87
|
+
}
|
|
88
|
+
/** Keys the scaffolder seeds into .env.production. Extend here when
|
|
89
|
+
* the starter gains new required env entries. */
|
|
90
|
+
function candidateKeys(config) {
|
|
91
|
+
const base = ["MONGODB_URI", "BETTER_AUTH_SECRET", "BETTER_AUTH_URL", "FRONTEND_URL"];
|
|
92
|
+
if (config.features.includes("stripe")) {
|
|
93
|
+
base.push("STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET");
|
|
94
|
+
}
|
|
95
|
+
if (config.features.includes("analytics")) {
|
|
96
|
+
base.push("SENTRY_DSN");
|
|
97
|
+
}
|
|
98
|
+
if (config.features.includes("s3")) {
|
|
99
|
+
base.push("S3_ENDPOINT", "S3_BUCKET_NAME", "S3_PUBLIC_URL", "AWS_REGION", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY");
|
|
100
|
+
}
|
|
101
|
+
if (config.features.includes("websocket")) {
|
|
102
|
+
base.push("REDIS_URL");
|
|
103
|
+
}
|
|
104
|
+
return base;
|
|
105
|
+
}
|
|
106
|
+
/** Parse the generated .env.keys + the header of .env.production to
|
|
107
|
+
* recover the hex-encoded production keypair. */
|
|
108
|
+
function readKeypair(envKeysPath, envPath) {
|
|
109
|
+
if (!existsSync(envKeysPath)) {
|
|
110
|
+
throw new Error(`dotenvx was expected to create ${envKeysPath}, but it's missing. Nothing was encrypted.`);
|
|
111
|
+
}
|
|
112
|
+
const keysContent = readFileSync(envKeysPath, "utf-8");
|
|
113
|
+
const privateMatch = keysContent.match(/^DOTENV_PRIVATE_KEY_PRODUCTION="?([0-9a-fA-F]+)"?/m);
|
|
114
|
+
if (!privateMatch) {
|
|
115
|
+
throw new Error(`Could not find DOTENV_PRIVATE_KEY_PRODUCTION in ${envKeysPath}.`);
|
|
116
|
+
}
|
|
117
|
+
const envContent = existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
118
|
+
const publicMatch = envContent.match(/^DOTENV_PUBLIC_KEY_PRODUCTION="?([0-9a-fA-F]+)"?/m);
|
|
119
|
+
return {
|
|
120
|
+
privateKey: privateMatch[1],
|
|
121
|
+
publicKey: publicMatch ? publicMatch[1] : "",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** Prompt the user for the subset of candidate keys that are
|
|
125
|
+
* sensitive + likely already known at scaffold time. Anything they
|
|
126
|
+
* leave blank falls through to CHANGE_ME. Skipped entirely in
|
|
127
|
+
* non-interactive mode — presets should carry the values instead. */
|
|
128
|
+
export function printDotenvxSummary(result, projectName) {
|
|
129
|
+
if (result.encryptedKeys.length > 0) {
|
|
130
|
+
console.log(chalk.green(` ✓ Encrypted ${result.encryptedKeys.length} values into .env.production`));
|
|
131
|
+
}
|
|
132
|
+
if (result.placeholderKeys.length > 0) {
|
|
133
|
+
console.log(chalk.dim(` Placeholders (edit later with \`dotenvx set KEY value -f .env.production\`):`));
|
|
134
|
+
for (const k of result.placeholderKeys) {
|
|
135
|
+
console.log(chalk.dim(` ${k}`));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
console.log(chalk.yellow(`\n dotenvx private key stored in macOS Keychain (hatchkit:${projectName}).`));
|
|
139
|
+
console.log(chalk.dim(` Retrieve anytime: hatchkit keys show ${projectName}`));
|
|
140
|
+
console.log(chalk.dim(` Push to Coolify: hatchkit keys push ${projectName}`));
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=dotenvx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dotenvx.js","sourceRoot":"","sources":["../../src/scaffold/dotenvx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProjectConfig } from "../prompts.js";
|
|
2
|
+
/** Generate Terraform tfvars for the project. */
|
|
3
|
+
export declare function generateTfvars(config: ProjectConfig): string;
|
|
4
|
+
/** Generate Coolify stack .env for the project. */
|
|
5
|
+
export declare function generateCoolifyEnv(config: ProjectConfig, extras?: {
|
|
6
|
+
repoUrl?: string;
|
|
7
|
+
serverPort?: number;
|
|
8
|
+
clientPort?: number;
|
|
9
|
+
}): string;
|
|
10
|
+
export interface ScaffoldInfraOptions {
|
|
11
|
+
repoUrl?: string;
|
|
12
|
+
serverPort?: number;
|
|
13
|
+
clientPort?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Write infra config files. */
|
|
16
|
+
export declare function scaffoldInfra(config: ProjectConfig, repoRoot: string, options?: ScaffoldInfraOptions): void;
|
|
17
|
+
//# sourceMappingURL=infra.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infra.d.ts","sourceRoot":"","sources":["../../src/scaffold/infra.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAkC5D;AAED,mDAAmD;AACnD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,MAAM,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1E,MAAM,CAqBR;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gCAAgC;AAChC,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,oBAAyB,GACjC,IAAI,CA0CN"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { getConfig } from "../config.js";
|
|
5
|
+
import { renderString } from "../utils/template.js";
|
|
6
|
+
/** Generate Terraform tfvars for the project. */
|
|
7
|
+
export function generateTfvars(config) {
|
|
8
|
+
// Avoid silent duplicate-key drop when the user's chosen subdomain
|
|
9
|
+
// collides with a reserved name ("admin", "api.<sub>"). Build the map
|
|
10
|
+
// iteratively and skip duplicates.
|
|
11
|
+
const subdomains = {};
|
|
12
|
+
const addSubdomain = (key, description) => {
|
|
13
|
+
if (!key)
|
|
14
|
+
return;
|
|
15
|
+
if (subdomains[key])
|
|
16
|
+
return;
|
|
17
|
+
subdomains[key] = description;
|
|
18
|
+
};
|
|
19
|
+
addSubdomain(config.subdomain, "Web app + API paths");
|
|
20
|
+
addSubdomain(`api.${config.subdomain}`, "REST API");
|
|
21
|
+
addSubdomain("admin", "Coolify dashboard");
|
|
22
|
+
if (config.deployTarget === "new") {
|
|
23
|
+
return renderString(TFVARS_TEMPLATE, {
|
|
24
|
+
name: config.name,
|
|
25
|
+
serverType: config.serverSize || "cpx21",
|
|
26
|
+
serverLocation: config.serverLocation || "nbg1",
|
|
27
|
+
domain: config.baseDomain,
|
|
28
|
+
subdomains,
|
|
29
|
+
s3Enabled: config.features.includes("s3") && config.s3Provider !== "existing",
|
|
30
|
+
s3BucketName: `${config.name}-assets`,
|
|
31
|
+
s3Location: config.serverLocation || "nbg1",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// For existing server: DNS-only tfvars
|
|
35
|
+
return renderString(DNS_ONLY_TFVARS_TEMPLATE, {
|
|
36
|
+
domain: config.baseDomain,
|
|
37
|
+
subdomains,
|
|
38
|
+
targetIpv4: config.serverIp || "",
|
|
39
|
+
targetIpv6: "",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Generate Coolify stack .env for the project. */
|
|
43
|
+
export function generateCoolifyEnv(config, extras = {}) {
|
|
44
|
+
const coolifyConfig = getConfig().providers.coolify;
|
|
45
|
+
const s3Config = getS3Config(config);
|
|
46
|
+
return renderString(COOLIFY_ENV_TEMPLATE, {
|
|
47
|
+
coolifyUrl: coolifyConfig?.url || "https://admin.example.com",
|
|
48
|
+
name: config.name,
|
|
49
|
+
domain: config.domain,
|
|
50
|
+
repoUrl: extras.repoUrl ?? "",
|
|
51
|
+
serverPort: extras.serverPort ?? 3000,
|
|
52
|
+
clientPort: extras.clientPort ?? 3000,
|
|
53
|
+
mongoEnabled: true,
|
|
54
|
+
redisEnabled: config.features.includes("websocket"),
|
|
55
|
+
s3Provider: config.s3Provider === "existing" ? "custom" : config.s3Provider,
|
|
56
|
+
s3Bucket: s3Config?.bucket || "",
|
|
57
|
+
s3Endpoint: s3Config?.endpoint || "",
|
|
58
|
+
s3Region: s3Config?.region || "",
|
|
59
|
+
stripe: config.features.includes("stripe"),
|
|
60
|
+
analytics: config.features.includes("analytics"),
|
|
61
|
+
mlServices: config.mlServices,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/** Write infra config files. */
|
|
65
|
+
export function scaffoldInfra(config, repoRoot, options = {}) {
|
|
66
|
+
// Fail early with a clear message if the infra submodule isn't
|
|
67
|
+
// populated — otherwise Terraform writes are silently skipped and the
|
|
68
|
+
// later terraform/coolify exec steps crash cryptically.
|
|
69
|
+
if (!config.dryRun && !existsSync(join(repoRoot, "terraform"))) {
|
|
70
|
+
throw new Error(`Infra submodule is empty at ${repoRoot}. Run 'git submodule update --init' in the monorepo root before deploying.`);
|
|
71
|
+
}
|
|
72
|
+
const stacksDir = join(repoRoot, "stacks");
|
|
73
|
+
if (!existsSync(stacksDir))
|
|
74
|
+
mkdirSync(stacksDir, { recursive: true });
|
|
75
|
+
const tfvars = generateTfvars(config);
|
|
76
|
+
const coolifyEnv = generateCoolifyEnv(config, {
|
|
77
|
+
repoUrl: options.repoUrl,
|
|
78
|
+
serverPort: options.serverPort,
|
|
79
|
+
clientPort: options.clientPort,
|
|
80
|
+
});
|
|
81
|
+
if (config.dryRun) {
|
|
82
|
+
console.log(chalk.bold("\n [dry-run] Infra config:\n"));
|
|
83
|
+
console.log(chalk.dim(" --- terraform.tfvars ---"));
|
|
84
|
+
console.log(chalk.dim(tfvars));
|
|
85
|
+
console.log(chalk.dim(" --- coolify .env ---"));
|
|
86
|
+
console.log(chalk.dim(coolifyEnv));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Write Terraform tfvars
|
|
90
|
+
const tfDir = config.deployTarget === "new"
|
|
91
|
+
? join(repoRoot, "terraform", "stacks", "node-realtime")
|
|
92
|
+
: join(repoRoot, "terraform", "stacks", "dns-only");
|
|
93
|
+
if (existsSync(tfDir)) {
|
|
94
|
+
writeFileSync(join(tfDir, `${config.name}.tfvars`), tfvars, "utf-8");
|
|
95
|
+
console.log(chalk.green(` ✓ Terraform config: ${tfDir}/${config.name}.tfvars`));
|
|
96
|
+
}
|
|
97
|
+
// Write Coolify .env
|
|
98
|
+
writeFileSync(join(stacksDir, `${config.name}.env`), coolifyEnv, "utf-8");
|
|
99
|
+
console.log(chalk.green(` ✓ Coolify config: stacks/${config.name}.env`));
|
|
100
|
+
}
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Helpers
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
function getS3Config(config) {
|
|
105
|
+
if (!config.features.includes("s3"))
|
|
106
|
+
return null;
|
|
107
|
+
if (config.s3Provider === "existing") {
|
|
108
|
+
return {
|
|
109
|
+
bucket: config.s3ExistingBucket || "",
|
|
110
|
+
endpoint: config.s3ExistingEndpoint || "",
|
|
111
|
+
region: config.s3ExistingRegion || "",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const providerConfig = getConfig().providers.s3[config.s3Provider];
|
|
115
|
+
return {
|
|
116
|
+
bucket: `${config.name}-assets`,
|
|
117
|
+
endpoint: providerConfig?.endpoint || "",
|
|
118
|
+
region: providerConfig?.region || "",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Templates (inline — small enough to not warrant separate files)
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
const TFVARS_TEMPLATE = `server_name = "{{name}}-prod"
|
|
125
|
+
server_type = "{{serverType}}"
|
|
126
|
+
server_location = "{{serverLocation}}"
|
|
127
|
+
ssh_key_name = "deploy-key"
|
|
128
|
+
ssh_public_key = "ssh-ed25519 CHANGE_ME"
|
|
129
|
+
|
|
130
|
+
domain = "{{domain}}"
|
|
131
|
+
subdomains = {
|
|
132
|
+
{{#each subdomains}}
|
|
133
|
+
"{{@key}}" = "{{this}}"
|
|
134
|
+
{{/each}}
|
|
135
|
+
}
|
|
136
|
+
dns_ttl = 300
|
|
137
|
+
|
|
138
|
+
firewall_enabled = true
|
|
139
|
+
|
|
140
|
+
{{#if s3Enabled}}
|
|
141
|
+
s3_enabled = true
|
|
142
|
+
s3_bucket_name = "{{s3BucketName}}"
|
|
143
|
+
s3_bucket_acl = "private"
|
|
144
|
+
s3_location = "{{s3Location}}"
|
|
145
|
+
{{else}}
|
|
146
|
+
s3_enabled = false
|
|
147
|
+
{{/if}}
|
|
148
|
+
`;
|
|
149
|
+
const DNS_ONLY_TFVARS_TEMPLATE = `domain = "{{domain}}"
|
|
150
|
+
subdomains = {
|
|
151
|
+
{{#each subdomains}}
|
|
152
|
+
"{{@key}}" = "{{this}}"
|
|
153
|
+
{{/each}}
|
|
154
|
+
}
|
|
155
|
+
target_ipv4 = "{{targetIpv4}}"
|
|
156
|
+
target_ipv6 = "{{targetIpv6}}"
|
|
157
|
+
dns_ttl = 300
|
|
158
|
+
`;
|
|
159
|
+
const COOLIFY_ENV_TEMPLATE = `COOLIFY_URL="{{coolifyUrl}}"
|
|
160
|
+
|
|
161
|
+
PROJECT_NAME="{{name}}"
|
|
162
|
+
ENVIRONMENT_NAME="production"
|
|
163
|
+
|
|
164
|
+
APP_NAME="{{name}}-web"
|
|
165
|
+
GITHUB_REPO_URL="{{repoUrl}}"
|
|
166
|
+
APP_PORT="{{clientPort}}"
|
|
167
|
+
SERVER_PORT="{{serverPort}}"
|
|
168
|
+
|
|
169
|
+
APP_DOMAIN="{{domain}}"
|
|
170
|
+
|
|
171
|
+
MONGO_ENABLED="yes"
|
|
172
|
+
REDIS_ENABLED="{{#if redisEnabled}}yes{{else}}no{{/if}}"
|
|
173
|
+
|
|
174
|
+
S3_PROVIDER="{{s3Provider}}"
|
|
175
|
+
S3_BUCKET="{{s3Bucket}}"
|
|
176
|
+
S3_ENDPOINT="{{s3Endpoint}}"
|
|
177
|
+
S3_ACCESS_KEY=""
|
|
178
|
+
S3_SECRET_KEY=""
|
|
179
|
+
S3_REGION="{{s3Region}}"
|
|
180
|
+
|
|
181
|
+
{{#if stripe}}
|
|
182
|
+
STRIPE_SECRET_KEY=""
|
|
183
|
+
STRIPE_PUBLISHABLE_KEY=""
|
|
184
|
+
STRIPE_WEBHOOK_SECRET=""
|
|
185
|
+
{{/if}}
|
|
186
|
+
|
|
187
|
+
{{#if analytics}}
|
|
188
|
+
GLITCHTIP_DSN=""
|
|
189
|
+
OPENPANEL_API_URL=""
|
|
190
|
+
OPENPANEL_CLIENT_ID=""
|
|
191
|
+
OPENPANEL_CLIENT_SECRET=""
|
|
192
|
+
{{/if}}
|
|
193
|
+
|
|
194
|
+
{{#each mlServices}}
|
|
195
|
+
ML_{{this}}_ENDPOINT=""
|
|
196
|
+
{{/each}}
|
|
197
|
+
|
|
198
|
+
TOKEN_SECRET=""
|
|
199
|
+
`;
|
|
200
|
+
//# sourceMappingURL=infra.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infra.js","sourceRoot":"","sources":["../../src/scaffold/infra.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Feature, GpuPlatform, MlService, ProjectConfig, S3Provider } from "../prompts.js";
|
|
2
|
+
import type { ProjectPorts } from "../utils/ports.js";
|
|
3
|
+
export declare const MANIFEST_FILENAME = ".hatchkit.json";
|
|
4
|
+
export declare const MANIFEST_VERSION = 1;
|
|
5
|
+
export interface ProjectManifest {
|
|
6
|
+
/** Schema version. Increment when the shape changes incompatibly. */
|
|
7
|
+
version: typeof MANIFEST_VERSION;
|
|
8
|
+
/** CLI version that produced this manifest (diagnostic only). */
|
|
9
|
+
cliVersion: string;
|
|
10
|
+
/** ISO timestamp of the scaffold. */
|
|
11
|
+
scaffoldedAt: string;
|
|
12
|
+
/** Project name — duplicated from package.json for convenience. */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Production domain — already public in DNS + .env.example. */
|
|
15
|
+
domain: string;
|
|
16
|
+
/** Feature flags selected at scaffold. */
|
|
17
|
+
features: Feature[];
|
|
18
|
+
/** ML services wired into the backend. */
|
|
19
|
+
mlServices: MlService[];
|
|
20
|
+
/** S3 provider name (`hetzner` / `aws` / `r2` / `existing` / `none`).
|
|
21
|
+
* Credentials are NOT stored here — only the choice. */
|
|
22
|
+
s3Provider: S3Provider;
|
|
23
|
+
/** Where the app deploys. `existing` vs `new` is public-safe; the
|
|
24
|
+
* actual serverId/IP is not in the manifest. */
|
|
25
|
+
deployTarget: "existing" | "new";
|
|
26
|
+
/** GPU platform choice, if any ML services need deployment. */
|
|
27
|
+
gpuPlatform?: GpuPlatform;
|
|
28
|
+
/** HF model ID for custom-hf, if selected. HF models are public. */
|
|
29
|
+
customHfModelId?: string;
|
|
30
|
+
/** GPU tier (T4/A10G/A100/H100) — public product names. */
|
|
31
|
+
customHfGpuType?: string;
|
|
32
|
+
/** Ports assigned to this project. They're already public in the
|
|
33
|
+
* scaffolded .env.development files and docker-compose.yml. */
|
|
34
|
+
ports: {
|
|
35
|
+
server: number;
|
|
36
|
+
client: number;
|
|
37
|
+
nativeHmr?: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Build a manifest from the internal ProjectConfig, explicitly
|
|
41
|
+
* whitelisting only the safe fields. Any new field on ProjectConfig
|
|
42
|
+
* will NOT leak into the manifest unless added here on purpose. */
|
|
43
|
+
export declare function toManifest(config: ProjectConfig, ports: ProjectPorts, cliVersion: string): ProjectManifest;
|
|
44
|
+
export declare function writeManifest(outputDir: string, manifest: ProjectManifest): void;
|
|
45
|
+
/** Read + validate a manifest from a scaffolded project directory.
|
|
46
|
+
* Returns null if the file doesn't exist. Throws on malformed JSON
|
|
47
|
+
* or an unknown schema version so downstream code doesn't silently
|
|
48
|
+
* operate on a wrong shape. */
|
|
49
|
+
export declare function readManifest(projectDir: string): ProjectManifest | null;
|
|
50
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/scaffold/manifest.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AAClD,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,OAAO,EAAE,OAAO,gBAAgB,CAAC;IACjC,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,0CAA0C;IAC1C,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB;6DACyD;IACzD,UAAU,EAAE,UAAU,CAAC;IACvB;qDACiD;IACjD,YAAY,EAAE,UAAU,GAAG,KAAK,CAAC;IACjC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;oEACgE;IAChE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/D;AAED;;oEAEoE;AACpE,wBAAgB,UAAU,CACxB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,YAAY,EACnB,UAAU,EAAE,MAAM,GACjB,eAAe,CAoBjB;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAGhF;AAED;;;gCAGgC;AAChC,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAiBvE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Project manifest — .hatchkit.json at the root of a scaffolded project.
|
|
3
|
+
*
|
|
4
|
+
* Purpose: capture just enough about how this project was scaffolded
|
|
5
|
+
* that `hatchkit update` can diff the current feature set against a
|
|
6
|
+
* desired new one and apply only the delta.
|
|
7
|
+
*
|
|
8
|
+
* ============================================================
|
|
9
|
+
* SECURITY: WHAT GOES IN — AND WHAT ABSOLUTELY DOES NOT
|
|
10
|
+
* ============================================================
|
|
11
|
+
*
|
|
12
|
+
* The manifest file gets committed to the project's git repo. Treat
|
|
13
|
+
* every field as eventually public. The included fields below are all
|
|
14
|
+
* already public in one way or another (package.json `name`, the
|
|
15
|
+
* domain is in DNS + .env.example, feature flags are inferable from
|
|
16
|
+
* dependency lists, ports are in .env.* and docker-compose.yml).
|
|
17
|
+
*
|
|
18
|
+
* Fields that MUST NEVER be written here:
|
|
19
|
+
* - tokens, passwords, API keys (any credential)
|
|
20
|
+
* - serverIp, serverId (Coolify server coordinates)
|
|
21
|
+
* - s3ExistingAccessKey / SecretKey / Endpoint / Bucket (user creds)
|
|
22
|
+
* - serverSize / serverLocation (infrastructure cost signal)
|
|
23
|
+
*
|
|
24
|
+
* ProjectConfig has those fields; the `toManifest` function below is
|
|
25
|
+
* the single choke point that picks out the safe subset. Any time a
|
|
26
|
+
* new field is added to ProjectConfig, it must be triaged here — the
|
|
27
|
+
* default is to NOT include it.
|
|
28
|
+
*/
|
|
29
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
export const MANIFEST_FILENAME = ".hatchkit.json";
|
|
32
|
+
export const MANIFEST_VERSION = 1;
|
|
33
|
+
/** Build a manifest from the internal ProjectConfig, explicitly
|
|
34
|
+
* whitelisting only the safe fields. Any new field on ProjectConfig
|
|
35
|
+
* will NOT leak into the manifest unless added here on purpose. */
|
|
36
|
+
export function toManifest(config, ports, cliVersion) {
|
|
37
|
+
return {
|
|
38
|
+
version: MANIFEST_VERSION,
|
|
39
|
+
cliVersion,
|
|
40
|
+
scaffoldedAt: new Date().toISOString(),
|
|
41
|
+
name: config.name,
|
|
42
|
+
domain: config.domain,
|
|
43
|
+
features: [...config.features],
|
|
44
|
+
mlServices: [...config.mlServices],
|
|
45
|
+
s3Provider: config.s3Provider,
|
|
46
|
+
deployTarget: config.deployTarget,
|
|
47
|
+
gpuPlatform: config.gpuPlatform,
|
|
48
|
+
customHfModelId: config.customHfModelId,
|
|
49
|
+
customHfGpuType: config.customHfGpuType,
|
|
50
|
+
ports: {
|
|
51
|
+
server: ports.server,
|
|
52
|
+
client: ports.client,
|
|
53
|
+
nativeHmr: ports.nativeHmr,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function writeManifest(outputDir, manifest) {
|
|
58
|
+
const path = join(outputDir, MANIFEST_FILENAME);
|
|
59
|
+
writeFileSync(path, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
60
|
+
}
|
|
61
|
+
/** Read + validate a manifest from a scaffolded project directory.
|
|
62
|
+
* Returns null if the file doesn't exist. Throws on malformed JSON
|
|
63
|
+
* or an unknown schema version so downstream code doesn't silently
|
|
64
|
+
* operate on a wrong shape. */
|
|
65
|
+
export function readManifest(projectDir) {
|
|
66
|
+
const path = join(projectDir, MANIFEST_FILENAME);
|
|
67
|
+
if (!existsSync(path))
|
|
68
|
+
return null;
|
|
69
|
+
let parsed;
|
|
70
|
+
try {
|
|
71
|
+
parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
throw new Error(`Manifest at ${path} is not valid JSON: ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
if (!parsed ||
|
|
77
|
+
typeof parsed !== "object" ||
|
|
78
|
+
parsed.version !== MANIFEST_VERSION) {
|
|
79
|
+
throw new Error(`Manifest at ${path} has unknown version. Expected ${MANIFEST_VERSION}.`);
|
|
80
|
+
}
|
|
81
|
+
return parsed;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/scaffold/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,MAAM,CAAC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAClD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAkClC;;oEAEoE;AACpE,MAAM,UAAU,UAAU,CACxB,MAAqB,EACrB,KAAmB,EACnB,UAAkB;IAElB,OAAO;QACL,OAAO,EAAE,gBAAgB;QACzB,UAAU;QACV,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC9B,UAAU,EAAE,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;QAClC,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,KAAK,EAAE;YACL,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,QAAyB;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAChD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACzE,CAAC;AAED;;;gCAGgC;AAChC,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,uBAAwB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,IACE,CAAC,MAAM;QACP,OAAO,MAAM,KAAK,QAAQ;QACzB,MAAgC,CAAC,OAAO,KAAK,gBAAgB,EAC9D,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,kCAAkC,gBAAgB,GAAG,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,MAAyB,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type MlServiceEntry } from "../config.js";
|
|
2
|
+
import type { MlService, ProjectConfig } from "../prompts.js";
|
|
3
|
+
/** Resolve ML services — reuse existing or mark for deployment.
|
|
4
|
+
* Services in config.forceRedeployMl bypass the registry and always
|
|
5
|
+
* go to the deploy list, which recovers from stale registry entries. */
|
|
6
|
+
export declare function resolveMlServices(config: ProjectConfig): Promise<{
|
|
7
|
+
reuse: Array<{
|
|
8
|
+
service: MlService;
|
|
9
|
+
entry: MlServiceEntry;
|
|
10
|
+
}>;
|
|
11
|
+
deploy: MlService[];
|
|
12
|
+
}>;
|
|
13
|
+
/** Get the env var name for an ML service endpoint. */
|
|
14
|
+
export declare function mlEnvVarName(service: MlService): string;
|
|
15
|
+
/** Print ML service resolution summary. */
|
|
16
|
+
export declare function printMlSummary(reuse: Array<{
|
|
17
|
+
service: MlService;
|
|
18
|
+
entry: MlServiceEntry;
|
|
19
|
+
}>, deploy: MlService[]): void;
|
|
20
|
+
//# sourceMappingURL=ml-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ml-client.d.ts","sourceRoot":"","sources":["../../src/scaffold/ml-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9D;;yEAEyE;AACzE,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC;IACtE,KAAK,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC;IAC5D,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB,CAAC,CAeD;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAGvD;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAC5B,KAAK,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAAC,EAC3D,MAAM,EAAE,SAAS,EAAE,GAClB,IAAI,CAUN"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { getMlServices } from "../config.js";
|
|
3
|
+
/** Resolve ML services — reuse existing or mark for deployment.
|
|
4
|
+
* Services in config.forceRedeployMl bypass the registry and always
|
|
5
|
+
* go to the deploy list, which recovers from stale registry entries. */
|
|
6
|
+
export async function resolveMlServices(config) {
|
|
7
|
+
const registry = getMlServices();
|
|
8
|
+
const forceSet = new Set(config.forceRedeployMl ?? []);
|
|
9
|
+
const reuse = [];
|
|
10
|
+
const deploy = [];
|
|
11
|
+
for (const service of config.mlServices) {
|
|
12
|
+
if (registry[service] && !forceSet.has(service)) {
|
|
13
|
+
reuse.push({ service, entry: registry[service] });
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
deploy.push(service);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { reuse, deploy };
|
|
20
|
+
}
|
|
21
|
+
/** Get the env var name for an ML service endpoint. */
|
|
22
|
+
export function mlEnvVarName(service) {
|
|
23
|
+
const name = service.replace(/-/g, "_").toUpperCase();
|
|
24
|
+
return `ML_${name}_ENDPOINT`;
|
|
25
|
+
}
|
|
26
|
+
/** Print ML service resolution summary. */
|
|
27
|
+
export function printMlSummary(reuse, deploy) {
|
|
28
|
+
if (reuse.length > 0) {
|
|
29
|
+
console.log(chalk.dim("\n Reusing existing ML services:"));
|
|
30
|
+
for (const { service, entry } of reuse) {
|
|
31
|
+
console.log(chalk.dim(` ${service} → ${entry.endpoint}`));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (deploy.length > 0) {
|
|
35
|
+
console.log(chalk.yellow(`\n New ML services to deploy: ${deploy.join(", ")}`));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=ml-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ml-client.js","sourceRoot":"","sources":["../../src/scaffold/ml-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAuB,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlE;;yEAEyE;AACzE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAqB;IAI3D,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,KAAK,GAAyD,EAAE,CAAC;IACvE,MAAM,MAAM,GAAgB,EAAE,CAAC;IAE/B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACxC,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,YAAY,CAAC,OAAkB;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,OAAO,MAAM,IAAI,WAAW,CAAC;AAC/B,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,cAAc,CAC5B,KAA2D,EAC3D,MAAmB;IAEnB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC,CAAC;QAC5D,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC;YACvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,OAAO,MAAM,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,kCAAkC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function stripPackageJsonScripts(outputDir: string, names: string[]): void;
|
|
2
|
+
export declare function stripPackageJsonDeps(outputDir: string, names: string[]): void;
|
|
3
|
+
export declare function stripPackageJsonBuildBlock(outputDir: string): void;
|
|
4
|
+
/** Drop the `pnpm typecheck:electron` segment from the root `typecheck`
|
|
5
|
+
* script regardless of where it sits in the `&&` chain. Handles:
|
|
6
|
+
* "A && pnpm typecheck:electron" → "A"
|
|
7
|
+
* "pnpm typecheck:electron && A" → "A"
|
|
8
|
+
* "pnpm typecheck:electron" → <script deleted entirely>
|
|
9
|
+
*/
|
|
10
|
+
export declare function unchainTypecheckScript(outputDir: string): void;
|
|
11
|
+
/** Set a specific script entry in the root `package.json`. Creates
|
|
12
|
+
* `scripts` if missing. */
|
|
13
|
+
export declare function setPackageJsonScript(outputDir: string, name: string, value: string): void;
|
|
14
|
+
/** Read the `name` field from a workspace package's package.json. */
|
|
15
|
+
export declare function readPackageName(pkgDir: string): string | undefined;
|
|
16
|
+
/** Return every workspace package's `name` field from its package.json.
|
|
17
|
+
* Used to populate Next's `transpilePackages` dynamically instead of
|
|
18
|
+
* hardcoding `@starter/*` names. */
|
|
19
|
+
export declare function readWorkspacePackageNames(outputDir: string): string[];
|
|
20
|
+
//# sourceMappingURL=pkg-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkg-json.d.ts","sourceRoot":"","sources":["../../src/scaffold/pkg-json.ts"],"names":[],"mappings":"AAaA,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAOhF;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAU7E;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAMlE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAkB9D;AAED;4BAC4B;AAC5B,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAOzF;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASlE;AAED;;qCAEqC;AACrC,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CASrE"}
|