@larkup/cli 0.2.4 → 0.2.6

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.
@@ -0,0 +1,424 @@
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
+ // Native tool dependencies can take longer under Docker Desktop's
150
+ // amd64 emulation. Let the request finish instead of reporting a
151
+ // misleading connection error part way through installation.
152
+ timeout: target === "docker" ? 3e5 : 12e4,
153
+ env: {
154
+ ...process.env,
155
+ NODE_ENV: "production",
156
+ // The Docker image runs as an unprivileged user. Keep npm's cache
157
+ // beside the isolated install so a named .larkup volume is always
158
+ // writable and installing a Marketplace tool does not fail before
159
+ // it reaches the registry.
160
+ HOME: process.env.HOME && process.env.HOME !== "/nonexistent" ? process.env.HOME : toolsDir,
161
+ npm_config_cache: path.join(toolsDir, ".npm-cache"),
162
+ npm_config_update_notifier: "false"
163
+ }
164
+ });
165
+ if (stderr && !stderr.includes("npm warn")) {
166
+ console.warn(`[marketplace] Install stderr: ${stderr}`);
167
+ }
168
+ };
169
+ try {
170
+ await install(`${packageName}@${version}`);
171
+ } catch (err) {
172
+ const message = err instanceof Error ? err.message : "Install command failed";
173
+ if (!message.includes("ETARGET") && !message.includes("No matching version found")) {
174
+ throw new Error(`Failed to install ${packageName}: ${message}`);
175
+ }
176
+ onProgress?.(
177
+ `Catalog version ${version} is unavailable; installing the latest published version\u2026`
178
+ );
179
+ try {
180
+ await install(`${packageName}@latest`);
181
+ } catch (latestErr) {
182
+ const latestMessage = latestErr instanceof Error ? latestErr.message : "Install command failed";
183
+ throw new Error(`Failed to install ${packageName}: ${latestMessage}`);
184
+ }
185
+ }
186
+ const resolvedPath = path.join(toolsDir, "node_modules", packageName);
187
+ try {
188
+ await fs.access(resolvedPath);
189
+ } catch {
190
+ throw new Error(
191
+ `Package ${packageName} was installed but could not be found at ${resolvedPath}`
192
+ );
193
+ }
194
+ const packageJson = JSON.parse(
195
+ await fs.readFile(path.join(resolvedPath, "package.json"), "utf8")
196
+ );
197
+ return { resolvedPath, version: packageJson.version ?? version };
198
+ }
199
+ case "serverless": {
200
+ onProgress?.("Serverless environment detected \u2014 queuing for next deploy");
201
+ const pendingPath = path.join(toolsDir, "pending-tools.json");
202
+ let pending = { tools: [] };
203
+ try {
204
+ const raw = await fs.readFile(pendingPath, "utf8");
205
+ pending = JSON.parse(raw);
206
+ } catch {
207
+ }
208
+ pending.tools.push({ packageName, version });
209
+ await ensureDir();
210
+ await fs.writeFile(pendingPath, JSON.stringify(pending, null, 2), "utf8");
211
+ return { resolvedPath: path.join(toolsDir, "node_modules", packageName), version };
212
+ }
213
+ case "sandbox": {
214
+ onProgress?.("Installing in sandbox environment");
215
+ await ensureToolsPackageJson();
216
+ const installCmd = `npm install ${packageName}@${version} --prefix "${toolsDir}" --save --no-audit --no-fund`;
217
+ await execAsync(installCmd, { cwd: toolsDir, timeout: 12e4 });
218
+ return { resolvedPath: path.join(toolsDir, "node_modules", packageName), version };
219
+ }
220
+ default:
221
+ throw new Error(`Unsupported deployment target: ${target}`);
222
+ }
223
+ }
224
+ async function resolveManifest(toolId) {
225
+ const descriptor = await getToolById(toolId);
226
+ if (descriptor) return descriptor;
227
+ throw new Error(`Unknown tool: ${toolId}. Not found in local registry or Hub.`);
228
+ }
229
+ async function isWorkspaceTool(packageName) {
230
+ try {
231
+ const { stdout } = await execAsync(`pnpm ls -r --depth -1 --json 2>/dev/null || true`, {
232
+ cwd: process.cwd(),
233
+ timeout: 1e4
234
+ });
235
+ const data = JSON.parse(stdout || "[]");
236
+ return Array.isArray(data) && data.some((pkg) => pkg.name === packageName);
237
+ } catch {
238
+ return false;
239
+ }
240
+ }
241
+ async function installTool(toolId, onProgress) {
242
+ const report = (stage, percent, message) => {
243
+ onProgress?.({ toolId, stage, percent, message });
244
+ };
245
+ try {
246
+ report("checking-deps", 5, "Resolving tool manifest\u2026");
247
+ const descriptor = await resolveManifest(toolId);
248
+ if (descriptor.comingSoon) {
249
+ throw new Error(`${descriptor.name} is coming soon.`);
250
+ }
251
+ report("checking-deps", 15, "Checking system dependencies\u2026");
252
+ const missing = await checkSystemDeps(toolId);
253
+ if (missing.length > 0) {
254
+ const msg = `Missing system dependencies: ${missing.join(", ")}. Please install them first.`;
255
+ report("failed", 0, msg);
256
+ throw new Error(msg);
257
+ }
258
+ const isWorkspace = await isWorkspaceTool(descriptor.packageName);
259
+ const target = detectDeploymentTarget();
260
+ let resolvedPath;
261
+ let installedVersion = descriptor.version;
262
+ let source;
263
+ if (isWorkspace) {
264
+ report("downloading", 30, "Resolving workspace package\u2026");
265
+ try {
266
+ const modulePath = __require.resolve(descriptor.packageName, {
267
+ paths: [process.cwd()]
268
+ });
269
+ resolvedPath = path.dirname(modulePath);
270
+ } catch {
271
+ resolvedPath = path.join(process.cwd(), "node_modules", descriptor.packageName);
272
+ }
273
+ source = "local";
274
+ report("installing", 60, "Workspace package resolved.");
275
+ } else {
276
+ report("downloading", 30, `Downloading ${descriptor.packageName}@${descriptor.version}\u2026`);
277
+ const installed = await execInstall(
278
+ descriptor.packageName,
279
+ descriptor.version,
280
+ target,
281
+ (msg) => report("downloading", 50, msg)
282
+ );
283
+ resolvedPath = installed.resolvedPath;
284
+ installedVersion = installed.version;
285
+ source = target === "sandbox" ? "sandbox" : "registry";
286
+ report("installing", 70, "Package installed.");
287
+ }
288
+ report("installing", 80, "Registering tool\u2026");
289
+ await ensureDir();
290
+ const manifest = await readManifest();
291
+ const existing = manifest.tools.findIndex((t) => t.id === toolId);
292
+ const entry = {
293
+ id: toolId,
294
+ version: installedVersion,
295
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
296
+ packageName: descriptor.packageName,
297
+ resolvedPath,
298
+ source,
299
+ config: buildDefaultConfig(descriptor)
300
+ };
301
+ if (existing >= 0) {
302
+ manifest.tools[existing] = entry;
303
+ } else {
304
+ manifest.tools.push(entry);
305
+ }
306
+ manifest.downloadCounts[toolId] = (manifest.downloadCounts[toolId] ?? 0) + 1;
307
+ await writeManifest(manifest);
308
+ notifyHubInstall(toolId).catch(() => {
309
+ });
310
+ invalidateRegistryCache();
311
+ report("completed", 100, "Installed successfully.");
312
+ } catch (err) {
313
+ const message = err instanceof Error ? err.message : "Installation failed.";
314
+ report("failed", 0, message);
315
+ throw err;
316
+ }
317
+ }
318
+ async function uninstallTool(toolId) {
319
+ const manifest = await readManifest();
320
+ const tool = manifest.tools.find((t) => t.id === toolId);
321
+ manifest.tools = manifest.tools.filter((t) => t.id !== toolId);
322
+ await writeManifest(manifest);
323
+ if (tool?.source === "registry" && tool.packageName) {
324
+ try {
325
+ const toolsDir = getToolsDir();
326
+ await execAsync(`npm uninstall ${tool.packageName} --prefix "${toolsDir}"`, {
327
+ cwd: toolsDir,
328
+ timeout: 3e4
329
+ });
330
+ } catch {
331
+ }
332
+ }
333
+ invalidateRegistryCache();
334
+ const { unloadTool: unloadTool2 } = await import("./tool-loader-QU6HBUCA.js");
335
+ unloadTool2(toolId);
336
+ }
337
+ async function notifyHubInstall(toolId) {
338
+ const hubUrl = process.env.LARKUP_HUB_URL ?? "https://hub.larkup.de";
339
+ try {
340
+ const controller = new AbortController();
341
+ const timeout = setTimeout(() => controller.abort(), 3e3);
342
+ await fetch(`${hubUrl}/v1/tools/${toolId}/installed`, {
343
+ method: "POST",
344
+ signal: controller.signal,
345
+ headers: { "Content-Type": "application/json" }
346
+ });
347
+ clearTimeout(timeout);
348
+ } catch {
349
+ }
350
+ }
351
+ function buildDefaultConfig(descriptor) {
352
+ const config = {};
353
+ for (const field of descriptor.configSchema ?? []) {
354
+ if (field.defaultValue !== void 0) {
355
+ config[field.key] = field.defaultValue;
356
+ }
357
+ }
358
+ return config;
359
+ }
360
+
361
+ // ../../packages/marketplace/src/tool-loader.ts
362
+ var moduleCache = /* @__PURE__ */ new Map();
363
+ async function loadTool(toolId) {
364
+ if (moduleCache.has(toolId)) return moduleCache.get(toolId);
365
+ const installed = await getInstalledTool(toolId);
366
+ if (!installed) return null;
367
+ try {
368
+ const importPath = await resolveImportPath(installed);
369
+ const mod = await import(
370
+ /* webpackIgnore: true */
371
+ importPath
372
+ );
373
+ moduleCache.set(toolId, mod);
374
+ return mod;
375
+ } catch (err) {
376
+ console.error(`[marketplace] Failed to load tool "${toolId}":`, err);
377
+ return null;
378
+ }
379
+ }
380
+ async function resolveImportPath(installed) {
381
+ switch (installed.source) {
382
+ case "local":
383
+ return installed.packageName;
384
+ case "registry":
385
+ case "sandbox":
386
+ return resolvePackageEntry(installed.resolvedPath);
387
+ default:
388
+ return installed.packageName;
389
+ }
390
+ }
391
+ async function resolvePackageEntry(packageDir) {
392
+ const manifestPath = path2.join(packageDir, "package.json");
393
+ const manifest = JSON.parse(await fs2.readFile(manifestPath, "utf8"));
394
+ const rootExport = typeof manifest.exports === "object" ? manifest.exports["."] : manifest.exports;
395
+ const entry = (typeof rootExport === "object" ? rootExport.import ?? rootExport.default : rootExport) ?? manifest.main ?? "index.js";
396
+ return pathToFileURL(path2.resolve(packageDir, entry)).href;
397
+ }
398
+ function isToolLoaded(toolId) {
399
+ return moduleCache.has(toolId);
400
+ }
401
+ function unloadTool(toolId) {
402
+ moduleCache.delete(toolId);
403
+ }
404
+ async function hasCapability(capability) {
405
+ const { getToolsWithCapability } = await import("./tool-registry-W3JOGDE3.js");
406
+ const tools = await getToolsWithCapability(capability);
407
+ for (const t of tools) {
408
+ const installed = await getInstalledTool(t.id);
409
+ if (installed) return true;
410
+ }
411
+ return false;
412
+ }
413
+
414
+ export {
415
+ loadTool,
416
+ isToolLoaded,
417
+ unloadTool,
418
+ hasCapability,
419
+ getInstalledTools,
420
+ isToolInstalled,
421
+ getInstalledTool,
422
+ installTool,
423
+ uninstallTool
424
+ };
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-VY6I42DP.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.4",
37
+ version: "0.2.6",
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(path15) {
948
- if (path15.endsWith(".json")) return "json";
949
- if (path15.endsWith(".mjs") || path15.endsWith(".js")) return "javascript";
950
- if (path15.endsWith(".md")) return "markdown";
951
- if (path15.endsWith("Dockerfile")) return "dockerfile";
952
- if (path15.endsWith(".yml") || path15.endsWith(".yaml")) return "yaml";
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 fs14 = __require("fs");
2270
- const path15 = __require("path");
2271
- const faviconPath = path15.resolve(process.cwd(), "public/favicon2.ico");
2272
- if (fs14.existsSync(faviconPath)) {
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: fs14.readFileSync(faviconPath).toString("base64"),
2282
+ contents: fs13.readFileSync(faviconPath).toString("base64"),
2276
2283
  language: "ico",
2277
2284
  encoding: "base64"
2278
2285
  });
