@nodetool-ai/deploy 0.7.0-rc.11
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/admin-client.d.ts +58 -0
- package/dist/admin-client.d.ts.map +1 -0
- package/dist/admin-client.js +275 -0
- package/dist/admin-client.js.map +1 -0
- package/dist/admin-operations.d.ts +197 -0
- package/dist/admin-operations.d.ts.map +1 -0
- package/dist/admin-operations.js +433 -0
- package/dist/admin-operations.js.map +1 -0
- package/dist/admin-routes.d.ts +306 -0
- package/dist/admin-routes.d.ts.map +1 -0
- package/dist/admin-routes.js +383 -0
- package/dist/admin-routes.js.map +1 -0
- package/dist/api-user-manager.d.ts +72 -0
- package/dist/api-user-manager.d.ts.map +1 -0
- package/dist/api-user-manager.js +89 -0
- package/dist/api-user-manager.js.map +1 -0
- package/dist/auth.d.ts +67 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +142 -0
- package/dist/auth.js.map +1 -0
- package/dist/collection-routes.d.ts +37 -0
- package/dist/collection-routes.d.ts.map +1 -0
- package/dist/collection-routes.js +43 -0
- package/dist/collection-routes.js.map +1 -0
- package/dist/compose.d.ts +52 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +153 -0
- package/dist/compose.js.map +1 -0
- package/dist/configure.d.ts +87 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +104 -0
- package/dist/configure.js.map +1 -0
- package/dist/deploy-to-gcp.d.ts +67 -0
- package/dist/deploy-to-gcp.d.ts.map +1 -0
- package/dist/deploy-to-gcp.js +268 -0
- package/dist/deploy-to-gcp.js.map +1 -0
- package/dist/deploy-to-runpod.d.ts +53 -0
- package/dist/deploy-to-runpod.d.ts.map +1 -0
- package/dist/deploy-to-runpod.js +207 -0
- package/dist/deploy-to-runpod.js.map +1 -0
- package/dist/deploy.d.ts +54 -0
- package/dist/deploy.d.ts.map +1 -0
- package/dist/deploy.js +82 -0
- package/dist/deploy.js.map +1 -0
- package/dist/deployment-config.d.ts +1073 -0
- package/dist/deployment-config.d.ts.map +1 -0
- package/dist/deployment-config.js +536 -0
- package/dist/deployment-config.js.map +1 -0
- package/dist/docker-run.d.ts +85 -0
- package/dist/docker-run.d.ts.map +1 -0
- package/dist/docker-run.js +202 -0
- package/dist/docker-run.js.map +1 -0
- package/dist/docker.d.ts +98 -0
- package/dist/docker.d.ts.map +1 -0
- package/dist/docker.js +396 -0
- package/dist/docker.js.map +1 -0
- package/dist/fly.d.ts +40 -0
- package/dist/fly.d.ts.map +1 -0
- package/dist/fly.js +291 -0
- package/dist/fly.js.map +1 -0
- package/dist/gcp.d.ts +59 -0
- package/dist/gcp.d.ts.map +1 -0
- package/dist/gcp.js +339 -0
- package/dist/gcp.js.map +1 -0
- package/dist/google-cloud-run-api.d.ts +134 -0
- package/dist/google-cloud-run-api.d.ts.map +1 -0
- package/dist/google-cloud-run-api.js +532 -0
- package/dist/google-cloud-run-api.js.map +1 -0
- package/dist/huggingface.d.ts +69 -0
- package/dist/huggingface.d.ts.map +1 -0
- package/dist/huggingface.js +346 -0
- package/dist/huggingface.js.map +1 -0
- package/dist/image-builder.d.ts +35 -0
- package/dist/image-builder.d.ts.map +1 -0
- package/dist/image-builder.js +157 -0
- package/dist/image-builder.js.map +1 -0
- package/dist/image-spec.d.ts +41 -0
- package/dist/image-spec.d.ts.map +1 -0
- package/dist/image-spec.js +49 -0
- package/dist/image-spec.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +121 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +225 -0
- package/dist/manager.js.map +1 -0
- package/dist/progress.d.ts +91 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +262 -0
- package/dist/progress.js.map +1 -0
- package/dist/railway.d.ts +65 -0
- package/dist/railway.d.ts.map +1 -0
- package/dist/railway.js +284 -0
- package/dist/railway.js.map +1 -0
- package/dist/remote-users.d.ts +74 -0
- package/dist/remote-users.d.ts.map +1 -0
- package/dist/remote-users.js +172 -0
- package/dist/remote-users.js.map +1 -0
- package/dist/runpod-api.d.ts +206 -0
- package/dist/runpod-api.d.ts.map +1 -0
- package/dist/runpod-api.js +703 -0
- package/dist/runpod-api.js.map +1 -0
- package/dist/runpod.d.ts +82 -0
- package/dist/runpod.d.ts.map +1 -0
- package/dist/runpod.js +188 -0
- package/dist/runpod.js.map +1 -0
- package/dist/self-hosted.d.ts +157 -0
- package/dist/self-hosted.d.ts.map +1 -0
- package/dist/self-hosted.js +799 -0
- package/dist/self-hosted.js.map +1 -0
- package/dist/ssh.d.ts +166 -0
- package/dist/ssh.d.ts.map +1 -0
- package/dist/ssh.js +430 -0
- package/dist/ssh.js.map +1 -0
- package/dist/state.d.ts +83 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +368 -0
- package/dist/state.js.map +1 -0
- package/dist/storage-routes.d.ts +55 -0
- package/dist/storage-routes.d.ts.map +1 -0
- package/dist/storage-routes.js +160 -0
- package/dist/storage-routes.js.map +1 -0
- package/dist/sync.d.ts +38 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +130 -0
- package/dist/sync.js.map +1 -0
- package/dist/workflow-syncer.d.ts +51 -0
- package/dist/workflow-syncer.d.ts.map +1 -0
- package/dist/workflow-syncer.js +192 -0
- package/dist/workflow-syncer.js.map +1 -0
- package/package.json +58 -0
package/dist/compose.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker Compose file generation for self-hosted deployments.
|
|
3
|
+
*
|
|
4
|
+
* This module generates docker-compose.yml files from deployment configuration,
|
|
5
|
+
* supporting multi-container setups with GPU assignments, volume mounts, and
|
|
6
|
+
* environment variables.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash } from "crypto";
|
|
9
|
+
import { writeFileSync } from "fs";
|
|
10
|
+
import yaml from "js-yaml";
|
|
11
|
+
import { imageConfigFullName } from "./deployment-config.js";
|
|
12
|
+
import { INTERNAL_API_PORT, APP_ENV_PORT } from "./docker-run.js";
|
|
13
|
+
/**
|
|
14
|
+
* Generates docker-compose.yml configuration from deployment settings.
|
|
15
|
+
*
|
|
16
|
+
* This class handles the conversion of NodeTool deployment configuration
|
|
17
|
+
* into a docker-compose.yml file suitable for deployment.
|
|
18
|
+
*/
|
|
19
|
+
export class ComposeGenerator {
|
|
20
|
+
deployment;
|
|
21
|
+
constructor(deployment) {
|
|
22
|
+
this.deployment = deployment;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Generate docker-compose.yml content as a string.
|
|
26
|
+
*/
|
|
27
|
+
generate() {
|
|
28
|
+
const composeDict = this.buildComposeDict();
|
|
29
|
+
return yaml.dump(composeDict, {
|
|
30
|
+
flowLevel: -1,
|
|
31
|
+
sortKeys: false
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate a SHA256 hash of the compose configuration.
|
|
36
|
+
* This can be used to detect changes in the configuration.
|
|
37
|
+
*/
|
|
38
|
+
generateHash() {
|
|
39
|
+
const content = this.generate();
|
|
40
|
+
return createHash("sha256").update(content).digest("hex");
|
|
41
|
+
}
|
|
42
|
+
buildComposeDict() {
|
|
43
|
+
const compose = {
|
|
44
|
+
version: "3.8",
|
|
45
|
+
services: {}
|
|
46
|
+
};
|
|
47
|
+
const container = this.deployment.container;
|
|
48
|
+
const serviceName = this.sanitizeServiceName(container.name);
|
|
49
|
+
compose.services[serviceName] = this.buildService(container);
|
|
50
|
+
return compose;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sanitize container name for use as docker-compose service name.
|
|
54
|
+
*/
|
|
55
|
+
sanitizeServiceName(name) {
|
|
56
|
+
let sanitized = name
|
|
57
|
+
.split("")
|
|
58
|
+
.map((c) => (/[a-zA-Z0-9\-_]/.test(c) ? c : "-"))
|
|
59
|
+
.join("");
|
|
60
|
+
if (sanitized.length > 0 && !/[a-zA-Z0-9]/.test(sanitized[0])) {
|
|
61
|
+
sanitized = "c" + sanitized;
|
|
62
|
+
}
|
|
63
|
+
return sanitized.toLowerCase();
|
|
64
|
+
}
|
|
65
|
+
buildService(container) {
|
|
66
|
+
const service = {
|
|
67
|
+
image: imageConfigFullName(this.deployment.image),
|
|
68
|
+
container_name: `nodetool-${container.name}`,
|
|
69
|
+
ports: [`${container.port}:${INTERNAL_API_PORT}`],
|
|
70
|
+
volumes: this.buildVolumes(),
|
|
71
|
+
environment: this.buildEnvironment(container),
|
|
72
|
+
restart: "unless-stopped",
|
|
73
|
+
healthcheck: {
|
|
74
|
+
test: [
|
|
75
|
+
"CMD",
|
|
76
|
+
"curl",
|
|
77
|
+
"-f",
|
|
78
|
+
`http://localhost:${INTERNAL_API_PORT}/health`
|
|
79
|
+
],
|
|
80
|
+
interval: "30s",
|
|
81
|
+
timeout: "10s",
|
|
82
|
+
retries: 3,
|
|
83
|
+
start_period: "40s"
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
if (container.gpu) {
|
|
87
|
+
service.deploy = this.buildDeployConfig(container);
|
|
88
|
+
}
|
|
89
|
+
return service;
|
|
90
|
+
}
|
|
91
|
+
buildVolumes() {
|
|
92
|
+
const volumes = [];
|
|
93
|
+
volumes.push(`${this.deployment.paths.workspace}:/workspace`);
|
|
94
|
+
volumes.push(`${this.deployment.paths.hf_cache}:/hf-cache:ro`);
|
|
95
|
+
return volumes;
|
|
96
|
+
}
|
|
97
|
+
buildEnvironment(container) {
|
|
98
|
+
const env = container.environment
|
|
99
|
+
? { ...container.environment }
|
|
100
|
+
: {};
|
|
101
|
+
env["PORT"] = String(APP_ENV_PORT);
|
|
102
|
+
env["NODETOOL_API_URL"] = `http://localhost:${container.port}`;
|
|
103
|
+
if (container.workflows && container.workflows.length > 0) {
|
|
104
|
+
env["NODETOOL_WORKFLOWS"] = container.workflows.join(",");
|
|
105
|
+
}
|
|
106
|
+
return Object.entries(env).map(([key, value]) => `${key}=${value}`);
|
|
107
|
+
}
|
|
108
|
+
buildDeployConfig(container) {
|
|
109
|
+
if (!container.gpu) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const gpuIds = container.gpu.split(",").map((g) => g.trim());
|
|
113
|
+
return {
|
|
114
|
+
resources: {
|
|
115
|
+
reservations: {
|
|
116
|
+
devices: [
|
|
117
|
+
{
|
|
118
|
+
driver: "nvidia",
|
|
119
|
+
device_ids: gpuIds,
|
|
120
|
+
capabilities: ["gpu"]
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Generate docker-compose.yml file from deployment configuration.
|
|
130
|
+
*
|
|
131
|
+
* @param deployment - Docker deployment configuration
|
|
132
|
+
* @param outputPath - Optional path to write the compose file to
|
|
133
|
+
* @returns Generated docker-compose.yml content as string
|
|
134
|
+
*/
|
|
135
|
+
export function generateComposeFile(deployment, outputPath) {
|
|
136
|
+
const generator = new ComposeGenerator(deployment);
|
|
137
|
+
const content = generator.generate();
|
|
138
|
+
if (outputPath) {
|
|
139
|
+
writeFileSync(outputPath, content, "utf-8");
|
|
140
|
+
}
|
|
141
|
+
return content;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get hash of the compose configuration for change detection.
|
|
145
|
+
*
|
|
146
|
+
* @param deployment - Docker deployment configuration
|
|
147
|
+
* @returns SHA256 hash of the compose configuration
|
|
148
|
+
*/
|
|
149
|
+
export function getComposeHash(deployment) {
|
|
150
|
+
const generator = new ComposeGenerator(deployment);
|
|
151
|
+
return generator.generateHash();
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-interactive configuration helpers for deployments.
|
|
3
|
+
*
|
|
4
|
+
* Instead of interactive prompts (click.prompt / click.confirm from Python),
|
|
5
|
+
* these functions accept typed config parameter objects.
|
|
6
|
+
*/
|
|
7
|
+
import type { DockerDeployment, RunPodDeployment, GCPDeployment } from "./deployment-config.js";
|
|
8
|
+
/**
|
|
9
|
+
* Detect the default HuggingFace cache directory.
|
|
10
|
+
*
|
|
11
|
+
* Checks the HF_HOME / HF_HUB_CACHE env vars, falling back to
|
|
12
|
+
* ~/.cache/huggingface/hub.
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectHfCacheDefault(): string;
|
|
15
|
+
export interface DockerConfigParams {
|
|
16
|
+
/** Remote host address (IP or hostname). */
|
|
17
|
+
host: string;
|
|
18
|
+
/** SSH username (required for non-localhost hosts). */
|
|
19
|
+
sshUser?: string;
|
|
20
|
+
/** SSH key path (default: ~/.ssh/id_rsa). */
|
|
21
|
+
sshKeyPath?: string;
|
|
22
|
+
/** Docker image name. */
|
|
23
|
+
imageName?: string;
|
|
24
|
+
/** Docker image tag. */
|
|
25
|
+
imageTag?: string;
|
|
26
|
+
/** Container name. */
|
|
27
|
+
containerName?: string;
|
|
28
|
+
/** Container port. */
|
|
29
|
+
containerPort?: number;
|
|
30
|
+
/** GPU device(s) (e.g., "0" or "0,1"). */
|
|
31
|
+
gpu?: string;
|
|
32
|
+
/** Workflow IDs to assign. */
|
|
33
|
+
workflows?: string[];
|
|
34
|
+
/** Workspace folder path. */
|
|
35
|
+
workspacePath?: string;
|
|
36
|
+
/** HuggingFace cache folder path. */
|
|
37
|
+
hfCachePath?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Configure a Docker deployment from typed parameters.
|
|
41
|
+
*
|
|
42
|
+
* @param name - Deployment name (used as default container name suffix).
|
|
43
|
+
* @param params - Configuration parameters.
|
|
44
|
+
* @returns A fully validated DockerDeployment object.
|
|
45
|
+
*/
|
|
46
|
+
export declare function configureDocker(name: string, params: DockerConfigParams): DockerDeployment;
|
|
47
|
+
export interface RunPodConfigParams {
|
|
48
|
+
/** Docker image name. */
|
|
49
|
+
imageName: string;
|
|
50
|
+
/** Docker image tag (default: "latest"). */
|
|
51
|
+
imageTag?: string;
|
|
52
|
+
/** Docker registry (default: "docker.io"). */
|
|
53
|
+
registry?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Configure a RunPod deployment from typed parameters.
|
|
57
|
+
*
|
|
58
|
+
* @param _name - Deployment name (unused, kept for API consistency).
|
|
59
|
+
* @param params - Configuration parameters.
|
|
60
|
+
* @returns A fully validated RunPodDeployment object.
|
|
61
|
+
*/
|
|
62
|
+
export declare function configureRunPod(_name: string, params: RunPodConfigParams): RunPodDeployment;
|
|
63
|
+
export interface GCPConfigParams {
|
|
64
|
+
/** GCP Project ID. */
|
|
65
|
+
projectId: string;
|
|
66
|
+
/** GCP region (default: "us-central1"). */
|
|
67
|
+
region?: string;
|
|
68
|
+
/** Cloud Run service name. */
|
|
69
|
+
serviceName?: string;
|
|
70
|
+
/** Docker image repository (e.g., "project/repo/image"). */
|
|
71
|
+
imageRepository: string;
|
|
72
|
+
/** Docker image tag (default: "latest"). */
|
|
73
|
+
imageTag?: string;
|
|
74
|
+
/** CPU cores (default: "4"). */
|
|
75
|
+
cpu?: string;
|
|
76
|
+
/** Memory (default: "16Gi"). */
|
|
77
|
+
memory?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Configure a GCP deployment from typed parameters.
|
|
81
|
+
*
|
|
82
|
+
* @param name - Deployment name (used as default service name).
|
|
83
|
+
* @param params - Configuration parameters.
|
|
84
|
+
* @returns A fully validated GCPDeployment object.
|
|
85
|
+
*/
|
|
86
|
+
export declare function configureGCP(name: string, params: GCPConfigParams): GCPDeployment;
|
|
87
|
+
//# sourceMappingURL=configure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACd,MAAM,wBAAwB,CAAC;AAWhC;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAMD,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,kBAAkB,GACzB,gBAAgB,CAiClB;AAMD,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,kBAAkB,GACzB,gBAAgB,CASlB;AAMD,MAAM,WAAW,eAAe;IAC9B,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,eAAe,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,eAAe,GACtB,aAAa,CAef"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-interactive configuration helpers for deployments.
|
|
3
|
+
*
|
|
4
|
+
* Instead of interactive prompts (click.prompt / click.confirm from Python),
|
|
5
|
+
* these functions accept typed config parameter objects.
|
|
6
|
+
*/
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { DockerDeploymentSchema, RunPodDeploymentSchema, GCPDeploymentSchema } from "./deployment-config.js";
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Common helper
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Detect the default HuggingFace cache directory.
|
|
15
|
+
*
|
|
16
|
+
* Checks the HF_HOME / HF_HUB_CACHE env vars, falling back to
|
|
17
|
+
* ~/.cache/huggingface/hub.
|
|
18
|
+
*/
|
|
19
|
+
export function detectHfCacheDefault() {
|
|
20
|
+
if (process.env["HF_HUB_CACHE"]) {
|
|
21
|
+
return process.env["HF_HUB_CACHE"];
|
|
22
|
+
}
|
|
23
|
+
if (process.env["HF_HOME"]) {
|
|
24
|
+
return join(process.env["HF_HOME"], "hub");
|
|
25
|
+
}
|
|
26
|
+
return join(homedir(), ".cache", "huggingface", "hub");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Configure a Docker deployment from typed parameters.
|
|
30
|
+
*
|
|
31
|
+
* @param name - Deployment name (used as default container name suffix).
|
|
32
|
+
* @param params - Configuration parameters.
|
|
33
|
+
* @returns A fully validated DockerDeployment object.
|
|
34
|
+
*/
|
|
35
|
+
export function configureDocker(name, params) {
|
|
36
|
+
const isLocalhost = ["localhost", "127.0.0.1", "::1", "0.0.0.0"].includes(params.host.toLowerCase());
|
|
37
|
+
const sshConfig = !isLocalhost && params.sshUser
|
|
38
|
+
? {
|
|
39
|
+
user: params.sshUser,
|
|
40
|
+
key_path: params.sshKeyPath ?? "~/.ssh/id_rsa",
|
|
41
|
+
port: 22
|
|
42
|
+
}
|
|
43
|
+
: undefined;
|
|
44
|
+
return DockerDeploymentSchema.parse({
|
|
45
|
+
type: "docker",
|
|
46
|
+
host: params.host,
|
|
47
|
+
ssh: sshConfig,
|
|
48
|
+
image: {
|
|
49
|
+
name: params.imageName ?? "ghcr.io/nodetool-ai/nodetool",
|
|
50
|
+
tag: params.imageTag ?? "latest"
|
|
51
|
+
},
|
|
52
|
+
container: {
|
|
53
|
+
name: params.containerName ?? `nodetool-${name}`,
|
|
54
|
+
port: params.containerPort ?? 8000,
|
|
55
|
+
gpu: params.gpu,
|
|
56
|
+
workflows: params.workflows
|
|
57
|
+
},
|
|
58
|
+
paths: {
|
|
59
|
+
workspace: params.workspacePath ?? join(homedir(), ".nodetool-workspace"),
|
|
60
|
+
hf_cache: params.hfCachePath ?? detectHfCacheDefault()
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Configure a RunPod deployment from typed parameters.
|
|
66
|
+
*
|
|
67
|
+
* @param _name - Deployment name (unused, kept for API consistency).
|
|
68
|
+
* @param params - Configuration parameters.
|
|
69
|
+
* @returns A fully validated RunPodDeployment object.
|
|
70
|
+
*/
|
|
71
|
+
export function configureRunPod(_name, params) {
|
|
72
|
+
return RunPodDeploymentSchema.parse({
|
|
73
|
+
type: "runpod",
|
|
74
|
+
image: {
|
|
75
|
+
name: params.imageName,
|
|
76
|
+
tag: params.imageTag ?? "latest",
|
|
77
|
+
registry: params.registry ?? "docker.io"
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Configure a GCP deployment from typed parameters.
|
|
83
|
+
*
|
|
84
|
+
* @param name - Deployment name (used as default service name).
|
|
85
|
+
* @param params - Configuration parameters.
|
|
86
|
+
* @returns A fully validated GCPDeployment object.
|
|
87
|
+
*/
|
|
88
|
+
export function configureGCP(name, params) {
|
|
89
|
+
return GCPDeploymentSchema.parse({
|
|
90
|
+
type: "gcp",
|
|
91
|
+
project_id: params.projectId,
|
|
92
|
+
region: params.region ?? "us-central1",
|
|
93
|
+
service_name: params.serviceName ?? name,
|
|
94
|
+
image: {
|
|
95
|
+
repository: params.imageRepository,
|
|
96
|
+
tag: params.imageTag ?? "latest"
|
|
97
|
+
},
|
|
98
|
+
resources: {
|
|
99
|
+
cpu: params.cpu ?? "4",
|
|
100
|
+
memory: params.memory ?? "16Gi"
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=configure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Cloud Run Deployment Script for NodeTool Workflows
|
|
3
|
+
*
|
|
4
|
+
* Automates the deployment of NodeTool services to Google Cloud Run:
|
|
5
|
+
* 1. Builds a Docker container for Cloud Run execution
|
|
6
|
+
* 2. Pushes the image to Google Container Registry or Artifact Registry
|
|
7
|
+
* 3. Deploys the service to Google Cloud Run
|
|
8
|
+
*
|
|
9
|
+
* Ported from nodetool.deploy.deploy_to_gcp (Python).
|
|
10
|
+
*/
|
|
11
|
+
import type { GCPDeployment } from "./deployment-config.js";
|
|
12
|
+
/**
|
|
13
|
+
* Sanitize a name for use as Cloud Run service name.
|
|
14
|
+
*
|
|
15
|
+
* Cloud Run service names must:
|
|
16
|
+
* - Start with a letter
|
|
17
|
+
* - Contain only lowercase letters, numbers, and hyphens
|
|
18
|
+
* - Be 63 characters or less
|
|
19
|
+
* - End with a letter or number
|
|
20
|
+
*/
|
|
21
|
+
export declare function sanitizeServiceName(name: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Infer Artifact Registry URL from region.
|
|
24
|
+
*/
|
|
25
|
+
export declare function inferRegistryFromRegion(region: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Get the default environment variables for a GCP deployment.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getGcpDefaultEnv(deployment: GCPDeployment): Record<string, string>;
|
|
30
|
+
export interface DeployToGcpOptions {
|
|
31
|
+
deployment: GCPDeployment;
|
|
32
|
+
env?: Record<string, string>;
|
|
33
|
+
skipBuild?: boolean;
|
|
34
|
+
skipPush?: boolean;
|
|
35
|
+
skipDeploy?: boolean;
|
|
36
|
+
noCache?: boolean;
|
|
37
|
+
skipPermissionSetup?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Deploy nodetool service to Google Cloud Run infrastructure.
|
|
41
|
+
*
|
|
42
|
+
* This is the main deployment function that orchestrates the entire process.
|
|
43
|
+
*/
|
|
44
|
+
export declare function deployToGcp(options: DeployToGcpOptions): Promise<void>;
|
|
45
|
+
interface DeploymentSummaryOptions {
|
|
46
|
+
imageName: string;
|
|
47
|
+
imageTag: string;
|
|
48
|
+
gcpImageUrl: string | null;
|
|
49
|
+
serviceName: string;
|
|
50
|
+
region: string;
|
|
51
|
+
projectId: string;
|
|
52
|
+
deploymentInfo?: Record<string, unknown> | null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Print a summary of the Google Cloud Run deployment results.
|
|
56
|
+
*/
|
|
57
|
+
export declare function printGcpDeploymentSummary(opts: DeploymentSummaryOptions): void;
|
|
58
|
+
/**
|
|
59
|
+
* Delete a Google Cloud Run service.
|
|
60
|
+
*/
|
|
61
|
+
export declare function deleteGcpService(serviceName: string, region?: string, projectId?: string | null): Promise<boolean>;
|
|
62
|
+
/**
|
|
63
|
+
* List Google Cloud Run services.
|
|
64
|
+
*/
|
|
65
|
+
export declare function listGcpServices(region?: string, projectId?: string | null): Promise<Record<string, unknown>[]>;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=deploy-to-gcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-to-gcp.d.ts","sourceRoot":"","sources":["../src/deploy-to-gcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAmB5D;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+BxD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,aAAa,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA+BxB;AAMD,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,aAAa,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+J5E;AAMD,UAAU,wBAAwB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACjD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,wBAAwB,GAC7B,IAAI,CAwBN;AAMD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,MAAM,SAAgB,EACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,SAAgB,EACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAIpC"}
|