@larkup/cli 0.2.4 → 0.2.5
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/chunk-TH2MPRMA.js +412 -0
- package/dist/index.js +84 -439
- package/dist/tool-loader-B2J2R64F.js +14 -0
- package/package.json +3 -3
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getToolById,
|
|
3
|
+
invalidateRegistryCache
|
|
4
|
+
} from "./chunk-P5JVXA7X.js";
|
|
5
|
+
import {
|
|
6
|
+
__require
|
|
7
|
+
} from "./chunk-3RG5ZIWI.js";
|
|
8
|
+
|
|
9
|
+
// ../../packages/marketplace/src/tool-loader.ts
|
|
10
|
+
import { promises as fs2 } from "fs";
|
|
11
|
+
import path2 from "path";
|
|
12
|
+
import { pathToFileURL } from "url";
|
|
13
|
+
|
|
14
|
+
// ../../packages/marketplace/src/tool-installer.ts
|
|
15
|
+
import { promises as fs } from "fs";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { exec as execCb } from "child_process";
|
|
18
|
+
import { promisify } from "util";
|
|
19
|
+
var execAsync = promisify(execCb);
|
|
20
|
+
function getToolsDir() {
|
|
21
|
+
return path.join(process.cwd(), ".larkup", "tools");
|
|
22
|
+
}
|
|
23
|
+
function getManifestPath() {
|
|
24
|
+
return path.join(getToolsDir(), "installed.json");
|
|
25
|
+
}
|
|
26
|
+
async function ensureDir() {
|
|
27
|
+
await fs.mkdir(getToolsDir(), { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
async function readManifest() {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await fs.readFile(getManifestPath(), "utf8");
|
|
32
|
+
const parsed = JSON.parse(raw);
|
|
33
|
+
if (!parsed.downloadCounts) parsed.downloadCounts = {};
|
|
34
|
+
for (const tool of parsed.tools ?? []) {
|
|
35
|
+
if (tool.packagePath && !tool.packageName) {
|
|
36
|
+
tool.packageName = tool.packagePath;
|
|
37
|
+
delete tool.packagePath;
|
|
38
|
+
}
|
|
39
|
+
if (!tool.source) tool.source = "local";
|
|
40
|
+
if (!tool.resolvedPath) {
|
|
41
|
+
tool.resolvedPath = tool.packageName;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return parsed;
|
|
45
|
+
} catch {
|
|
46
|
+
return { tools: [], downloadCounts: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function writeManifest(manifest) {
|
|
50
|
+
await ensureDir();
|
|
51
|
+
manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
52
|
+
await fs.writeFile(getManifestPath(), JSON.stringify(manifest, null, 2), "utf8");
|
|
53
|
+
}
|
|
54
|
+
async function getInstalledTools() {
|
|
55
|
+
const manifest = await readManifest();
|
|
56
|
+
const tools = [...manifest.tools];
|
|
57
|
+
const bundledIds = (process.env.LARKUP_BUNDLED_TOOLS ?? "").split(",").map((id) => id.trim()).filter(Boolean);
|
|
58
|
+
for (const toolId of bundledIds) {
|
|
59
|
+
if (tools.some((tool) => tool.id === toolId)) continue;
|
|
60
|
+
const descriptor = await getToolById(toolId);
|
|
61
|
+
if (!descriptor) continue;
|
|
62
|
+
tools.push({
|
|
63
|
+
id: toolId,
|
|
64
|
+
version: descriptor.version,
|
|
65
|
+
installedAt: "bundled",
|
|
66
|
+
packageName: descriptor.packageName,
|
|
67
|
+
resolvedPath: descriptor.packageName,
|
|
68
|
+
source: "local",
|
|
69
|
+
config: buildDefaultConfig(descriptor)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return tools;
|
|
73
|
+
}
|
|
74
|
+
async function isToolInstalled(toolId) {
|
|
75
|
+
const tools = await getInstalledTools();
|
|
76
|
+
return tools.some((t) => t.id === toolId);
|
|
77
|
+
}
|
|
78
|
+
async function getInstalledTool(toolId) {
|
|
79
|
+
const tools = await getInstalledTools();
|
|
80
|
+
return tools.find((t) => t.id === toolId);
|
|
81
|
+
}
|
|
82
|
+
async function checkSystemDeps(toolId) {
|
|
83
|
+
const descriptor = await getToolById(toolId);
|
|
84
|
+
if (!descriptor?.systemDeps?.length) return [];
|
|
85
|
+
const target = detectDeploymentTarget();
|
|
86
|
+
const missing = [];
|
|
87
|
+
for (const dep of descriptor.systemDeps) {
|
|
88
|
+
if (target === "docker" && dep === "docker") continue;
|
|
89
|
+
try {
|
|
90
|
+
await execAsync(`which ${dep}`);
|
|
91
|
+
} catch {
|
|
92
|
+
missing.push(dep);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return missing;
|
|
96
|
+
}
|
|
97
|
+
function detectDeploymentTarget() {
|
|
98
|
+
const explicit = process.env.LARKUP_DEPLOYMENT_TARGET;
|
|
99
|
+
if (explicit && ["local", "docker", "serverless", "sandbox"].includes(explicit)) {
|
|
100
|
+
return explicit;
|
|
101
|
+
}
|
|
102
|
+
if (process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME) return "serverless";
|
|
103
|
+
if (process.env.DOCKER_CONTAINER || isDockerEnvironment()) return "docker";
|
|
104
|
+
if (process.env.E2B_SANDBOX_ID || process.env.MODAL_TASK_ID) return "sandbox";
|
|
105
|
+
return "local";
|
|
106
|
+
}
|
|
107
|
+
function isDockerEnvironment() {
|
|
108
|
+
try {
|
|
109
|
+
const fs3 = __require("fs");
|
|
110
|
+
return fs3.existsSync("/.dockerenv");
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function ensureToolsPackageJson() {
|
|
116
|
+
const toolsDir = getToolsDir();
|
|
117
|
+
const pkgPath = path.join(toolsDir, "package.json");
|
|
118
|
+
try {
|
|
119
|
+
await fs.access(pkgPath);
|
|
120
|
+
} catch {
|
|
121
|
+
await ensureDir();
|
|
122
|
+
await fs.writeFile(
|
|
123
|
+
pkgPath,
|
|
124
|
+
JSON.stringify(
|
|
125
|
+
{
|
|
126
|
+
name: "larkup-tools",
|
|
127
|
+
version: "1.0.0",
|
|
128
|
+
private: true,
|
|
129
|
+
description: "Isolated directory for installed Larkup marketplace tools."
|
|
130
|
+
},
|
|
131
|
+
null,
|
|
132
|
+
2
|
|
133
|
+
),
|
|
134
|
+
"utf8"
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function execInstall(packageName, version, target, onProgress) {
|
|
139
|
+
const toolsDir = getToolsDir();
|
|
140
|
+
switch (target) {
|
|
141
|
+
case "local":
|
|
142
|
+
case "docker": {
|
|
143
|
+
await ensureToolsPackageJson();
|
|
144
|
+
const install = async (specifier) => {
|
|
145
|
+
const installCmd = `npm install ${specifier} --prefix "${toolsDir}" --save --no-audit --no-fund`;
|
|
146
|
+
onProgress?.(`Running: npm install ${specifier}`);
|
|
147
|
+
const { stderr } = await execAsync(installCmd, {
|
|
148
|
+
cwd: toolsDir,
|
|
149
|
+
timeout: 12e4,
|
|
150
|
+
// 2 minute timeout
|
|
151
|
+
env: { ...process.env, NODE_ENV: "production" }
|
|
152
|
+
});
|
|
153
|
+
if (stderr && !stderr.includes("npm warn")) {
|
|
154
|
+
console.warn(`[marketplace] Install stderr: ${stderr}`);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
try {
|
|
158
|
+
await install(`${packageName}@${version}`);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
const message = err instanceof Error ? err.message : "Install command failed";
|
|
161
|
+
if (!message.includes("ETARGET") && !message.includes("No matching version found")) {
|
|
162
|
+
throw new Error(`Failed to install ${packageName}: ${message}`);
|
|
163
|
+
}
|
|
164
|
+
onProgress?.(
|
|
165
|
+
`Catalog version ${version} is unavailable; installing the latest published version\u2026`
|
|
166
|
+
);
|
|
167
|
+
try {
|
|
168
|
+
await install(`${packageName}@latest`);
|
|
169
|
+
} catch (latestErr) {
|
|
170
|
+
const latestMessage = latestErr instanceof Error ? latestErr.message : "Install command failed";
|
|
171
|
+
throw new Error(`Failed to install ${packageName}: ${latestMessage}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const resolvedPath = path.join(toolsDir, "node_modules", packageName);
|
|
175
|
+
try {
|
|
176
|
+
await fs.access(resolvedPath);
|
|
177
|
+
} catch {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Package ${packageName} was installed but could not be found at ${resolvedPath}`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const packageJson = JSON.parse(
|
|
183
|
+
await fs.readFile(path.join(resolvedPath, "package.json"), "utf8")
|
|
184
|
+
);
|
|
185
|
+
return { resolvedPath, version: packageJson.version ?? version };
|
|
186
|
+
}
|
|
187
|
+
case "serverless": {
|
|
188
|
+
onProgress?.("Serverless environment detected \u2014 queuing for next deploy");
|
|
189
|
+
const pendingPath = path.join(toolsDir, "pending-tools.json");
|
|
190
|
+
let pending = { tools: [] };
|
|
191
|
+
try {
|
|
192
|
+
const raw = await fs.readFile(pendingPath, "utf8");
|
|
193
|
+
pending = JSON.parse(raw);
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
pending.tools.push({ packageName, version });
|
|
197
|
+
await ensureDir();
|
|
198
|
+
await fs.writeFile(pendingPath, JSON.stringify(pending, null, 2), "utf8");
|
|
199
|
+
return { resolvedPath: path.join(toolsDir, "node_modules", packageName), version };
|
|
200
|
+
}
|
|
201
|
+
case "sandbox": {
|
|
202
|
+
onProgress?.("Installing in sandbox environment");
|
|
203
|
+
await ensureToolsPackageJson();
|
|
204
|
+
const installCmd = `npm install ${packageName}@${version} --prefix "${toolsDir}" --save --no-audit --no-fund`;
|
|
205
|
+
await execAsync(installCmd, { cwd: toolsDir, timeout: 12e4 });
|
|
206
|
+
return { resolvedPath: path.join(toolsDir, "node_modules", packageName), version };
|
|
207
|
+
}
|
|
208
|
+
default:
|
|
209
|
+
throw new Error(`Unsupported deployment target: ${target}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function resolveManifest(toolId) {
|
|
213
|
+
const descriptor = await getToolById(toolId);
|
|
214
|
+
if (descriptor) return descriptor;
|
|
215
|
+
throw new Error(`Unknown tool: ${toolId}. Not found in local registry or Hub.`);
|
|
216
|
+
}
|
|
217
|
+
async function isWorkspaceTool(packageName) {
|
|
218
|
+
try {
|
|
219
|
+
const { stdout } = await execAsync(`pnpm ls -r --depth -1 --json 2>/dev/null || true`, {
|
|
220
|
+
cwd: process.cwd(),
|
|
221
|
+
timeout: 1e4
|
|
222
|
+
});
|
|
223
|
+
const data = JSON.parse(stdout || "[]");
|
|
224
|
+
return Array.isArray(data) && data.some((pkg) => pkg.name === packageName);
|
|
225
|
+
} catch {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function installTool(toolId, onProgress) {
|
|
230
|
+
const report = (stage, percent, message) => {
|
|
231
|
+
onProgress?.({ toolId, stage, percent, message });
|
|
232
|
+
};
|
|
233
|
+
try {
|
|
234
|
+
report("checking-deps", 5, "Resolving tool manifest\u2026");
|
|
235
|
+
const descriptor = await resolveManifest(toolId);
|
|
236
|
+
if (descriptor.comingSoon) {
|
|
237
|
+
throw new Error(`${descriptor.name} is coming soon.`);
|
|
238
|
+
}
|
|
239
|
+
report("checking-deps", 15, "Checking system dependencies\u2026");
|
|
240
|
+
const missing = await checkSystemDeps(toolId);
|
|
241
|
+
if (missing.length > 0) {
|
|
242
|
+
const msg = `Missing system dependencies: ${missing.join(", ")}. Please install them first.`;
|
|
243
|
+
report("failed", 0, msg);
|
|
244
|
+
throw new Error(msg);
|
|
245
|
+
}
|
|
246
|
+
const isWorkspace = await isWorkspaceTool(descriptor.packageName);
|
|
247
|
+
const target = detectDeploymentTarget();
|
|
248
|
+
let resolvedPath;
|
|
249
|
+
let installedVersion = descriptor.version;
|
|
250
|
+
let source;
|
|
251
|
+
if (isWorkspace) {
|
|
252
|
+
report("downloading", 30, "Resolving workspace package\u2026");
|
|
253
|
+
try {
|
|
254
|
+
const modulePath = __require.resolve(descriptor.packageName, {
|
|
255
|
+
paths: [process.cwd()]
|
|
256
|
+
});
|
|
257
|
+
resolvedPath = path.dirname(modulePath);
|
|
258
|
+
} catch {
|
|
259
|
+
resolvedPath = path.join(process.cwd(), "node_modules", descriptor.packageName);
|
|
260
|
+
}
|
|
261
|
+
source = "local";
|
|
262
|
+
report("installing", 60, "Workspace package resolved.");
|
|
263
|
+
} else {
|
|
264
|
+
report("downloading", 30, `Downloading ${descriptor.packageName}@${descriptor.version}\u2026`);
|
|
265
|
+
const installed = await execInstall(
|
|
266
|
+
descriptor.packageName,
|
|
267
|
+
descriptor.version,
|
|
268
|
+
target,
|
|
269
|
+
(msg) => report("downloading", 50, msg)
|
|
270
|
+
);
|
|
271
|
+
resolvedPath = installed.resolvedPath;
|
|
272
|
+
installedVersion = installed.version;
|
|
273
|
+
source = target === "sandbox" ? "sandbox" : "registry";
|
|
274
|
+
report("installing", 70, "Package installed.");
|
|
275
|
+
}
|
|
276
|
+
report("installing", 80, "Registering tool\u2026");
|
|
277
|
+
await ensureDir();
|
|
278
|
+
const manifest = await readManifest();
|
|
279
|
+
const existing = manifest.tools.findIndex((t) => t.id === toolId);
|
|
280
|
+
const entry = {
|
|
281
|
+
id: toolId,
|
|
282
|
+
version: installedVersion,
|
|
283
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
284
|
+
packageName: descriptor.packageName,
|
|
285
|
+
resolvedPath,
|
|
286
|
+
source,
|
|
287
|
+
config: buildDefaultConfig(descriptor)
|
|
288
|
+
};
|
|
289
|
+
if (existing >= 0) {
|
|
290
|
+
manifest.tools[existing] = entry;
|
|
291
|
+
} else {
|
|
292
|
+
manifest.tools.push(entry);
|
|
293
|
+
}
|
|
294
|
+
manifest.downloadCounts[toolId] = (manifest.downloadCounts[toolId] ?? 0) + 1;
|
|
295
|
+
await writeManifest(manifest);
|
|
296
|
+
notifyHubInstall(toolId).catch(() => {
|
|
297
|
+
});
|
|
298
|
+
invalidateRegistryCache();
|
|
299
|
+
report("completed", 100, "Installed successfully.");
|
|
300
|
+
} catch (err) {
|
|
301
|
+
const message = err instanceof Error ? err.message : "Installation failed.";
|
|
302
|
+
report("failed", 0, message);
|
|
303
|
+
throw err;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function uninstallTool(toolId) {
|
|
307
|
+
const manifest = await readManifest();
|
|
308
|
+
const tool = manifest.tools.find((t) => t.id === toolId);
|
|
309
|
+
manifest.tools = manifest.tools.filter((t) => t.id !== toolId);
|
|
310
|
+
await writeManifest(manifest);
|
|
311
|
+
if (tool?.source === "registry" && tool.packageName) {
|
|
312
|
+
try {
|
|
313
|
+
const toolsDir = getToolsDir();
|
|
314
|
+
await execAsync(`npm uninstall ${tool.packageName} --prefix "${toolsDir}"`, {
|
|
315
|
+
cwd: toolsDir,
|
|
316
|
+
timeout: 3e4
|
|
317
|
+
});
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
invalidateRegistryCache();
|
|
322
|
+
const { unloadTool: unloadTool2 } = await import("./tool-loader-B2J2R64F.js");
|
|
323
|
+
unloadTool2(toolId);
|
|
324
|
+
}
|
|
325
|
+
async function notifyHubInstall(toolId) {
|
|
326
|
+
const hubUrl = process.env.LARKUP_HUB_URL ?? "https://hub.larkup.de";
|
|
327
|
+
try {
|
|
328
|
+
const controller = new AbortController();
|
|
329
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
330
|
+
await fetch(`${hubUrl}/v1/tools/${toolId}/installed`, {
|
|
331
|
+
method: "POST",
|
|
332
|
+
signal: controller.signal,
|
|
333
|
+
headers: { "Content-Type": "application/json" }
|
|
334
|
+
});
|
|
335
|
+
clearTimeout(timeout);
|
|
336
|
+
} catch {
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function buildDefaultConfig(descriptor) {
|
|
340
|
+
const config = {};
|
|
341
|
+
for (const field of descriptor.configSchema ?? []) {
|
|
342
|
+
if (field.defaultValue !== void 0) {
|
|
343
|
+
config[field.key] = field.defaultValue;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return config;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ../../packages/marketplace/src/tool-loader.ts
|
|
350
|
+
var moduleCache = /* @__PURE__ */ new Map();
|
|
351
|
+
async function loadTool(toolId) {
|
|
352
|
+
if (moduleCache.has(toolId)) return moduleCache.get(toolId);
|
|
353
|
+
const installed = await getInstalledTool(toolId);
|
|
354
|
+
if (!installed) return null;
|
|
355
|
+
try {
|
|
356
|
+
const importPath = await resolveImportPath(installed);
|
|
357
|
+
const mod = await import(
|
|
358
|
+
/* webpackIgnore: true */
|
|
359
|
+
importPath
|
|
360
|
+
);
|
|
361
|
+
moduleCache.set(toolId, mod);
|
|
362
|
+
return mod;
|
|
363
|
+
} catch (err) {
|
|
364
|
+
console.error(`[marketplace] Failed to load tool "${toolId}":`, err);
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function resolveImportPath(installed) {
|
|
369
|
+
switch (installed.source) {
|
|
370
|
+
case "local":
|
|
371
|
+
return installed.packageName;
|
|
372
|
+
case "registry":
|
|
373
|
+
case "sandbox":
|
|
374
|
+
return resolvePackageEntry(installed.resolvedPath);
|
|
375
|
+
default:
|
|
376
|
+
return installed.packageName;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async function resolvePackageEntry(packageDir) {
|
|
380
|
+
const manifestPath = path2.join(packageDir, "package.json");
|
|
381
|
+
const manifest = JSON.parse(await fs2.readFile(manifestPath, "utf8"));
|
|
382
|
+
const rootExport = typeof manifest.exports === "object" ? manifest.exports["."] : manifest.exports;
|
|
383
|
+
const entry = (typeof rootExport === "object" ? rootExport.import ?? rootExport.default : rootExport) ?? manifest.main ?? "index.js";
|
|
384
|
+
return pathToFileURL(path2.resolve(packageDir, entry)).href;
|
|
385
|
+
}
|
|
386
|
+
function isToolLoaded(toolId) {
|
|
387
|
+
return moduleCache.has(toolId);
|
|
388
|
+
}
|
|
389
|
+
function unloadTool(toolId) {
|
|
390
|
+
moduleCache.delete(toolId);
|
|
391
|
+
}
|
|
392
|
+
async function hasCapability(capability) {
|
|
393
|
+
const { getToolsWithCapability } = await import("./tool-registry-W3JOGDE3.js");
|
|
394
|
+
const tools = await getToolsWithCapability(capability);
|
|
395
|
+
for (const t of tools) {
|
|
396
|
+
const installed = await getInstalledTool(t.id);
|
|
397
|
+
if (installed) return true;
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export {
|
|
403
|
+
loadTool,
|
|
404
|
+
isToolLoaded,
|
|
405
|
+
unloadTool,
|
|
406
|
+
hasCapability,
|
|
407
|
+
getInstalledTools,
|
|
408
|
+
isToolInstalled,
|
|
409
|
+
getInstalledTool,
|
|
410
|
+
installTool,
|
|
411
|
+
uninstallTool
|
|
412
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -12,10 +12,17 @@ import {
|
|
|
12
12
|
setActiveServer,
|
|
13
13
|
trackUsageEvent
|
|
14
14
|
} from "./chunk-ZJLA4SIG.js";
|
|
15
|
+
import {
|
|
16
|
+
getInstalledTool,
|
|
17
|
+
getInstalledTools,
|
|
18
|
+
installTool,
|
|
19
|
+
isToolInstalled,
|
|
20
|
+
loadTool,
|
|
21
|
+
uninstallTool
|
|
22
|
+
} from "./chunk-TH2MPRMA.js";
|
|
15
23
|
import {
|
|
16
24
|
getAllTools,
|
|
17
|
-
getToolById
|
|
18
|
-
invalidateRegistryCache
|
|
25
|
+
getToolById
|
|
19
26
|
} from "./chunk-P5JVXA7X.js";
|
|
20
27
|
import {
|
|
21
28
|
__require
|
|
@@ -27,7 +34,7 @@ import { Command } from "commander";
|
|
|
27
34
|
// package.json
|
|
28
35
|
var package_default = {
|
|
29
36
|
name: "@larkup/cli",
|
|
30
|
-
version: "0.2.
|
|
37
|
+
version: "0.2.5",
|
|
31
38
|
publishConfig: {
|
|
32
39
|
access: "public"
|
|
33
40
|
},
|
|
@@ -944,12 +951,12 @@ function addChatProviderDependency(dependencies, provider) {
|
|
|
944
951
|
}
|
|
945
952
|
|
|
946
953
|
// ../../packages/core/src/generator/generate-server.ts
|
|
947
|
-
function lang(
|
|
948
|
-
if (
|
|
949
|
-
if (
|
|
950
|
-
if (
|
|
951
|
-
if (
|
|
952
|
-
if (
|
|
954
|
+
function lang(path14) {
|
|
955
|
+
if (path14.endsWith(".json")) return "json";
|
|
956
|
+
if (path14.endsWith(".mjs") || path14.endsWith(".js")) return "javascript";
|
|
957
|
+
if (path14.endsWith(".md")) return "markdown";
|
|
958
|
+
if (path14.endsWith("Dockerfile")) return "dockerfile";
|
|
959
|
+
if (path14.endsWith(".yml") || path14.endsWith(".yaml")) return "yaml";
|
|
953
960
|
return "text";
|
|
954
961
|
}
|
|
955
962
|
function lancedbStore() {
|
|
@@ -2266,22 +2273,22 @@ function generateServer(config) {
|
|
|
2266
2273
|
{ path: "README.md", contents: readme(config, server) }
|
|
2267
2274
|
].map((f) => ({ ...f, language: lang(f.path) }));
|
|
2268
2275
|
try {
|
|
2269
|
-
const
|
|
2270
|
-
const
|
|
2271
|
-
const faviconPath =
|
|
2272
|
-
if (
|
|
2276
|
+
const fs13 = __require("fs");
|
|
2277
|
+
const path14 = __require("path");
|
|
2278
|
+
const faviconPath = path14.resolve(process.cwd(), "public/favicon2.ico");
|
|
2279
|
+
if (fs13.existsSync(faviconPath)) {
|
|
2273
2280
|
files.push({
|
|
2274
2281
|
path: "public/favicon2.ico",
|
|
2275
|
-
contents:
|
|
2282
|
+
contents: fs13.readFileSync(faviconPath).toString("base64"),
|
|
2276
2283
|
language: "ico",
|
|
2277
2284
|
encoding: "base64"
|
|
2278
2285
|
});
|
|
2279
2286
|
} else {
|
|
2280
|
-
const webFaviconPath =
|
|
2281
|
-
if (
|
|
2287
|
+
const webFaviconPath = path14.resolve(process.cwd(), "apps/web/public/favicon2.ico");
|
|
2288
|
+
if (fs13.existsSync(webFaviconPath)) {
|
|
2282
2289
|
files.push({
|
|
2283
2290
|
path: "public/favicon2.ico",
|
|
2284
|
-
contents:
|
|
2291
|
+
contents: fs13.readFileSync(webFaviconPath).toString("base64"),
|
|
2285
2292
|
language: "ico",
|
|
2286
2293
|
encoding: "base64"
|
|
2287
2294
|
});
|
|
@@ -2295,12 +2302,12 @@ function generateServer(config) {
|
|
|
2295
2302
|
}
|
|
2296
2303
|
|
|
2297
2304
|
// ../../packages/core/src/generator/generate-agent-server.ts
|
|
2298
|
-
function lang2(
|
|
2299
|
-
if (
|
|
2300
|
-
if (
|
|
2301
|
-
if (
|
|
2302
|
-
if (
|
|
2303
|
-
if (
|
|
2305
|
+
function lang2(path14) {
|
|
2306
|
+
if (path14.endsWith(".json")) return "json";
|
|
2307
|
+
if (path14.endsWith(".mjs") || path14.endsWith(".js")) return "javascript";
|
|
2308
|
+
if (path14.endsWith(".md")) return "markdown";
|
|
2309
|
+
if (path14.endsWith("Dockerfile")) return "dockerfile";
|
|
2310
|
+
if (path14.endsWith(".yml") || path14.endsWith(".yaml")) return "yaml";
|
|
2304
2311
|
return "text";
|
|
2305
2312
|
}
|
|
2306
2313
|
function lancedbStore2() {
|
|
@@ -3314,22 +3321,22 @@ function generateAgentServer(config) {
|
|
|
3314
3321
|
{ path: "README.md", contents: readme2(config, server) }
|
|
3315
3322
|
].map((f) => ({ ...f, language: lang2(f.path) }));
|
|
3316
3323
|
try {
|
|
3317
|
-
const
|
|
3318
|
-
const
|
|
3319
|
-
const faviconPath =
|
|
3320
|
-
if (
|
|
3324
|
+
const fs13 = __require("fs");
|
|
3325
|
+
const path14 = __require("path");
|
|
3326
|
+
const faviconPath = path14.resolve(process.cwd(), "public/favicon2.ico");
|
|
3327
|
+
if (fs13.existsSync(faviconPath)) {
|
|
3321
3328
|
files.push({
|
|
3322
3329
|
path: "public/favicon2.ico",
|
|
3323
|
-
contents:
|
|
3330
|
+
contents: fs13.readFileSync(faviconPath).toString("base64"),
|
|
3324
3331
|
language: "ico",
|
|
3325
3332
|
encoding: "base64"
|
|
3326
3333
|
});
|
|
3327
3334
|
} else {
|
|
3328
|
-
const webFaviconPath =
|
|
3329
|
-
if (
|
|
3335
|
+
const webFaviconPath = path14.resolve(process.cwd(), "apps/web/public/favicon2.ico");
|
|
3336
|
+
if (fs13.existsSync(webFaviconPath)) {
|
|
3330
3337
|
files.push({
|
|
3331
3338
|
path: "public/favicon2.ico",
|
|
3332
|
-
contents:
|
|
3339
|
+
contents: fs13.readFileSync(webFaviconPath).toString("base64"),
|
|
3333
3340
|
language: "ico",
|
|
3334
3341
|
encoding: "base64"
|
|
3335
3342
|
});
|
|
@@ -4633,8 +4640,8 @@ async function readTextFiles(files) {
|
|
|
4633
4640
|
}
|
|
4634
4641
|
|
|
4635
4642
|
// src/commands/media.ts
|
|
4636
|
-
import { promises as
|
|
4637
|
-
import
|
|
4643
|
+
import { promises as fs9 } from "fs";
|
|
4644
|
+
import path10 from "path";
|
|
4638
4645
|
|
|
4639
4646
|
// ../../packages/core/src/media-store.ts
|
|
4640
4647
|
import { promises as fs7 } from "fs";
|
|
@@ -4776,406 +4783,44 @@ function claimMediaAsset(id) {
|
|
|
4776
4783
|
});
|
|
4777
4784
|
}
|
|
4778
4785
|
|
|
4779
|
-
// ../../packages/marketplace/src/
|
|
4786
|
+
// ../../packages/marketplace/src/storage-provider.ts
|
|
4780
4787
|
import { promises as fs8 } from "fs";
|
|
4781
4788
|
import path9 from "path";
|
|
4782
|
-
import { exec as execCb } from "child_process";
|
|
4783
|
-
import { promisify as promisify2 } from "util";
|
|
4784
|
-
var execAsync2 = promisify2(execCb);
|
|
4785
|
-
function getToolsDir() {
|
|
4786
|
-
return path9.join(process.cwd(), ".larkup", "tools");
|
|
4787
|
-
}
|
|
4788
|
-
function getManifestPath() {
|
|
4789
|
-
return path9.join(getToolsDir(), "installed.json");
|
|
4790
|
-
}
|
|
4791
|
-
async function ensureDir() {
|
|
4792
|
-
await fs8.mkdir(getToolsDir(), { recursive: true });
|
|
4793
|
-
}
|
|
4794
|
-
async function readManifest() {
|
|
4795
|
-
try {
|
|
4796
|
-
const raw = await fs8.readFile(getManifestPath(), "utf8");
|
|
4797
|
-
const parsed = JSON.parse(raw);
|
|
4798
|
-
if (!parsed.downloadCounts) parsed.downloadCounts = {};
|
|
4799
|
-
for (const tool2 of parsed.tools ?? []) {
|
|
4800
|
-
if (tool2.packagePath && !tool2.packageName) {
|
|
4801
|
-
tool2.packageName = tool2.packagePath;
|
|
4802
|
-
delete tool2.packagePath;
|
|
4803
|
-
}
|
|
4804
|
-
if (!tool2.source) tool2.source = "local";
|
|
4805
|
-
if (!tool2.resolvedPath) {
|
|
4806
|
-
tool2.resolvedPath = tool2.packageName;
|
|
4807
|
-
}
|
|
4808
|
-
}
|
|
4809
|
-
return parsed;
|
|
4810
|
-
} catch {
|
|
4811
|
-
return { tools: [], downloadCounts: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4812
|
-
}
|
|
4813
|
-
}
|
|
4814
|
-
async function writeManifest(manifest) {
|
|
4815
|
-
await ensureDir();
|
|
4816
|
-
manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4817
|
-
await fs8.writeFile(getManifestPath(), JSON.stringify(manifest, null, 2), "utf8");
|
|
4818
|
-
}
|
|
4819
|
-
async function getInstalledTools() {
|
|
4820
|
-
const manifest = await readManifest();
|
|
4821
|
-
const tools = [...manifest.tools];
|
|
4822
|
-
const bundledIds = (process.env.LARKUP_BUNDLED_TOOLS ?? "").split(",").map((id) => id.trim()).filter(Boolean);
|
|
4823
|
-
for (const toolId of bundledIds) {
|
|
4824
|
-
if (tools.some((tool2) => tool2.id === toolId)) continue;
|
|
4825
|
-
const descriptor = await getToolById(toolId);
|
|
4826
|
-
if (!descriptor) continue;
|
|
4827
|
-
tools.push({
|
|
4828
|
-
id: toolId,
|
|
4829
|
-
version: descriptor.version,
|
|
4830
|
-
installedAt: "bundled",
|
|
4831
|
-
packageName: descriptor.packageName,
|
|
4832
|
-
resolvedPath: descriptor.packageName,
|
|
4833
|
-
source: "local",
|
|
4834
|
-
config: buildDefaultConfig(descriptor)
|
|
4835
|
-
});
|
|
4836
|
-
}
|
|
4837
|
-
return tools;
|
|
4838
|
-
}
|
|
4839
|
-
async function isToolInstalled(toolId) {
|
|
4840
|
-
const tools = await getInstalledTools();
|
|
4841
|
-
return tools.some((t) => t.id === toolId);
|
|
4842
|
-
}
|
|
4843
|
-
async function getInstalledTool(toolId) {
|
|
4844
|
-
const tools = await getInstalledTools();
|
|
4845
|
-
return tools.find((t) => t.id === toolId);
|
|
4846
|
-
}
|
|
4847
|
-
async function checkSystemDeps(toolId) {
|
|
4848
|
-
const descriptor = await getToolById(toolId);
|
|
4849
|
-
if (!descriptor?.systemDeps?.length) return [];
|
|
4850
|
-
const missing = [];
|
|
4851
|
-
for (const dep of descriptor.systemDeps) {
|
|
4852
|
-
try {
|
|
4853
|
-
await execAsync2(`which ${dep}`);
|
|
4854
|
-
} catch {
|
|
4855
|
-
missing.push(dep);
|
|
4856
|
-
}
|
|
4857
|
-
}
|
|
4858
|
-
return missing;
|
|
4859
|
-
}
|
|
4860
|
-
function detectDeploymentTarget() {
|
|
4861
|
-
const explicit = process.env.LARKUP_DEPLOYMENT_TARGET;
|
|
4862
|
-
if (explicit && ["local", "docker", "serverless", "sandbox"].includes(explicit)) {
|
|
4863
|
-
return explicit;
|
|
4864
|
-
}
|
|
4865
|
-
if (process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME) return "serverless";
|
|
4866
|
-
if (process.env.DOCKER_CONTAINER || isDockerEnvironment()) return "docker";
|
|
4867
|
-
if (process.env.E2B_SANDBOX_ID || process.env.MODAL_TASK_ID) return "sandbox";
|
|
4868
|
-
return "local";
|
|
4869
|
-
}
|
|
4870
|
-
function isDockerEnvironment() {
|
|
4871
|
-
try {
|
|
4872
|
-
const fs14 = __require("fs");
|
|
4873
|
-
return fs14.existsSync("/.dockerenv");
|
|
4874
|
-
} catch {
|
|
4875
|
-
return false;
|
|
4876
|
-
}
|
|
4877
|
-
}
|
|
4878
|
-
async function ensureToolsPackageJson() {
|
|
4879
|
-
const toolsDir = getToolsDir();
|
|
4880
|
-
const pkgPath = path9.join(toolsDir, "package.json");
|
|
4881
|
-
try {
|
|
4882
|
-
await fs8.access(pkgPath);
|
|
4883
|
-
} catch {
|
|
4884
|
-
await ensureDir();
|
|
4885
|
-
await fs8.writeFile(
|
|
4886
|
-
pkgPath,
|
|
4887
|
-
JSON.stringify(
|
|
4888
|
-
{
|
|
4889
|
-
name: "larkup-tools",
|
|
4890
|
-
version: "1.0.0",
|
|
4891
|
-
private: true,
|
|
4892
|
-
description: "Isolated directory for installed Larkup marketplace tools."
|
|
4893
|
-
},
|
|
4894
|
-
null,
|
|
4895
|
-
2
|
|
4896
|
-
),
|
|
4897
|
-
"utf8"
|
|
4898
|
-
);
|
|
4899
|
-
}
|
|
4900
|
-
}
|
|
4901
|
-
async function execInstall(packageName, version, target, onProgress) {
|
|
4902
|
-
const toolsDir = getToolsDir();
|
|
4903
|
-
switch (target) {
|
|
4904
|
-
case "local":
|
|
4905
|
-
case "docker": {
|
|
4906
|
-
await ensureToolsPackageJson();
|
|
4907
|
-
const install = async (specifier) => {
|
|
4908
|
-
const installCmd = `npm install ${specifier} --prefix "${toolsDir}" --save --no-audit --no-fund`;
|
|
4909
|
-
onProgress?.(`Running: npm install ${specifier}`);
|
|
4910
|
-
const { stderr } = await execAsync2(installCmd, {
|
|
4911
|
-
cwd: toolsDir,
|
|
4912
|
-
timeout: 12e4,
|
|
4913
|
-
// 2 minute timeout
|
|
4914
|
-
env: { ...process.env, NODE_ENV: "production" }
|
|
4915
|
-
});
|
|
4916
|
-
if (stderr && !stderr.includes("npm warn")) {
|
|
4917
|
-
console.warn(`[marketplace] Install stderr: ${stderr}`);
|
|
4918
|
-
}
|
|
4919
|
-
};
|
|
4920
|
-
try {
|
|
4921
|
-
await install(`${packageName}@${version}`);
|
|
4922
|
-
} catch (err) {
|
|
4923
|
-
const message = err instanceof Error ? err.message : "Install command failed";
|
|
4924
|
-
if (!message.includes("ETARGET") && !message.includes("No matching version found")) {
|
|
4925
|
-
throw new Error(`Failed to install ${packageName}: ${message}`);
|
|
4926
|
-
}
|
|
4927
|
-
onProgress?.(
|
|
4928
|
-
`Catalog version ${version} is unavailable; installing the latest published version\u2026`
|
|
4929
|
-
);
|
|
4930
|
-
try {
|
|
4931
|
-
await install(`${packageName}@latest`);
|
|
4932
|
-
} catch (latestErr) {
|
|
4933
|
-
const latestMessage = latestErr instanceof Error ? latestErr.message : "Install command failed";
|
|
4934
|
-
throw new Error(`Failed to install ${packageName}: ${latestMessage}`);
|
|
4935
|
-
}
|
|
4936
|
-
}
|
|
4937
|
-
const resolvedPath = path9.join(toolsDir, "node_modules", packageName);
|
|
4938
|
-
try {
|
|
4939
|
-
await fs8.access(resolvedPath);
|
|
4940
|
-
} catch {
|
|
4941
|
-
throw new Error(
|
|
4942
|
-
`Package ${packageName} was installed but could not be found at ${resolvedPath}`
|
|
4943
|
-
);
|
|
4944
|
-
}
|
|
4945
|
-
const packageJson = JSON.parse(
|
|
4946
|
-
await fs8.readFile(path9.join(resolvedPath, "package.json"), "utf8")
|
|
4947
|
-
);
|
|
4948
|
-
return { resolvedPath, version: packageJson.version ?? version };
|
|
4949
|
-
}
|
|
4950
|
-
case "serverless": {
|
|
4951
|
-
onProgress?.("Serverless environment detected \u2014 queuing for next deploy");
|
|
4952
|
-
const pendingPath = path9.join(toolsDir, "pending-tools.json");
|
|
4953
|
-
let pending = { tools: [] };
|
|
4954
|
-
try {
|
|
4955
|
-
const raw = await fs8.readFile(pendingPath, "utf8");
|
|
4956
|
-
pending = JSON.parse(raw);
|
|
4957
|
-
} catch {
|
|
4958
|
-
}
|
|
4959
|
-
pending.tools.push({ packageName, version });
|
|
4960
|
-
await ensureDir();
|
|
4961
|
-
await fs8.writeFile(pendingPath, JSON.stringify(pending, null, 2), "utf8");
|
|
4962
|
-
return { resolvedPath: path9.join(toolsDir, "node_modules", packageName), version };
|
|
4963
|
-
}
|
|
4964
|
-
case "sandbox": {
|
|
4965
|
-
onProgress?.("Installing in sandbox environment");
|
|
4966
|
-
await ensureToolsPackageJson();
|
|
4967
|
-
const installCmd = `npm install ${packageName}@${version} --prefix "${toolsDir}" --save --no-audit --no-fund`;
|
|
4968
|
-
await execAsync2(installCmd, { cwd: toolsDir, timeout: 12e4 });
|
|
4969
|
-
return { resolvedPath: path9.join(toolsDir, "node_modules", packageName), version };
|
|
4970
|
-
}
|
|
4971
|
-
default:
|
|
4972
|
-
throw new Error(`Unsupported deployment target: ${target}`);
|
|
4973
|
-
}
|
|
4974
|
-
}
|
|
4975
|
-
async function resolveManifest(toolId) {
|
|
4976
|
-
const descriptor = await getToolById(toolId);
|
|
4977
|
-
if (descriptor) return descriptor;
|
|
4978
|
-
throw new Error(`Unknown tool: ${toolId}. Not found in local registry or Hub.`);
|
|
4979
|
-
}
|
|
4980
|
-
async function isWorkspaceTool(packageName) {
|
|
4981
|
-
try {
|
|
4982
|
-
const { stdout } = await execAsync2(`pnpm ls -r --depth -1 --json 2>/dev/null || true`, {
|
|
4983
|
-
cwd: process.cwd(),
|
|
4984
|
-
timeout: 1e4
|
|
4985
|
-
});
|
|
4986
|
-
const data = JSON.parse(stdout || "[]");
|
|
4987
|
-
return Array.isArray(data) && data.some((pkg) => pkg.name === packageName);
|
|
4988
|
-
} catch {
|
|
4989
|
-
return false;
|
|
4990
|
-
}
|
|
4991
|
-
}
|
|
4992
|
-
async function installTool(toolId, onProgress) {
|
|
4993
|
-
const report = (stage, percent, message) => {
|
|
4994
|
-
onProgress?.({ toolId, stage, percent, message });
|
|
4995
|
-
};
|
|
4996
|
-
try {
|
|
4997
|
-
report("checking-deps", 5, "Resolving tool manifest\u2026");
|
|
4998
|
-
const descriptor = await resolveManifest(toolId);
|
|
4999
|
-
if (descriptor.comingSoon) {
|
|
5000
|
-
throw new Error(`${descriptor.name} is coming soon.`);
|
|
5001
|
-
}
|
|
5002
|
-
report("checking-deps", 15, "Checking system dependencies\u2026");
|
|
5003
|
-
const missing = await checkSystemDeps(toolId);
|
|
5004
|
-
if (missing.length > 0) {
|
|
5005
|
-
const msg = `Missing system dependencies: ${missing.join(", ")}. Please install them first.`;
|
|
5006
|
-
report("failed", 0, msg);
|
|
5007
|
-
throw new Error(msg);
|
|
5008
|
-
}
|
|
5009
|
-
const isWorkspace = await isWorkspaceTool(descriptor.packageName);
|
|
5010
|
-
const target = detectDeploymentTarget();
|
|
5011
|
-
let resolvedPath;
|
|
5012
|
-
let installedVersion = descriptor.version;
|
|
5013
|
-
let source;
|
|
5014
|
-
if (isWorkspace) {
|
|
5015
|
-
report("downloading", 30, "Resolving workspace package\u2026");
|
|
5016
|
-
try {
|
|
5017
|
-
const modulePath = __require.resolve(descriptor.packageName, {
|
|
5018
|
-
paths: [process.cwd()]
|
|
5019
|
-
});
|
|
5020
|
-
resolvedPath = path9.dirname(modulePath);
|
|
5021
|
-
} catch {
|
|
5022
|
-
resolvedPath = path9.join(process.cwd(), "node_modules", descriptor.packageName);
|
|
5023
|
-
}
|
|
5024
|
-
source = "local";
|
|
5025
|
-
report("installing", 60, "Workspace package resolved.");
|
|
5026
|
-
} else {
|
|
5027
|
-
report("downloading", 30, `Downloading ${descriptor.packageName}@${descriptor.version}\u2026`);
|
|
5028
|
-
const installed = await execInstall(
|
|
5029
|
-
descriptor.packageName,
|
|
5030
|
-
descriptor.version,
|
|
5031
|
-
target,
|
|
5032
|
-
(msg) => report("downloading", 50, msg)
|
|
5033
|
-
);
|
|
5034
|
-
resolvedPath = installed.resolvedPath;
|
|
5035
|
-
installedVersion = installed.version;
|
|
5036
|
-
source = target === "sandbox" ? "sandbox" : "registry";
|
|
5037
|
-
report("installing", 70, "Package installed.");
|
|
5038
|
-
}
|
|
5039
|
-
report("installing", 80, "Registering tool\u2026");
|
|
5040
|
-
await ensureDir();
|
|
5041
|
-
const manifest = await readManifest();
|
|
5042
|
-
const existing = manifest.tools.findIndex((t) => t.id === toolId);
|
|
5043
|
-
const entry = {
|
|
5044
|
-
id: toolId,
|
|
5045
|
-
version: installedVersion,
|
|
5046
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5047
|
-
packageName: descriptor.packageName,
|
|
5048
|
-
resolvedPath,
|
|
5049
|
-
source,
|
|
5050
|
-
config: buildDefaultConfig(descriptor)
|
|
5051
|
-
};
|
|
5052
|
-
if (existing >= 0) {
|
|
5053
|
-
manifest.tools[existing] = entry;
|
|
5054
|
-
} else {
|
|
5055
|
-
manifest.tools.push(entry);
|
|
5056
|
-
}
|
|
5057
|
-
manifest.downloadCounts[toolId] = (manifest.downloadCounts[toolId] ?? 0) + 1;
|
|
5058
|
-
await writeManifest(manifest);
|
|
5059
|
-
notifyHubInstall(toolId).catch(() => {
|
|
5060
|
-
});
|
|
5061
|
-
invalidateRegistryCache();
|
|
5062
|
-
report("completed", 100, "Installed successfully.");
|
|
5063
|
-
} catch (err) {
|
|
5064
|
-
const message = err instanceof Error ? err.message : "Installation failed.";
|
|
5065
|
-
report("failed", 0, message);
|
|
5066
|
-
throw err;
|
|
5067
|
-
}
|
|
5068
|
-
}
|
|
5069
|
-
async function uninstallTool(toolId) {
|
|
5070
|
-
const manifest = await readManifest();
|
|
5071
|
-
const tool2 = manifest.tools.find((t) => t.id === toolId);
|
|
5072
|
-
manifest.tools = manifest.tools.filter((t) => t.id !== toolId);
|
|
5073
|
-
await writeManifest(manifest);
|
|
5074
|
-
if (tool2?.source === "registry" && tool2.packageName) {
|
|
5075
|
-
try {
|
|
5076
|
-
const toolsDir = getToolsDir();
|
|
5077
|
-
await execAsync2(`npm uninstall ${tool2.packageName} --prefix "${toolsDir}"`, {
|
|
5078
|
-
cwd: toolsDir,
|
|
5079
|
-
timeout: 3e4
|
|
5080
|
-
});
|
|
5081
|
-
} catch {
|
|
5082
|
-
}
|
|
5083
|
-
}
|
|
5084
|
-
invalidateRegistryCache();
|
|
5085
|
-
}
|
|
5086
|
-
async function notifyHubInstall(toolId) {
|
|
5087
|
-
const hubUrl = process.env.LARKUP_HUB_URL ?? "https://hub.larkup.de";
|
|
5088
|
-
try {
|
|
5089
|
-
const controller = new AbortController();
|
|
5090
|
-
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
5091
|
-
await fetch(`${hubUrl}/v1/tools/${toolId}/installed`, {
|
|
5092
|
-
method: "POST",
|
|
5093
|
-
signal: controller.signal,
|
|
5094
|
-
headers: { "Content-Type": "application/json" }
|
|
5095
|
-
});
|
|
5096
|
-
clearTimeout(timeout);
|
|
5097
|
-
} catch {
|
|
5098
|
-
}
|
|
5099
|
-
}
|
|
5100
|
-
function buildDefaultConfig(descriptor) {
|
|
5101
|
-
const config = {};
|
|
5102
|
-
for (const field of descriptor.configSchema ?? []) {
|
|
5103
|
-
if (field.defaultValue !== void 0) {
|
|
5104
|
-
config[field.key] = field.defaultValue;
|
|
5105
|
-
}
|
|
5106
|
-
}
|
|
5107
|
-
return config;
|
|
5108
|
-
}
|
|
5109
|
-
|
|
5110
|
-
// ../../packages/marketplace/src/tool-loader.ts
|
|
5111
|
-
var moduleCache = /* @__PURE__ */ new Map();
|
|
5112
|
-
async function loadTool(toolId) {
|
|
5113
|
-
if (moduleCache.has(toolId)) return moduleCache.get(toolId);
|
|
5114
|
-
const installed = await getInstalledTool(toolId);
|
|
5115
|
-
if (!installed) return null;
|
|
5116
|
-
try {
|
|
5117
|
-
const importPath = resolveImportPath(installed);
|
|
5118
|
-
const mod = await import(
|
|
5119
|
-
/* webpackIgnore: true */
|
|
5120
|
-
importPath
|
|
5121
|
-
);
|
|
5122
|
-
moduleCache.set(toolId, mod);
|
|
5123
|
-
return mod;
|
|
5124
|
-
} catch (err) {
|
|
5125
|
-
console.error(`[marketplace] Failed to load tool "${toolId}":`, err);
|
|
5126
|
-
return null;
|
|
5127
|
-
}
|
|
5128
|
-
}
|
|
5129
|
-
function resolveImportPath(installed) {
|
|
5130
|
-
switch (installed.source) {
|
|
5131
|
-
case "local":
|
|
5132
|
-
return installed.packageName;
|
|
5133
|
-
case "registry":
|
|
5134
|
-
case "sandbox":
|
|
5135
|
-
return installed.resolvedPath;
|
|
5136
|
-
default:
|
|
5137
|
-
return installed.packageName;
|
|
5138
|
-
}
|
|
5139
|
-
}
|
|
5140
|
-
|
|
5141
|
-
// ../../packages/marketplace/src/storage-provider.ts
|
|
5142
|
-
import { promises as fs9 } from "fs";
|
|
5143
|
-
import path10 from "path";
|
|
5144
4789
|
var LocalStorageProvider = class {
|
|
5145
4790
|
id = "local";
|
|
5146
4791
|
name = "Local Storage";
|
|
5147
4792
|
async mediaDir() {
|
|
5148
4793
|
const dataDir = await requireDataDir();
|
|
5149
|
-
const dir =
|
|
5150
|
-
await
|
|
4794
|
+
const dir = path9.join(dataDir, "media");
|
|
4795
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
5151
4796
|
return dir;
|
|
5152
4797
|
}
|
|
5153
4798
|
async store(key, data, _mimeType) {
|
|
5154
4799
|
const dir = await this.mediaDir();
|
|
5155
|
-
const filePath =
|
|
5156
|
-
await
|
|
5157
|
-
await
|
|
4800
|
+
const filePath = path9.join(dir, key);
|
|
4801
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
4802
|
+
await fs8.writeFile(filePath, data);
|
|
5158
4803
|
return `local://${key}`;
|
|
5159
4804
|
}
|
|
5160
4805
|
async storeFile(key, sourcePath, _mimeType) {
|
|
5161
4806
|
const dir = await this.mediaDir();
|
|
5162
|
-
const filePath =
|
|
5163
|
-
await
|
|
5164
|
-
await
|
|
4807
|
+
const filePath = path9.join(dir, key);
|
|
4808
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
4809
|
+
await fs8.copyFile(sourcePath, filePath);
|
|
5165
4810
|
return `local://${key}`;
|
|
5166
4811
|
}
|
|
5167
4812
|
async retrieve(uri) {
|
|
5168
4813
|
const filePath = await this.resolvePath(uri);
|
|
5169
4814
|
if (!filePath) throw new Error(`Unsupported local storage URI: ${uri}`);
|
|
5170
|
-
return
|
|
4815
|
+
return fs8.readFile(filePath);
|
|
5171
4816
|
}
|
|
5172
4817
|
async resolvePath(uri) {
|
|
5173
4818
|
if (!uri.startsWith("local://")) return void 0;
|
|
5174
4819
|
const key = uri.slice("local://".length);
|
|
5175
4820
|
const dir = await this.mediaDir();
|
|
5176
|
-
const filePath =
|
|
5177
|
-
const relative =
|
|
5178
|
-
if (relative.startsWith("..") ||
|
|
4821
|
+
const filePath = path9.resolve(dir, key);
|
|
4822
|
+
const relative = path9.relative(dir, filePath);
|
|
4823
|
+
if (relative.startsWith("..") || path9.isAbsolute(relative)) {
|
|
5179
4824
|
throw new Error("Invalid local storage URI");
|
|
5180
4825
|
}
|
|
5181
4826
|
return filePath;
|
|
@@ -5184,7 +4829,7 @@ var LocalStorageProvider = class {
|
|
|
5184
4829
|
const key = uri.replace("local://", "");
|
|
5185
4830
|
const dir = await this.mediaDir();
|
|
5186
4831
|
try {
|
|
5187
|
-
await
|
|
4832
|
+
await fs8.unlink(path9.join(dir, key));
|
|
5188
4833
|
} catch {
|
|
5189
4834
|
}
|
|
5190
4835
|
}
|
|
@@ -5203,17 +4848,17 @@ async function computeDirStats(dir) {
|
|
|
5203
4848
|
async function walk2(d) {
|
|
5204
4849
|
let entries;
|
|
5205
4850
|
try {
|
|
5206
|
-
entries = await
|
|
4851
|
+
entries = await fs8.readdir(d, { withFileTypes: true });
|
|
5207
4852
|
} catch {
|
|
5208
4853
|
return;
|
|
5209
4854
|
}
|
|
5210
4855
|
for (const entry of entries) {
|
|
5211
|
-
const full =
|
|
4856
|
+
const full = path9.join(d, entry.name);
|
|
5212
4857
|
if (entry.isDirectory()) {
|
|
5213
4858
|
await walk2(full);
|
|
5214
4859
|
} else {
|
|
5215
4860
|
try {
|
|
5216
|
-
const stat = await
|
|
4861
|
+
const stat = await fs8.stat(full);
|
|
5217
4862
|
usedBytes += stat.size;
|
|
5218
4863
|
fileCount++;
|
|
5219
4864
|
} catch {
|
|
@@ -5270,12 +4915,12 @@ async function processMediaCommand(inputs, options) {
|
|
|
5270
4915
|
const assets = await Promise.all(
|
|
5271
4916
|
mediaFiles.map(async (filePath) => {
|
|
5272
4917
|
const type = mediaType(filePath);
|
|
5273
|
-
const stat = await
|
|
5274
|
-
const extension =
|
|
4918
|
+
const stat = await fs9.stat(filePath);
|
|
4919
|
+
const extension = path10.extname(filePath).toLowerCase() || ".bin";
|
|
5275
4920
|
const key = `${type}s/${Date.now()}_${Math.random().toString(36).slice(2)}${extension}`;
|
|
5276
4921
|
const mimeType = MIME_TYPES[extension] || "application/octet-stream";
|
|
5277
|
-
const storageUri = storage.storeFile ? await storage.storeFile(key, filePath, mimeType) : await storage.store(key, await
|
|
5278
|
-
return { type, fileName:
|
|
4922
|
+
const storageUri = storage.storeFile ? await storage.storeFile(key, filePath, mimeType) : await storage.store(key, await fs9.readFile(filePath), mimeType);
|
|
4923
|
+
return { type, fileName: path10.basename(filePath), mimeType, storageUri, fileSize: stat.size };
|
|
5279
4924
|
})
|
|
5280
4925
|
);
|
|
5281
4926
|
const stored = await addMediaAssets(assets);
|
|
@@ -5357,7 +5002,7 @@ async function processMediaAsset(asset) {
|
|
|
5357
5002
|
if (!tool2.processVideo)
|
|
5358
5003
|
throw new Error("The installed Video & Audio tool cannot process videos.");
|
|
5359
5004
|
await update("Extracting video audio", 20);
|
|
5360
|
-
const outputDir = await
|
|
5005
|
+
const outputDir = await fs9.mkdtemp(path10.join(process.cwd(), ".larkup", "tmp-media-"));
|
|
5361
5006
|
try {
|
|
5362
5007
|
const video = await tool2.processVideo(localFile, {
|
|
5363
5008
|
outputDir,
|
|
@@ -5378,7 +5023,7 @@ async function processMediaAsset(asset) {
|
|
|
5378
5023
|
dimensions = { width: video.meta.width, height: video.meta.height };
|
|
5379
5024
|
frameCount = video.frames.length;
|
|
5380
5025
|
} finally {
|
|
5381
|
-
await
|
|
5026
|
+
await fs9.rm(outputDir, { recursive: true, force: true }).catch(() => {
|
|
5382
5027
|
});
|
|
5383
5028
|
}
|
|
5384
5029
|
} else {
|
|
@@ -5456,7 +5101,7 @@ async function indexPendingMedia() {
|
|
|
5456
5101
|
spinner2.stop(`Indexed ${finalRun.totalChunks} chunks`);
|
|
5457
5102
|
}
|
|
5458
5103
|
function mediaType(filePath) {
|
|
5459
|
-
const extension =
|
|
5104
|
+
const extension = path10.extname(filePath).toLowerCase();
|
|
5460
5105
|
if ([".png", ".jpg", ".jpeg", ".webp", ".gif"].includes(extension)) return "image";
|
|
5461
5106
|
if ([".mp4", ".webm", ".mov", ".mkv"].includes(extension)) return "video";
|
|
5462
5107
|
if ([".mp3", ".m4a", ".wav", ".ogg", ".flac"].includes(extension)) return "audio";
|
|
@@ -5543,20 +5188,20 @@ async function indexCommand(inputs, options) {
|
|
|
5543
5188
|
}
|
|
5544
5189
|
|
|
5545
5190
|
// src/commands/generate.ts
|
|
5546
|
-
import { promises as
|
|
5547
|
-
import
|
|
5191
|
+
import { promises as fs10 } from "fs";
|
|
5192
|
+
import path11 from "path";
|
|
5548
5193
|
async function generateCommand(options) {
|
|
5549
5194
|
await inServerScope(options.server, async () => {
|
|
5550
5195
|
await requireActive();
|
|
5551
5196
|
const config = await readConfig();
|
|
5552
5197
|
if (options.out) {
|
|
5553
|
-
const dir =
|
|
5198
|
+
const dir = path11.isAbsolute(options.out) ? options.out : path11.join(process.cwd(), options.out);
|
|
5554
5199
|
const server2 = generateServer(config);
|
|
5555
|
-
await
|
|
5200
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
5556
5201
|
for (const f of server2.files) {
|
|
5557
|
-
const dest =
|
|
5558
|
-
await
|
|
5559
|
-
await
|
|
5202
|
+
const dest = path11.join(dir, f.path);
|
|
5203
|
+
await fs10.mkdir(path11.dirname(dest), { recursive: true });
|
|
5204
|
+
await fs10.writeFile(dest, f.contents, "utf8");
|
|
5560
5205
|
}
|
|
5561
5206
|
log.success(`Generated ${server2.files.length} files \u2192 ${dir}`);
|
|
5562
5207
|
} else {
|
|
@@ -5904,8 +5549,8 @@ async function marketplaceCommand(action = "list", toolId) {
|
|
|
5904
5549
|
}
|
|
5905
5550
|
|
|
5906
5551
|
// src/commands/deploy.ts
|
|
5907
|
-
import { promises as
|
|
5908
|
-
import
|
|
5552
|
+
import { promises as fs11 } from "fs";
|
|
5553
|
+
import path12 from "path";
|
|
5909
5554
|
async function deployCommand(target, options) {
|
|
5910
5555
|
await inServerScope(options.server, async () => {
|
|
5911
5556
|
await requireActive();
|
|
@@ -5941,13 +5586,13 @@ async function deployCommand(target, options) {
|
|
|
5941
5586
|
});
|
|
5942
5587
|
}
|
|
5943
5588
|
async function copyGenerated(source, output) {
|
|
5944
|
-
const destination =
|
|
5945
|
-
const relative =
|
|
5946
|
-
if (relative === "" || !relative.startsWith("..") && !
|
|
5589
|
+
const destination = path12.resolve(output);
|
|
5590
|
+
const relative = path12.relative(source, destination);
|
|
5591
|
+
if (relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative)) {
|
|
5947
5592
|
throw new Error("Deployment output must be outside the generated server directory.");
|
|
5948
5593
|
}
|
|
5949
|
-
await
|
|
5950
|
-
await
|
|
5594
|
+
await fs11.mkdir(destination, { recursive: true });
|
|
5595
|
+
await fs11.cp(source, destination, { recursive: true, force: true });
|
|
5951
5596
|
return destination;
|
|
5952
5597
|
}
|
|
5953
5598
|
|
|
@@ -6032,8 +5677,8 @@ function openCommandForPlatform(url) {
|
|
|
6032
5677
|
}
|
|
6033
5678
|
|
|
6034
5679
|
// src/commands/documents.ts
|
|
6035
|
-
import { promises as
|
|
6036
|
-
import
|
|
5680
|
+
import { promises as fs12 } from "fs";
|
|
5681
|
+
import path13 from "path";
|
|
6037
5682
|
|
|
6038
5683
|
// ../../packages/core/src/corpus-retriever.ts
|
|
6039
5684
|
function applyFilters(docs, filter) {
|
|
@@ -6152,10 +5797,10 @@ async function documentsCommand(action = "list", idOrPath, options) {
|
|
|
6152
5797
|
}
|
|
6153
5798
|
if (action === "export") {
|
|
6154
5799
|
const format = options.format === "jsonl" ? "jsonl" : "csv";
|
|
6155
|
-
const output =
|
|
5800
|
+
const output = path13.resolve(options.out || idOrPath || `corpus.${format}`);
|
|
6156
5801
|
const content = format === "jsonl" ? await exportCorpusAsJSONL(void 0, Number.MAX_SAFE_INTEGER) : await exportCorpusAsCSV(void 0, Number.MAX_SAFE_INTEGER);
|
|
6157
|
-
await
|
|
6158
|
-
await
|
|
5802
|
+
await fs12.mkdir(path13.dirname(output), { recursive: true });
|
|
5803
|
+
await fs12.writeFile(output, content, "utf8");
|
|
6159
5804
|
log.success(`Exported corpus \u2192 ${output}`);
|
|
6160
5805
|
return;
|
|
6161
5806
|
}
|
|
@@ -6181,7 +5826,7 @@ ${document.content}`);
|
|
|
6181
5826
|
return;
|
|
6182
5827
|
}
|
|
6183
5828
|
if (action === "update") {
|
|
6184
|
-
const content = options.file ? await
|
|
5829
|
+
const content = options.file ? await fs12.readFile(path13.resolve(options.file), "utf8") : options.text;
|
|
6185
5830
|
const updated = await updateDocument(document.id, {
|
|
6186
5831
|
title: options.title,
|
|
6187
5832
|
content,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larkup/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"commander": "^15.0.0",
|
|
30
30
|
"zod": "^4.4.3",
|
|
31
31
|
"@larkup/core": "0.2.2",
|
|
32
|
-
"@larkup/
|
|
33
|
-
"@larkup/
|
|
32
|
+
"@larkup/vector-stores": "0.1.22",
|
|
33
|
+
"@larkup/marketplace": "0.1.5"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"tsup": "^8.0.0",
|