blokctl 0.6.18 → 0.6.20
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/create/project.d.ts +3 -0
- package/dist/commands/create/project.js +94 -53
- package/dist/commands/create/utils/Examples.d.ts +3 -3
- package/dist/commands/create/utils/Examples.js +994 -323
- package/dist/commands/dev/index.js +7 -1
- package/dist/commands/runtime/add.d.ts +2 -0
- package/dist/commands/runtime/add.js +143 -0
- package/dist/commands/runtime/index.d.ts +1 -0
- package/dist/commands/runtime/index.js +43 -0
- package/dist/commands/runtime/list.d.ts +2 -0
- package/dist/commands/runtime/list.js +60 -0
- package/dist/commands/runtime/remove.d.ts +2 -0
- package/dist/commands/runtime/remove.js +114 -0
- package/dist/commands/runtime/shared.d.ts +22 -0
- package/dist/commands/runtime/shared.js +164 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/services/runtime-mutations.d.ts +7 -0
- package/dist/services/runtime-mutations.js +67 -0
- package/package.json +2 -2
- package/dist/commands/marketplace/runtime.d.ts +0 -54
- package/dist/commands/marketplace/runtime.js +0 -350
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { generateRuntimeEnvVars, generateSupervisordConfig, } from "./runtime-setup.js";
|
|
2
|
+
const RUNTIME_ENV_HEADER = "# Runtimes (auto-configured by blokctl)";
|
|
3
|
+
export function runtimeEnvKey(kind) {
|
|
4
|
+
return kind === "csharp" ? "CSHARP" : kind.toUpperCase();
|
|
5
|
+
}
|
|
6
|
+
export function withRuntime(config, rc) {
|
|
7
|
+
return { ...config, runtimes: { ...(config.runtimes ?? {}), [rc.kind]: rc } };
|
|
8
|
+
}
|
|
9
|
+
export function withoutRuntime(config, kind) {
|
|
10
|
+
if (!config.runtimes || !(kind in config.runtimes))
|
|
11
|
+
return config;
|
|
12
|
+
const runtimes = Object.fromEntries(Object.entries(config.runtimes).filter(([k]) => k !== kind));
|
|
13
|
+
return { ...config, runtimes: Object.keys(runtimes).length === 0 ? undefined : runtimes };
|
|
14
|
+
}
|
|
15
|
+
export function rewriteRuntimeEnvBlock(envContent, runtimes) {
|
|
16
|
+
const isManaged = (line) => {
|
|
17
|
+
const t = line.trimStart();
|
|
18
|
+
return (line.trim() === RUNTIME_ENV_HEADER ||
|
|
19
|
+
/^RUNTIME_[A-Z0-9]+_(HOST|PORT|GRPC_PORT)=/.test(t) ||
|
|
20
|
+
/^BLOK_TRANSPORT=/.test(t));
|
|
21
|
+
};
|
|
22
|
+
const kept = envContent
|
|
23
|
+
.split("\n")
|
|
24
|
+
.filter((line) => !isManaged(line))
|
|
25
|
+
.join("\n")
|
|
26
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
27
|
+
.replace(/\n+$/, "");
|
|
28
|
+
const block = generateRuntimeEnvVars(runtimes);
|
|
29
|
+
if (!block)
|
|
30
|
+
return kept.length > 0 ? `${kept}\n` : "";
|
|
31
|
+
return `${kept}\n${block}\n`;
|
|
32
|
+
}
|
|
33
|
+
export function rewriteSupervisordRuntimes(supervisordContent, runtimes) {
|
|
34
|
+
const out = [];
|
|
35
|
+
let skipping = false;
|
|
36
|
+
for (const line of supervisordContent.split("\n")) {
|
|
37
|
+
const t = line.trimStart();
|
|
38
|
+
if (/^\[program:[\w-]+_runtime\]/.test(t)) {
|
|
39
|
+
skipping = true;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (/^\[/.test(t))
|
|
43
|
+
skipping = false;
|
|
44
|
+
if (!skipping)
|
|
45
|
+
out.push(line);
|
|
46
|
+
}
|
|
47
|
+
const kept = out.join("\n").replace(/\n+$/, "");
|
|
48
|
+
const block = generateSupervisordConfig(runtimes);
|
|
49
|
+
return block ? `${kept}\n${block}\n` : `${kept}\n`;
|
|
50
|
+
}
|
|
51
|
+
const RUNTIME_GITIGNORE_GLOBS = [
|
|
52
|
+
".blok/runtimes/**/bin/",
|
|
53
|
+
".blok/runtimes/**/obj/",
|
|
54
|
+
".blok/runtimes/**/target/",
|
|
55
|
+
".blok/runtimes/**/__pycache__/",
|
|
56
|
+
".blok/runtimes/**/python3_runtime/",
|
|
57
|
+
".blok/runtimes/**/vendor/",
|
|
58
|
+
];
|
|
59
|
+
export function ensureRuntimeGitignore(gitignoreContent) {
|
|
60
|
+
if (/^\.blok\/\s*$/m.test(gitignoreContent))
|
|
61
|
+
return gitignoreContent;
|
|
62
|
+
const missing = RUNTIME_GITIGNORE_GLOBS.filter((glob) => !gitignoreContent.includes(glob));
|
|
63
|
+
if (missing.length === 0)
|
|
64
|
+
return gitignoreContent;
|
|
65
|
+
const base = gitignoreContent.replace(/\n+$/, "");
|
|
66
|
+
return `${base}\n\n# Blok runtime build artifacts (managed by blokctl)\n${missing.join("\n")}\n`;
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blokctl",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.20",
|
|
4
4
|
"author": "Deskree Technologies Inc.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "cli for blok",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"keywords": ["blokctl", "cli", "blok", "blok"],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@ai-sdk/openai": "^1.3.22",
|
|
33
|
-
"@blokjs/runner": "^0.6.
|
|
33
|
+
"@blokjs/runner": "^0.6.20",
|
|
34
34
|
"@clack/prompts": "^1.0.0",
|
|
35
35
|
"ai": "^4.3.16",
|
|
36
36
|
"better-sqlite3": "^12.6.2",
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { CatalogStats, RuntimeKind, RuntimePackageManifest } from "@blokjs/runner";
|
|
2
|
-
import { Command } from "../../services/commander.js";
|
|
3
|
-
export interface MarketplaceConfig {
|
|
4
|
-
registryUrl: string;
|
|
5
|
-
cacheDir: string;
|
|
6
|
-
cacheTtlMs: number;
|
|
7
|
-
}
|
|
8
|
-
export interface MarketplaceListOptions {
|
|
9
|
-
runtime?: RuntimeKind;
|
|
10
|
-
query?: string;
|
|
11
|
-
tags?: string[];
|
|
12
|
-
verified?: boolean;
|
|
13
|
-
sortBy?: "name" | "downloads" | "rating" | "updated";
|
|
14
|
-
limit?: number;
|
|
15
|
-
format?: "table" | "json";
|
|
16
|
-
}
|
|
17
|
-
export interface MarketplaceInstallOptions {
|
|
18
|
-
name: string;
|
|
19
|
-
version?: string;
|
|
20
|
-
runtime?: RuntimeKind;
|
|
21
|
-
force?: boolean;
|
|
22
|
-
}
|
|
23
|
-
export interface MarketplacePublishOptions {
|
|
24
|
-
manifestPath: string;
|
|
25
|
-
dockerImage: string;
|
|
26
|
-
runtime: RuntimeKind;
|
|
27
|
-
tags?: string[];
|
|
28
|
-
description?: string;
|
|
29
|
-
}
|
|
30
|
-
export declare class RuntimeMarketplaceCommand {
|
|
31
|
-
private config;
|
|
32
|
-
constructor(config?: Partial<MarketplaceConfig>);
|
|
33
|
-
search(options: MarketplaceListOptions): Promise<RuntimePackageManifest[]>;
|
|
34
|
-
install(options: MarketplaceInstallOptions): Promise<{
|
|
35
|
-
success: boolean;
|
|
36
|
-
message: string;
|
|
37
|
-
}>;
|
|
38
|
-
publish(options: MarketplacePublishOptions): Promise<{
|
|
39
|
-
success: boolean;
|
|
40
|
-
packageId: string;
|
|
41
|
-
version: string;
|
|
42
|
-
}>;
|
|
43
|
-
info(name: string, version?: string): Promise<RuntimePackageManifest | null>;
|
|
44
|
-
stats(): Promise<CatalogStats>;
|
|
45
|
-
list(runtime: RuntimeKind): Promise<RuntimePackageManifest[]>;
|
|
46
|
-
formatTable(packages: RuntimePackageManifest[]): string;
|
|
47
|
-
formatJson(packages: RuntimePackageManifest[]): string;
|
|
48
|
-
private getCacheKey;
|
|
49
|
-
private readCache;
|
|
50
|
-
private writeCache;
|
|
51
|
-
private fetchFromApi;
|
|
52
|
-
}
|
|
53
|
-
declare const _default: Command;
|
|
54
|
-
export default _default;
|
|
@@ -1,350 +0,0 @@
|
|
|
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
|
-
});
|