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,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for custom HuggingFace model: {{customHfModelId}}
|
|
3
|
+
* Deployed to: {{gpuPlatform}}
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const ENDPOINT = process.env.ML_CUSTOM_HF_ENDPOINT || "";
|
|
7
|
+
|
|
8
|
+
export async function predict(input: unknown): Promise<unknown> {
|
|
9
|
+
const res = await fetch(ENDPOINT, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: { "Content-Type": "application/json" },
|
|
12
|
+
body: JSON.stringify({ input }),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
throw new Error(`ML prediction failed: ${res.status} ${res.statusText}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return res.json();
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { config } from "../config.js";
|
|
2
|
+
|
|
3
|
+
export interface RecognitionResult {
|
|
4
|
+
label: string;
|
|
5
|
+
score: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function recognizeImage(imageBuffer: Buffer): Promise<RecognitionResult[]> {
|
|
9
|
+
const formData = new FormData();
|
|
10
|
+
formData.append("file", new Blob([imageBuffer]));
|
|
11
|
+
|
|
12
|
+
const res = await fetch(config.ML_IMAGE_RECOGNITION_ENDPOINT, {
|
|
13
|
+
method: "POST",
|
|
14
|
+
body: formData,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
throw new Error(`Image recognition failed: ${res.status} ${res.statusText}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return res.json() as Promise<RecognitionResult[]>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { config } from "../config.js";
|
|
2
|
+
|
|
3
|
+
export interface SubtitleSegment {
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function generateSubtitles(audioBuffer: Buffer): Promise<{
|
|
10
|
+
segments: SubtitleSegment[];
|
|
11
|
+
srt: string;
|
|
12
|
+
}> {
|
|
13
|
+
const formData = new FormData();
|
|
14
|
+
formData.append("file", new Blob([audioBuffer]));
|
|
15
|
+
|
|
16
|
+
const res = await fetch(config.ML_SUBTITLES_ENDPOINT, {
|
|
17
|
+
method: "POST",
|
|
18
|
+
body: formData,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
throw new Error(`Subtitle generation failed: ${res.status} ${res.statusText}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return res.json() as Promise<{ segments: SubtitleSegment[]; srt: string }>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface CoolifyServer {
|
|
2
|
+
id: number;
|
|
3
|
+
name: string;
|
|
4
|
+
ip: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CoolifyApiOptions {
|
|
8
|
+
url: string;
|
|
9
|
+
token: string;
|
|
10
|
+
}
|
|
11
|
+
/** Coolify REST API client. */
|
|
12
|
+
export declare class CoolifyApi {
|
|
13
|
+
private url;
|
|
14
|
+
private token;
|
|
15
|
+
constructor(options: CoolifyApiOptions);
|
|
16
|
+
private request;
|
|
17
|
+
/** Test connection and get Coolify version. */
|
|
18
|
+
getVersion(): Promise<string>;
|
|
19
|
+
/** List all servers. */
|
|
20
|
+
listServers(): Promise<CoolifyServer[]>;
|
|
21
|
+
/** List all projects. */
|
|
22
|
+
listProjects(): Promise<Array<{
|
|
23
|
+
id: number;
|
|
24
|
+
name: string;
|
|
25
|
+
}>>;
|
|
26
|
+
/** Create a new project. */
|
|
27
|
+
createProject(name: string): Promise<{
|
|
28
|
+
id: number;
|
|
29
|
+
name: string;
|
|
30
|
+
}>;
|
|
31
|
+
/** List Coolify apps/services so callers can resolve a UUID by name. */
|
|
32
|
+
listApplications(): Promise<Array<{
|
|
33
|
+
uuid: string;
|
|
34
|
+
name: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
}>>;
|
|
37
|
+
/** Upsert an env variable on a Coolify application. The Coolify API
|
|
38
|
+
* accepts multiple envs in one call so this is idempotent. */
|
|
39
|
+
setAppEnv(appUuid: string, envs: Record<string, string>, options?: {
|
|
40
|
+
isPreview?: boolean;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
/** Verify Coolify connection. Returns version string or throws. */
|
|
44
|
+
export declare function verifyCoolify(url: string, token: string): Promise<string>;
|
|
45
|
+
//# sourceMappingURL=coolify-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coolify-api.d.ts","sourceRoot":"","sources":["../../src/utils/coolify-api.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,+BAA+B;AAC/B,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,EAAE,iBAAiB;YAKxB,OAAO;IAqBrB,+CAA+C;IACzC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAKnC,wBAAwB;IAClB,WAAW,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAkB7C,yBAAyB;IACnB,YAAY,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAIlE,4BAA4B;IACtB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAIxE,wEAAwE;IAClE,gBAAgB,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAI9F;mEAC+D;IACzD,SAAS,CACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO,GACpC,OAAO,CAAC,IAAI,CAAC;CAYjB;AAED,mEAAmE;AACnE,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG/E"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Coolify REST API client. */
|
|
2
|
+
export class CoolifyApi {
|
|
3
|
+
url;
|
|
4
|
+
token;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.url = options.url.replace(/\/$/, "");
|
|
7
|
+
this.token = options.token;
|
|
8
|
+
}
|
|
9
|
+
async request(method, path, body) {
|
|
10
|
+
const res = await fetch(`${this.url}/api/v1${path}`, {
|
|
11
|
+
method,
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${this.token}`,
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
Accept: "application/json",
|
|
16
|
+
},
|
|
17
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const text = await res.text().catch(() => "");
|
|
21
|
+
throw new Error(`Coolify API ${method} ${path} failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
22
|
+
}
|
|
23
|
+
return res.json();
|
|
24
|
+
}
|
|
25
|
+
/** Test connection and get Coolify version. */
|
|
26
|
+
async getVersion() {
|
|
27
|
+
const data = await this.request("GET", "/version");
|
|
28
|
+
return data.version;
|
|
29
|
+
}
|
|
30
|
+
/** List all servers. */
|
|
31
|
+
async listServers() {
|
|
32
|
+
const data = await this.request("GET", "/servers");
|
|
33
|
+
return data.map((s) => ({
|
|
34
|
+
id: s.id,
|
|
35
|
+
name: s.name,
|
|
36
|
+
ip: s.ip,
|
|
37
|
+
description: s.description,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
/** List all projects. */
|
|
41
|
+
async listProjects() {
|
|
42
|
+
return this.request("GET", "/projects");
|
|
43
|
+
}
|
|
44
|
+
/** Create a new project. */
|
|
45
|
+
async createProject(name) {
|
|
46
|
+
return this.request("POST", "/projects", { name });
|
|
47
|
+
}
|
|
48
|
+
/** List Coolify apps/services so callers can resolve a UUID by name. */
|
|
49
|
+
async listApplications() {
|
|
50
|
+
return this.request("GET", "/applications");
|
|
51
|
+
}
|
|
52
|
+
/** Upsert an env variable on a Coolify application. The Coolify API
|
|
53
|
+
* accepts multiple envs in one call so this is idempotent. */
|
|
54
|
+
async setAppEnv(appUuid, envs, options = {}) {
|
|
55
|
+
const body = {
|
|
56
|
+
data: Object.entries(envs).map(([key, value]) => ({
|
|
57
|
+
key,
|
|
58
|
+
value,
|
|
59
|
+
is_preview: options.isPreview ?? false,
|
|
60
|
+
is_build_time: false,
|
|
61
|
+
is_literal: true,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
await this.request("PATCH", `/applications/${appUuid}/envs/bulk`, body);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Verify Coolify connection. Returns version string or throws. */
|
|
68
|
+
export async function verifyCoolify(url, token) {
|
|
69
|
+
const api = new CoolifyApi({ url, token });
|
|
70
|
+
return api.getVersion();
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=coolify-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coolify-api.js","sourceRoot":"","sources":["../../src/utils/coolify-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAeA,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAsBpE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Error-message helpers.
|
|
3
|
+
*
|
|
4
|
+
* `explainFsError` maps common Node I/O error codes to actionable
|
|
5
|
+
* recovery hints. The returned string is safe to display to users
|
|
6
|
+
* (no internal paths beyond what the error already contains).
|
|
7
|
+
*/
|
|
8
|
+
export function explainFsError(err, context) {
|
|
9
|
+
const e = err;
|
|
10
|
+
const path = e.path ? ` at ${e.path}` : "";
|
|
11
|
+
switch (e.code) {
|
|
12
|
+
case "EACCES":
|
|
13
|
+
return `${context}: permission denied${path}. Fix: check the parent directory's write permissions, or run with sufficient privileges.`;
|
|
14
|
+
case "EPERM":
|
|
15
|
+
return `${context}: operation not permitted${path}. Fix: the file may be locked or owned by another user; check ownership.`;
|
|
16
|
+
case "ENOSPC":
|
|
17
|
+
return `${context}: out of disk space${path}. Fix: free up space and retry.`;
|
|
18
|
+
case "ENOENT":
|
|
19
|
+
return `${context}: file not found${path}. Fix: make sure the starter submodule is initialized (\`git submodule update --init\` in the monorepo root).`;
|
|
20
|
+
case "EEXIST":
|
|
21
|
+
return `${context}: file already exists${path}. Fix: move or remove it, then retry.`;
|
|
22
|
+
case "EROFS":
|
|
23
|
+
return `${context}: filesystem is read-only${path}. Fix: scaffold into a writable location.`;
|
|
24
|
+
case "EMFILE":
|
|
25
|
+
case "ENFILE":
|
|
26
|
+
return `${context}: too many open files. Fix: close other processes or raise \`ulimit -n\`.`;
|
|
27
|
+
default:
|
|
28
|
+
return `${context}: ${e.message ?? String(err)}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,OAAe;IAC1D,MAAM,CAAC,GAAG,GAAiB,CAAC;IAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3C,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,GAAG,OAAO,sBAAsB,IAAI,2FAA2F,CAAC;QACzI,KAAK,OAAO;YACV,OAAO,GAAG,OAAO,4BAA4B,IAAI,0EAA0E,CAAC;QAC9H,KAAK,QAAQ;YACX,OAAO,GAAG,OAAO,sBAAsB,IAAI,iCAAiC,CAAC;QAC/E,KAAK,QAAQ;YACX,OAAO,GAAG,OAAO,mBAAmB,IAAI,+GAA+G,CAAC;QAC1J,KAAK,QAAQ;YACX,OAAO,GAAG,OAAO,wBAAwB,IAAI,uCAAuC,CAAC;QACvF,KAAK,OAAO;YACV,OAAO,GAAG,OAAO,4BAA4B,IAAI,2CAA2C,CAAC;QAC/F,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,GAAG,OAAO,2EAA2E,CAAC;QAC/F;YACE,OAAO,GAAG,OAAO,KAAK,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACrD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface ExecResult {
|
|
2
|
+
stdout: string;
|
|
3
|
+
stderr: string;
|
|
4
|
+
exitCode: number;
|
|
5
|
+
}
|
|
6
|
+
/** Run a shell command with a spinner. */
|
|
7
|
+
export declare function exec(command: string, args: string[], options?: {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
spinner?: string;
|
|
11
|
+
silent?: boolean;
|
|
12
|
+
}): Promise<ExecResult>;
|
|
13
|
+
/** Run a shell command and return true if exit code is 0. */
|
|
14
|
+
export declare function execOk(command: string, args: string[], options?: {
|
|
15
|
+
cwd?: string;
|
|
16
|
+
env?: Record<string, string>;
|
|
17
|
+
}): Promise<boolean>;
|
|
18
|
+
/** Run a shell command, stream output to terminal. */
|
|
19
|
+
export declare function execStream(command: string, args: string[], options?: {
|
|
20
|
+
cwd?: string;
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
}): Promise<number>;
|
|
23
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0CAA0C;AAC1C,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IACR,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GACA,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED,6DAA6D;AAC7D,wBAAsB,MAAM,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACvD,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,sDAAsD;AACtD,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACvD,OAAO,CAAC,MAAM,CAAC,CAQjB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
/** Run a shell command with a spinner. */
|
|
4
|
+
export async function exec(command, args, options) {
|
|
5
|
+
const spinner = options?.spinner ? ora(options.spinner).start() : null;
|
|
6
|
+
try {
|
|
7
|
+
const result = await execa(command, args, {
|
|
8
|
+
cwd: options?.cwd,
|
|
9
|
+
env: { ...process.env, ...options?.env },
|
|
10
|
+
reject: false,
|
|
11
|
+
});
|
|
12
|
+
if (result.exitCode !== 0) {
|
|
13
|
+
spinner?.fail();
|
|
14
|
+
if (!options?.silent) {
|
|
15
|
+
console.error(result.stderr || result.stdout);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
spinner?.succeed();
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
stdout: result.stdout,
|
|
23
|
+
stderr: result.stderr,
|
|
24
|
+
exitCode: result.exitCode ?? 1,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
spinner?.fail();
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Run a shell command and return true if exit code is 0. */
|
|
33
|
+
export async function execOk(command, args, options) {
|
|
34
|
+
const result = await exec(command, args, { ...options, silent: true });
|
|
35
|
+
return result.exitCode === 0;
|
|
36
|
+
}
|
|
37
|
+
/** Run a shell command, stream output to terminal. */
|
|
38
|
+
export async function execStream(command, args, options) {
|
|
39
|
+
const result = await execa(command, args, {
|
|
40
|
+
cwd: options?.cwd,
|
|
41
|
+
env: { ...process.env, ...options?.env },
|
|
42
|
+
stdio: "inherit",
|
|
43
|
+
reject: false,
|
|
44
|
+
});
|
|
45
|
+
return result.exitCode ?? 1;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,GAAG,MAAM,KAAK,CAAC;AAQtB,0CAA0C;AAC1C,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,OAAe,EACf,IAAc,EACd,OAKC;IAED,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YACxC,GAAG,EAAE,OAAO,EAAE,GAAG;YACjB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE;YACxC,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,IAAI,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBACrB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,CAAC;QAED,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC;SAC/B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,CAAC;QAChB,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,OAAe,EACf,IAAc,EACd,OAAwD;IAExD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,sDAAsD;AACtD,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,IAAc,EACd,OAAwD;IAExD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;QACxC,GAAG,EAAE,OAAO,EAAE,GAAG;QACjB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE;QACxC,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProjectConfig } from "../prompts.js";
|
|
2
|
+
export interface ParsedCreateFlags {
|
|
3
|
+
/** Non-interactive: accept defaults, fail if a required value with
|
|
4
|
+
* no default is missing rather than prompting. */
|
|
5
|
+
yes: boolean;
|
|
6
|
+
/** --dry-run — already handled in index.ts but re-parsed here so
|
|
7
|
+
* one place knows the full set of supported flags. */
|
|
8
|
+
dryRun: boolean;
|
|
9
|
+
/** Preset values parsed from individual flags + --config file. */
|
|
10
|
+
presets: Partial<ProjectConfig>;
|
|
11
|
+
/** --no-github / --no-deploy hard-disable those steps regardless of
|
|
12
|
+
* what the preset / prompts would say. */
|
|
13
|
+
forceNoGithub: boolean;
|
|
14
|
+
forceNoDeploy: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function parseCreateFlags(argv: string[]): ParsedCreateFlags;
|
|
17
|
+
//# sourceMappingURL=flags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/utils/flags.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAsB,aAAa,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC;uDACmD;IACnD,GAAG,EAAE,OAAO,CAAC;IACb;2DACuD;IACvD,MAAM,EAAE,OAAO,CAAC;IAChB,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC;+CAC2C;IAC3C,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;CACxB;AAkBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,CA6FlE"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Command-line flag parsing for `hatchkit create`.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the flag surface deliberately small — a few common flags plus
|
|
5
|
+
* a `--config <path>` escape hatch that reads a JSON file matching
|
|
6
|
+
* Partial<ProjectConfig>. Flags win over config file; config file
|
|
7
|
+
* wins over prompt defaults.
|
|
8
|
+
*
|
|
9
|
+
* Design choice: don't try to expose every ProjectConfig field as a
|
|
10
|
+
* flag. The combinatorial space is big and awkward to document. For
|
|
11
|
+
* anything beyond the common case, point users at `--config <path>`.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
const KNOWN_FEATURES = [
|
|
16
|
+
"websocket",
|
|
17
|
+
"stripe",
|
|
18
|
+
"analytics",
|
|
19
|
+
"s3",
|
|
20
|
+
"desktop",
|
|
21
|
+
"mobile",
|
|
22
|
+
];
|
|
23
|
+
const KNOWN_ML_SERVICES = [
|
|
24
|
+
"3d-extraction",
|
|
25
|
+
"subtitles",
|
|
26
|
+
"image-recognition",
|
|
27
|
+
"background-removal",
|
|
28
|
+
"custom-hf",
|
|
29
|
+
];
|
|
30
|
+
export function parseCreateFlags(argv) {
|
|
31
|
+
const get = (name) => {
|
|
32
|
+
// Accept both `--name value` and `--name=value`.
|
|
33
|
+
const equalsIdx = argv.findIndex((a) => a.startsWith(`--${name}=`));
|
|
34
|
+
if (equalsIdx !== -1)
|
|
35
|
+
return argv[equalsIdx].slice(name.length + 3);
|
|
36
|
+
const idx = argv.indexOf(`--${name}`);
|
|
37
|
+
if (idx === -1 || idx === argv.length - 1)
|
|
38
|
+
return undefined;
|
|
39
|
+
const next = argv[idx + 1];
|
|
40
|
+
return next.startsWith("--") ? undefined : next;
|
|
41
|
+
};
|
|
42
|
+
const has = (name) => argv.includes(`--${name}`) || argv.some((a) => a.startsWith(`--${name}=`));
|
|
43
|
+
const yes = argv.includes("--yes") || argv.includes("-y");
|
|
44
|
+
const dryRun = argv.includes("--dry-run");
|
|
45
|
+
const forceNoGithub = argv.includes("--no-github");
|
|
46
|
+
const forceNoDeploy = argv.includes("--no-deploy");
|
|
47
|
+
// Start from --config <path> if present, then layer individual flags on top.
|
|
48
|
+
const presets = {};
|
|
49
|
+
const configPath = get("config");
|
|
50
|
+
if (configPath) {
|
|
51
|
+
const absPath = resolve(configPath);
|
|
52
|
+
if (!existsSync(absPath)) {
|
|
53
|
+
throw new Error(`--config file not found: ${absPath}`);
|
|
54
|
+
}
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(readFileSync(absPath, "utf-8"));
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
throw new Error(`--config file is not valid JSON: ${absPath} (${err.message})`);
|
|
61
|
+
}
|
|
62
|
+
if (!parsed || typeof parsed !== "object") {
|
|
63
|
+
throw new Error(`--config file must be a JSON object: ${absPath}`);
|
|
64
|
+
}
|
|
65
|
+
Object.assign(presets, parsed);
|
|
66
|
+
}
|
|
67
|
+
const name = get("name");
|
|
68
|
+
if (name)
|
|
69
|
+
presets.name = name;
|
|
70
|
+
const domain = get("domain");
|
|
71
|
+
if (domain)
|
|
72
|
+
presets.domain = domain;
|
|
73
|
+
const features = get("features");
|
|
74
|
+
if (features !== undefined) {
|
|
75
|
+
const list = features
|
|
76
|
+
.split(",")
|
|
77
|
+
.map((s) => s.trim())
|
|
78
|
+
.filter(Boolean);
|
|
79
|
+
const invalid = list.filter((f) => !KNOWN_FEATURES.includes(f));
|
|
80
|
+
if (invalid.length > 0) {
|
|
81
|
+
throw new Error(`Unknown --features values: ${invalid.join(", ")}. Valid: ${KNOWN_FEATURES.join(", ")}`);
|
|
82
|
+
}
|
|
83
|
+
presets.features = list;
|
|
84
|
+
}
|
|
85
|
+
const mlServices = get("ml-services");
|
|
86
|
+
if (mlServices !== undefined) {
|
|
87
|
+
const list = mlServices
|
|
88
|
+
.split(",")
|
|
89
|
+
.map((s) => s.trim())
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
const invalid = list.filter((f) => !KNOWN_ML_SERVICES.includes(f));
|
|
92
|
+
if (invalid.length > 0) {
|
|
93
|
+
throw new Error(`Unknown --ml-services values: ${invalid.join(", ")}. Valid: ${KNOWN_ML_SERVICES.join(", ")}`);
|
|
94
|
+
}
|
|
95
|
+
presets.mlServices = list;
|
|
96
|
+
}
|
|
97
|
+
const deployTarget = get("deploy-target");
|
|
98
|
+
if (deployTarget === "existing" || deployTarget === "new") {
|
|
99
|
+
presets.deployTarget = deployTarget;
|
|
100
|
+
}
|
|
101
|
+
else if (deployTarget !== undefined) {
|
|
102
|
+
throw new Error(`--deploy-target must be 'existing' or 'new' (got '${deployTarget}')`);
|
|
103
|
+
}
|
|
104
|
+
if (forceNoGithub)
|
|
105
|
+
presets.createGithubRepo = false;
|
|
106
|
+
if (forceNoDeploy)
|
|
107
|
+
presets.runDeployment = false;
|
|
108
|
+
// Validate that presets-provided values look right before we pass
|
|
109
|
+
// them on (the prompt layer would also catch these, but failing
|
|
110
|
+
// early in non-interactive mode gives a cleaner error).
|
|
111
|
+
if (has("config") && typeof presets.name !== "string" && yes) {
|
|
112
|
+
// fine — may still come from --name flag or default
|
|
113
|
+
}
|
|
114
|
+
return { yes, dryRun, presets, forceNoGithub, forceNoDeploy };
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=flags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/utils/flags.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ModelPipelineTag = "text-generation" | "text-classification" | "token-classification" | "image-classification" | "object-detection" | "image-segmentation" | "text-to-image" | "image-to-text" | "automatic-speech-recognition" | "translation" | "summarization" | "feature-extraction" | "other";
|
|
2
|
+
export interface HfModelInfo {
|
|
3
|
+
modelId: string;
|
|
4
|
+
pipelineTag: ModelPipelineTag;
|
|
5
|
+
library: string;
|
|
6
|
+
tags: string[];
|
|
7
|
+
private: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Query the HuggingFace Hub API to get model info. */
|
|
10
|
+
export declare function getModelInfo(modelId: string): Promise<HfModelInfo>;
|
|
11
|
+
/** Suggest GPU type based on model pipeline and tags. */
|
|
12
|
+
export declare function suggestGpu(info: HfModelInfo): string;
|
|
13
|
+
//# sourceMappingURL=hf-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hf-api.d.ts","sourceRoot":"","sources":["../../src/utils/hf-api.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,qBAAqB,GACrB,sBAAsB,GACtB,sBAAsB,GACtB,kBAAkB,GAClB,oBAAoB,GACpB,eAAe,GACf,eAAe,GACf,8BAA8B,GAC9B,aAAa,GACb,eAAe,GACf,oBAAoB,GACpB,OAAO,CAAC;AAEZ,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,uDAAuD;AACvD,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAyBxE;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAWpD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Query the HuggingFace Hub API to get model info. */
|
|
2
|
+
export async function getModelInfo(modelId) {
|
|
3
|
+
const res = await fetch(`https://huggingface.co/api/models/${modelId}`);
|
|
4
|
+
if (!res.ok) {
|
|
5
|
+
if (res.status === 404) {
|
|
6
|
+
throw new Error(`Model not found: ${modelId}`);
|
|
7
|
+
}
|
|
8
|
+
throw new Error(`HuggingFace API error: ${res.status} ${res.statusText}`);
|
|
9
|
+
}
|
|
10
|
+
const data = (await res.json());
|
|
11
|
+
return {
|
|
12
|
+
modelId: data.id,
|
|
13
|
+
pipelineTag: data.pipeline_tag || "other",
|
|
14
|
+
library: data.library_name || "unknown",
|
|
15
|
+
tags: data.tags || [],
|
|
16
|
+
private: data.private || false,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Suggest GPU type based on model pipeline and tags. */
|
|
20
|
+
export function suggestGpu(info) {
|
|
21
|
+
if (info.pipelineTag === "text-generation" ||
|
|
22
|
+
info.tags.some((t) => t.includes("llama") || t.includes("70b"))) {
|
|
23
|
+
return "A100";
|
|
24
|
+
}
|
|
25
|
+
if (info.pipelineTag === "text-to-image" || info.pipelineTag === "image-segmentation") {
|
|
26
|
+
return "A10G";
|
|
27
|
+
}
|
|
28
|
+
return "T4";
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=hf-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hf-api.js","sourceRoot":"","sources":["../../src/utils/hf-api.ts"],"names":[],"mappings":"AAuBA,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe;IAChD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,qCAAqC,OAAO,EAAE,CAAC,CAAC;IAExE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAM7B,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,EAAE;QAChB,WAAW,EAAG,IAAI,CAAC,YAAiC,IAAI,OAAO;QAC/D,OAAO,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;QACvC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;KAC/B,CAAC;AACJ,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,UAAU,CAAC,IAAiB;IAC1C,IACE,IAAI,CAAC,WAAW,KAAK,iBAAiB;QACtC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAC/D,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,KAAK,eAAe,IAAI,IAAI,CAAC,WAAW,KAAK,oBAAoB,EAAE,CAAC;QACtF,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const PORT_RANGES: {
|
|
2
|
+
server: readonly [5000, 5999];
|
|
3
|
+
client: readonly [6000, 6999];
|
|
4
|
+
nativeHmr: readonly [7000, 7999];
|
|
5
|
+
};
|
|
6
|
+
/** Test whether a port is free to bind on 127.0.0.1. Resolves with
|
|
7
|
+
* `true` if we could successfully open + immediately close a listener
|
|
8
|
+
* on it; `false` if the bind failed for any reason (EADDRINUSE,
|
|
9
|
+
* permission, etc.). Always resolves — never throws. */
|
|
10
|
+
export declare function isPortFree(port: number): Promise<boolean>;
|
|
11
|
+
/** Pick a port in [min, max] that is (a) not in the used-ports
|
|
12
|
+
* registry and (b) actually free to bind on 127.0.0.1. Scans
|
|
13
|
+
* sequentially from a random starting point, wrapping once. Throws
|
|
14
|
+
* if every port in the range is either registered or occupied. */
|
|
15
|
+
export declare function pickPort(min: number, max: number, used: Set<number>): Promise<number>;
|
|
16
|
+
export interface ProjectPorts {
|
|
17
|
+
server: number;
|
|
18
|
+
client: number;
|
|
19
|
+
/** Only set when desktop or mobile is selected. */
|
|
20
|
+
nativeHmr?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Pick a coherent port set for a new project, avoiding ports already
|
|
23
|
+
* assigned to prior scaffolds AND ports that are actually busy on the
|
|
24
|
+
* host. Caller is responsible for persisting the returned ports into
|
|
25
|
+
* the used-ports registry. */
|
|
26
|
+
export declare function pickProjectPorts(used: number[], options: {
|
|
27
|
+
nativeHmr: boolean;
|
|
28
|
+
}): Promise<ProjectPorts>;
|
|
29
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/utils/ports.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF;;;yDAGyD;AACzD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQzD;AAED;;;mEAGmE;AACnE,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB3F;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;+BAG+B;AAC/B,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,GAC9B,OAAO,CAAC,YAAY,CAAC,CAWvB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Port picker.
|
|
3
|
+
*
|
|
4
|
+
* At scaffold time we assign each project a fixed set of ports so dev
|
|
5
|
+
* servers across multiple scaffolded projects don't collide on
|
|
6
|
+
* localhost. Ports come from three disjoint 1000-slot ranges:
|
|
7
|
+
*
|
|
8
|
+
* server 5000 – 5999 Express API / backend
|
|
9
|
+
* client 6000 – 6999 Next.js web dev server
|
|
10
|
+
* nativeHmr 7000 – 7999 Next dev server for Capacitor + Electron
|
|
11
|
+
* (only picked when desktop or mobile is
|
|
12
|
+
* selected, so regular web + native can
|
|
13
|
+
* run side-by-side without stomping)
|
|
14
|
+
*
|
|
15
|
+
* Two layers of collision avoidance:
|
|
16
|
+
* 1. The CLI config tracks ports handed out to prior `hatchkit
|
|
17
|
+
* create` runs (usedPorts registry) — avoids picking the same
|
|
18
|
+
* port twice across scaffolds on this machine.
|
|
19
|
+
* 2. Each candidate is tested by actually binding to it on
|
|
20
|
+
* 127.0.0.1. If a non-scaffolded process holds the port (someone
|
|
21
|
+
* else's dev server, a system service, etc.) we skip it and try
|
|
22
|
+
* the next. This is the bit that makes the picked port "real"
|
|
23
|
+
* enough to commit to .env.development.
|
|
24
|
+
*
|
|
25
|
+
* The scan order is: pick a random starting point inside the range,
|
|
26
|
+
* then step forward from there, wrapping at the end. That gives
|
|
27
|
+
* decent distribution across the range while also feeling like
|
|
28
|
+
* "auto-increment until free" at the low level.
|
|
29
|
+
*/
|
|
30
|
+
import { createServer } from "node:net";
|
|
31
|
+
export const PORT_RANGES = {
|
|
32
|
+
server: [5000, 5999],
|
|
33
|
+
client: [6000, 6999],
|
|
34
|
+
nativeHmr: [7000, 7999],
|
|
35
|
+
};
|
|
36
|
+
/** Test whether a port is free to bind on 127.0.0.1. Resolves with
|
|
37
|
+
* `true` if we could successfully open + immediately close a listener
|
|
38
|
+
* on it; `false` if the bind failed for any reason (EADDRINUSE,
|
|
39
|
+
* permission, etc.). Always resolves — never throws. */
|
|
40
|
+
export function isPortFree(port) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const server = createServer();
|
|
43
|
+
server.once("error", () => resolve(false));
|
|
44
|
+
server.listen(port, "127.0.0.1", () => {
|
|
45
|
+
server.close(() => resolve(true));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** Pick a port in [min, max] that is (a) not in the used-ports
|
|
50
|
+
* registry and (b) actually free to bind on 127.0.0.1. Scans
|
|
51
|
+
* sequentially from a random starting point, wrapping once. Throws
|
|
52
|
+
* if every port in the range is either registered or occupied. */
|
|
53
|
+
export async function pickPort(min, max, used) {
|
|
54
|
+
const span = max - min + 1;
|
|
55
|
+
const start = Math.floor(Math.random() * span);
|
|
56
|
+
let registryCollisions = 0;
|
|
57
|
+
let busyPorts = 0;
|
|
58
|
+
for (let i = 0; i < span; i++) {
|
|
59
|
+
const port = min + ((start + i) % span);
|
|
60
|
+
if (used.has(port)) {
|
|
61
|
+
registryCollisions++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (await isPortFree(port))
|
|
65
|
+
return port;
|
|
66
|
+
busyPorts++;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`No free port found in range ${min}-${max} — ${registryCollisions} registered to other scaffolds, ${busyPorts} held by other processes. Run \`hatchkit config reset\` to clear the registry if it's stale.`);
|
|
69
|
+
}
|
|
70
|
+
/** Pick a coherent port set for a new project, avoiding ports already
|
|
71
|
+
* assigned to prior scaffolds AND ports that are actually busy on the
|
|
72
|
+
* host. Caller is responsible for persisting the returned ports into
|
|
73
|
+
* the used-ports registry. */
|
|
74
|
+
export async function pickProjectPorts(used, options) {
|
|
75
|
+
const usedSet = new Set(used);
|
|
76
|
+
const server = await pickPort(PORT_RANGES.server[0], PORT_RANGES.server[1], usedSet);
|
|
77
|
+
usedSet.add(server);
|
|
78
|
+
const client = await pickPort(PORT_RANGES.client[0], PORT_RANGES.client[1], usedSet);
|
|
79
|
+
usedSet.add(client);
|
|
80
|
+
const ports = { server, client };
|
|
81
|
+
if (options.nativeHmr) {
|
|
82
|
+
ports.nativeHmr = await pickPort(PORT_RANGES.nativeHmr[0], PORT_RANGES.nativeHmr[1], usedSet);
|
|
83
|
+
}
|
|
84
|
+
return ports;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=ports.js.map
|