2279
2286
  } else {
2280
- const webFaviconPath = path15.resolve(process.cwd(), "apps/web/public/favicon2.ico");
2281
- if (fs14.existsSync(webFaviconPath)) {
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: fs14.readFileSync(webFaviconPath).toString("base64"),
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(path15) {
2299
- if (path15.endsWith(".json")) return "json";
2300
- if (path15.endsWith(".mjs") || path15.endsWith(".js")) return "javascript";
2301
- if (path15.endsWith(".md")) return "markdown";
2302
- if (path15.endsWith("Dockerfile")) return "dockerfile";
2303
- if (path15.endsWith(".yml") || path15.endsWith(".yaml")) return "yaml";
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 fs14 = __require("fs");
3318
- const path15 = __require("path");
3319
- const faviconPath = path15.resolve(process.cwd(), "public/favicon2.ico");
3320
- if (fs14.existsSync(faviconPath)) {
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: fs14.readFileSync(faviconPath).toString("base64"),
3330
+ contents: fs13.readFileSync(faviconPath).toString("base64"),
3324
3331
  language: "ico",
3325
3332
  encoding: "base64"
3326
3333
  });
3327
3334
  } else {
3328
- const webFaviconPath = path15.resolve(process.cwd(), "apps/web/public/favicon2.ico");
3329
- if (fs14.existsSync(webFaviconPath)) {
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: fs14.readFileSync(webFaviconPath).toString("base64"),
3339
+ contents: fs13.readFileSync(webFaviconPath).toString("base64"),
3333
3340
  language: "ico",
3334
3341
  encoding: "base64"
3335
3342
  });
@@ -3406,6 +3413,18 @@ async function writeState(state) {
3406
3413
  if (file) await fs2.writeFile(file, JSON.stringify(state, null, 2), "utf8");
3407
3414
  return state;
3408
3415
  }
3416
+ async function withActiveVectorTable(config) {
3417
+ const server = await getActiveServer();
3418
+ if (!server || config.vectorStore && config.vectorStore !== "lancedb") return config;
3419
+ const safeId = server.id.replace(/[^a-zA-Z0-9_-]/g, "_");
3420
+ return {
3421
+ ...config,
3422
+ storeConfig: {
3423
+ ...config.storeConfig,
3424
+ tableName: `documents_${safeId}`
3425
+ }
3426
+ };
3427
+ }
3409
3428
  function pidAlive(pid) {
3410
3429
  if (!pid) return false;
3411
3430
  try {
@@ -3416,7 +3435,7 @@ function pidAlive(pid) {
3416
3435
  }
3417
3436
  }
3418
3437
  async function emitToDisk(config) {
3419
- const server = generateServer(config);
3438
+ const server = generateServer(await withActiveVectorTable(config));
3420
3439
  const dir = await outDir(true);
3421
3440
  if (!dir) throw new Error("No active server to emit to.");
3422
3441
  await fs2.mkdir(dir, { recursive: true });
@@ -3452,7 +3471,8 @@ async function startServer(config, serverApiKey) {
3452
3471
  }
3453
3472
  }
3454
3473
  await killPort(port);
3455
- const dir = await emitToDisk(config);
3474
+ const runtimeConfig = await withActiveVectorTable(config);
3475
+ const dir = await emitToDisk(runtimeConfig);
3456
3476
  try {
3457
3477
  await execAsync("npm install --omit=dev", {
3458
3478
  cwd: dir,
@@ -3467,7 +3487,7 @@ async function startServer(config, serverApiKey) {
3467
3487
  const message = err instanceof Error ? err.message : "npm install failed";
3468
3488
  return writeState({ ...emptyState(port), lastError: message });
3469
3489
  }
3470
- const dbPath = config.storeConfig.dbPath || "./.larkup/lancedb";
3490
+ const dbPath = runtimeConfig.storeConfig.dbPath || "./.larkup/lancedb";
3471
3491
  const absDb = path2.isAbsolute(dbPath) ? dbPath : path2.join(process.cwd(), dbPath);
3472
3492
  const logPath = path2.join(dir, "server.log");
3473
3493
  await fs2.writeFile(logPath, "", "utf8");
@@ -3480,35 +3500,35 @@ async function startServer(config, serverApiKey) {
3480
3500
  env: {
3481
3501
  ...process.env,
3482
3502
  PORT: String(port),
3483
- TOP_K: String(config.topK),
3503
+ TOP_K: String(runtimeConfig.topK),
3484
3504
  SERVER_API_KEY: serverApiKey || "",
3485
- EMBEDDING_API_KEY: config.embeddingApiKey || process.env.EMBEDDING_API_KEY || process.env.OPENAI_API_KEY || "",
3486
- CHAT_API_KEY: config.chatApiKey || config.customChatModels?.find(
3487
- (model) => model.modelName === config.chatModelId?.replace(/^custom:/, "")
3505
+ EMBEDDING_API_KEY: runtimeConfig.embeddingApiKey || process.env.EMBEDDING_API_KEY || process.env.OPENAI_API_KEY || "",
3506
+ CHAT_API_KEY: runtimeConfig.chatApiKey || runtimeConfig.customChatModels?.find(
3507
+ (model) => model.modelName === runtimeConfig.chatModelId?.replace(/^custom:/, "")
3488
3508
  )?.apiKey || process.env.CHAT_API_KEY || process.env.OPENAI_API_KEY || "",
3489
- CHAT_MODEL: process.env.CHAT_MODEL || resolveChatModel(config),
3490
- CHAT_BASE_URL: config.customChatModels?.find(
3491
- (model) => model.modelName === config.chatModelId?.replace(/^custom:/, "")
3509
+ CHAT_MODEL: process.env.CHAT_MODEL || resolveChatModel(runtimeConfig),
3510
+ CHAT_BASE_URL: runtimeConfig.customChatModels?.find(
3511
+ (model) => model.modelName === runtimeConfig.chatModelId?.replace(/^custom:/, "")
3492
3512
  )?.baseUrl || process.env.CHAT_BASE_URL || "",
3493
- OPENAI_API_KEY: config.embeddingApiKey || config.chatApiKey || process.env.OPENAI_API_KEY || "",
3494
- ANTHROPIC_API_KEY: config.chatApiKey || config.embeddingApiKey || process.env.ANTHROPIC_API_KEY || "",
3495
- COHERE_API_KEY: config.embeddingApiKey || config.chatApiKey || process.env.COHERE_API_KEY || "",
3496
- GOOGLE_GENERATIVE_AI_API_KEY: config.embeddingApiKey || config.chatApiKey || process.env.GOOGLE_GENERATIVE_AI_API_KEY || "",
3497
- PINECONE_API_KEY: config.storeConfig.apiKey || "",
3498
- PINECONE_INDEX: config.storeConfig.indexName || "",
3499
- PINECONE_NAMESPACE: config.storeConfig.namespace || "",
3500
- PINECONE_SPARSE_MODEL: config.storeConfig.sparseModel || "",
3501
- PINECONE_SPARSE_INDEX: config.storeConfig.sparseIndexName || "",
3502
- LANCEDB_MODE: config.storeConfig.mode || "local",
3513
+ OPENAI_API_KEY: runtimeConfig.embeddingApiKey || runtimeConfig.chatApiKey || process.env.OPENAI_API_KEY || "",
3514
+ ANTHROPIC_API_KEY: runtimeConfig.chatApiKey || runtimeConfig.embeddingApiKey || process.env.ANTHROPIC_API_KEY || "",
3515
+ COHERE_API_KEY: runtimeConfig.embeddingApiKey || runtimeConfig.chatApiKey || process.env.COHERE_API_KEY || "",
3516
+ GOOGLE_GENERATIVE_AI_API_KEY: runtimeConfig.embeddingApiKey || runtimeConfig.chatApiKey || process.env.GOOGLE_GENERATIVE_AI_API_KEY || "",
3517
+ PINECONE_API_KEY: runtimeConfig.storeConfig.apiKey || "",
3518
+ PINECONE_INDEX: runtimeConfig.storeConfig.indexName || "",
3519
+ PINECONE_NAMESPACE: runtimeConfig.storeConfig.namespace || "",
3520
+ PINECONE_SPARSE_MODEL: runtimeConfig.storeConfig.sparseModel || "",
3521
+ PINECONE_SPARSE_INDEX: runtimeConfig.storeConfig.sparseIndexName || "",
3522
+ LANCEDB_MODE: runtimeConfig.storeConfig.mode || "local",
3503
3523
  LANCEDB_PATH: absDb,
3504
- LANCEDB_URI: config.storeConfig.uri || "",
3505
- LANCEDB_API_KEY: config.storeConfig.apiKey || "",
3506
- LANCEDB_S3_URI: config.storeConfig.s3Uri || "",
3507
- AWS_ENDPOINT: config.storeConfig.s3Endpoint || "",
3508
- AWS_REGION: config.storeConfig.s3Region || "",
3509
- AWS_ACCESS_KEY_ID: config.storeConfig.s3AccessKeyId || "",
3510
- AWS_SECRET_ACCESS_KEY: config.storeConfig.s3SecretAccessKey || "",
3511
- LANCEDB_TABLE: config.storeConfig.tableName || "documents"
3524
+ LANCEDB_URI: runtimeConfig.storeConfig.uri || "",
3525
+ LANCEDB_API_KEY: runtimeConfig.storeConfig.apiKey || "",
3526
+ LANCEDB_S3_URI: runtimeConfig.storeConfig.s3Uri || "",
3527
+ AWS_ENDPOINT: runtimeConfig.storeConfig.s3Endpoint || "",
3528
+ AWS_REGION: runtimeConfig.storeConfig.s3Region || "",
3529
+ AWS_ACCESS_KEY_ID: runtimeConfig.storeConfig.s3AccessKeyId || "",
3530
+ AWS_SECRET_ACCESS_KEY: runtimeConfig.storeConfig.s3SecretAccessKey || "",
3531
+ LANCEDB_TABLE: runtimeConfig.storeConfig.tableName || "documents"
3512
3532
  }
3513
3533
  });
3514
3534
  child.unref();
@@ -4633,8 +4653,8 @@ async function readTextFiles(files) {
4633
4653
  }
4634
4654
 
4635
4655
  // src/commands/media.ts
4636
- import { promises as fs10 } from "fs";
4637
- import path11 from "path";
4656
+ import { promises as fs9 } from "fs";
4657
+ import path10 from "path";
4638
4658
 
4639
4659
  // ../../packages/core/src/media-store.ts
4640
4660
  import { promises as fs7 } from "fs";
@@ -4776,406 +4796,44 @@ function claimMediaAsset(id) {
4776
4796
  });
4777
4797
  }
4778
4798
 
4779
- // ../../packages/marketplace/src/tool-installer.ts
4799
+ // ../../packages/marketplace/src/storage-provider.ts
4780
4800
  import { promises as fs8 } from "fs";
4781
4801
  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
4802
  var LocalStorageProvider = class {
5145
4803
  id = "local";
5146
4804
  name = "Local Storage";
5147
4805
  async mediaDir() {
5148
4806
  const dataDir = await requireDataDir();
5149
- const dir = path10.join(dataDir, "media");
5150
- await fs9.mkdir(dir, { recursive: true });
4807
+ const dir = path9.join(dataDir, "media");
4808
+ await fs8.mkdir(dir, { recursive: true });
5151
4809
  return dir;
5152
4810
  }
5153
4811
  async store(key, data, _mimeType) {
5154
4812
  const dir = await this.mediaDir();
5155
- const filePath = path10.join(dir, key);
5156
- await fs9.mkdir(path10.dirname(filePath), { recursive: true });
5157
- await fs9.writeFile(filePath, data);
4813
+ const filePath = path9.join(dir, key);
4814
+ await fs8.mkdir(path9.dirname(filePath), { recursive: true });
4815
+ await fs8.writeFile(filePath, data);
5158
4816
  return `local://${key}`;
5159
4817
  }
5160
4818
  async storeFile(key, sourcePath, _mimeType) {
5161
4819
  const dir = await this.mediaDir();
5162
- const filePath = path10.join(dir, key);
5163
- await fs9.mkdir(path10.dirname(filePath), { recursive: true });
5164
- await fs9.copyFile(sourcePath, filePath);
4820
+ const filePath = path9.join(dir, key);
4821
+ await fs8.mkdir(path9.dirname(filePath), { recursive: true });
4822
+ await fs8.copyFile(sourcePath, filePath);
5165
4823
  return `local://${key}`;
5166
4824
  }
5167
4825
  async retrieve(uri) {
5168
4826
  const filePath = await this.resolvePath(uri);
5169
4827
  if (!filePath) throw new Error(`Unsupported local storage URI: ${uri}`);
5170
- return fs9.readFile(filePath);
4828
+ return fs8.readFile(filePath);
5171
4829
  }
5172
4830
  async resolvePath(uri) {
5173
4831
  if (!uri.startsWith("local://")) return void 0;
5174
4832
  const key = uri.slice("local://".length);
5175
4833
  const dir = await this.mediaDir();
5176
- const filePath = path10.resolve(dir, key);
5177
- const relative = path10.relative(dir, filePath);
5178
- if (relative.startsWith("..") || path10.isAbsolute(relative)) {
4834
+ const filePath = path9.resolve(dir, key);
4835
+ const relative = path9.relative(dir, filePath);
4836
+ if (relative.startsWith("..") || path9.isAbsolute(relative)) {
5179
4837
  throw new Error("Invalid local storage URI");
5180
4838
  }
5181
4839
  return filePath;
@@ -5184,7 +4842,7 @@ var LocalStorageProvider = class {
5184
4842
  const key = uri.replace("local://", "");
5185
4843
  const dir = await this.mediaDir();
5186
4844
  try {
5187
- await fs9.unlink(path10.join(dir, key));
4845
+ await fs8.unlink(path9.join(dir, key));
5188
4846
  } catch {
5189
4847
  }
5190
4848
  }
@@ -5203,17 +4861,17 @@ async function computeDirStats(dir) {
5203
4861
  async function walk2(d) {
5204
4862
  let entries;
5205
4863
  try {
5206
- entries = await fs9.readdir(d, { withFileTypes: true });
4864
+ entries = await fs8.readdir(d, { withFileTypes: true });
5207
4865
  } catch {
5208
4866
  return;
5209
4867
  }
5210
4868
  for (const entry of entries) {
5211
- const full = path10.join(d, entry.name);
4869
+ const full = path9.join(d, entry.name);
5212
4870
  if (entry.isDirectory()) {
5213
4871
  await walk2(full);
5214
4872
  } else {
5215
4873
  try {
5216
- const stat = await fs9.stat(full);
4874
+ const stat = await fs8.stat(full);
5217
4875
  usedBytes += stat.size;
5218
4876
  fileCount++;
5219
4877
  } catch {
@@ -5270,12 +4928,12 @@ async function processMediaCommand(inputs, options) {
5270
4928
  const assets = await Promise.all(
5271
4929
  mediaFiles.map(async (filePath) => {
5272
4930
  const type = mediaType(filePath);
5273
- const stat = await fs10.stat(filePath);
5274
- const extension = path11.extname(filePath).toLowerCase() || ".bin";
4931
+ const stat = await fs9.stat(filePath);
4932
+ const extension = path10.extname(filePath).toLowerCase() || ".bin";
5275
4933
  const key = `${type}s/${Date.now()}_${Math.random().toString(36).slice(2)}${extension}`;
5276
4934
  const mimeType = MIME_TYPES[extension] || "application/octet-stream";
5277
- const storageUri = storage.storeFile ? await storage.storeFile(key, filePath, mimeType) : await storage.store(key, await fs10.readFile(filePath), mimeType);
5278
- return { type, fileName: path11.basename(filePath), mimeType, storageUri, fileSize: stat.size };
4935
+ const storageUri = storage.storeFile ? await storage.storeFile(key, filePath, mimeType) : await storage.store(key, await fs9.readFile(filePath), mimeType);
4936
+ return { type, fileName: path10.basename(filePath), mimeType, storageUri, fileSize: stat.size };
5279
4937
  })
5280
4938
  );
5281
4939
  const stored = await addMediaAssets(assets);
@@ -5357,7 +5015,7 @@ async function processMediaAsset(asset) {
5357
5015
  if (!tool2.processVideo)
5358
5016
  throw new Error("The installed Video & Audio tool cannot process videos.");
5359
5017
  await update("Extracting video audio", 20);
5360
- const outputDir = await fs10.mkdtemp(path11.join(process.cwd(), ".larkup", "tmp-media-"));
5018
+ const outputDir = await fs9.mkdtemp(path10.join(process.cwd(), ".larkup", "tmp-media-"));
5361
5019
  try {
5362
5020
  const video = await tool2.processVideo(localFile, {
5363
5021
  outputDir,
@@ -5378,7 +5036,7 @@ async function processMediaAsset(asset) {
5378
5036
  dimensions = { width: video.meta.width, height: video.meta.height };
5379
5037
  frameCount = video.frames.length;
5380
5038
  } finally {
5381
- await fs10.rm(outputDir, { recursive: true, force: true }).catch(() => {
5039
+ await fs9.rm(outputDir, { recursive: true, force: true }).catch(() => {
5382
5040
  });
5383
5041
  }
5384
5042
  } else {
@@ -5456,7 +5114,7 @@ async function indexPendingMedia() {
5456
5114
  spinner2.stop(`Indexed ${finalRun.totalChunks} chunks`);
5457
5115
  }
5458
5116
  function mediaType(filePath) {
5459
- const extension = path11.extname(filePath).toLowerCase();
5117
+ const extension = path10.extname(filePath).toLowerCase();
5460
5118
  if ([".png", ".jpg", ".jpeg", ".webp", ".gif"].includes(extension)) return "image";
5461
5119
  if ([".mp4", ".webm", ".mov", ".mkv"].includes(extension)) return "video";
5462
5120
  if ([".mp3", ".m4a", ".wav", ".ogg", ".flac"].includes(extension)) return "audio";
@@ -5543,20 +5201,20 @@ async function indexCommand(inputs, options) {
5543
5201
  }
5544
5202
 
5545
5203
  // src/commands/generate.ts
5546
- import { promises as fs11 } from "fs";
5547
- import path12 from "path";
5204
+ import { promises as fs10 } from "fs";
5205
+ import path11 from "path";
5548
5206
  async function generateCommand(options) {
5549
5207
  await inServerScope(options.server, async () => {
5550
5208
  await requireActive();
5551
5209
  const config = await readConfig();
5552
5210
  if (options.out) {
5553
- const dir = path12.isAbsolute(options.out) ? options.out : path12.join(process.cwd(), options.out);
5211
+ const dir = path11.isAbsolute(options.out) ? options.out : path11.join(process.cwd(), options.out);
5554
5212
  const server2 = generateServer(config);
5555
- await fs11.mkdir(dir, { recursive: true });
5213
+ await fs10.mkdir(dir, { recursive: true });
5556
5214
  for (const f of server2.files) {
5557
- const dest = path12.join(dir, f.path);
5558
- await fs11.mkdir(path12.dirname(dest), { recursive: true });
5559
- await fs11.writeFile(dest, f.contents, "utf8");
5215
+ const dest = path11.join(dir, f.path);
5216
+ await fs10.mkdir(path11.dirname(dest), { recursive: true });
5217
+ await fs10.writeFile(dest, f.contents, "utf8");
5560
5218
  }
5561
5219
  log.success(`Generated ${server2.files.length} files \u2192 ${dir}`);
5562
5220
  } else {
@@ -5904,8 +5562,8 @@ async function marketplaceCommand(action = "list", toolId) {
5904
5562
  }
5905
5563
 
5906
5564
  // src/commands/deploy.ts
5907
- import { promises as fs12 } from "fs";
5908
- import path13 from "path";
5565
+ import { promises as fs11 } from "fs";
5566
+ import path12 from "path";
5909
5567
  async function deployCommand(target, options) {
5910
5568
  await inServerScope(options.server, async () => {
5911
5569
  await requireActive();
@@ -5941,13 +5599,13 @@ async function deployCommand(target, options) {
5941
5599
  });
5942
5600
  }
5943
5601
  async function copyGenerated(source, output) {
5944
- const destination = path13.resolve(output);
5945
- const relative = path13.relative(source, destination);
5946
- if (relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative)) {
5602
+ const destination = path12.resolve(output);
5603
+ const relative = path12.relative(source, destination);
5604
+ if (relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative)) {
5947
5605
  throw new Error("Deployment output must be outside the generated server directory.");
5948
5606
  }
5949
- await fs12.mkdir(destination, { recursive: true });
5950
- await fs12.cp(source, destination, { recursive: true, force: true });
5607
+ await fs11.mkdir(destination, { recursive: true });
5608
+ await fs11.cp(source, destination, { recursive: true, force: true });
5951
5609
  return destination;
5952
5610
  }
5953
5611
 
@@ -6032,8 +5690,8 @@ function openCommandForPlatform(url) {
6032
5690
  }
6033
5691
 
6034
5692
  // src/commands/documents.ts
6035
- import { promises as fs13 } from "fs";
6036
- import path14 from "path";
5693
+ import { promises as fs12 } from "fs";
5694
+ import path13 from "path";
6037
5695
 
6038
5696
  // ../../packages/core/src/corpus-retriever.ts
6039
5697
  function applyFilters(docs, filter) {
@@ -6152,10 +5810,10 @@ async function documentsCommand(action = "list", idOrPath, options) {
6152
5810
  }
6153
5811
  if (action === "export") {
6154
5812
  const format = options.format === "jsonl" ? "jsonl" : "csv";
6155
- const output = path14.resolve(options.out || idOrPath || `corpus.${format}`);
5813
+ const output = path13.resolve(options.out || idOrPath || `corpus.${format}`);
6156
5814
  const content = format === "jsonl" ? await exportCorpusAsJSONL(void 0, Number.MAX_SAFE_INTEGER) : await exportCorpusAsCSV(void 0, Number.MAX_SAFE_INTEGER);
6157
- await fs13.mkdir(path14.dirname(output), { recursive: true });
6158
- await fs13.writeFile(output, content, "utf8");
5815
+ await fs12.mkdir(path13.dirname(output), { recursive: true });
5816
+ await fs12.writeFile(output, content, "utf8");
6159
5817
  log.success(`Exported corpus \u2192 ${output}`);
6160
5818
  return;
6161
5819
  }
@@ -6181,7 +5839,7 @@ ${document.content}`);
6181
5839
  return;
6182
5840
  }
6183
5841
  if (action === "update") {
6184
- const content = options.file ? await fs13.readFile(path14.resolve(options.file), "utf8") : options.text;
5842
+ const content = options.file ? await fs12.readFile(path13.resolve(options.file), "utf8") : options.text;
6185
5843
  const updated = await updateDocument(document.id, {
6186
5844
  title: options.title,
6187
5845
  content,
@@ -0,0 +1,14 @@
1
+ import {
2
+ hasCapability,
3
+ isToolLoaded,
4
+ loadTool,
5
+ unloadTool
6
+ } from "./chunk-VY6I42DP.js";
7
+ import "./chunk-P5JVXA7X.js";
8
+ import "./chunk-3RG5ZIWI.js";
9
+ export {
10
+ hasCapability,
11
+ isToolLoaded,
12
+ loadTool,
13
+ unloadTool
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larkup/cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
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/marketplace": "0.1.4",
33
- "@larkup/vector-stores": "0.1.22"
31
+ "@larkup/marketplace": "0.1.6",
32
+ "@larkup/vector-stores": "0.1.22",
33
+ "@larkup/core": "0.2.3"
34
34
  },
35
35
  "devDependencies": {
36
36
  "tsup": "^8.0.0",