@larkup/cli 0.2.5 → 0.2.7

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/index.js CHANGED
@@ -12,18 +12,6 @@ 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";
23
- import {
24
- getAllTools,
25
- getToolById
26
- } from "./chunk-P5JVXA7X.js";
27
15
  import {
28
16
  __require
29
17
  } from "./chunk-3RG5ZIWI.js";
@@ -34,7 +22,7 @@ import { Command } from "commander";
34
22
  // package.json
35
23
  var package_default = {
36
24
  name: "@larkup/cli",
37
- version: "0.2.5",
25
+ version: "0.2.7",
38
26
  publishConfig: {
39
27
  access: "public"
40
28
  },
@@ -951,12 +939,12 @@ function addChatProviderDependency(dependencies, provider) {
951
939
  }
952
940
 
953
941
  // ../../packages/core/src/generator/generate-server.ts
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";
942
+ function lang(path13) {
943
+ if (path13.endsWith(".json")) return "json";
944
+ if (path13.endsWith(".mjs") || path13.endsWith(".js")) return "javascript";
945
+ if (path13.endsWith(".md")) return "markdown";
946
+ if (path13.endsWith("Dockerfile")) return "dockerfile";
947
+ if (path13.endsWith(".yml") || path13.endsWith(".yaml")) return "yaml";
960
948
  return "text";
961
949
  }
962
950
  function lancedbStore() {
@@ -1415,7 +1403,7 @@ const server = createServer(async (req, res) => {
1415
1403
  <svg class="icon" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
1416
1404
  API Reference
1417
1405
  </a>
1418
- <a href="https://github.com/Larkup-AI/larkup-rag" target="_blank" rel="noopener" class="btn btn-outline">
1406
+ <a href="https://github.com/Larkup-AI/larkup" target="_blank" rel="noopener" class="btn btn-outline">
1419
1407
  <svg class="icon" viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
1420
1408
  Larkup Team
1421
1409
  </a>
@@ -1424,7 +1412,7 @@ const server = createServer(async (req, res) => {
1424
1412
  Contact
1425
1413
  </a>
1426
1414
  </div>
1427
- <div class="footer">Built with <a href="https://github.com/Larkup-AI/larkup-rag/buddy-rag" target="_blank" rel="noopener">larkup</a> \xB7 v1.0</div>
1415
+ <div class="footer">Built with <a href="https://github.com/Larkup-AI/larkup/buddy-rag" target="_blank" rel="noopener">larkup</a> \xB7 v1.0</div>
1428
1416
  </div>
1429
1417
  </body>
1430
1418
  </html>\`)
@@ -2273,22 +2261,22 @@ function generateServer(config) {
2273
2261
  { path: "README.md", contents: readme(config, server) }
2274
2262
  ].map((f) => ({ ...f, language: lang(f.path) }));
2275
2263
  try {
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)) {
2264
+ const fs12 = __require("fs");
2265
+ const path13 = __require("path");
2266
+ const faviconPath = path13.resolve(process.cwd(), "public/favicon2.ico");
2267
+ if (fs12.existsSync(faviconPath)) {
2280
2268
  files.push({
2281
2269
  path: "public/favicon2.ico",
2282
- contents: fs13.readFileSync(faviconPath).toString("base64"),
2270
+ contents: fs12.readFileSync(faviconPath).toString("base64"),
2283
2271
  language: "ico",
2284
2272
  encoding: "base64"
2285
2273
  });
2286
2274
  } else {
2287
- const webFaviconPath = path14.resolve(process.cwd(), "apps/web/public/favicon2.ico");
2288
- if (fs13.existsSync(webFaviconPath)) {
2275
+ const webFaviconPath = path13.resolve(process.cwd(), "apps/web/public/favicon2.ico");
2276
+ if (fs12.existsSync(webFaviconPath)) {
2289
2277
  files.push({
2290
2278
  path: "public/favicon2.ico",
2291
- contents: fs13.readFileSync(webFaviconPath).toString("base64"),
2279
+ contents: fs12.readFileSync(webFaviconPath).toString("base64"),
2292
2280
  language: "ico",
2293
2281
  encoding: "base64"
2294
2282
  });
@@ -2302,12 +2290,12 @@ function generateServer(config) {
2302
2290
  }
2303
2291
 
2304
2292
  // ../../packages/core/src/generator/generate-agent-server.ts
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";
2293
+ function lang2(path13) {
2294
+ if (path13.endsWith(".json")) return "json";
2295
+ if (path13.endsWith(".mjs") || path13.endsWith(".js")) return "javascript";
2296
+ if (path13.endsWith(".md")) return "markdown";
2297
+ if (path13.endsWith("Dockerfile")) return "dockerfile";
2298
+ if (path13.endsWith(".yml") || path13.endsWith(".yaml")) return "yaml";
2311
2299
  return "text";
2312
2300
  }
2313
2301
  function lancedbStore2() {
@@ -3321,22 +3309,22 @@ function generateAgentServer(config) {
3321
3309
  { path: "README.md", contents: readme2(config, server) }
3322
3310
  ].map((f) => ({ ...f, language: lang2(f.path) }));
3323
3311
  try {
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)) {
3312
+ const fs12 = __require("fs");
3313
+ const path13 = __require("path");
3314
+ const faviconPath = path13.resolve(process.cwd(), "public/favicon2.ico");
3315
+ if (fs12.existsSync(faviconPath)) {
3328
3316
  files.push({
3329
3317
  path: "public/favicon2.ico",
3330
- contents: fs13.readFileSync(faviconPath).toString("base64"),
3318
+ contents: fs12.readFileSync(faviconPath).toString("base64"),
3331
3319
  language: "ico",
3332
3320
  encoding: "base64"
3333
3321
  });
3334
3322
  } else {
3335
- const webFaviconPath = path14.resolve(process.cwd(), "apps/web/public/favicon2.ico");
3336
- if (fs13.existsSync(webFaviconPath)) {
3323
+ const webFaviconPath = path13.resolve(process.cwd(), "apps/web/public/favicon2.ico");
3324
+ if (fs12.existsSync(webFaviconPath)) {
3337
3325
  files.push({
3338
3326
  path: "public/favicon2.ico",
3339
- contents: fs13.readFileSync(webFaviconPath).toString("base64"),
3327
+ contents: fs12.readFileSync(webFaviconPath).toString("base64"),
3340
3328
  language: "ico",
3341
3329
  encoding: "base64"
3342
3330
  });
@@ -3413,6 +3401,18 @@ async function writeState(state) {
3413
3401
  if (file) await fs2.writeFile(file, JSON.stringify(state, null, 2), "utf8");
3414
3402
  return state;
3415
3403
  }
3404
+ async function withActiveVectorTable(config) {
3405
+ const server = await getActiveServer();
3406
+ if (!server || config.vectorStore && config.vectorStore !== "lancedb") return config;
3407
+ const safeId = server.id.replace(/[^a-zA-Z0-9_-]/g, "_");
3408
+ return {
3409
+ ...config,
3410
+ storeConfig: {
3411
+ ...config.storeConfig,
3412
+ tableName: `documents_${safeId}`
3413
+ }
3414
+ };
3415
+ }
3416
3416
  function pidAlive(pid) {
3417
3417
  if (!pid) return false;
3418
3418
  try {
@@ -3423,7 +3423,7 @@ function pidAlive(pid) {
3423
3423
  }
3424
3424
  }
3425
3425
  async function emitToDisk(config) {
3426
- const server = generateServer(config);
3426
+ const server = generateServer(await withActiveVectorTable(config));
3427
3427
  const dir = await outDir(true);
3428
3428
  if (!dir) throw new Error("No active server to emit to.");
3429
3429
  await fs2.mkdir(dir, { recursive: true });
@@ -3459,7 +3459,8 @@ async function startServer(config, serverApiKey) {
3459
3459
  }
3460
3460
  }
3461
3461
  await killPort(port);
3462
- const dir = await emitToDisk(config);
3462
+ const runtimeConfig = await withActiveVectorTable(config);
3463
+ const dir = await emitToDisk(runtimeConfig);
3463
3464
  try {
3464
3465
  await execAsync("npm install --omit=dev", {
3465
3466
  cwd: dir,
@@ -3474,7 +3475,7 @@ async function startServer(config, serverApiKey) {
3474
3475
  const message = err instanceof Error ? err.message : "npm install failed";
3475
3476
  return writeState({ ...emptyState(port), lastError: message });
3476
3477
  }
3477
- const dbPath = config.storeConfig.dbPath || "./.larkup/lancedb";
3478
+ const dbPath = runtimeConfig.storeConfig.dbPath || "./.larkup/lancedb";
3478
3479
  const absDb = path2.isAbsolute(dbPath) ? dbPath : path2.join(process.cwd(), dbPath);
3479
3480
  const logPath = path2.join(dir, "server.log");
3480
3481
  await fs2.writeFile(logPath, "", "utf8");
@@ -3487,35 +3488,35 @@ async function startServer(config, serverApiKey) {
3487
3488
  env: {
3488
3489
  ...process.env,
3489
3490
  PORT: String(port),
3490
- TOP_K: String(config.topK),
3491
+ TOP_K: String(runtimeConfig.topK),
3491
3492
  SERVER_API_KEY: serverApiKey || "",
3492
- EMBEDDING_API_KEY: config.embeddingApiKey || process.env.EMBEDDING_API_KEY || process.env.OPENAI_API_KEY || "",
3493
- CHAT_API_KEY: config.chatApiKey || config.customChatModels?.find(
3494
- (model) => model.modelName === config.chatModelId?.replace(/^custom:/, "")
3493
+ EMBEDDING_API_KEY: runtimeConfig.embeddingApiKey || process.env.EMBEDDING_API_KEY || process.env.OPENAI_API_KEY || "",
3494
+ CHAT_API_KEY: runtimeConfig.chatApiKey || runtimeConfig.customChatModels?.find(
3495
+ (model) => model.modelName === runtimeConfig.chatModelId?.replace(/^custom:/, "")
3495
3496
  )?.apiKey || process.env.CHAT_API_KEY || process.env.OPENAI_API_KEY || "",
3496
- CHAT_MODEL: process.env.CHAT_MODEL || resolveChatModel(config),
3497
- CHAT_BASE_URL: config.customChatModels?.find(
3498
- (model) => model.modelName === config.chatModelId?.replace(/^custom:/, "")
3497
+ CHAT_MODEL: process.env.CHAT_MODEL || resolveChatModel(runtimeConfig),
3498
+ CHAT_BASE_URL: runtimeConfig.customChatModels?.find(
3499
+ (model) => model.modelName === runtimeConfig.chatModelId?.replace(/^custom:/, "")
3499
3500
  )?.baseUrl || process.env.CHAT_BASE_URL || "",
3500
- OPENAI_API_KEY: config.embeddingApiKey || config.chatApiKey || process.env.OPENAI_API_KEY || "",
3501
- ANTHROPIC_API_KEY: config.chatApiKey || config.embeddingApiKey || process.env.ANTHROPIC_API_KEY || "",
3502
- COHERE_API_KEY: config.embeddingApiKey || config.chatApiKey || process.env.COHERE_API_KEY || "",
3503
- GOOGLE_GENERATIVE_AI_API_KEY: config.embeddingApiKey || config.chatApiKey || process.env.GOOGLE_GENERATIVE_AI_API_KEY || "",
3504
- PINECONE_API_KEY: config.storeConfig.apiKey || "",
3505
- PINECONE_INDEX: config.storeConfig.indexName || "",
3506
- PINECONE_NAMESPACE: config.storeConfig.namespace || "",
3507
- PINECONE_SPARSE_MODEL: config.storeConfig.sparseModel || "",
3508
- PINECONE_SPARSE_INDEX: config.storeConfig.sparseIndexName || "",
3509
- LANCEDB_MODE: config.storeConfig.mode || "local",
3501
+ OPENAI_API_KEY: runtimeConfig.embeddingApiKey || runtimeConfig.chatApiKey || process.env.OPENAI_API_KEY || "",
3502
+ ANTHROPIC_API_KEY: runtimeConfig.chatApiKey || runtimeConfig.embeddingApiKey || process.env.ANTHROPIC_API_KEY || "",
3503
+ COHERE_API_KEY: runtimeConfig.embeddingApiKey || runtimeConfig.chatApiKey || process.env.COHERE_API_KEY || "",
3504
+ GOOGLE_GENERATIVE_AI_API_KEY: runtimeConfig.embeddingApiKey || runtimeConfig.chatApiKey || process.env.GOOGLE_GENERATIVE_AI_API_KEY || "",
3505
+ PINECONE_API_KEY: runtimeConfig.storeConfig.apiKey || "",
3506
+ PINECONE_INDEX: runtimeConfig.storeConfig.indexName || "",
3507
+ PINECONE_NAMESPACE: runtimeConfig.storeConfig.namespace || "",
3508
+ PINECONE_SPARSE_MODEL: runtimeConfig.storeConfig.sparseModel || "",
3509
+ PINECONE_SPARSE_INDEX: runtimeConfig.storeConfig.sparseIndexName || "",
3510
+ LANCEDB_MODE: runtimeConfig.storeConfig.mode || "local",
3510
3511
  LANCEDB_PATH: absDb,
3511
- LANCEDB_URI: config.storeConfig.uri || "",
3512
- LANCEDB_API_KEY: config.storeConfig.apiKey || "",
3513
- LANCEDB_S3_URI: config.storeConfig.s3Uri || "",
3514
- AWS_ENDPOINT: config.storeConfig.s3Endpoint || "",
3515
- AWS_REGION: config.storeConfig.s3Region || "",
3516
- AWS_ACCESS_KEY_ID: config.storeConfig.s3AccessKeyId || "",
3517
- AWS_SECRET_ACCESS_KEY: config.storeConfig.s3SecretAccessKey || "",
3518
- LANCEDB_TABLE: config.storeConfig.tableName || "documents"
3512
+ LANCEDB_URI: runtimeConfig.storeConfig.uri || "",
3513
+ LANCEDB_API_KEY: runtimeConfig.storeConfig.apiKey || "",
3514
+ LANCEDB_S3_URI: runtimeConfig.storeConfig.s3Uri || "",
3515
+ AWS_ENDPOINT: runtimeConfig.storeConfig.s3Endpoint || "",
3516
+ AWS_REGION: runtimeConfig.storeConfig.s3Region || "",
3517
+ AWS_ACCESS_KEY_ID: runtimeConfig.storeConfig.s3AccessKeyId || "",
3518
+ AWS_SECRET_ACCESS_KEY: runtimeConfig.storeConfig.s3SecretAccessKey || "",
3519
+ LANCEDB_TABLE: runtimeConfig.storeConfig.tableName || "documents"
3519
3520
  }
3520
3521
  });
3521
3522
  child.unref();
@@ -4300,7 +4301,7 @@ async function createAdapter(config, onRateLimit) {
4300
4301
  }
4301
4302
  case "lancedb":
4302
4303
  default: {
4303
- const { LanceDBAdapter } = await import("./lancedb-ITW7V3K5.js");
4304
+ const { LanceDBAdapter } = await import("./lancedb-RUTGJYW7.js");
4304
4305
  return new LanceDBAdapter({
4305
4306
  mode: overrides.mode,
4306
4307
  dbPath: overrides.dbPath,
@@ -4640,8 +4641,8 @@ async function readTextFiles(files) {
4640
4641
  }
4641
4642
 
4642
4643
  // src/commands/media.ts
4643
- import { promises as fs9 } from "fs";
4644
- import path10 from "path";
4644
+ import { promises as fs8 } from "fs";
4645
+ import path9 from "path";
4645
4646
 
4646
4647
  // ../../packages/core/src/media-store.ts
4647
4648
  import { promises as fs7 } from "fs";
@@ -4783,97 +4784,10 @@ function claimMediaAsset(id) {
4783
4784
  });
4784
4785
  }
4785
4786
 
4786
- // ../../packages/marketplace/src/storage-provider.ts
4787
- import { promises as fs8 } from "fs";
4788
- import path9 from "path";
4789
- var LocalStorageProvider = class {
4790
- id = "local";
4791
- name = "Local Storage";
4792
- async mediaDir() {
4793
- const dataDir = await requireDataDir();
4794
- const dir = path9.join(dataDir, "media");
4795
- await fs8.mkdir(dir, { recursive: true });
4796
- return dir;
4797
- }
4798
- async store(key, data, _mimeType) {
4799
- const dir = await this.mediaDir();
4800
- const filePath = path9.join(dir, key);
4801
- await fs8.mkdir(path9.dirname(filePath), { recursive: true });
4802
- await fs8.writeFile(filePath, data);
4803
- return `local://${key}`;
4804
- }
4805
- async storeFile(key, sourcePath, _mimeType) {
4806
- const dir = await this.mediaDir();
4807
- const filePath = path9.join(dir, key);
4808
- await fs8.mkdir(path9.dirname(filePath), { recursive: true });
4809
- await fs8.copyFile(sourcePath, filePath);
4810
- return `local://${key}`;
4811
- }
4812
- async retrieve(uri) {
4813
- const filePath = await this.resolvePath(uri);
4814
- if (!filePath) throw new Error(`Unsupported local storage URI: ${uri}`);
4815
- return fs8.readFile(filePath);
4816
- }
4817
- async resolvePath(uri) {
4818
- if (!uri.startsWith("local://")) return void 0;
4819
- const key = uri.slice("local://".length);
4820
- const dir = await this.mediaDir();
4821
- const filePath = path9.resolve(dir, key);
4822
- const relative = path9.relative(dir, filePath);
4823
- if (relative.startsWith("..") || path9.isAbsolute(relative)) {
4824
- throw new Error("Invalid local storage URI");
4825
- }
4826
- return filePath;
4827
- }
4828
- async delete(uri) {
4829
- const key = uri.replace("local://", "");
4830
- const dir = await this.mediaDir();
4831
- try {
4832
- await fs8.unlink(path9.join(dir, key));
4833
- } catch {
4834
- }
4835
- }
4836
- async stats() {
4837
- try {
4838
- const dir = await this.mediaDir();
4839
- return await computeDirStats(dir);
4840
- } catch {
4841
- return { usedBytes: 0, fileCount: 0 };
4842
- }
4843
- }
4844
- };
4845
- async function computeDirStats(dir) {
4846
- let usedBytes = 0;
4847
- let fileCount = 0;
4848
- async function walk2(d) {
4849
- let entries;
4850
- try {
4851
- entries = await fs8.readdir(d, { withFileTypes: true });
4852
- } catch {
4853
- return;
4854
- }
4855
- for (const entry of entries) {
4856
- const full = path9.join(d, entry.name);
4857
- if (entry.isDirectory()) {
4858
- await walk2(full);
4859
- } else {
4860
- try {
4861
- const stat = await fs8.stat(full);
4862
- usedBytes += stat.size;
4863
- fileCount++;
4864
- } catch {
4865
- }
4866
- }
4867
- }
4868
- }
4869
- await walk2(dir);
4870
- return { usedBytes, fileCount };
4871
- }
4872
- function createStorageProvider(_config) {
4873
- return new LocalStorageProvider();
4874
- }
4875
-
4876
4787
  // src/commands/media.ts
4788
+ import { isToolInstalled } from "@larkup/marketplace/installer";
4789
+ import { loadTool } from "@larkup/marketplace/loader";
4790
+ import { createStorageProvider } from "@larkup/marketplace/storage";
4877
4791
  var MIME_TYPES = {
4878
4792
  ".png": "image/png",
4879
4793
  ".jpg": "image/jpeg",
@@ -4915,12 +4829,12 @@ async function processMediaCommand(inputs, options) {
4915
4829
  const assets = await Promise.all(
4916
4830
  mediaFiles.map(async (filePath) => {
4917
4831
  const type = mediaType(filePath);
4918
- const stat = await fs9.stat(filePath);
4919
- const extension = path10.extname(filePath).toLowerCase() || ".bin";
4832
+ const stat = await fs8.stat(filePath);
4833
+ const extension = path9.extname(filePath).toLowerCase() || ".bin";
4920
4834
  const key = `${type}s/${Date.now()}_${Math.random().toString(36).slice(2)}${extension}`;
4921
4835
  const mimeType = MIME_TYPES[extension] || "application/octet-stream";
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 };
4836
+ const storageUri = storage.storeFile ? await storage.storeFile(key, filePath, mimeType) : await storage.store(key, await fs8.readFile(filePath), mimeType);
4837
+ return { type, fileName: path9.basename(filePath), mimeType, storageUri, fileSize: stat.size };
4924
4838
  })
4925
4839
  );
4926
4840
  const stored = await addMediaAssets(assets);
@@ -5002,7 +4916,7 @@ async function processMediaAsset(asset) {
5002
4916
  if (!tool2.processVideo)
5003
4917
  throw new Error("The installed Video & Audio tool cannot process videos.");
5004
4918
  await update("Extracting video audio", 20);
5005
- const outputDir = await fs9.mkdtemp(path10.join(process.cwd(), ".larkup", "tmp-media-"));
4919
+ const outputDir = await fs8.mkdtemp(path9.join(process.cwd(), ".larkup", "tmp-media-"));
5006
4920
  try {
5007
4921
  const video = await tool2.processVideo(localFile, {
5008
4922
  outputDir,
@@ -5023,7 +4937,7 @@ async function processMediaAsset(asset) {
5023
4937
  dimensions = { width: video.meta.width, height: video.meta.height };
5024
4938
  frameCount = video.frames.length;
5025
4939
  } finally {
5026
- await fs9.rm(outputDir, { recursive: true, force: true }).catch(() => {
4940
+ await fs8.rm(outputDir, { recursive: true, force: true }).catch(() => {
5027
4941
  });
5028
4942
  }
5029
4943
  } else {
@@ -5101,7 +5015,7 @@ async function indexPendingMedia() {
5101
5015
  spinner2.stop(`Indexed ${finalRun.totalChunks} chunks`);
5102
5016
  }
5103
5017
  function mediaType(filePath) {
5104
- const extension = path10.extname(filePath).toLowerCase();
5018
+ const extension = path9.extname(filePath).toLowerCase();
5105
5019
  if ([".png", ".jpg", ".jpeg", ".webp", ".gif"].includes(extension)) return "image";
5106
5020
  if ([".mp4", ".webm", ".mov", ".mkv"].includes(extension)) return "video";
5107
5021
  if ([".mp3", ".m4a", ".wav", ".ogg", ".flac"].includes(extension)) return "audio";
@@ -5188,20 +5102,20 @@ async function indexCommand(inputs, options) {
5188
5102
  }
5189
5103
 
5190
5104
  // src/commands/generate.ts
5191
- import { promises as fs10 } from "fs";
5192
- import path11 from "path";
5105
+ import { promises as fs9 } from "fs";
5106
+ import path10 from "path";
5193
5107
  async function generateCommand(options) {
5194
5108
  await inServerScope(options.server, async () => {
5195
5109
  await requireActive();
5196
5110
  const config = await readConfig();
5197
5111
  if (options.out) {
5198
- const dir = path11.isAbsolute(options.out) ? options.out : path11.join(process.cwd(), options.out);
5112
+ const dir = path10.isAbsolute(options.out) ? options.out : path10.join(process.cwd(), options.out);
5199
5113
  const server2 = generateServer(config);
5200
- await fs10.mkdir(dir, { recursive: true });
5114
+ await fs9.mkdir(dir, { recursive: true });
5201
5115
  for (const f of server2.files) {
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");
5116
+ const dest = path10.join(dir, f.path);
5117
+ await fs9.mkdir(path10.dirname(dest), { recursive: true });
5118
+ await fs9.writeFile(dest, f.contents, "utf8");
5205
5119
  }
5206
5120
  log.success(`Generated ${server2.files.length} files \u2192 ${dir}`);
5207
5121
  } else {
@@ -5506,6 +5420,13 @@ async function settingsCommand(options) {
5506
5420
  }
5507
5421
 
5508
5422
  // src/commands/marketplace.ts
5423
+ import { getAllTools, getToolById } from "@larkup/marketplace/registry";
5424
+ import {
5425
+ getInstalledTool,
5426
+ getInstalledTools,
5427
+ installTool,
5428
+ uninstallTool
5429
+ } from "@larkup/marketplace/installer";
5509
5430
  async function marketplaceCommand(action = "list", toolId) {
5510
5431
  if (action === "list") {
5511
5432
  const [tools, installed] = await Promise.all([getAllTools(), getInstalledTools()]);
@@ -5549,8 +5470,8 @@ async function marketplaceCommand(action = "list", toolId) {
5549
5470
  }
5550
5471
 
5551
5472
  // src/commands/deploy.ts
5552
- import { promises as fs11 } from "fs";
5553
- import path12 from "path";
5473
+ import { promises as fs10 } from "fs";
5474
+ import path11 from "path";
5554
5475
  async function deployCommand(target, options) {
5555
5476
  await inServerScope(options.server, async () => {
5556
5477
  await requireActive();
@@ -5586,13 +5507,13 @@ async function deployCommand(target, options) {
5586
5507
  });
5587
5508
  }
5588
5509
  async function copyGenerated(source, output) {
5589
- const destination = path12.resolve(output);
5590
- const relative = path12.relative(source, destination);
5591
- if (relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative)) {
5510
+ const destination = path11.resolve(output);
5511
+ const relative = path11.relative(source, destination);
5512
+ if (relative === "" || !relative.startsWith("..") && !path11.isAbsolute(relative)) {
5592
5513
  throw new Error("Deployment output must be outside the generated server directory.");
5593
5514
  }
5594
- await fs11.mkdir(destination, { recursive: true });
5595
- await fs11.cp(source, destination, { recursive: true, force: true });
5515
+ await fs10.mkdir(destination, { recursive: true });
5516
+ await fs10.cp(source, destination, { recursive: true, force: true });
5596
5517
  return destination;
5597
5518
  }
5598
5519
 
@@ -5677,8 +5598,8 @@ function openCommandForPlatform(url) {
5677
5598
  }
5678
5599
 
5679
5600
  // src/commands/documents.ts
5680
- import { promises as fs12 } from "fs";
5681
- import path13 from "path";
5601
+ import { promises as fs11 } from "fs";
5602
+ import path12 from "path";
5682
5603
 
5683
5604
  // ../../packages/core/src/corpus-retriever.ts
5684
5605
  function applyFilters(docs, filter) {
@@ -5797,10 +5718,10 @@ async function documentsCommand(action = "list", idOrPath, options) {
5797
5718
  }
5798
5719
  if (action === "export") {
5799
5720
  const format = options.format === "jsonl" ? "jsonl" : "csv";
5800
- const output = path13.resolve(options.out || idOrPath || `corpus.${format}`);
5721
+ const output = path12.resolve(options.out || idOrPath || `corpus.${format}`);
5801
5722
  const content = format === "jsonl" ? await exportCorpusAsJSONL(void 0, Number.MAX_SAFE_INTEGER) : await exportCorpusAsCSV(void 0, Number.MAX_SAFE_INTEGER);
5802
- await fs12.mkdir(path13.dirname(output), { recursive: true });
5803
- await fs12.writeFile(output, content, "utf8");
5723
+ await fs11.mkdir(path12.dirname(output), { recursive: true });
5724
+ await fs11.writeFile(output, content, "utf8");
5804
5725
  log.success(`Exported corpus \u2192 ${output}`);
5805
5726
  return;
5806
5727
  }
@@ -5826,7 +5747,7 @@ ${document.content}`);
5826
5747
  return;
5827
5748
  }
5828
5749
  if (action === "update") {
5829
- const content = options.file ? await fs12.readFile(path13.resolve(options.file), "utf8") : options.text;
5750
+ const content = options.file ? await fs11.readFile(path12.resolve(options.file), "utf8") : options.text;
5830
5751
  const updated = await updateDocument(document.id, {
5831
5752
  title: options.title,
5832
5753
  content,
@@ -112,7 +112,7 @@ var LanceDBAdapter = class {
112
112
  source: r.source,
113
113
  documentId: r.documentId,
114
114
  chunkIndex: r.chunkIndex,
115
- metadata: r.metadata ? JSON.stringify(r.metadata) : void 0
115
+ metadata: JSON.stringify(r.metadata ?? {})
116
116
  };
117
117
  }
118
118
  async upsert(records) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larkup/cli",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -28,9 +28,9 @@
28
28
  "cli-table3": "^0.6.5",
29
29
  "commander": "^15.0.0",
30
30
  "zod": "^4.4.3",
31
- "@larkup/core": "0.2.2",
32
- "@larkup/vector-stores": "0.1.22",
33
- "@larkup/marketplace": "0.1.5"
31
+ "@larkup/core": "0.2.4",
32
+ "@larkup/marketplace": "0.1.7",
33
+ "@larkup/vector-stores": "0.1.23"
34
34
  },
35
35
  "devDependencies": {
36
36
  "tsup": "^8.0.0",
@@ -1,231 +0,0 @@
1
- // ../../packages/marketplace/src/tool-registry.ts
2
- import { promises as fs } from "fs";
3
- import path from "path";
4
-
5
- // ../../packages/marketplace/src/types.ts
6
- var DEFAULT_HUB_URL = process.env.LARKUP_HUB_URL ?? "https://hub.larkup.de";
7
- var STORAGE_WARNING_THRESHOLDS = [
8
- 1 * 1024 * 1024 * 1024,
9
- // 1 GB
10
- 5 * 1024 * 1024 * 1024,
11
- // 5 GB
12
- 10 * 1024 * 1024 * 1024
13
- // 10 GB
14
- ];
15
-
16
- // ../../packages/marketplace/src/tool-registry.ts
17
- var cachedRegistry = null;
18
- async function discoverLocalManifests() {
19
- const registry = {};
20
- const searchPaths = [
21
- // Monorepo development: packages/tools/*
22
- path.resolve(process.cwd(), "packages", "tools"),
23
- // Next.js dev server runs in apps/web
24
- path.resolve(process.cwd(), "..", "..", "packages", "tools"),
25
- // Installed tools: .larkup/tools/node_modules/@larkup
26
- path.resolve(process.cwd(), ".larkup", "tools", "node_modules", "@larkup"),
27
- path.resolve(process.cwd(), "..", "..", ".larkup", "tools", "node_modules", "@larkup")
28
- ];
29
- for (const searchPath of searchPaths) {
30
- try {
31
- const entries = await fs.readdir(searchPath, { withFileTypes: true });
32
- for (const entry of entries) {
33
- if (!entry.isDirectory()) continue;
34
- const manifestPath = path.join(searchPath, entry.name, "tool.manifest.json");
35
- try {
36
- const raw = await fs.readFile(manifestPath, "utf8");
37
- const manifest = JSON.parse(raw);
38
- if (manifest.id) {
39
- registry[manifest.id] = manifest;
40
- }
41
- } catch {
42
- }
43
- }
44
- } catch {
45
- }
46
- }
47
- return registry;
48
- }
49
- async function fetchHubCatalog(hubUrl) {
50
- const baseUrl = hubUrl ?? DEFAULT_HUB_URL;
51
- try {
52
- const controller = new AbortController();
53
- const timeout = setTimeout(() => controller.abort(), 5e3);
54
- const res = await fetch(`${baseUrl}/v1/tools`, {
55
- signal: controller.signal,
56
- headers: { Accept: "application/json" }
57
- });
58
- clearTimeout(timeout);
59
- if (!res.ok) return {};
60
- const data = await res.json();
61
- const registry = {};
62
- for (const tool of data.tools ?? []) {
63
- if (tool.id) registry[tool.id] = tool;
64
- }
65
- return registry;
66
- } catch {
67
- return {};
68
- }
69
- }
70
- async function fetchToolFromHub(toolId, hubUrl) {
71
- const baseUrl = hubUrl ?? DEFAULT_HUB_URL;
72
- try {
73
- const controller = new AbortController();
74
- const timeout = setTimeout(() => controller.abort(), 5e3);
75
- const res = await fetch(`${baseUrl}/v1/tools/${toolId}`, {
76
- signal: controller.signal,
77
- headers: { Accept: "application/json" }
78
- });
79
- clearTimeout(timeout);
80
- if (!res.ok) return null;
81
- const data = await res.json();
82
- return data.tool ?? null;
83
- } catch {
84
- return null;
85
- }
86
- }
87
- var FALLBACK_REGISTRY = {
88
- "video-audio": {
89
- id: "video-audio",
90
- name: "Video & Audio",
91
- description: "Index video and audio files with transcription and frame analysis.",
92
- category: "media",
93
- version: "0.2.1",
94
- pricing: "free",
95
- emoji: "\u{1F3AC}",
96
- icon: "Film",
97
- packageName: "@larkup/tool-video-audio",
98
- installSize: "~15 MB",
99
- systemDeps: ["ffmpeg"],
100
- author: "Larkup",
101
- capabilities: [
102
- "video-indexing",
103
- "audio-indexing",
104
- "transcription",
105
- "frame-extraction",
106
- "youtube-import"
107
- ],
108
- tags: ["transcription", "ffmpeg", "video", "audio", "whisper"],
109
- downloads: 0,
110
- repositoryUrl: "https://github.com/Larkup-AI/larkup-rag",
111
- license: "Apache-2.0",
112
- updatedAt: "2026-07-20",
113
- configSchema: [
114
- {
115
- key: "frameInterval",
116
- label: "Frame extraction interval (seconds)",
117
- type: "text",
118
- defaultValue: "10",
119
- help: "Baseline cadence for duration-aware coverage frames; scene changes and a reserved ending pass are added separately."
120
- },
121
- {
122
- key: "audioProvider",
123
- label: "Audio Provider",
124
- type: "select",
125
- help: "Choose the provider used only for audio and video transcription.",
126
- options: [
127
- { label: "OpenAI", value: "openai" },
128
- { label: "Groq", value: "groq" },
129
- { label: "Deepgram", value: "deepgram" },
130
- { label: "ElevenLabs", value: "elevenlabs" },
131
- { label: "Local Whisper", value: "local" }
132
- ]
133
- },
134
- {
135
- key: "audioLanguage",
136
- label: "Transcription language",
137
- type: "text",
138
- defaultValue: "auto",
139
- help: "Use auto to infer Arabic from the media title and use provider detection otherwise. Enter a language code such as ar, ar-EG, en, or de to override it."
140
- },
141
- {
142
- key: "audioApiKey",
143
- label: "Audio API Key",
144
- type: "password",
145
- help: "Required for the selected audio provider and kept separate from the chat model key."
146
- }
147
- ]
148
- },
149
- "clip-embeddings": {
150
- id: "clip-embeddings",
151
- name: "CLIP Image Search",
152
- description: "Direct image-to-image similarity search using CLIP/SigLIP embeddings.",
153
- category: "embedding",
154
- version: "0.1.0",
155
- pricing: "free",
156
- emoji: "\u{1F50D}",
157
- icon: "ScanEye",
158
- packageName: "@larkup/tool-clip-embeddings",
159
- installSize: "~200 MB",
160
- author: "Larkup",
161
- capabilities: ["clip-embeddings", "image-similarity"],
162
- tags: ["clip", "siglip", "image-search", "visual-similarity"],
163
- downloads: 0,
164
- repositoryUrl: "https://github.com/Larkup-AI/larkup-rag",
165
- license: "Apache-2.0",
166
- updatedAt: "2026-07-20",
167
- comingSoon: true
168
- },
169
- "doc-editor": {
170
- id: "doc-editor",
171
- name: "Document Editor",
172
- description: "AI-powered form filling and document editing with Canvas-style live preview.",
173
- category: "utility",
174
- version: "0.2.3",
175
- pricing: "free",
176
- emoji: "\u{1F4DD}",
177
- icon: "FileEdit",
178
- packageName: "@larkup/tool-doc-editor",
179
- installSize: "~5 MB",
180
- systemDeps: ["docker"],
181
- author: "Larkup",
182
- capabilities: ["document-editing", "form-filling", "document-preview"],
183
- tags: ["pdf", "docx", "pptx", "form", "canvas", "editor", "fill"],
184
- downloads: 0,
185
- repositoryUrl: "https://github.com/Larkup-AI/larkup-rag",
186
- license: "Apache-2.0",
187
- updatedAt: "2026-07-20"
188
- }
189
- };
190
- async function buildRegistry(opts) {
191
- if (cachedRegistry) return cachedRegistry;
192
- const registry = { ...FALLBACK_REGISTRY };
193
- const localManifests = await discoverLocalManifests();
194
- Object.assign(registry, localManifests);
195
- if (!opts?.skipHub) {
196
- const hubCatalog = await fetchHubCatalog(opts?.hubUrl);
197
- Object.assign(registry, hubCatalog);
198
- }
199
- cachedRegistry = registry;
200
- return registry;
201
- }
202
- function invalidateRegistryCache() {
203
- cachedRegistry = null;
204
- }
205
- async function getAllTools() {
206
- const registry = await buildRegistry();
207
- return Object.values(registry).sort((a, b) => a.name.localeCompare(b.name));
208
- }
209
- async function getToolById(id) {
210
- const registry = await buildRegistry();
211
- return registry[id];
212
- }
213
- async function getToolsWithCapability(capability) {
214
- const all = await getAllTools();
215
- return all.filter((t) => t.capabilities.includes(capability));
216
- }
217
- async function getAllCategories() {
218
- const all = await getAllTools();
219
- const cats = new Set(all.map((t) => t.category));
220
- return Array.from(cats).sort();
221
- }
222
-
223
- export {
224
- fetchToolFromHub,
225
- buildRegistry,
226
- invalidateRegistryCache,
227
- getAllTools,
228
- getToolById,
229
- getToolsWithCapability,
230
- getAllCategories
231
- };
@@ -1,412 +0,0 @@
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
- };
@@ -1,14 +0,0 @@
1
- import {
2
- hasCapability,
3
- isToolLoaded,
4
- loadTool,
5
- unloadTool
6
- } from "./chunk-TH2MPRMA.js";
7
- import "./chunk-P5JVXA7X.js";
8
- import "./chunk-3RG5ZIWI.js";
9
- export {
10
- hasCapability,
11
- isToolLoaded,
12
- loadTool,
13
- unloadTool
14
- };
@@ -1,19 +0,0 @@
1
- import {
2
- buildRegistry,
3
- fetchToolFromHub,
4
- getAllCategories,
5
- getAllTools,
6
- getToolById,
7
- getToolsWithCapability,
8
- invalidateRegistryCache
9
- } from "./chunk-P5JVXA7X.js";
10
- import "./chunk-3RG5ZIWI.js";
11
- export {
12
- buildRegistry,
13
- fetchToolFromHub,
14
- getAllCategories,
15
- getAllTools,
16
- getToolById,
17
- getToolsWithCapability,
18
- invalidateRegistryCache
19
- };