@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 +112 -191
- package/dist/{lancedb-ITW7V3K5.js → lancedb-RUTGJYW7.js} +1 -1
- package/package.json +4 -4
- package/dist/chunk-P5JVXA7X.js +0 -231
- package/dist/chunk-TH2MPRMA.js +0 -412
- package/dist/tool-loader-B2J2R64F.js +0 -14
- package/dist/tool-registry-W3JOGDE3.js +0 -19
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.
|
|
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(
|
|
955
|
-
if (
|
|
956
|
-
if (
|
|
957
|
-
if (
|
|
958
|
-
if (
|
|
959
|
-
if (
|
|
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
|
|
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
|
|
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
|
|
2277
|
-
const
|
|
2278
|
-
const faviconPath =
|
|
2279
|
-
if (
|
|
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:
|
|
2270
|
+
contents: fs12.readFileSync(faviconPath).toString("base64"),
|
|
2283
2271
|
language: "ico",
|
|
2284
2272
|
encoding: "base64"
|
|
2285
2273
|
});
|
|
2286
2274
|
} else {
|
|
2287
|
-
const webFaviconPath =
|
|
2288
|
-
if (
|
|
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:
|
|
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(
|
|
2306
|
-
if (
|
|
2307
|
-
if (
|
|
2308
|
-
if (
|
|
2309
|
-
if (
|
|
2310
|
-
if (
|
|
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
|
|
3325
|
-
const
|
|
3326
|
-
const faviconPath =
|
|
3327
|
-
if (
|
|
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:
|
|
3318
|
+
contents: fs12.readFileSync(faviconPath).toString("base64"),
|
|
3331
3319
|
language: "ico",
|
|
3332
3320
|
encoding: "base64"
|
|
3333
3321
|
});
|
|
3334
3322
|
} else {
|
|
3335
|
-
const webFaviconPath =
|
|
3336
|
-
if (
|
|
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:
|
|
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
|
|
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 =
|
|
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(
|
|
3491
|
+
TOP_K: String(runtimeConfig.topK),
|
|
3491
3492
|
SERVER_API_KEY: serverApiKey || "",
|
|
3492
|
-
EMBEDDING_API_KEY:
|
|
3493
|
-
CHAT_API_KEY:
|
|
3494
|
-
(model) => model.modelName ===
|
|
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(
|
|
3497
|
-
CHAT_BASE_URL:
|
|
3498
|
-
(model) => model.modelName ===
|
|
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:
|
|
3501
|
-
ANTHROPIC_API_KEY:
|
|
3502
|
-
COHERE_API_KEY:
|
|
3503
|
-
GOOGLE_GENERATIVE_AI_API_KEY:
|
|
3504
|
-
PINECONE_API_KEY:
|
|
3505
|
-
PINECONE_INDEX:
|
|
3506
|
-
PINECONE_NAMESPACE:
|
|
3507
|
-
PINECONE_SPARSE_MODEL:
|
|
3508
|
-
PINECONE_SPARSE_INDEX:
|
|
3509
|
-
LANCEDB_MODE:
|
|
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:
|
|
3512
|
-
LANCEDB_API_KEY:
|
|
3513
|
-
LANCEDB_S3_URI:
|
|
3514
|
-
AWS_ENDPOINT:
|
|
3515
|
-
AWS_REGION:
|
|
3516
|
-
AWS_ACCESS_KEY_ID:
|
|
3517
|
-
AWS_SECRET_ACCESS_KEY:
|
|
3518
|
-
LANCEDB_TABLE:
|
|
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-
|
|
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
|
|
4644
|
-
import
|
|
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
|
|
4919
|
-
const extension =
|
|
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
|
|
4923
|
-
return { type, fileName:
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
5192
|
-
import
|
|
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 =
|
|
5112
|
+
const dir = path10.isAbsolute(options.out) ? options.out : path10.join(process.cwd(), options.out);
|
|
5199
5113
|
const server2 = generateServer(config);
|
|
5200
|
-
await
|
|
5114
|
+
await fs9.mkdir(dir, { recursive: true });
|
|
5201
5115
|
for (const f of server2.files) {
|
|
5202
|
-
const dest =
|
|
5203
|
-
await
|
|
5204
|
-
await
|
|
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
|
|
5553
|
-
import
|
|
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 =
|
|
5590
|
-
const relative =
|
|
5591
|
-
if (relative === "" || !relative.startsWith("..") && !
|
|
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
|
|
5595
|
-
await
|
|
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
|
|
5681
|
-
import
|
|
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 =
|
|
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
|
|
5803
|
-
await
|
|
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
|
|
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:
|
|
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.
|
|
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.
|
|
32
|
-
"@larkup/
|
|
33
|
-
"@larkup/
|
|
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",
|
package/dist/chunk-P5JVXA7X.js
DELETED
|
@@ -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
|
-
};
|
package/dist/chunk-TH2MPRMA.js
DELETED
|
@@ -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,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
|
-
};
|