@larkup/cli 0.3.1 → 0.3.3

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.
Files changed (43) hide show
  1. package/dist/{chunk-ODWAFTDP.js → chunk-2IVYLF4F.js} +1 -1
  2. package/dist/chunk-3GL3SZFJ.js +212 -0
  3. package/dist/chunk-5BX2UVG2.js +2352 -0
  4. package/dist/chunk-5RGEHTEF.js +646 -0
  5. package/dist/{chunk-65EVYGDT.js → chunk-7KURHXYU.js} +38 -38
  6. package/dist/chunk-A2PXF6Y7.js +1426 -0
  7. package/dist/{chunk-3EBUYTA7.js → chunk-ERDDY5EQ.js} +1 -1
  8. package/dist/{chunk-N6IF5QLH.js → chunk-HQPSE5ZU.js} +28 -36
  9. package/dist/chunk-IPKRK5UI.js +73 -0
  10. package/dist/{chunk-HAABLEPO.js → chunk-QAJSSODU.js} +1 -12
  11. package/dist/{chunk-BDMLJQRN.js → chunk-QKPH65NY.js} +1 -1
  12. package/dist/{chunk-S5DCG3CZ.js → chunk-RN25NOBI.js} +3 -3
  13. package/dist/{chunk-WVF2A44B.js → chunk-SAQHMCEF.js} +3 -3
  14. package/dist/{chunk-77BPWBHR.js → chunk-SLNKM4UO.js} +1 -1
  15. package/dist/{chunk-HOPZALAP.js → chunk-TJPYJ5TS.js} +1 -1
  16. package/dist/{chunk-4XN2IRCG.js → chunk-ZBGNJVTC.js} +3 -3
  17. package/dist/chunk-ZYHZIBTH.js +1272 -0
  18. package/dist/{dist-es-S5FAFKBD.js → dist-es-3L6W7F7P.js} +4 -4
  19. package/dist/{dist-es-YD724GH3.js → dist-es-6MPMKRLG.js} +21 -21
  20. package/dist/{dist-es-F6WXDZQY.js → dist-es-7LQAPSS6.js} +7 -7
  21. package/dist/{dist-es-NEADJWQM.js → dist-es-CVOBCRJ5.js} +9 -9
  22. package/dist/{dist-es-5DTOPEBY.js → dist-es-DAE2AYZV.js} +30 -23
  23. package/dist/{dist-es-BM4STLL5.js → dist-es-G4H4NZOY.js} +7 -7
  24. package/dist/{dist-es-LH5Y4P7D.js → dist-es-HV6ZWB2M.js} +8 -8
  25. package/dist/{dist-es-KMHWFNXI.js → dist-es-PQXH3VYE.js} +12 -9
  26. package/dist/{dist-es-3QFDPX44.js → dist-es-RGSF6JKX.js} +255 -231
  27. package/dist/{dist-es-DS5SFRTK.js → dist-es-UWL5EGDP.js} +1 -1
  28. package/dist/{chunk-FUTLPXFR.js → event-streams-NVY5IGD3.js} +31 -31
  29. package/dist/{event-streams-NPIZBVLJ.js → event-streams-RZ2JU5EF.js} +1 -2
  30. package/dist/index.js +33 -10
  31. package/dist/{loadSso-HXXA5UNF.js → loadSso-DEHYSFO4.js} +66 -62
  32. package/dist/{signin-7FUGJFG3.js → signin-CTAMN4QW.js} +70 -66
  33. package/dist/{sso-oidc-EPMF7LMF.js → sso-oidc-ZYLSPNAE.js} +69 -65
  34. package/dist/{sts-RBEOIBEJ.js → sts-EQDDSBAR.js} +75 -71
  35. package/dist/{tool-loader-2WHU3H77.js → tool-loader-V7TWBSTT.js} +2 -2
  36. package/dist/{tool-registry-BNRTSH77.js → tool-registry-5RFLM6AW.js} +1 -1
  37. package/package.json +3 -3
  38. package/dist/chunk-A7HMSODH.js +0 -126
  39. package/dist/chunk-FY7KWENQ.js +0 -598
  40. package/dist/chunk-MYOADNR2.js +0 -515
  41. package/dist/chunk-YUC4VMEE.js +0 -776
  42. package/dist/tool-loader-UCLZNPMP.js +0 -14
  43. package/dist/tool-registry-JS5NUNN6.js +0 -21
