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.
Files changed (149) hide show
  1. package/dist/config.d.ts +131 -0
  2. package/dist/config.d.ts.map +1 -0
  3. package/dist/config.js +629 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/deploy/coolify.d.ts +4 -0
  6. package/dist/deploy/coolify.d.ts.map +1 -0
  7. package/dist/deploy/coolify.js +20 -0
  8. package/dist/deploy/coolify.js.map +1 -0
  9. package/dist/deploy/github.d.ts +4 -0
  10. package/dist/deploy/github.d.ts.map +1 -0
  11. package/dist/deploy/github.js +39 -0
  12. package/dist/deploy/github.js.map +1 -0
  13. package/dist/deploy/gpu.d.ts +4 -0
  14. package/dist/deploy/gpu.d.ts.map +1 -0
  15. package/dist/deploy/gpu.js +97 -0
  16. package/dist/deploy/gpu.js.map +1 -0
  17. package/dist/deploy/keys.d.ts +9 -0
  18. package/dist/deploy/keys.d.ts.map +1 -0
  19. package/dist/deploy/keys.js +73 -0
  20. package/dist/deploy/keys.js.map +1 -0
  21. package/dist/deploy/terraform.d.ts +4 -0
  22. package/dist/deploy/terraform.d.ts.map +1 -0
  23. package/dist/deploy/terraform.js +55 -0
  24. package/dist/deploy/terraform.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +599 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/prompts.d.ts +52 -0
  30. package/dist/prompts.d.ts.map +1 -0
  31. package/dist/prompts.js +313 -0
  32. package/dist/prompts.js.map +1 -0
  33. package/dist/provision/glitchtip.d.ts +6 -0
  34. package/dist/provision/glitchtip.d.ts.map +1 -0
  35. package/dist/provision/glitchtip.js +46 -0
  36. package/dist/provision/glitchtip.js.map +1 -0
  37. package/dist/provision/index.d.ts +9 -0
  38. package/dist/provision/index.d.ts.map +1 -0
  39. package/dist/provision/index.js +108 -0
  40. package/dist/provision/index.js.map +1 -0
  41. package/dist/provision/openpanel.d.ts +8 -0
  42. package/dist/provision/openpanel.d.ts.map +1 -0
  43. package/dist/provision/openpanel.js +66 -0
  44. package/dist/provision/openpanel.js.map +1 -0
  45. package/dist/provision/resend.d.ts +16 -0
  46. package/dist/provision/resend.d.ts.map +1 -0
  47. package/dist/provision/resend.js +43 -0
  48. package/dist/provision/resend.js.map +1 -0
  49. package/dist/scaffold/app.d.ts +13 -0
  50. package/dist/scaffold/app.d.ts.map +1 -0
  51. package/dist/scaffold/app.js +340 -0
  52. package/dist/scaffold/app.js.map +1 -0
  53. package/dist/scaffold/dotenvx.d.ts +30 -0
  54. package/dist/scaffold/dotenvx.d.ts.map +1 -0
  55. package/dist/scaffold/dotenvx.js +142 -0
  56. package/dist/scaffold/dotenvx.js.map +1 -0
  57. package/dist/scaffold/infra.d.ts +17 -0
  58. package/dist/scaffold/infra.d.ts.map +1 -0
  59. package/dist/scaffold/infra.js +200 -0
  60. package/dist/scaffold/infra.js.map +1 -0
  61. package/dist/scaffold/manifest.d.ts +50 -0
  62. package/dist/scaffold/manifest.d.ts.map +1 -0
  63. package/dist/scaffold/manifest.js +83 -0
  64. package/dist/scaffold/manifest.js.map +1 -0
  65. package/dist/scaffold/ml-client.d.ts +20 -0
  66. package/dist/scaffold/ml-client.d.ts.map +1 -0
  67. package/dist/scaffold/ml-client.js +38 -0
  68. package/dist/scaffold/ml-client.js.map +1 -0
  69. package/dist/scaffold/pkg-json.d.ts +20 -0
  70. package/dist/scaffold/pkg-json.d.ts.map +1 -0
  71. package/dist/scaffold/pkg-json.js +113 -0
  72. package/dist/scaffold/pkg-json.js.map +1 -0
  73. package/dist/scaffold/starter-files.d.ts +40 -0
  74. package/dist/scaffold/starter-files.d.ts.map +1 -0
  75. package/dist/scaffold/starter-files.js +197 -0
  76. package/dist/scaffold/starter-files.js.map +1 -0
  77. package/dist/scaffold/update.d.ts +8 -0
  78. package/dist/scaffold/update.d.ts.map +1 -0
  79. package/dist/scaffold/update.js +255 -0
  80. package/dist/scaffold/update.js.map +1 -0
  81. package/dist/templates/addons/analytics/middleware.ts.hbs +13 -0
  82. package/dist/templates/addons/analytics/sentry.ts.hbs +16 -0
  83. package/dist/templates/addons/storage/s3.ts.hbs +40 -0
  84. package/dist/templates/addons/storage/upload.ts.hbs +23 -0
  85. package/dist/templates/addons/stripe/checkout.ts.hbs +27 -0
  86. package/dist/templates/addons/stripe/client.ts.hbs +6 -0
  87. package/dist/templates/addons/stripe/webhook.ts.hbs +39 -0
  88. package/dist/templates/addons/websocket/redis.ts.hbs +25 -0
  89. package/dist/templates/addons/websocket/ws.ts.hbs +32 -0
  90. package/dist/templates/base/.dockerignore.hbs +7 -0
  91. package/dist/templates/base/Dockerfile.hbs +18 -0
  92. package/dist/templates/base/env.example.hbs +60 -0
  93. package/dist/templates/base/github-actions.yml.hbs +35 -0
  94. package/dist/templates/base/gitignore.hbs +5 -0
  95. package/dist/templates/base/package.json.hbs +36 -0
  96. package/dist/templates/base/src/auth/auth.ts.hbs +13 -0
  97. package/dist/templates/base/src/auth/routes.ts.hbs +19 -0
  98. package/dist/templates/base/src/config.ts.hbs +36 -0
  99. package/dist/templates/base/src/db.ts.hbs +12 -0
  100. package/dist/templates/base/src/index.ts.hbs +80 -0
  101. package/dist/templates/base/src/routes/health.ts.hbs +12 -0
  102. package/dist/templates/base/tsconfig.json.hbs +18 -0
  103. package/dist/templates/ml-clients/3d-extraction.ts.hbs +20 -0
  104. package/dist/templates/ml-clients/background-removal.ts.hbs +17 -0
  105. package/dist/templates/ml-clients/custom-hf.ts.hbs +20 -0
  106. package/dist/templates/ml-clients/image-recognition.ts.hbs +22 -0
  107. package/dist/templates/ml-clients/subtitles.ts.hbs +26 -0
  108. package/dist/utils/coolify-api.d.ts +45 -0
  109. package/dist/utils/coolify-api.d.ts.map +1 -0
  110. package/dist/utils/coolify-api.js +72 -0
  111. package/dist/utils/coolify-api.js.map +1 -0
  112. package/dist/utils/errors.d.ts +2 -0
  113. package/dist/utils/errors.d.ts.map +1 -0
  114. package/dist/utils/errors.js +31 -0
  115. package/dist/utils/errors.js.map +1 -0
  116. package/dist/utils/exec.d.ts +23 -0
  117. package/dist/utils/exec.d.ts.map +1 -0
  118. package/dist/utils/exec.js +47 -0
  119. package/dist/utils/exec.js.map +1 -0
  120. package/dist/utils/flags.d.ts +17 -0
  121. package/dist/utils/flags.d.ts.map +1 -0
  122. package/dist/utils/flags.js +116 -0
  123. package/dist/utils/flags.js.map +1 -0
  124. package/dist/utils/hf-api.d.ts +13 -0
  125. package/dist/utils/hf-api.d.ts.map +1 -0
  126. package/dist/utils/hf-api.js +30 -0
  127. package/dist/utils/hf-api.js.map +1 -0
  128. package/dist/utils/ports.d.ts +29 -0
  129. package/dist/utils/ports.d.ts.map +1 -0
  130. package/dist/utils/ports.js +86 -0
  131. package/dist/utils/ports.js.map +1 -0
  132. package/dist/utils/secrets.d.ts +25 -0
  133. package/dist/utils/secrets.d.ts.map +1 -0
  134. package/dist/utils/secrets.js +51 -0
  135. package/dist/utils/secrets.js.map +1 -0
  136. package/dist/utils/template.d.ts +7 -0
  137. package/dist/utils/template.d.ts.map +1 -0
  138. package/dist/utils/template.js +35 -0
  139. package/dist/utils/template.js.map +1 -0
  140. package/dist/utils/validate.d.ts +16 -0
  141. package/dist/utils/validate.d.ts.map +1 -0
  142. package/dist/utils/validate.js +60 -0
  143. package/dist/utils/validate.js.map +1 -0
  144. package/dist/utils/version.d.ts +2 -0
  145. package/dist/utils/version.d.ts.map +1 -0
  146. package/dist/utils/version.js +21 -0
  147. package/dist/utils/version.js.map +1 -0
  148. package/package.json +48 -0
  149. package/scripts/copy-templates.mjs +20 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/utils/ports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /** Well-known secret keys used across the CLI. New secrets should add
2
+ * their key here so `clearAllSecrets` can reach them on reset. */
3
+ export declare const SECRET_KEYS: {
4
+ readonly coolifyToken: "coolify:token";
5
+ readonly hetznerToken: "hetzner:token";
6
+ readonly dnsInwxPassword: "dns:inwx:password";
7
+ readonly dnsCloudflareToken: "dns:cloudflare:token";
8
+ readonly s3AccessKey: (provider: string) => string;
9
+ readonly s3SecretKey: (provider: string) => string;
10
+ readonly gpuApiKey: (platform: string) => string;
11
+ readonly glitchtipToken: "glitchtip:auth-token";
12
+ readonly openpanelToken: "openpanel:personal-access-token";
13
+ readonly openpanelClientSecret: (name: string) => string;
14
+ readonly resendApiKey: "resend:api-key";
15
+ /** Per-scaffolded-project dotenvx private key for .env.production.
16
+ * Stored in the OS keychain so the CLI's on-disk state never holds
17
+ * decryption material for the starter's encrypted env. */
18
+ readonly dotenvxPrivateKey: (projectName: string) => string;
19
+ };
20
+ export declare function getSecret(key: string): Promise<string | null>;
21
+ export declare function setSecret(key: string, value: string): Promise<void>;
22
+ export declare function deleteSecret(key: string): Promise<boolean>;
23
+ /** Wipe every secret belonging to this CLI from the keychain. */
24
+ export declare function clearAllSecrets(): Promise<void>;
25
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/utils/secrets.ts"],"names":[],"mappings":"AAoBA;mEACmE;AACnE,eAAO,MAAM,WAAW;;;;;qCAKE,MAAM;qCACN,MAAM;mCACR,MAAM;;;2CAGE,MAAM;;IAEpC;;+DAE2D;8CAC1B,MAAM;CAC/B,CAAC;AAEX,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEhE;AAED,iEAAiE;AACjE,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrD"}
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Secrets store backed by the OS keychain (macOS Keychain / Windows
3
+ * Credential Vault / GNOME libsecret) via `keytar`.
4
+ *
5
+ * Everything sensitive (tokens, passwords, access keys) goes here. The
6
+ * Conf-backed JSON store in config.ts keeps only metadata (URLs, cached
7
+ * server lists, `lastVerified` timestamps, status flags).
8
+ *
9
+ * Naming:
10
+ * service = "hatchkit"
11
+ * account = stable slug, e.g. "coolify:token", "s3:hetzner:secret-key"
12
+ */
13
+ import keytar from "keytar";
14
+ // Tests set HATCHKIT_KEYTAR_SERVICE to a throwaway value so they
15
+ // don't pollute the real user's keychain with scaffold-test artifacts.
16
+ // In normal runs this is unset and everything lives under "hatchkit".
17
+ const SERVICE = process.env.HATCHKIT_KEYTAR_SERVICE ?? "hatchkit";
18
+ /** Well-known secret keys used across the CLI. New secrets should add
19
+ * their key here so `clearAllSecrets` can reach them on reset. */
20
+ export const SECRET_KEYS = {
21
+ coolifyToken: "coolify:token",
22
+ hetznerToken: "hetzner:token",
23
+ dnsInwxPassword: "dns:inwx:password",
24
+ dnsCloudflareToken: "dns:cloudflare:token",
25
+ s3AccessKey: (provider) => `s3:${provider}:access-key`,
26
+ s3SecretKey: (provider) => `s3:${provider}:secret-key`,
27
+ gpuApiKey: (platform) => `gpu:${platform}:api-key`,
28
+ glitchtipToken: "glitchtip:auth-token",
29
+ openpanelToken: "openpanel:personal-access-token",
30
+ openpanelClientSecret: (name) => `openpanel:${name}:client-secret`,
31
+ resendApiKey: "resend:api-key",
32
+ /** Per-scaffolded-project dotenvx private key for .env.production.
33
+ * Stored in the OS keychain so the CLI's on-disk state never holds
34
+ * decryption material for the starter's encrypted env. */
35
+ dotenvxPrivateKey: (projectName) => `dotenvx:${projectName}:production-private-key`,
36
+ };
37
+ export async function getSecret(key) {
38
+ return keytar.getPassword(SERVICE, key);
39
+ }
40
+ export async function setSecret(key, value) {
41
+ await keytar.setPassword(SERVICE, key, value);
42
+ }
43
+ export async function deleteSecret(key) {
44
+ return keytar.deletePassword(SERVICE, key);
45
+ }
46
+ /** Wipe every secret belonging to this CLI from the keychain. */
47
+ export async function clearAllSecrets() {
48
+ const entries = await keytar.findCredentials(SERVICE);
49
+ await Promise.all(entries.map((e) => keytar.deletePassword(SERVICE, e.account)));
50
+ }
51
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/utils/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,iEAAiE;AACjE,uEAAuE;AACvE,sEAAsE;AACtE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,UAAU,CAAC;AAElE;mEACmE;AACnE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,eAAe,EAAE,mBAAmB;IACpC,kBAAkB,EAAE,sBAAsB;IAC1C,WAAW,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,QAAQ,aAAa;IAC9D,WAAW,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,QAAQ,aAAa;IAC9D,SAAS,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,OAAO,QAAQ,UAAU;IAC1D,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,iCAAiC;IACjD,qBAAqB,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,aAAa,IAAI,gBAAgB;IAC1E,YAAY,EAAE,gBAAgB;IAC9B;;+DAE2D;IAC3D,iBAAiB,EAAE,CAAC,WAAmB,EAAE,EAAE,CAAC,WAAW,WAAW,yBAAyB;CACnF,CAAC;AAEX,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,KAAa;IACxD,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /** Render a Handlebars template file with the given context. */
2
+ export declare function renderTemplate(templatePath: string, context: Record<string, unknown>): string;
3
+ /** Render a template string (not from file) with the given context. */
4
+ export declare function renderString(source: string, context: Record<string, unknown>): string;
5
+ /** Get the templates directory path. */
6
+ export declare function getTemplatesDir(): string;
7
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/utils/template.ts"],"names":[],"mappings":"AAuBA,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAQ7F;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAGrF;AAED,wCAAwC;AACxC,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
@@ -0,0 +1,35 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import Handlebars from "handlebars";
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const TEMPLATES_DIR = join(__dirname, "..", "templates");
7
+ /** Register Handlebars helpers. */
8
+ Handlebars.registerHelper("ifIn", function (value, list, options) {
9
+ if (list?.includes(value)) {
10
+ return options.fn(this);
11
+ }
12
+ return options.inverse(this);
13
+ });
14
+ Handlebars.registerHelper("join", (arr, separator) => arr.join(separator));
15
+ Handlebars.registerHelper("json", (obj) => JSON.stringify(obj, null, 2));
16
+ /** Render a Handlebars template file with the given context. */
17
+ export function renderTemplate(templatePath, context) {
18
+ const fullPath = join(TEMPLATES_DIR, templatePath);
19
+ if (!existsSync(fullPath)) {
20
+ throw new Error(`Template not found: ${fullPath}`);
21
+ }
22
+ const source = readFileSync(fullPath, "utf-8");
23
+ const template = Handlebars.compile(source, { noEscape: true });
24
+ return template(context);
25
+ }
26
+ /** Render a template string (not from file) with the given context. */
27
+ export function renderString(source, context) {
28
+ const template = Handlebars.compile(source, { noEscape: true });
29
+ return template(context);
30
+ }
31
+ /** Get the templates directory path. */
32
+ export function getTemplatesDir() {
33
+ return TEMPLATES_DIR;
34
+ }
35
+ //# sourceMappingURL=template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/utils/template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAEzD,mCAAmC;AACnC,UAAU,CAAC,cAAc,CACvB,MAAM,EACN,UAAyB,KAAa,EAAE,IAAc,EAAE,OAAiC;IACvF,IAAI,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC,CACF,CAAC;AAEF,UAAU,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,GAAa,EAAE,SAAiB,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;AAE7F,UAAU,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,GAAY,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAElF,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,YAAoB,EAAE,OAAgC;IACnF,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IACnD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,OAAgC;IAC3E,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,eAAe;IAC7B,OAAO,aAAa,CAAC;AACvB,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** Validate a domain name (e.g. chess.ricos.site) */
2
+ export declare function validateDomain(value: string): boolean | string;
3
+ /** Validate a project name (kebab-case) */
4
+ export declare function validateProjectName(value: string): boolean | string;
5
+ /** Validate an S3 bucket name */
6
+ export declare function validateBucketName(value: string): boolean | string;
7
+ /** Validate a URL */
8
+ export declare function validateUrl(value: string): boolean | string;
9
+ /** Validate non-empty string */
10
+ export declare function validateRequired(value: string): boolean | string;
11
+ /** Extract base domain and subdomain from a full domain. */
12
+ export declare function parseDomain(domain: string): {
13
+ baseDomain: string;
14
+ subdomain: string;
15
+ };
16
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAM9D;AAED,2CAA2C;AAC3C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CASnE;AAED,iCAAiC;AACjC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAWlE;AAED,qBAAqB;AACrB,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAO3D;AAED,gCAAgC;AAChC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAKhE;AAED,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAQA"}
@@ -0,0 +1,60 @@
1
+ /** Validate a domain name (e.g. chess.ricos.site) */
2
+ export function validateDomain(value) {
3
+ const pattern = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;
4
+ if (!pattern.test(value)) {
5
+ return "Invalid domain. Use lowercase letters, numbers, and hyphens (e.g. chess.ricos.site)";
6
+ }
7
+ return true;
8
+ }
9
+ /** Validate a project name (kebab-case) */
10
+ export function validateProjectName(value) {
11
+ const pattern = /^[a-z][a-z0-9-]*$/;
12
+ if (!pattern.test(value)) {
13
+ return "Project name must be kebab-case (lowercase letters, numbers, hyphens)";
14
+ }
15
+ if (value.length > 40) {
16
+ return "Project name must be 40 characters or less";
17
+ }
18
+ return true;
19
+ }
20
+ /** Validate an S3 bucket name */
21
+ export function validateBucketName(value) {
22
+ if (value.length < 3 || value.length > 63) {
23
+ return "Bucket name must be 3-63 characters";
24
+ }
25
+ if (!/^[a-z0-9][a-z0-9.-]*[a-z0-9]$/.test(value)) {
26
+ return "Bucket name must be lowercase, start/end with letter or number";
27
+ }
28
+ if (value.includes("_")) {
29
+ return "Bucket name cannot contain underscores";
30
+ }
31
+ return true;
32
+ }
33
+ /** Validate a URL */
34
+ export function validateUrl(value) {
35
+ try {
36
+ new URL(value);
37
+ return true;
38
+ }
39
+ catch {
40
+ return "Invalid URL";
41
+ }
42
+ }
43
+ /** Validate non-empty string */
44
+ export function validateRequired(value) {
45
+ if (!value.trim()) {
46
+ return "This field is required";
47
+ }
48
+ return true;
49
+ }
50
+ /** Extract base domain and subdomain from a full domain. */
51
+ export function parseDomain(domain) {
52
+ const parts = domain.split(".");
53
+ if (parts.length < 3) {
54
+ return { baseDomain: domain, subdomain: "" };
55
+ }
56
+ const baseDomain = parts.slice(-2).join(".");
57
+ const subdomain = parts.slice(0, -2).join(".");
58
+ return { baseDomain, subdomain };
59
+ }
60
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,OAAO,GAAG,mEAAmE,CAAC;IACpF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,qFAAqF,CAAC;IAC/F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,OAAO,GAAG,mBAAmB,CAAC;IACpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,uEAAuE,CAAC;IACjF,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACtB,OAAO,4CAA4C,CAAC;IACtD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC1C,OAAO,qCAAqC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,gEAAgE,CAAC;IAC1E,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,wCAAwC,CAAC;IAClD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qBAAqB;AACrB,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,aAAa,CAAC;IACvB,CAAC;AACH,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB,OAAO,wBAAwB,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,MAAc;IAIxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC/C,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function getCliVersion(): string;
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/utils/version.ts"],"names":[],"mappings":"AAYA,wBAAgB,aAAa,IAAI,MAAM,CAQtC"}
@@ -0,0 +1,21 @@
1
+ /*
2
+ * CLI self-version lookup.
3
+ *
4
+ * Reads the `version` field from the CLI's own `package.json`. Works
5
+ * from both the compiled `cli/dist/` entry point and the `tsx` dev
6
+ * path (`cli/src/`). Falls back to "0.0.0" on any failure so callers
7
+ * can safely splat it into logs without worrying about exceptions.
8
+ */
9
+ import { readFileSync } from "node:fs";
10
+ import { join, resolve } from "node:path";
11
+ export function getCliVersion() {
12
+ try {
13
+ const pkgPath = resolve(join(import.meta.dirname, "..", "..", "package.json"));
14
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
15
+ return pkg.version ?? "0.0.0";
16
+ }
17
+ catch {
18
+ return "0.0.0";
19
+ }
20
+ }
21
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/utils/version.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;QAC/E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACvD,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "hatchkit",
3
+ "version": "0.1.1",
4
+ "description": "Interactive CLI for scaffolding full-stack projects and provisioning observability/email clients",
5
+ "type": "module",
6
+ "bin": {
7
+ "hatchkit": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "scripts",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && node scripts/copy-templates.mjs",
16
+ "dev": "tsx src/index.ts",
17
+ "start": "node dist/index.js",
18
+ "test": "tsx test-scaffold.ts",
19
+ "typecheck": "tsc --noEmit",
20
+ "lint": "biome check src",
21
+ "lint:fix": "biome check --write src",
22
+ "format": "biome format --write src",
23
+ "check": "pnpm run typecheck && pnpm run lint && pnpm run test",
24
+ "install-local": "pnpm run build && npm install -g .",
25
+ "release": "pnpm run release:patch",
26
+ "release:patch": "npm version patch -m \"chore: release v%s\" && pnpm run _release:finish",
27
+ "release:minor": "npm version minor -m \"chore: release v%s\" && pnpm run _release:finish",
28
+ "release:major": "npm version major -m \"chore: release v%s\" && pnpm run _release:finish",
29
+ "_release:finish": "pnpm run build && pnpm run typecheck && npm publish --access public && git push --follow-tags && npm install -g .",
30
+ "prepublishOnly": "pnpm run build"
31
+ },
32
+ "dependencies": {
33
+ "@inquirer/prompts": "^7.0.0",
34
+ "chalk": "^5.3.0",
35
+ "conf": "^13.0.0",
36
+ "execa": "^9.0.0",
37
+ "handlebars": "^4.7.8",
38
+ "keytar": "^7.9.0",
39
+ "ora": "^8.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@biomejs/biome": "^1.9.4",
43
+ "@dotenvx/dotenvx": "^1.61.2",
44
+ "@types/node": "^22.0.0",
45
+ "tsx": "^4.0.0",
46
+ "typescript": "^5.6.0"
47
+ }
48
+ }
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copy non-TS assets (Handlebars templates) from src/ into dist/ so
4
+ * the compiled CLI can resolve them at runtime via
5
+ * `dist/utils/../templates/...`. Run automatically from `pnpm build`.
6
+ */
7
+ import { cpSync, existsSync } from "node:fs";
8
+ import { dirname, join } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ const here = dirname(fileURLToPath(import.meta.url));
12
+ const root = join(here, "..");
13
+ const from = join(root, "src", "templates");
14
+ const to = join(root, "dist", "templates");
15
+
16
+ if (!existsSync(from)) {
17
+ console.error(`copy-templates: source missing at ${from}`);
18
+ process.exit(1);
19
+ }
20
+ cpSync(from, to, { recursive: true });