@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
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage route handlers for the NodeTool worker.
|
|
3
|
+
*
|
|
4
|
+
* Provides typed handler functions (not framework-specific) for:
|
|
5
|
+
* 1. Admin storage (full CRUD operations)
|
|
6
|
+
* 2. Public storage (read-only access)
|
|
7
|
+
*
|
|
8
|
+
* The caller is responsible for wiring these into their HTTP framework
|
|
9
|
+
* (Express, Fastify, etc.) and applying auth middleware.
|
|
10
|
+
*/
|
|
11
|
+
import { extname } from "path";
|
|
12
|
+
/** Common content-type mappings by file extension. */
|
|
13
|
+
const EXTENSION_TO_CONTENT_TYPE = {
|
|
14
|
+
jpg: "image/jpeg",
|
|
15
|
+
jpeg: "image/jpeg",
|
|
16
|
+
png: "image/png",
|
|
17
|
+
gif: "image/gif",
|
|
18
|
+
webp: "image/webp",
|
|
19
|
+
svg: "image/svg+xml",
|
|
20
|
+
mp3: "audio/mpeg",
|
|
21
|
+
wav: "audio/wav",
|
|
22
|
+
ogg: "audio/ogg",
|
|
23
|
+
flac: "audio/flac",
|
|
24
|
+
mp4: "video/mp4",
|
|
25
|
+
webm: "video/webm",
|
|
26
|
+
avi: "video/x-msvideo",
|
|
27
|
+
mov: "video/quicktime",
|
|
28
|
+
pdf: "application/pdf",
|
|
29
|
+
json: "application/json",
|
|
30
|
+
txt: "text/plain",
|
|
31
|
+
csv: "text/csv",
|
|
32
|
+
html: "text/html",
|
|
33
|
+
css: "text/css",
|
|
34
|
+
js: "application/javascript",
|
|
35
|
+
xml: "application/xml",
|
|
36
|
+
zip: "application/zip",
|
|
37
|
+
tar: "application/x-tar",
|
|
38
|
+
gz: "application/gzip"
|
|
39
|
+
};
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Key validation
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
/**
|
|
44
|
+
* Validate and normalize a storage key, preventing traversal while allowing
|
|
45
|
+
* nested prefixes.
|
|
46
|
+
*
|
|
47
|
+
* @returns The normalized key (POSIX-style separators).
|
|
48
|
+
* @throws Error if validation fails (caller should map to HTTP 400).
|
|
49
|
+
*/
|
|
50
|
+
export function validateKey(key) {
|
|
51
|
+
const normalized = key.replace(/\\/g, "/");
|
|
52
|
+
const parts = normalized
|
|
53
|
+
.split("/")
|
|
54
|
+
.filter((part) => part !== "" && part !== ".");
|
|
55
|
+
if (parts.length === 0) {
|
|
56
|
+
throw new Error("Invalid key: key must not be empty");
|
|
57
|
+
}
|
|
58
|
+
if (normalized.startsWith("/")) {
|
|
59
|
+
throw new Error("Invalid key: absolute paths are not allowed");
|
|
60
|
+
}
|
|
61
|
+
if (parts.some((part) => part === "..")) {
|
|
62
|
+
throw new Error("Invalid key: path traversal is not allowed");
|
|
63
|
+
}
|
|
64
|
+
return parts.join("/");
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Shared handler implementations
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
/**
|
|
70
|
+
* Return file metadata (HEAD request).
|
|
71
|
+
*/
|
|
72
|
+
export async function headFile(storage, key) {
|
|
73
|
+
const safeKey = validateKey(key);
|
|
74
|
+
if (!(await storage.fileExists(safeKey))) {
|
|
75
|
+
return { status: 404, headers: {} };
|
|
76
|
+
}
|
|
77
|
+
const lastModified = await storage.getMtime(safeKey);
|
|
78
|
+
if (!lastModified) {
|
|
79
|
+
return { status: 404, headers: {} };
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
status: 200,
|
|
83
|
+
headers: {
|
|
84
|
+
"Last-Modified": lastModified.toUTCString()
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Return a file as a stream with range support (GET request).
|
|
90
|
+
*/
|
|
91
|
+
export async function getFile(storage, key, request) {
|
|
92
|
+
const safeKey = validateKey(key);
|
|
93
|
+
if (!(await storage.fileExists(safeKey))) {
|
|
94
|
+
return { status: 404, headers: {} };
|
|
95
|
+
}
|
|
96
|
+
const lastModified = await storage.getMtime(safeKey);
|
|
97
|
+
if (!lastModified) {
|
|
98
|
+
return { status: 404, headers: {} };
|
|
99
|
+
}
|
|
100
|
+
// If-Modified-Since handling
|
|
101
|
+
const ifModifiedSince = request.headers["if-modified-since"];
|
|
102
|
+
if (ifModifiedSince) {
|
|
103
|
+
const ifModifiedDate = new Date(ifModifiedSince);
|
|
104
|
+
if (!isNaN(ifModifiedDate.getTime()) && ifModifiedDate >= lastModified) {
|
|
105
|
+
return { status: 304, headers: {} };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const ext = extname(safeKey).slice(1);
|
|
109
|
+
const mediaType = EXTENSION_TO_CONTENT_TYPE[ext] ?? "application/octet-stream";
|
|
110
|
+
const headers = {
|
|
111
|
+
"Last-Modified": lastModified.toUTCString(),
|
|
112
|
+
"Accept-Ranges": "bytes",
|
|
113
|
+
"Content-Type": mediaType
|
|
114
|
+
};
|
|
115
|
+
// Range request handling
|
|
116
|
+
const rangeHeader = request.headers["range"];
|
|
117
|
+
if (rangeHeader) {
|
|
118
|
+
const rangeMatch = rangeHeader.match(/^bytes=(\d+)-(\d*)$/);
|
|
119
|
+
if (rangeMatch) {
|
|
120
|
+
const start = parseInt(rangeMatch[1], 10);
|
|
121
|
+
const data = await storage.download(safeKey);
|
|
122
|
+
const end = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : data.length - 1;
|
|
123
|
+
headers["Content-Range"] = `bytes ${start}-${end}/${data.length}`;
|
|
124
|
+
headers["Content-Length"] = String(end - start + 1);
|
|
125
|
+
return {
|
|
126
|
+
status: 206,
|
|
127
|
+
headers,
|
|
128
|
+
body: data.slice(start, end + 1)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// If range is invalid, fall through to full content
|
|
132
|
+
}
|
|
133
|
+
const size = await storage.getSize(safeKey);
|
|
134
|
+
headers["Content-Length"] = String(size);
|
|
135
|
+
return {
|
|
136
|
+
status: 200,
|
|
137
|
+
headers,
|
|
138
|
+
body: storage.downloadStream(safeKey)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Upload or update a file (PUT request).
|
|
143
|
+
*/
|
|
144
|
+
export async function putFile(storage, key, data) {
|
|
145
|
+
const safeKey = validateKey(key);
|
|
146
|
+
await storage.upload(safeKey, data);
|
|
147
|
+
return { status: 200, headers: {}, body: null };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Delete a file (DELETE request).
|
|
151
|
+
*/
|
|
152
|
+
export async function deleteFile(storage, key) {
|
|
153
|
+
const safeKey = validateKey(key);
|
|
154
|
+
if (!(await storage.fileExists(safeKey))) {
|
|
155
|
+
return { status: 404, headers: {} };
|
|
156
|
+
}
|
|
157
|
+
await storage.delete(safeKey);
|
|
158
|
+
return { status: 204, headers: {} };
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=storage-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-routes.js","sourceRoot":"","sources":["../src/storage-routes.ts"],"names":[],"mappings":"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"}
|
package/dist/sync.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow model extraction utilities.
|
|
3
|
+
*
|
|
4
|
+
* Extracts HuggingFace and Ollama model references from workflow graphs
|
|
5
|
+
* so they can be pre-downloaded before execution.
|
|
6
|
+
*/
|
|
7
|
+
/** A model reference extracted from a workflow graph. */
|
|
8
|
+
export interface ExtractedModel {
|
|
9
|
+
type: string;
|
|
10
|
+
repo_id?: string;
|
|
11
|
+
path?: string | null;
|
|
12
|
+
variant?: string | null;
|
|
13
|
+
allow_patterns?: string[] | null;
|
|
14
|
+
ignore_patterns?: string[] | null;
|
|
15
|
+
provider?: string;
|
|
16
|
+
id?: string;
|
|
17
|
+
}
|
|
18
|
+
interface WorkflowNode {
|
|
19
|
+
data?: Record<string, unknown>;
|
|
20
|
+
type?: string;
|
|
21
|
+
}
|
|
22
|
+
interface WorkflowData {
|
|
23
|
+
graph?: {
|
|
24
|
+
nodes?: WorkflowNode[];
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extract both HuggingFace and Ollama models from a workflow graph.
|
|
29
|
+
*
|
|
30
|
+
* Scans through all nodes in the workflow graph to find models that need to be
|
|
31
|
+
* pre-downloaded. This includes:
|
|
32
|
+
* - HuggingFace models (type starts with "hf.")
|
|
33
|
+
* - Ollama language models (type="language_model" and provider="ollama")
|
|
34
|
+
* - llama_cpp language models (type="language_model" and provider="llama_cpp")
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractModels(workflowData: WorkflowData): ExtractedModel[];
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,YAAY;IACpB,KAAK,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;KACxB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,YAAY,GAAG,cAAc,EAAE,CA0I1E"}
|
package/dist/sync.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow model extraction utilities.
|
|
3
|
+
*
|
|
4
|
+
* Extracts HuggingFace and Ollama model references from workflow graphs
|
|
5
|
+
* so they can be pre-downloaded before execution.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Extract both HuggingFace and Ollama models from a workflow graph.
|
|
9
|
+
*
|
|
10
|
+
* Scans through all nodes in the workflow graph to find models that need to be
|
|
11
|
+
* pre-downloaded. This includes:
|
|
12
|
+
* - HuggingFace models (type starts with "hf.")
|
|
13
|
+
* - Ollama language models (type="language_model" and provider="ollama")
|
|
14
|
+
* - llama_cpp language models (type="language_model" and provider="llama_cpp")
|
|
15
|
+
*/
|
|
16
|
+
export function extractModels(workflowData) {
|
|
17
|
+
const models = [];
|
|
18
|
+
const seenModels = new Set();
|
|
19
|
+
const nodes = workflowData.graph?.nodes;
|
|
20
|
+
if (!nodes) {
|
|
21
|
+
return models;
|
|
22
|
+
}
|
|
23
|
+
for (const node of nodes) {
|
|
24
|
+
if (!node.data) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const nodeData = node.data;
|
|
28
|
+
// Check for model field containing HuggingFace or Ollama models
|
|
29
|
+
const model = nodeData["model"];
|
|
30
|
+
if (model && typeof model === "object" && !Array.isArray(model)) {
|
|
31
|
+
const m = model;
|
|
32
|
+
// HuggingFace models
|
|
33
|
+
if (typeof m["type"] === "string" &&
|
|
34
|
+
m["type"].startsWith("hf.") &&
|
|
35
|
+
typeof m["repo_id"] === "string" &&
|
|
36
|
+
m["repo_id"]) {
|
|
37
|
+
const modelKey = `hf|${m["type"]}|${m["repo_id"]}|${m["path"] ?? ""}|${m["variant"] ?? ""}`;
|
|
38
|
+
if (!seenModels.has(modelKey)) {
|
|
39
|
+
seenModels.add(modelKey);
|
|
40
|
+
models.push({
|
|
41
|
+
type: m["type"] || "hf.model",
|
|
42
|
+
repo_id: m["repo_id"],
|
|
43
|
+
path: m["path"] ?? null,
|
|
44
|
+
variant: m["variant"] ?? null,
|
|
45
|
+
allow_patterns: m["allow_patterns"] ?? null,
|
|
46
|
+
ignore_patterns: m["ignore_patterns"] ?? null
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Ollama language models
|
|
51
|
+
else if (m["type"] === "language_model" &&
|
|
52
|
+
m["provider"] === "ollama" &&
|
|
53
|
+
m["id"]) {
|
|
54
|
+
const modelKey = `ollama|${m["id"]}`;
|
|
55
|
+
if (!seenModels.has(modelKey)) {
|
|
56
|
+
seenModels.add(modelKey);
|
|
57
|
+
models.push({
|
|
58
|
+
type: "language_model",
|
|
59
|
+
provider: "ollama",
|
|
60
|
+
id: m["id"]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// llama_cpp language models (HuggingFace GGUF models)
|
|
65
|
+
else if (m["type"] === "language_model" &&
|
|
66
|
+
m["provider"] === "llama_cpp" &&
|
|
67
|
+
typeof m["id"] === "string") {
|
|
68
|
+
const modelId = m["id"];
|
|
69
|
+
if (modelId.includes(":")) {
|
|
70
|
+
const [repoId, filePath] = modelId.split(":", 2);
|
|
71
|
+
const modelKey = `hf|hf.gguf|${repoId}|${filePath}|`;
|
|
72
|
+
if (!seenModels.has(modelKey)) {
|
|
73
|
+
seenModels.add(modelKey);
|
|
74
|
+
models.push({
|
|
75
|
+
type: "hf.gguf",
|
|
76
|
+
repo_id: repoId,
|
|
77
|
+
path: filePath,
|
|
78
|
+
variant: null,
|
|
79
|
+
allow_patterns: null,
|
|
80
|
+
ignore_patterns: null
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check for language models at the root level
|
|
87
|
+
if (nodeData["type"] === "language_model" &&
|
|
88
|
+
nodeData["provider"] === "ollama" &&
|
|
89
|
+
nodeData["id"]) {
|
|
90
|
+
const modelKey = `ollama|${nodeData["id"]}`;
|
|
91
|
+
if (!seenModels.has(modelKey)) {
|
|
92
|
+
seenModels.add(modelKey);
|
|
93
|
+
models.push({
|
|
94
|
+
type: "language_model",
|
|
95
|
+
provider: "ollama",
|
|
96
|
+
id: nodeData["id"]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Check for nested model references (e.g., in arrays like loras)
|
|
101
|
+
for (const [, value] of Object.entries(nodeData)) {
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
for (const item of value) {
|
|
104
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
105
|
+
const it = item;
|
|
106
|
+
if (typeof it["type"] === "string" &&
|
|
107
|
+
it["type"].startsWith("hf.") &&
|
|
108
|
+
typeof it["repo_id"] === "string" &&
|
|
109
|
+
it["repo_id"]) {
|
|
110
|
+
const modelKey = `hf|${it["type"]}|${it["repo_id"]}|${it["path"] ?? ""}|${it["variant"] ?? ""}`;
|
|
111
|
+
if (!seenModels.has(modelKey)) {
|
|
112
|
+
seenModels.add(modelKey);
|
|
113
|
+
models.push({
|
|
114
|
+
type: it["type"] || "hf.model",
|
|
115
|
+
repo_id: it["repo_id"],
|
|
116
|
+
path: it["path"] ?? null,
|
|
117
|
+
variant: it["variant"] ?? null,
|
|
118
|
+
allow_patterns: it["allow_patterns"] ?? null,
|
|
119
|
+
ignore_patterns: it["ignore_patterns"] ?? null
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return models;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=sync.js.map
|
package/dist/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowSyncer — syncs workflows, assets, and required models
|
|
3
|
+
* to a remote NodeTool instance via the AdminHTTPClient.
|
|
4
|
+
*/
|
|
5
|
+
import type { AdminHTTPClient } from "./admin-client.js";
|
|
6
|
+
/** Minimal asset representation for syncing. */
|
|
7
|
+
export interface AssetInfo {
|
|
8
|
+
id: string;
|
|
9
|
+
user_id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
content_type: string;
|
|
12
|
+
parent_id?: string | null;
|
|
13
|
+
workflow_id?: string | null;
|
|
14
|
+
metadata?: Record<string, unknown> | null;
|
|
15
|
+
file_name?: string | null;
|
|
16
|
+
has_thumbnail?: boolean;
|
|
17
|
+
thumb_file_name?: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** Storage interface for downloading asset data. */
|
|
20
|
+
export interface SyncerAssetStorage {
|
|
21
|
+
download(key: string): Promise<Uint8Array>;
|
|
22
|
+
}
|
|
23
|
+
/** Functions the caller must supply so WorkflowSyncer can resolve local data. */
|
|
24
|
+
export interface WorkflowSyncerDeps {
|
|
25
|
+
/** Retrieve a workflow by ID and return it as a serialisable dict. */
|
|
26
|
+
getWorkflowData(workflowId: string): Promise<Record<string, unknown> | null>;
|
|
27
|
+
/** Retrieve local asset metadata by ID. */
|
|
28
|
+
getAsset(assetId: string): Promise<AssetInfo | null>;
|
|
29
|
+
/** Get the local asset storage instance. */
|
|
30
|
+
getSyncerAssetStorage(): SyncerAssetStorage;
|
|
31
|
+
}
|
|
32
|
+
export declare class WorkflowSyncer {
|
|
33
|
+
private client;
|
|
34
|
+
private deps;
|
|
35
|
+
constructor(client: AdminHTTPClient, deps: WorkflowSyncerDeps);
|
|
36
|
+
/**
|
|
37
|
+
* Sync a workflow, its assets, and required models to the remote instance.
|
|
38
|
+
*
|
|
39
|
+
* @returns true if sync was successful, false otherwise.
|
|
40
|
+
*/
|
|
41
|
+
syncWorkflow(workflowId: string): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Extract model references from workflow and download them on remote.
|
|
44
|
+
*/
|
|
45
|
+
private extractAndDownloadModels;
|
|
46
|
+
/**
|
|
47
|
+
* Extract asset references from workflow and sync them to remote.
|
|
48
|
+
*/
|
|
49
|
+
private extractAndSyncAssets;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=workflow-syncer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-syncer.d.ts","sourceRoot":"","sources":["../src/workflow-syncer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,gDAAgD;AAChD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC5C;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAE7E,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAErD,4CAA4C;IAC5C,qBAAqB,IAAI,kBAAkB,CAAC;CAC7C;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,IAAI,CAAqB;gBAErB,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,kBAAkB;IAK7D;;;;OAIG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA6BxD;;OAEG;YACW,wBAAwB;IAuFtC;;OAEG;YACW,oBAAoB;CAqFnC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowSyncer — syncs workflows, assets, and required models
|
|
3
|
+
* to a remote NodeTool instance via the AdminHTTPClient.
|
|
4
|
+
*/
|
|
5
|
+
import { extractModels } from "./sync.js";
|
|
6
|
+
export class WorkflowSyncer {
|
|
7
|
+
client;
|
|
8
|
+
deps;
|
|
9
|
+
constructor(client, deps) {
|
|
10
|
+
this.client = client;
|
|
11
|
+
this.deps = deps;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Sync a workflow, its assets, and required models to the remote instance.
|
|
15
|
+
*
|
|
16
|
+
* @returns true if sync was successful, false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
async syncWorkflow(workflowId) {
|
|
19
|
+
try {
|
|
20
|
+
const workflowData = await this.deps.getWorkflowData(workflowId);
|
|
21
|
+
if (!workflowData) {
|
|
22
|
+
console.error(`Workflow not found locally: ${workflowId}`);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
// Sync assets first
|
|
26
|
+
const syncedAssets = await this.extractAndSyncAssets(workflowData);
|
|
27
|
+
if (syncedAssets > 0) {
|
|
28
|
+
console.log(`Synced ${syncedAssets} asset(s)`);
|
|
29
|
+
}
|
|
30
|
+
// Download models required by the workflow
|
|
31
|
+
const syncedModels = await this.extractAndDownloadModels(workflowData);
|
|
32
|
+
if (syncedModels > 0) {
|
|
33
|
+
console.log(`Downloaded ${syncedModels} model(s)`);
|
|
34
|
+
}
|
|
35
|
+
// Sync workflow
|
|
36
|
+
await this.client.updateWorkflow(workflowId, workflowData);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
console.error(`Failed to sync workflow: ${e}`);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extract model references from workflow and download them on remote.
|
|
46
|
+
*/
|
|
47
|
+
async extractAndDownloadModels(workflowData) {
|
|
48
|
+
const models = extractModels(workflowData);
|
|
49
|
+
if (models.length === 0) {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
console.log(`Found ${models.length} model(s) to download`);
|
|
53
|
+
let downloadedCount = 0;
|
|
54
|
+
for (const model of models) {
|
|
55
|
+
try {
|
|
56
|
+
const modelType = model.type ?? "";
|
|
57
|
+
// Handle HuggingFace models
|
|
58
|
+
if (modelType.startsWith("hf.")) {
|
|
59
|
+
const repoId = model.repo_id;
|
|
60
|
+
if (!repoId) {
|
|
61
|
+
console.error(" Error: repo_id is required for HF models");
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
console.log(` Downloading HF model: ${repoId}`);
|
|
65
|
+
let lastStatus;
|
|
66
|
+
for await (const progress of this.client.downloadHuggingfaceModel({
|
|
67
|
+
repoId,
|
|
68
|
+
filePath: model.path ?? undefined,
|
|
69
|
+
ignorePatterns: model.ignore_patterns ?? undefined,
|
|
70
|
+
allowPatterns: model.allow_patterns ?? undefined
|
|
71
|
+
})) {
|
|
72
|
+
lastStatus = progress["status"];
|
|
73
|
+
if (lastStatus === "downloading") {
|
|
74
|
+
const fileName = progress["file"] ?? "";
|
|
75
|
+
const percent = progress["percent"] ?? 0;
|
|
76
|
+
process.stdout.write(` ${fileName}: ${percent.toFixed(1)}%\r`);
|
|
77
|
+
}
|
|
78
|
+
else if (lastStatus === "complete") {
|
|
79
|
+
console.log(` Downloaded ${repoId}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (lastStatus !== "complete") {
|
|
83
|
+
console.log(` Downloaded ${repoId}`);
|
|
84
|
+
}
|
|
85
|
+
downloadedCount++;
|
|
86
|
+
}
|
|
87
|
+
// Handle Ollama models
|
|
88
|
+
else if (modelType === "language_model" &&
|
|
89
|
+
model.provider === "ollama") {
|
|
90
|
+
const modelId = model.id;
|
|
91
|
+
if (!modelId) {
|
|
92
|
+
console.error(" Error: model id is required for Ollama models");
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
console.log(` Downloading Ollama model: ${modelId}`);
|
|
96
|
+
let lastStatus;
|
|
97
|
+
for await (const progress of this.client.downloadOllamaModel(modelId)) {
|
|
98
|
+
lastStatus = progress["status"];
|
|
99
|
+
if (lastStatus && lastStatus !== "success") {
|
|
100
|
+
process.stdout.write(` ${lastStatus}\r`);
|
|
101
|
+
}
|
|
102
|
+
else if (lastStatus === "success") {
|
|
103
|
+
console.log(` Downloaded ${modelId}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (lastStatus !== "success") {
|
|
107
|
+
console.log(` Downloaded ${modelId}`);
|
|
108
|
+
}
|
|
109
|
+
downloadedCount++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
console.error(` Failed to download model: ${e}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return downloadedCount;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extract asset references from workflow and sync them to remote.
|
|
120
|
+
*/
|
|
121
|
+
async extractAndSyncAssets(workflowData) {
|
|
122
|
+
const assetIds = new Set();
|
|
123
|
+
const graph = workflowData["graph"];
|
|
124
|
+
const nodes = graph?.nodes ?? [];
|
|
125
|
+
for (const node of nodes) {
|
|
126
|
+
const nodeType = node["type"] ?? "";
|
|
127
|
+
if (nodeType.startsWith("nodetool.constant.")) {
|
|
128
|
+
const data = node["data"];
|
|
129
|
+
const value = data?.["value"];
|
|
130
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
131
|
+
const assetId = value["asset_id"];
|
|
132
|
+
if (typeof assetId === "string" && assetId) {
|
|
133
|
+
assetIds.add(assetId);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (assetIds.size === 0) {
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
console.log(`Found ${assetIds.size} asset(s) to sync`);
|
|
142
|
+
const storage = this.deps.getSyncerAssetStorage();
|
|
143
|
+
let syncedCount = 0;
|
|
144
|
+
for (const assetId of assetIds) {
|
|
145
|
+
try {
|
|
146
|
+
const asset = await this.deps.getAsset(assetId);
|
|
147
|
+
if (!asset) {
|
|
148
|
+
console.warn(` Asset ${assetId} not found locally, skipping`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
console.log(` Syncing asset: ${asset.name}`);
|
|
152
|
+
// Check if asset already exists on remote
|
|
153
|
+
try {
|
|
154
|
+
await this.client.getAsset(assetId);
|
|
155
|
+
console.log(" Asset already exists on remote, skipping");
|
|
156
|
+
syncedCount++;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Asset doesn't exist, continue with sync
|
|
161
|
+
}
|
|
162
|
+
// Create asset metadata on remote (preserve asset ID)
|
|
163
|
+
await this.client.createAsset({
|
|
164
|
+
id: asset.id,
|
|
165
|
+
userId: asset.user_id,
|
|
166
|
+
name: asset.name,
|
|
167
|
+
contentType: asset.content_type,
|
|
168
|
+
parentId: asset.parent_id ?? undefined,
|
|
169
|
+
workflowId: asset.workflow_id ?? undefined,
|
|
170
|
+
metadata: asset.metadata ?? undefined
|
|
171
|
+
});
|
|
172
|
+
// Upload asset file if it's not a folder
|
|
173
|
+
if (asset.content_type !== "folder" && asset.file_name) {
|
|
174
|
+
const fileData = await storage.download(asset.file_name);
|
|
175
|
+
await this.client.uploadAssetFile(asset.file_name, fileData);
|
|
176
|
+
// Upload thumbnail if exists
|
|
177
|
+
if (asset.has_thumbnail && asset.thumb_file_name) {
|
|
178
|
+
const thumbData = await storage.download(asset.thumb_file_name);
|
|
179
|
+
await this.client.uploadAssetFile(asset.thumb_file_name, thumbData);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
console.log(` Synced ${asset.name}`);
|
|
183
|
+
syncedCount++;
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
console.error(` Failed to sync asset ${assetId}: ${e}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return syncedCount;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=workflow-syncer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-syncer.js","sourceRoot":"","sources":["../src/workflow-syncer.ts"],"names":[],"mappings":"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"}
|