@@ -1,598 +0,0 @@
1
- import {
2
- getToolById,
3
- invalidateRegistryCache
4
- } from "./chunk-YUC4VMEE.js";
5
- import {
6
- __require
7
- } from "./chunk-QGM4M3NI.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 installingTools = /* @__PURE__ */ new Set();
20
- var uninstallingTools = /* @__PURE__ */ new Set();
21
- var execAsync = promisify(execCb);
22
- var packageMutationChain = Promise.resolve();
23
- function serializePackageMutation(fn) {
24
- const run = packageMutationChain.then(fn, fn);
25
- packageMutationChain = run.catch(() => {
26
- });
27
- return run;
28
- }
29
- function getToolsDir() {
30
- const dataDir = process.env.LARKUP_DATA_DIR?.trim();
31
- return path.join(dataDir ? path.resolve(dataDir) : path.join(process.cwd(), ".larkup"), "tools");
32
- }
33
- function getManifestPath() {
34
- return path.join(getToolsDir(), "installed.json");
35
- }
36
- async function ensureDir() {
37
- await fs.mkdir(getToolsDir(), { recursive: true });
38
- }
39
- async function readManifest() {
40
- try {
41
- const raw = await fs.readFile(getManifestPath(), "utf8");
42
- const parsed = JSON.parse(raw);
43
- if (!parsed.downloadCounts) parsed.downloadCounts = {};
44
- for (const tool of parsed.tools ?? []) {
45
- if (tool.packagePath && !tool.packageName) {
46
- tool.packageName = tool.packagePath;
47
- delete tool.packagePath;
48
- }
49
- if (!tool.source) tool.source = "local";
50
- if (!tool.resolvedPath) {
51
- tool.resolvedPath = tool.packageName;
52
- }
53
- }
54
- return parsed;
55
- } catch {
56
- return { tools: [], downloadCounts: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
57
- }
58
- }
59
- async function writeManifest(manifest) {
60
- await ensureDir();
61
- manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
62
- await fs.writeFile(getManifestPath(), JSON.stringify(manifest, null, 2), "utf8");
63
- }
64
- async function getInstalledTools() {
65
- const manifest = await readManifest();
66
- return [...manifest.tools];
67
- }
68
- async function isToolInstalled(toolId) {
69
- const tools = await getInstalledTools();
70
- return tools.some((t) => t.id === toolId);
71
- }
72
- async function getInstalledTool(toolId) {
73
- const tools = await getInstalledTools();
74
- return tools.find((t) => t.id === toolId);
75
- }
76
- var INSTALL_HINTS = {
77
- ffmpeg: {
78
- macos: "brew install ffmpeg",
79
- linux_apt: "sudo apt-get install -y ffmpeg",
80
- linux_dnf: "sudo dnf install -y ffmpeg",
81
- linux_pacman: "sudo pacman -S --noconfirm ffmpeg",
82
- linux_apk: "sudo apk add --no-cache ffmpeg"
83
- }
84
- };
85
- function detectPlatform() {
86
- if (process.platform === "darwin") return "macos";
87
- return "linux";
88
- }
89
- async function tryAutoInstallDep(dep, onProgress) {
90
- const hints = INSTALL_HINTS[dep];
91
- if (!hints) return false;
92
- const platform = detectPlatform();
93
- let installCmd;
94
- if (platform === "macos") {
95
- try {
96
- await execAsync("which brew");
97
- installCmd = hints.macos;
98
- } catch {
99
- return false;
100
- }
101
- } else {
102
- const managers = [
103
- { cmd: "apt-get", key: "linux_apt" },
104
- { cmd: "dnf", key: "linux_dnf" },
105
- { cmd: "pacman", key: "linux_pacman" },
106
- { cmd: "apk", key: "linux_apk" }
107
- ];
108
- for (const { cmd, key } of managers) {
109
- try {
110
- await execAsync(`which ${cmd}`);
111
- installCmd = hints[key];
112
- break;
113
- } catch {
114
- }
115
- }
116
- }
117
- if (!installCmd) return false;
118
- onProgress?.(`Auto-installing ${dep} via "${installCmd}"\u2026`);
119
- try {
120
- await execAsync(installCmd, { timeout: 3e5 });
121
- onProgress?.(`${dep} installation command finished.`);
122
- } catch (err) {
123
- const message = err instanceof Error ? err.message : String(err);
124
- onProgress?.(`Auto-install of ${dep} failed: ${message}`);
125
- return false;
126
- }
127
- try {
128
- await execAsync(`which ${dep}`);
129
- return true;
130
- } catch {
131
- return false;
132
- }
133
- }
134
- function buildMissingDepsMessage(deps) {
135
- const platform = detectPlatform();
136
- const lines = [`Missing system dependencies: ${deps.join(", ")}.`];
137
- for (const dep of deps) {
138
- const hints = INSTALL_HINTS[dep];
139
- if (!hints) {
140
- lines.push(` \u2022 ${dep}: Please install it manually.`);
141
- continue;
142
- }
143
- if (platform === "macos" && hints.macos) {
144
- lines.push(` \u2022 ${dep}: Run \`${hints.macos}\``);
145
- } else if (platform === "linux") {
146
- const linuxHint = hints.linux_apt || hints.linux_dnf || hints.linux_pacman || hints.linux_apk;
147
- if (linuxHint) {
148
- lines.push(` \u2022 ${dep}: Run \`${linuxHint}\``);
149
- }
150
- }
151
- }
152
- return lines.join("\n");
153
- }
154
- async function checkSystemDeps(toolId) {
155
- const descriptor = await getToolById(toolId);
156
- if (!descriptor?.systemDeps?.length) return [];
157
- const target = detectDeploymentTarget();
158
- const missing = [];
159
- for (const dep of descriptor.systemDeps) {
160
- if (toolId === "video-audio" && dep === "ffmpeg") continue;
161
- if (target === "docker" && dep === "docker") continue;
162
- try {
163
- await execAsync(`which ${dep}`);
164
- } catch {
165
- missing.push(dep);
166
- }
167
- }
168
- return missing;
169
- }
170
- function detectDeploymentTarget() {
171
- const explicit = process.env.LARKUP_DEPLOYMENT_TARGET;
172
- if (explicit && ["local", "docker", "serverless", "sandbox"].includes(explicit)) {
173
- return explicit;
174
- }
175
- if (process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME) return "serverless";
176
- if (process.env.DOCKER_CONTAINER || isDockerEnvironment()) return "docker";
177
- if (process.env.E2B_SANDBOX_ID || process.env.MODAL_TASK_ID) return "sandbox";
178
- return "local";
179
- }
180
- function isDockerEnvironment() {
181
- try {
182
- const fs3 = __require("fs");
183
- return fs3.existsSync("/.dockerenv");
184
- } catch {
185
- return false;
186
- }
187
- }
188
- async function ensureToolsPackageJson() {
189
- const toolsDir = getToolsDir();
190
- const pkgPath = path.join(toolsDir, "package.json");
191
- try {
192
- await fs.access(pkgPath);
193
- } catch {
194
- await ensureDir();
195
- await fs.writeFile(
196
- pkgPath,
197
- JSON.stringify(
198
- {
199
- name: "larkup-tools",
200
- version: "1.0.0",
201
- private: true,
202
- description: "Isolated directory for installed Larkup marketplace tools."
203
- },
204
- null,
205
- 2
206
- ),
207
- "utf8"
208
- );
209
- }
210
- }
211
- async function execInstall(packageName, version, target, onProgress) {
212
- return serializePackageMutation(
213
- () => execInstallUnlocked(packageName, version, target, onProgress)
214
- );
215
- }
216
- async function execInstallUnlocked(packageName, version, target, onProgress) {
217
- const toolsDir = getToolsDir();
218
- switch (target) {
219
- case "local":
220
- case "docker": {
221
- await ensureToolsPackageJson();
222
- const install = async (specifier) => {
223
- const installCmd = `npm install ${specifier} --prefix "${toolsDir}" --save --no-audit --no-fund --prefer-offline`;
224
- onProgress?.(`Running: npm install ${specifier}`);
225
- const { stderr } = await execAsync(installCmd, {
226
- cwd: toolsDir,
227
- // Native tool dependencies can take longer under Docker Desktop's
228
- // amd64 emulation. Let the request finish instead of reporting a
229
- // misleading connection error part way through installation.
230
- timeout: target === "docker" ? 3e5 : 12e4,
231
- env: {
232
- ...process.env,
233
- NODE_ENV: "production",
234
- // The Docker image runs as an unprivileged user. Keep npm's cache
235
- // beside the isolated install so a named .larkup volume is always
236
- // writable and installing a Marketplace tool does not fail before
237
- // it reaches the registry.
238
- HOME: process.env.HOME && process.env.HOME !== "/nonexistent" ? process.env.HOME : toolsDir,
239
- npm_config_cache: path.join(toolsDir, ".npm-cache"),
240
- npm_config_update_notifier: "false"
241
- }
242
- });
243
- if (stderr && !stderr.includes("npm warn")) {
244
- console.warn(`[marketplace] Install stderr: ${stderr}`);
245
- }
246
- };
247
- try {
248
- await install(`${packageName}@${version}`);
249
- } catch (err) {
250
- const message = err instanceof Error ? err.message : "Install command failed";
251
- if (!message.includes("ETARGET") && !message.includes("No matching version found")) {
252
- throw new Error(`Failed to install ${packageName}: ${message}`);
253
- }
254
- onProgress?.(
255
- `Catalog version ${version} is unavailable; installing the latest published version\u2026`
256
- );
257
- try {
258
- await install(`${packageName}@latest`);
259
- } catch (latestErr) {
260
- const latestMessage = latestErr instanceof Error ? latestErr.message : "Install command failed";
261
- throw new Error(`Failed to install ${packageName}: ${latestMessage}`);
262
- }
263
- }
264
- const resolvedPath = path.join(toolsDir, "node_modules", packageName);
265
- try {
266
- await fs.access(resolvedPath);
267
- } catch {
268
- throw new Error(
269
- `Package ${packageName} was installed but could not be found at ${resolvedPath}`
270
- );
271
- }
272
- const packageJson = JSON.parse(
273
- await fs.readFile(path.join(resolvedPath, "package.json"), "utf8")
274
- );
275
- return { resolvedPath, version: packageJson.version ?? version };
276
- }
277
- case "serverless": {
278
- onProgress?.("Serverless environment detected \u2014 queuing for next deploy");
279
- const pendingPath = path.join(toolsDir, "pending-tools.json");
280
- let pending = { tools: [] };
281
- try {
282
- const raw = await fs.readFile(pendingPath, "utf8");
283
- pending = JSON.parse(raw);
284
- } catch {
285
- }
286
- pending.tools.push({ packageName, version });
287
- await ensureDir();
288
- await fs.writeFile(pendingPath, JSON.stringify(pending, null, 2), "utf8");
289
- return { resolvedPath: path.join(toolsDir, "node_modules", packageName), version };
290
- }
291
- case "sandbox": {
292
- onProgress?.("Installing in sandbox environment");
293
- await ensureToolsPackageJson();
294
- const installCmd = `npm install ${packageName}@${version} --prefix "${toolsDir}" --save --no-audit --no-fund`;
295
- await execAsync(installCmd, { cwd: toolsDir, timeout: 12e4 });
296
- return { resolvedPath: path.join(toolsDir, "node_modules", packageName), version };
297
- }
298
- default:
299
- throw new Error(`Unsupported deployment target: ${target}`);
300
- }
301
- }
302
- async function resolveManifest(toolId) {
303
- const descriptor = await getToolById(toolId);
304
- if (descriptor) return descriptor;
305
- throw new Error(`Unknown tool: ${toolId}. Not found in local registry or Hub.`);
306
- }
307
- async function isWorkspaceTool(packageName) {
308
- return Boolean(await resolveWorkspaceToolPath(packageName));
309
- }
310
- async function resolveWorkspaceToolPath(packageName) {
311
- try {
312
- const linkedPath = path.join(process.cwd(), "node_modules", ...packageName.split("/"));
313
- const packageRoot = await fs.realpath(linkedPath);
314
- const manifest = JSON.parse(
315
- await fs.readFile(path.join(packageRoot, "package.json"), "utf8")
316
- );
317
- return manifest.name === packageName && !packageRoot.includes(`${path.sep}node_modules${path.sep}`) ? packageRoot : void 0;
318
- } catch {
319
- }
320
- let workspaceRoot = process.cwd();
321
- while (true) {
322
- try {
323
- await fs.access(path.join(workspaceRoot, "pnpm-workspace.yaml"));
324
- break;
325
- } catch {
326
- const parent = path.dirname(workspaceRoot);
327
- if (parent === workspaceRoot) return void 0;
328
- workspaceRoot = parent;
329
- }
330
- }
331
- const toolsRoot = path.join(workspaceRoot, "packages", "marketplace-tools");
332
- let entries;
333
- try {
334
- entries = await fs.readdir(toolsRoot, { withFileTypes: true });
335
- } catch {
336
- return void 0;
337
- }
338
- for (const entry of entries) {
339
- if (!entry.isDirectory()) continue;
340
- const packageRoot = path.join(toolsRoot, entry.name);
341
- try {
342
- const manifest = JSON.parse(
343
- await fs.readFile(path.join(packageRoot, "package.json"), "utf8")
344
- );
345
- if (manifest.name === packageName) return packageRoot;
346
- } catch {
347
- }
348
- }
349
- return void 0;
350
- }
351
- async function installTool(toolId, onProgress, initialConfig = {}) {
352
- installingTools.add(toolId);
353
- const report = (stage, percent, message) => {
354
- onProgress?.({ toolId, stage, percent, message });
355
- };
356
- try {
357
- report("checking-deps", 5, "Resolving tool manifest\u2026");
358
- const descriptor = await resolveManifest(toolId);
359
- if (descriptor.comingSoon) {
360
- throw new Error(`${descriptor.name} is coming soon.`);
361
- }
362
- report("checking-deps", 15, "Checking system dependencies\u2026");
363
- let missing = (await checkSystemDeps(toolId)).filter((dependency) => dependency !== "docker");
364
- if (missing.length > 0) {
365
- const stillMissing = [];
366
- for (const dep of missing) {
367
- report("checking-deps", 18, `Attempting to install ${dep}\u2026`);
368
- const installed = await tryAutoInstallDep(dep, (msg) => report("checking-deps", 20, msg));
369
- if (installed) {
370
- report("checking-deps", 22, `${dep} installed successfully.`);
371
- } else {
372
- stillMissing.push(dep);
373
- }
374
- }
375
- missing = stillMissing;
376
- }
377
- if (missing.length > 0) {
378
- const msg = buildMissingDepsMessage(missing);
379
- report("failed", 0, msg);
380
- throw new Error(msg);
381
- }
382
- const isWorkspace = await isWorkspaceTool(descriptor.packageName);
383
- const target = detectDeploymentTarget();
384
- let resolvedPath;
385
- let installedVersion = descriptor.version;
386
- let source;
387
- if (isWorkspace) {
388
- report("downloading", 30, "Resolving workspace package\u2026");
389
- resolvedPath = await resolveWorkspaceToolPath(descriptor.packageName) ?? path.join(process.cwd(), "node_modules", descriptor.packageName);
390
- source = "local";
391
- report("installing", 60, "Workspace package resolved.");
392
- } else {
393
- report("downloading", 30, `Downloading ${descriptor.packageName}@${descriptor.version}\u2026`);
394
- const installed = await execInstall(
395
- descriptor.packageName,
396
- descriptor.version,
397
- target,
398
- (msg) => report("downloading", 50, msg)
399
- );
400
- resolvedPath = installed.resolvedPath;
401
- installedVersion = installed.version;
402
- source = target === "sandbox" ? "sandbox" : "registry";
403
- report("installing", 70, "Package installed.");
404
- }
405
- report("installing", 80, "Registering tool\u2026");
406
- await ensureDir();
407
- const manifest = await readManifest();
408
- const existing = manifest.tools.findIndex((t) => t.id === toolId);
409
- const entry = {
410
- id: toolId,
411
- version: installedVersion,
412
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
413
- packageName: descriptor.packageName,
414
- resolvedPath,
415
- source,
416
- config: { ...buildDefaultConfig(descriptor), ...initialConfig }
417
- };
418
- if (existing >= 0) {
419
- manifest.tools[existing] = entry;
420
- } else {
421
- manifest.tools.push(entry);
422
- }
423
- manifest.downloadCounts[toolId] = (manifest.downloadCounts[toolId] ?? 0) + 1;
424
- await writeManifest(manifest);
425
- notifyHubInstall(toolId).catch(() => {
426
- });
427
- invalidateRegistryCache();
428
- report("completed", 100, "Installed successfully.");
429
- } catch (err) {
430
- const message = err instanceof Error ? err.message : "Installation failed.";
431
- report("failed", 0, message);
432
- throw err;
433
- } finally {
434
- installingTools.delete(toolId);
435
- }
436
- }
437
- async function uninstallTool(toolId) {
438
- uninstallingTools.add(toolId);
439
- try {
440
- const manifest = await readManifest();
441
- const tool = manifest.tools.find((t) => t.id === toolId);
442
- manifest.tools = manifest.tools.filter((t) => t.id !== toolId);
443
- await writeManifest(manifest);
444
- if (tool?.source === "registry" && tool.packageName) {
445
- try {
446
- const toolsDir = getToolsDir();
447
- await execAsync(`npm uninstall ${tool.packageName} --prefix "${toolsDir}"`, {
448
- cwd: toolsDir,
449
- timeout: 3e4
450
- });
451
- } catch {
452
- }
453
- }
454
- invalidateRegistryCache();
455
- const { unloadTool: unloadTool2 } = await import("./tool-loader-2WHU3H77.js");
456
- unloadTool2(toolId);
457
- } finally {
458
- uninstallingTools.delete(toolId);
459
- }
460
- }
461
- async function notifyHubInstall(toolId) {
462
- const hubUrl = process.env.LARKUP_HUB_URL ?? "https://hub.larkup.de";
463
- try {
464
- const controller = new AbortController();
465
- const timeout = setTimeout(() => controller.abort(), 3e3);
466
- await fetch(`${hubUrl}/v1/tools/${toolId}/installed`, {
467
- method: "POST",
468
- signal: controller.signal,
469
- headers: { "Content-Type": "application/json" }
470
- });
471
- clearTimeout(timeout);
472
- } catch {
473
- }
474
- }
475
- function buildDefaultConfig(descriptor) {
476
- const config = {};
477
- for (const field of descriptor.configSchema ?? []) {
478
- if (field.defaultValue !== void 0) {
479
- config[field.key] = field.defaultValue;
480
- }
481
- }
482
- return config;
483
- }
484
-
485
- // ../../packages/marketplace/src/tool-loader.ts
486
- var moduleCache = /* @__PURE__ */ new Map();
487
- var scopedImports = /* @__PURE__ */ new Map();
488
- async function getScopedImport(revision) {
489
- const namespace = `larkup-marketplace-tools${revision === "fixed" ? "" : `-${revision}`}`;
490
- const existing = scopedImports.get(namespace);
491
- if (existing) return existing;
492
- const { register } = await import(
493
- /* webpackIgnore: true */
494
- "./api-X4PHQJTH.js"
495
- );
496
- const importer = register({ namespace }).import;
497
- if (scopedImports.size >= 8) {
498
- scopedImports.delete(scopedImports.keys().next().value);
499
- }
500
- scopedImports.set(namespace, importer);
501
- return importer;
502
- }
503
- async function loadTool(toolId) {
504
- const installed = await getInstalledTool(toolId);
505
- if (!installed) return null;
506
- const importPath = await resolveImportPath(installed);
507
- const revision = await entryRevision(installed, importPath);
508
- const installationKey = `${installed.source}:${installed.resolvedPath}:${installed.version}:${installed.installedAt}:${revision}`;
509
- const cached = moduleCache.get(toolId);
510
- if (cached?.installationKey === installationKey) return cached.module;
511
- try {
512
- const busted = cacheBustFileImport(importPath, installationKey);
513
- const importer = await getScopedImport(revision);
514
- const mod = await importer(busted, import.meta.url);
515
- moduleCache.set(toolId, { installationKey, module: mod });
516
- return mod;
517
- } catch (err) {
518
- console.error(`[marketplace] Failed to load tool "${toolId}":`, err);
519
- return null;
520
- }
521
- }
522
- async function entryRevision(installed, importPath) {
523
- if (installed.source !== "local" || !importPath.startsWith("file:")) return "fixed";
524
- try {
525
- const directory = path2.dirname(new URL(importPath).pathname);
526
- const files = await fs2.readdir(directory, { withFileTypes: true });
527
- let newest = 0;
528
- let total = 0;
529
- for (const file of files) {
530
- if (!file.isFile()) continue;
531
- const { mtimeMs, size } = await fs2.stat(path2.join(directory, file.name));
532
- newest = Math.max(newest, mtimeMs);
533
- total += size;
534
- }
535
- return newest > 0 ? `${newest}-${total}` : "fixed";
536
- } catch {
537
- return "fixed";
538
- }
539
- }
540
- function cacheBustFileImport(importPath, installationKey) {
541
- if (!importPath.startsWith("file:")) return importPath;
542
- const url = new URL(importPath);
543
- url.searchParams.set("larkup-installation", installationKey);
544
- return url.href;
545
- }
546
- async function resolveImportPath(installed) {
547
- switch (installed.source) {
548
- case "local": {
549
- try {
550
- await fs2.access(path2.join(installed.resolvedPath, "package.json"));
551
- return resolvePackageEntry(installed.resolvedPath);
552
- } catch {
553
- const workspacePath = await resolveWorkspaceToolPath(installed.packageName);
554
- if (workspacePath) return resolvePackageEntry(workspacePath);
555
- return installed.packageName;
556
- }
557
- }
558
- case "registry":
559
- case "sandbox":
560
- return resolvePackageEntry(installed.resolvedPath);
561
- default:
562
- return installed.packageName;
563
- }
564
- }
565
- async function resolvePackageEntry(packageDir) {
566
- const manifestPath = path2.join(packageDir, "package.json");
567
- const manifest = JSON.parse(await fs2.readFile(manifestPath, "utf8"));
568
- const rootExport = typeof manifest.exports === "object" ? manifest.exports["."] : manifest.exports;
569
- const entry = (typeof rootExport === "object" ? rootExport.import ?? rootExport.default : rootExport) ?? manifest.main ?? "index.js";
570
- return pathToFileURL(path2.resolve(packageDir, entry)).href;
571
- }
572
- function isToolLoaded(toolId) {
573
- return moduleCache.has(toolId);
574
- }
575
- function unloadTool(toolId) {
576
- moduleCache.delete(toolId);
577
- }
578
- async function hasCapability(capability) {
579
- const { getToolsWithCapability } = await import("./tool-registry-JS5NUNN6.js");
580
- const tools = await getToolsWithCapability(capability);
581
- for (const t of tools) {
582
- const installed = await getInstalledTool(t.id);
583
- if (installed) return true;
584
- }
585
- return false;
586
- }
587
-
588
- export {
589
- loadTool,
590
- isToolLoaded,
591
- unloadTool,
592
- hasCapability,
593
- getInstalledTools,
594
- isToolInstalled,
595
- getInstalledTool,
596
- installTool,
597
- uninstallTool
598
- };