blokctl 0.2.0
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/commands/build/index.d.ts +2 -0
- package/dist/commands/build/index.js +210 -0
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.js +46 -0
- package/dist/commands/cost/index.d.ts +1 -0
- package/dist/commands/cost/index.js +74 -0
- package/dist/commands/create/node.d.ts +2 -0
- package/dist/commands/create/node.js +541 -0
- package/dist/commands/create/project.d.ts +2 -0
- package/dist/commands/create/project.js +941 -0
- package/dist/commands/create/utils/Examples.d.ts +39 -0
- package/dist/commands/create/utils/Examples.js +983 -0
- package/dist/commands/create/workflow.d.ts +2 -0
- package/dist/commands/create/workflow.js +109 -0
- package/dist/commands/deploy/index.d.ts +2 -0
- package/dist/commands/deploy/index.js +176 -0
- package/dist/commands/dev/index.d.ts +2 -0
- package/dist/commands/dev/index.js +190 -0
- package/dist/commands/generate/GenerationAnalytics.d.ts +61 -0
- package/dist/commands/generate/GenerationAnalytics.js +162 -0
- package/dist/commands/generate/GenerationAnalytics.test.d.ts +1 -0
- package/dist/commands/generate/GenerationAnalytics.test.js +407 -0
- package/dist/commands/generate/NodeFileWriter.d.ts +5 -0
- package/dist/commands/generate/NodeFileWriter.js +240 -0
- package/dist/commands/generate/NodeGenerator.d.ts +20 -0
- package/dist/commands/generate/NodeGenerator.js +181 -0
- package/dist/commands/generate/NodeGenerator.test.d.ts +1 -0
- package/dist/commands/generate/NodeGenerator.test.js +101 -0
- package/dist/commands/generate/PromptVersioning.d.ts +25 -0
- package/dist/commands/generate/PromptVersioning.js +71 -0
- package/dist/commands/generate/PromptVersioning.test.d.ts +1 -0
- package/dist/commands/generate/PromptVersioning.test.js +120 -0
- package/dist/commands/generate/RegisterNode.d.ts +3 -0
- package/dist/commands/generate/RegisterNode.js +37 -0
- package/dist/commands/generate/RuntimeGenerator.d.ts +40 -0
- package/dist/commands/generate/RuntimeGenerator.js +369 -0
- package/dist/commands/generate/RuntimeGenerator.test.d.ts +1 -0
- package/dist/commands/generate/RuntimeGenerator.test.js +553 -0
- package/dist/commands/generate/TriggerGenerator.d.ts +22 -0
- package/dist/commands/generate/TriggerGenerator.js +220 -0
- package/dist/commands/generate/TriggerGenerator.test.d.ts +1 -0
- package/dist/commands/generate/TriggerGenerator.test.js +209 -0
- package/dist/commands/generate/WorkflowGenerator.d.ts +20 -0
- package/dist/commands/generate/WorkflowGenerator.js +131 -0
- package/dist/commands/generate/WorkflowGenerator.test.d.ts +1 -0
- package/dist/commands/generate/WorkflowGenerator.test.js +77 -0
- package/dist/commands/generate/e2e/NodeGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/NodeGenerator.e2e.test.js +216 -0
- package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.js +759 -0
- package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.js +295 -0
- package/dist/commands/generate/e2e/WorkflowGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/WorkflowGenerator.e2e.test.js +353 -0
- package/dist/commands/generate/index.d.ts +1 -0
- package/dist/commands/generate/index.js +418 -0
- package/dist/commands/generate/prompts/create-fn-node.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-fn-node.system.js +256 -0
- package/dist/commands/generate/prompts/create-node-manifest.system.d.ts +4 -0
- package/dist/commands/generate/prompts/create-node-manifest.system.js +41 -0
- package/dist/commands/generate/prompts/create-node.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-node.system.js +114 -0
- package/dist/commands/generate/prompts/create-readme.system.d.ts +4 -0
- package/dist/commands/generate/prompts/create-readme.system.js +83 -0
- package/dist/commands/generate/prompts/create-runtime.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-runtime.system.js +284 -0
- package/dist/commands/generate/prompts/create-trigger.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-trigger.system.js +293 -0
- package/dist/commands/generate/prompts/create-workflow.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-workflow.system.js +476 -0
- package/dist/commands/generate/prompts/register-node.system.d.ts +4 -0
- package/dist/commands/generate/prompts/register-node.system.js +26 -0
- package/dist/commands/generate/validators/CompilationValidator.d.ts +9 -0
- package/dist/commands/generate/validators/CompilationValidator.js +86 -0
- package/dist/commands/generate/validators/CompilationValidator.test.d.ts +1 -0
- package/dist/commands/generate/validators/CompilationValidator.test.js +161 -0
- package/dist/commands/generate/validators/NodeValidator.d.ts +18 -0
- package/dist/commands/generate/validators/NodeValidator.js +217 -0
- package/dist/commands/generate/validators/NodeValidator.test.d.ts +1 -0
- package/dist/commands/generate/validators/NodeValidator.test.js +281 -0
- package/dist/commands/generate/validators/WorkflowValidator.d.ts +6 -0
- package/dist/commands/generate/validators/WorkflowValidator.js +301 -0
- package/dist/commands/generate/validators/WorkflowValidator.test.d.ts +1 -0
- package/dist/commands/generate/validators/WorkflowValidator.test.js +647 -0
- package/dist/commands/generate/validators/index.d.ts +4 -0
- package/dist/commands/generate/validators/index.js +2 -0
- package/dist/commands/graph/index.d.ts +1 -0
- package/dist/commands/graph/index.js +69 -0
- package/dist/commands/install/index.d.ts +1 -0
- package/dist/commands/install/index.js +4 -0
- package/dist/commands/install/node.d.ts +4 -0
- package/dist/commands/install/node.js +136 -0
- package/dist/commands/install/workflow.d.ts +4 -0
- package/dist/commands/install/workflow.js +62 -0
- package/dist/commands/login/index.d.ts +2 -0
- package/dist/commands/login/index.js +77 -0
- package/dist/commands/logout/index.d.ts +2 -0
- package/dist/commands/logout/index.js +20 -0
- package/dist/commands/marketplace/runtime.d.ts +54 -0
- package/dist/commands/marketplace/runtime.js +350 -0
- package/dist/commands/migrate/index.d.ts +1 -0
- package/dist/commands/migrate/index.js +14 -0
- package/dist/commands/migrate/node.d.ts +2 -0
- package/dist/commands/migrate/node.js +110 -0
- package/dist/commands/monitor/index.d.ts +1 -0
- package/dist/commands/monitor/index.js +28 -0
- package/dist/commands/monitor/monitor-component.d.ts +1 -0
- package/dist/commands/monitor/monitor-component.js +271 -0
- package/dist/commands/monitor/static/index.html +2124 -0
- package/dist/commands/monitor/static-web-server.d.ts +1 -0
- package/dist/commands/monitor/static-web-server.js +89 -0
- package/dist/commands/profile/index.d.ts +1 -0
- package/dist/commands/profile/index.js +112 -0
- package/dist/commands/publish/index.d.ts +1 -0
- package/dist/commands/publish/index.js +4 -0
- package/dist/commands/publish/node.d.ts +4 -0
- package/dist/commands/publish/node.js +231 -0
- package/dist/commands/publish/workflow.d.ts +4 -0
- package/dist/commands/publish/workflow.js +165 -0
- package/dist/commands/search/docs.d.ts +17 -0
- package/dist/commands/search/docs.js +179 -0
- package/dist/commands/search/index.d.ts +1 -0
- package/dist/commands/search/index.js +5 -0
- package/dist/commands/search/indexer.d.ts +10 -0
- package/dist/commands/search/indexer.js +265 -0
- package/dist/commands/search/nodes.d.ts +4 -0
- package/dist/commands/search/nodes.js +101 -0
- package/dist/commands/search/workflow.d.ts +4 -0
- package/dist/commands/search/workflow.js +100 -0
- package/dist/commands/trace/index.d.ts +1 -0
- package/dist/commands/trace/index.js +26 -0
- package/dist/commands/trace/startStudio.d.ts +8 -0
- package/dist/commands/trace/startStudio.js +116 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +186 -0
- package/dist/services/commander.d.ts +9 -0
- package/dist/services/commander.js +20 -0
- package/dist/services/constants.d.ts +1 -0
- package/dist/services/constants.js +3 -0
- package/dist/services/local-token-manager.d.ts +14 -0
- package/dist/services/local-token-manager.js +99 -0
- package/dist/services/non-interactive.d.ts +5 -0
- package/dist/services/non-interactive.js +30 -0
- package/dist/services/package-manager.d.ts +35 -0
- package/dist/services/package-manager.js +111 -0
- package/dist/services/posthog.d.ts +31 -0
- package/dist/services/posthog.js +159 -0
- package/dist/services/registry-manager.d.ts +9 -0
- package/dist/services/registry-manager.js +26 -0
- package/dist/services/runtime-detector.d.ts +23 -0
- package/dist/services/runtime-detector.js +181 -0
- package/dist/services/runtime-setup.d.ts +36 -0
- package/dist/services/runtime-setup.js +250 -0
- package/dist/services/utils.d.ts +2 -0
- package/dist/services/utils.js +29 -0
- package/dist/services/workflow-loader.d.ts +30 -0
- package/dist/services/workflow-loader.js +46 -0
- package/dist/studio-dist/assets/charts-Dso0hPUR.js +68 -0
- package/dist/studio-dist/assets/graph-CsV2nWGn.js +23 -0
- package/dist/studio-dist/assets/icons-zP8LLgPh.js +311 -0
- package/dist/studio-dist/assets/index-CLyEkXMx.css +1 -0
- package/dist/studio-dist/assets/index-CNXFX_ar.js +27 -0
- package/dist/studio-dist/assets/react-vendor--Eh9ivFN.js +17 -0
- package/dist/studio-dist/assets/tanstack-query-CiM1U6F5.js +1 -0
- package/dist/studio-dist/assets/tanstack-router-Btjy0MKq.js +25 -0
- package/dist/studio-dist/assets/tanstack-table-DhwRvuH2.js +22 -0
- package/dist/studio-dist/favicon.svg +5 -0
- package/dist/studio-dist/index.html +21 -0
- package/package.json +75 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
import { Command, trackCommandExecution } from "../../services/commander.js";
|
|
6
|
+
import { tokenManager } from "../../services/local-token-manager.js";
|
|
7
|
+
const DEFAULT_REGISTRY_URL = "https://marketplace.blok.dev/api/v1";
|
|
8
|
+
const DEFAULT_CACHE_DIR = path.join(os.homedir(), ".blok", "marketplace-cache");
|
|
9
|
+
const DEFAULT_CACHE_TTL_MS = 3_600_000;
|
|
10
|
+
export class RuntimeMarketplaceCommand {
|
|
11
|
+
config;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = {
|
|
14
|
+
registryUrl: config?.registryUrl ?? process.env.BLOK_MARKETPLACE_URL ?? DEFAULT_REGISTRY_URL,
|
|
15
|
+
cacheDir: config?.cacheDir ?? DEFAULT_CACHE_DIR,
|
|
16
|
+
cacheTtlMs: config?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS,
|
|
17
|
+
};
|
|
18
|
+
if (!fs.existsSync(this.config.cacheDir)) {
|
|
19
|
+
fs.mkdirSync(this.config.cacheDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async search(options) {
|
|
23
|
+
const cacheKey = this.getCacheKey(options);
|
|
24
|
+
const cached = this.readCache(cacheKey);
|
|
25
|
+
if (cached) {
|
|
26
|
+
return cached;
|
|
27
|
+
}
|
|
28
|
+
const searchOptions = {
|
|
29
|
+
query: options.query,
|
|
30
|
+
runtime: options.runtime,
|
|
31
|
+
tags: options.tags,
|
|
32
|
+
verified: options.verified,
|
|
33
|
+
sortBy: options.sortBy,
|
|
34
|
+
limit: options.limit,
|
|
35
|
+
};
|
|
36
|
+
const params = {};
|
|
37
|
+
if (searchOptions.query)
|
|
38
|
+
params.query = searchOptions.query;
|
|
39
|
+
if (searchOptions.runtime)
|
|
40
|
+
params.runtime = searchOptions.runtime;
|
|
41
|
+
if (searchOptions.tags && searchOptions.tags.length > 0)
|
|
42
|
+
params.tags = searchOptions.tags.join(",");
|
|
43
|
+
if (searchOptions.verified !== undefined)
|
|
44
|
+
params.verified = String(searchOptions.verified);
|
|
45
|
+
if (searchOptions.sortBy)
|
|
46
|
+
params.sortBy = searchOptions.sortBy;
|
|
47
|
+
if (searchOptions.limit)
|
|
48
|
+
params.limit = String(searchOptions.limit);
|
|
49
|
+
const result = await this.fetchFromApi("/packages/search", params);
|
|
50
|
+
const packages = result.packages;
|
|
51
|
+
this.writeCache(cacheKey, packages);
|
|
52
|
+
return packages;
|
|
53
|
+
}
|
|
54
|
+
async install(options) {
|
|
55
|
+
try {
|
|
56
|
+
let version = options.version;
|
|
57
|
+
if (!version) {
|
|
58
|
+
const versionInfo = await this.fetchFromApi(`/packages/${encodeURIComponent(options.name)}/versions/latest`);
|
|
59
|
+
version = versionInfo.latest;
|
|
60
|
+
}
|
|
61
|
+
const manifest = await this.fetchFromApi(`/packages/${encodeURIComponent(options.name)}/versions/${encodeURIComponent(version)}`);
|
|
62
|
+
if (options.runtime && manifest.runtime !== options.runtime) {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
message: `Package "${options.name}" is for runtime "${manifest.runtime}", not "${options.runtime}".`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (manifest.dockerImage) {
|
|
69
|
+
const { exec } = await import("node:child_process");
|
|
70
|
+
const { promisify } = await import("node:util");
|
|
71
|
+
const execAsync = promisify(exec);
|
|
72
|
+
const pullCommand = options.force
|
|
73
|
+
? `docker pull --force ${manifest.dockerImage}`
|
|
74
|
+
: `docker pull ${manifest.dockerImage}`;
|
|
75
|
+
try {
|
|
76
|
+
await execAsync(pullCommand);
|
|
77
|
+
}
|
|
78
|
+
catch (dockerError) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
message: `Failed to pull Docker image "${manifest.dockerImage}": ${dockerError.message}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const catalogDir = path.join(os.homedir(), ".blok", "catalog");
|
|
86
|
+
if (!fs.existsSync(catalogDir)) {
|
|
87
|
+
fs.mkdirSync(catalogDir, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
const catalogFile = path.join(catalogDir, "installed.json");
|
|
90
|
+
let catalog = {};
|
|
91
|
+
if (fs.existsSync(catalogFile)) {
|
|
92
|
+
catalog = JSON.parse(fs.readFileSync(catalogFile, "utf-8"));
|
|
93
|
+
}
|
|
94
|
+
const catalogKey = `${manifest.name}@${manifest.version}`;
|
|
95
|
+
if (catalog[catalogKey] && !options.force) {
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
message: `Package "${catalogKey}" is already installed. Use --force to reinstall.`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catalog[catalogKey] = manifest;
|
|
102
|
+
fs.writeFileSync(catalogFile, JSON.stringify(catalog, null, 2), "utf-8");
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
message: `Successfully installed ${manifest.name}@${manifest.version} (${manifest.runtime} runtime).`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
message: `Failed to install "${options.name}": ${error.message}`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async publish(options) {
|
|
116
|
+
try {
|
|
117
|
+
const manifestPath = path.resolve(options.manifestPath);
|
|
118
|
+
if (!fs.existsSync(manifestPath)) {
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
packageId: "",
|
|
122
|
+
version: "",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const rawManifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
126
|
+
const manifest = {
|
|
127
|
+
name: rawManifest.name,
|
|
128
|
+
version: rawManifest.version,
|
|
129
|
+
runtime: options.runtime,
|
|
130
|
+
description: options.description ?? rawManifest.description ?? "",
|
|
131
|
+
author: rawManifest.author ?? "",
|
|
132
|
+
license: rawManifest.license ?? "MIT",
|
|
133
|
+
repository: rawManifest.repository,
|
|
134
|
+
tags: options.tags ?? rawManifest.tags ?? [],
|
|
135
|
+
protocols: rawManifest.protocols ?? ["http"],
|
|
136
|
+
dockerImage: options.dockerImage,
|
|
137
|
+
minBlokVersion: rawManifest.minBlokVersion,
|
|
138
|
+
nodeCount: rawManifest.nodes?.length ?? 0,
|
|
139
|
+
nodes: rawManifest.nodes ?? [],
|
|
140
|
+
createdAt: Date.now(),
|
|
141
|
+
updatedAt: Date.now(),
|
|
142
|
+
downloads: 0,
|
|
143
|
+
rating: 0,
|
|
144
|
+
verified: false,
|
|
145
|
+
};
|
|
146
|
+
if (!manifest.name) {
|
|
147
|
+
return { success: false, packageId: "", version: "" };
|
|
148
|
+
}
|
|
149
|
+
if (!manifest.version) {
|
|
150
|
+
return { success: false, packageId: "", version: "" };
|
|
151
|
+
}
|
|
152
|
+
const token = tokenManager.getToken();
|
|
153
|
+
if (!token) {
|
|
154
|
+
return { success: false, packageId: "", version: "" };
|
|
155
|
+
}
|
|
156
|
+
const response = await fetch(`${this.config.registryUrl}/packages`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: {
|
|
159
|
+
"Content-Type": "application/json",
|
|
160
|
+
Authorization: `Bearer ${token}`,
|
|
161
|
+
},
|
|
162
|
+
body: JSON.stringify(manifest),
|
|
163
|
+
});
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
const errorText = await response.text();
|
|
166
|
+
throw new Error(`API error (${response.status}): ${errorText}`);
|
|
167
|
+
}
|
|
168
|
+
const result = await response.json();
|
|
169
|
+
return {
|
|
170
|
+
success: true,
|
|
171
|
+
packageId: result.packageId ?? manifest.name,
|
|
172
|
+
version: manifest.version,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
packageId: "",
|
|
179
|
+
version: "",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async info(name, version) {
|
|
184
|
+
try {
|
|
185
|
+
const versionPath = version
|
|
186
|
+
? `/packages/${encodeURIComponent(name)}/versions/${encodeURIComponent(version)}`
|
|
187
|
+
: `/packages/${encodeURIComponent(name)}`;
|
|
188
|
+
return await this.fetchFromApi(versionPath);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async stats() {
|
|
195
|
+
return this.fetchFromApi("/stats");
|
|
196
|
+
}
|
|
197
|
+
async list(runtime) {
|
|
198
|
+
const params = { runtime };
|
|
199
|
+
const result = await this.fetchFromApi("/packages", params);
|
|
200
|
+
return result.packages;
|
|
201
|
+
}
|
|
202
|
+
formatTable(packages) {
|
|
203
|
+
if (packages.length === 0) {
|
|
204
|
+
return "No packages found.";
|
|
205
|
+
}
|
|
206
|
+
const headers = ["Name", "Version", "Runtime", "Downloads", "Rating", "Verified", "Description"];
|
|
207
|
+
const rows = packages.map((pkg) => [
|
|
208
|
+
pkg.name,
|
|
209
|
+
pkg.version,
|
|
210
|
+
pkg.runtime,
|
|
211
|
+
String(pkg.downloads),
|
|
212
|
+
pkg.rating.toFixed(1),
|
|
213
|
+
pkg.verified ? "Yes" : "No",
|
|
214
|
+
pkg.description.length > 40 ? `${pkg.description.substring(0, 37)}...` : pkg.description,
|
|
215
|
+
]);
|
|
216
|
+
const colWidths = headers.map((header, index) => {
|
|
217
|
+
const maxDataWidth = rows.reduce((max, row) => Math.max(max, row[index].length), 0);
|
|
218
|
+
return Math.max(header.length, maxDataWidth);
|
|
219
|
+
});
|
|
220
|
+
const separator = `+-${colWidths.map((w) => "-".repeat(w)).join("-+-")}-+`;
|
|
221
|
+
const headerRow = `| ${headers.map((h, i) => h.padEnd(colWidths[i])).join(" | ")} |`;
|
|
222
|
+
const dataRows = rows.map((row) => `| ${row.map((cell, i) => cell.padEnd(colWidths[i])).join(" | ")} |`);
|
|
223
|
+
return [separator, headerRow, separator, ...dataRows, separator].join("\n");
|
|
224
|
+
}
|
|
225
|
+
formatJson(packages) {
|
|
226
|
+
return JSON.stringify(packages, null, 2);
|
|
227
|
+
}
|
|
228
|
+
getCacheKey(options) {
|
|
229
|
+
const parts = [];
|
|
230
|
+
if (options.runtime)
|
|
231
|
+
parts.push(`runtime:${options.runtime}`);
|
|
232
|
+
if (options.query)
|
|
233
|
+
parts.push(`query:${options.query}`);
|
|
234
|
+
if (options.tags && options.tags.length > 0)
|
|
235
|
+
parts.push(`tags:${options.tags.sort().join(",")}`);
|
|
236
|
+
if (options.verified !== undefined)
|
|
237
|
+
parts.push(`verified:${options.verified}`);
|
|
238
|
+
if (options.sortBy)
|
|
239
|
+
parts.push(`sortBy:${options.sortBy}`);
|
|
240
|
+
if (options.limit)
|
|
241
|
+
parts.push(`limit:${options.limit}`);
|
|
242
|
+
const key = parts.length > 0 ? parts.join("|") : "all";
|
|
243
|
+
return hashToSafeFilename(key);
|
|
244
|
+
}
|
|
245
|
+
readCache(key) {
|
|
246
|
+
try {
|
|
247
|
+
const cacheFile = path.join(this.config.cacheDir, `${key}.json`);
|
|
248
|
+
if (!fs.existsSync(cacheFile)) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const raw = fs.readFileSync(cacheFile, "utf-8");
|
|
252
|
+
const entry = JSON.parse(raw);
|
|
253
|
+
if (Date.now() - entry.timestamp > this.config.cacheTtlMs) {
|
|
254
|
+
fs.unlinkSync(cacheFile);
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
return entry.data;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
writeCache(key, data) {
|
|
264
|
+
try {
|
|
265
|
+
const cacheFile = path.join(this.config.cacheDir, `${key}.json`);
|
|
266
|
+
const entry = {
|
|
267
|
+
timestamp: Date.now(),
|
|
268
|
+
data,
|
|
269
|
+
};
|
|
270
|
+
fs.writeFileSync(cacheFile, JSON.stringify(entry), "utf-8");
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async fetchFromApi(apiPath, params) {
|
|
276
|
+
const url = new URL(`${this.config.registryUrl}${apiPath}`);
|
|
277
|
+
if (params) {
|
|
278
|
+
for (const [key, value] of Object.entries(params)) {
|
|
279
|
+
url.searchParams.set(key, value);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const token = tokenManager.getToken();
|
|
283
|
+
const headers = {
|
|
284
|
+
"Content-Type": "application/json",
|
|
285
|
+
};
|
|
286
|
+
if (token) {
|
|
287
|
+
headers.Authorization = `Bearer ${token}`;
|
|
288
|
+
}
|
|
289
|
+
const response = await fetch(url.toString(), {
|
|
290
|
+
method: "GET",
|
|
291
|
+
headers,
|
|
292
|
+
});
|
|
293
|
+
if (!response.ok) {
|
|
294
|
+
const errorText = await response.text();
|
|
295
|
+
throw new Error(`Marketplace API error (${response.status}): ${errorText}`);
|
|
296
|
+
}
|
|
297
|
+
return (await response.json());
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function hashToSafeFilename(input) {
|
|
301
|
+
let hash = 0;
|
|
302
|
+
for (let i = 0; i < input.length; i++) {
|
|
303
|
+
const char = input.charCodeAt(i);
|
|
304
|
+
hash = (hash << 5) - hash + char;
|
|
305
|
+
hash |= 0;
|
|
306
|
+
}
|
|
307
|
+
return Math.abs(hash).toString(36);
|
|
308
|
+
}
|
|
309
|
+
export default new Command()
|
|
310
|
+
.command("runtime")
|
|
311
|
+
.description("Manage runtime packages from the Blok marketplace")
|
|
312
|
+
.option("-r, --runtime <value>", "Filter by runtime kind (nodejs, python3, go, rust, etc.)")
|
|
313
|
+
.option("-q, --query <value>", "Search query string")
|
|
314
|
+
.option("-t, --tags <value>", "Comma-separated tags to filter by")
|
|
315
|
+
.option("--verified", "Show only verified packages")
|
|
316
|
+
.option("-s, --sort-by <value>", "Sort by: name, downloads, rating, updated", "downloads")
|
|
317
|
+
.option("-l, --limit <value>", "Limit number of results", "20")
|
|
318
|
+
.option("-f, --format <value>", "Output format: table or json", "table")
|
|
319
|
+
.action(async (options) => {
|
|
320
|
+
await trackCommandExecution({
|
|
321
|
+
command: "marketplace runtime",
|
|
322
|
+
args: options,
|
|
323
|
+
execution: async () => {
|
|
324
|
+
const logger = p.spinner();
|
|
325
|
+
try {
|
|
326
|
+
logger.start("Searching marketplace for runtime packages...");
|
|
327
|
+
const marketplace = new RuntimeMarketplaceCommand();
|
|
328
|
+
const listOptions = {
|
|
329
|
+
runtime: options.runtime,
|
|
330
|
+
query: options.query,
|
|
331
|
+
tags: options.tags ? options.tags.split(",") : undefined,
|
|
332
|
+
verified: options.verified,
|
|
333
|
+
sortBy: options.sortBy ?? "downloads",
|
|
334
|
+
limit: options.limit ? Number.parseInt(options.limit, 10) : 20,
|
|
335
|
+
format: options.format ?? "table",
|
|
336
|
+
};
|
|
337
|
+
const packages = await marketplace.search(listOptions);
|
|
338
|
+
if (packages.length === 0) {
|
|
339
|
+
logger.stop("No packages found matching your criteria.");
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const output = listOptions.format === "json" ? marketplace.formatJson(packages) : marketplace.formatTable(packages);
|
|
343
|
+
logger.stop(`Found ${packages.length} package(s):\n\n${output}`);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
logger.error(error.message);
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { program } from "../../services/commander.js";
|
|
3
|
+
import { migrateNode } from "./node.js";
|
|
4
|
+
const migrate = new Command("migrate").description("Migrate nodes and workflows to newer patterns");
|
|
5
|
+
const node = new Command("node")
|
|
6
|
+
.description("Migrate a class-based node to function-first pattern")
|
|
7
|
+
.option("-p, --path <value>", "Path to the node file to migrate")
|
|
8
|
+
.option("--backup", "Create backup before migration (default in non-interactive mode)")
|
|
9
|
+
.option("--no-backup", "Skip backup creation")
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
await migrateNode(options);
|
|
12
|
+
});
|
|
13
|
+
migrate.addCommand(node);
|
|
14
|
+
program.addCommand(migrate);
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
|
+
import fsExtra from "fs-extra";
|
|
4
|
+
import color from "picocolors";
|
|
5
|
+
import { isNonInteractive } from "../../services/non-interactive.js";
|
|
6
|
+
export async function migrateNode(opts) {
|
|
7
|
+
console.log(color.cyan("\n🔄 Node Migration Tool"));
|
|
8
|
+
console.log(color.dim("Converts class-based nodes to function-first pattern\n"));
|
|
9
|
+
const nodePath = opts.path;
|
|
10
|
+
if (!nodePath) {
|
|
11
|
+
p.cancel("Error: Please provide a node path using --path");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const s = p.spinner();
|
|
15
|
+
s.start("Analyzing node...");
|
|
16
|
+
try {
|
|
17
|
+
const absolutePath = path.isAbsolute(nodePath) ? nodePath : path.resolve(process.cwd(), nodePath);
|
|
18
|
+
if (!fsExtra.existsSync(absolutePath)) {
|
|
19
|
+
s.stop("Error: File not found");
|
|
20
|
+
console.log(color.red(`\n❌ File not found: ${absolutePath}`));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const fileContent = fsExtra.readFileSync(absolutePath, "utf8");
|
|
24
|
+
if (fileContent.includes("defineNode")) {
|
|
25
|
+
s.stop("Already using function-first pattern");
|
|
26
|
+
console.log(color.yellow("\n⚠️ This node is already using the function-first pattern!"));
|
|
27
|
+
console.log(color.dim("No migration needed.\n"));
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
if (!fileContent.includes("extends BlokService")) {
|
|
31
|
+
s.stop("Not a class-based node");
|
|
32
|
+
console.log(color.red("\n❌ This doesn't appear to be a class-based BlokService node."));
|
|
33
|
+
console.log(color.dim("Migration is only supported for nodes extending BlokService.\n"));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
s.stop("Node analyzed");
|
|
37
|
+
console.log(color.green("\n✅ This is a class-based node that can be migrated!"));
|
|
38
|
+
console.log(`\n${color.bold("Migration Steps:")}\n`);
|
|
39
|
+
console.log(`1. ${color.cyan("Backup your current file:")}`);
|
|
40
|
+
console.log(color.dim(` cp ${nodePath} ${nodePath}.backup`));
|
|
41
|
+
console.log(`\n2. ${color.cyan("Follow the migration guide:")}`);
|
|
42
|
+
console.log(color.dim(" https://github.com/yourrepo/blok/blob/main/MIGRATION_GUIDE.md"));
|
|
43
|
+
console.log(`\n3. ${color.cyan("Key changes needed:")}`);
|
|
44
|
+
console.log(color.dim(" • Replace class with defineNode()"));
|
|
45
|
+
console.log(color.dim(" • Convert JSON Schema to Zod schemas"));
|
|
46
|
+
console.log(color.dim(" • Move handle() logic to execute()"));
|
|
47
|
+
console.log(color.dim(" • Remove BlokResponse boilerplate"));
|
|
48
|
+
console.log(color.dim(" • Return plain objects instead of response.setSuccess()"));
|
|
49
|
+
console.log(`\n4. ${color.cyan("Example transformation:")}`);
|
|
50
|
+
console.log(color.dim("\n Before (class-based):"));
|
|
51
|
+
console.log(color.dim(`
|
|
52
|
+
export default class MyNode extends BlokService<InputType> {
|
|
53
|
+
async handle(ctx: Context, inputs: InputType) {
|
|
54
|
+
const response = new BlokResponse();
|
|
55
|
+
try {
|
|
56
|
+
const result = await doSomething(inputs.value);
|
|
57
|
+
response.setSuccess({ result });
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const nodeError = new GlobalError(error.message);
|
|
60
|
+
response.setError(nodeError);
|
|
61
|
+
}
|
|
62
|
+
return response;
|
|
63
|
+
}
|
|
64
|
+
}`));
|
|
65
|
+
console.log(color.dim("\n After (function-first):"));
|
|
66
|
+
console.log(color.dim(`
|
|
67
|
+
export default defineNode({
|
|
68
|
+
name: "my-node",
|
|
69
|
+
input: z.object({ value: z.string() }),
|
|
70
|
+
output: z.object({ result: z.string() }),
|
|
71
|
+
async execute(ctx, input) {
|
|
72
|
+
const result = await doSomething(input.value);
|
|
73
|
+
return { result };
|
|
74
|
+
}
|
|
75
|
+
});`));
|
|
76
|
+
console.log(`\n5. ${color.cyan("Test your migrated node:")}`);
|
|
77
|
+
console.log(color.dim(" • Run existing tests to verify behavior"));
|
|
78
|
+
console.log(color.dim(" • Check that input/output validation works"));
|
|
79
|
+
console.log(color.dim(" • Verify error handling is correct"));
|
|
80
|
+
console.log(`\n6. ${color.cyan("Resources:")}`);
|
|
81
|
+
console.log(color.dim(" • Full Migration Guide: MIGRATION_GUIDE.md"));
|
|
82
|
+
console.log(color.dim(" • defineNode API Docs: core/runner/FUNCTION_FIRST_NODES.md"));
|
|
83
|
+
console.log(color.dim(" • Example Nodes: core/runner/examples/function-first/"));
|
|
84
|
+
let shouldBackup;
|
|
85
|
+
if (opts.backup !== undefined) {
|
|
86
|
+
shouldBackup = opts.backup;
|
|
87
|
+
}
|
|
88
|
+
else if (isNonInteractive()) {
|
|
89
|
+
shouldBackup = true;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
shouldBackup = await p.confirm({
|
|
93
|
+
message: "Would you like to create a backup of this file now?",
|
|
94
|
+
initialValue: true,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (shouldBackup) {
|
|
98
|
+
const backupPath = `${absolutePath}.backup`;
|
|
99
|
+
fsExtra.copyFileSync(absolutePath, backupPath);
|
|
100
|
+
console.log(color.green(`\n✅ Backup created: ${backupPath}`));
|
|
101
|
+
}
|
|
102
|
+
console.log(color.cyan("\n💡 Tip:") +
|
|
103
|
+
color.dim(" Start by migrating one node as a reference, then use it as a template for others.\n"));
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
s.stop("Error occurred");
|
|
107
|
+
console.log(color.red(`\n❌ Error: ${error.message}\n`));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { program, trackCommandExecution } from "../../services/commander.js";
|
|
2
|
+
import { tokenManager } from "../../services/local-token-manager.js";
|
|
3
|
+
import { runMonitor } from "./monitor-component.js";
|
|
4
|
+
import startWebMonitorUI from "./static-web-server.js";
|
|
5
|
+
program
|
|
6
|
+
.command("monitor")
|
|
7
|
+
.description("Monitor the performance of your workflows")
|
|
8
|
+
.option("--web", "Open the metrics dashboard in your browser")
|
|
9
|
+
.option("--host <host>", "Remote prometheus host")
|
|
10
|
+
.option("--token <token>", "Remote prometheus token")
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
await trackCommandExecution({
|
|
13
|
+
command: "monitor",
|
|
14
|
+
args: options,
|
|
15
|
+
execution: async () => {
|
|
16
|
+
let token = tokenManager.getToken();
|
|
17
|
+
if (!token) {
|
|
18
|
+
token = options.token;
|
|
19
|
+
}
|
|
20
|
+
if (options.web) {
|
|
21
|
+
await startWebMonitorUI(options.host || "http://localhost:9090", token);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
runMonitor(options.host, token);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const runMonitor: (host?: string, token?: string) => void;
|