@larkup/cli 0.1.17 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +157 -22
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -66,6 +66,92 @@ var prompts = {
66
66
  spinner: p.spinner
67
67
  };
68
68
 
69
+ // package.json
70
+ var package_default = {
71
+ name: "@larkup/cli",
72
+ version: "0.1.20",
73
+ publishConfig: {
74
+ access: "public"
75
+ },
76
+ type: "module",
77
+ bin: {
78
+ larkup: "./dist/index.js"
79
+ },
80
+ files: [
81
+ "dist"
82
+ ],
83
+ scripts: {
84
+ build: "tsup",
85
+ dev: "tsup --watch",
86
+ larkup: "tsx src/index.ts"
87
+ },
88
+ dependencies: {
89
+ "@ai-sdk/cohere": "^3.0.39",
90
+ "@ai-sdk/deepseek": "^2.0.39",
91
+ "@ai-sdk/gateway": "^3.0.133",
92
+ "@ai-sdk/google": "^3.0.83",
93
+ "@ai-sdk/mistral": "^3.0.40",
94
+ "@ai-sdk/openai": "^3.0.68",
95
+ "@ai-sdk/openai-compatible": "^2.0.51",
96
+ "@clack/prompts": "^0.10.1",
97
+ "@lancedb/lancedb": "^0.30.0",
98
+ "@larkup/core": "workspace:*",
99
+ "@larkup/vector-stores": "workspace:*",
100
+ "@pinecone-database/pinecone": "^7.2.0",
101
+ ai: "^6.0.197",
102
+ chalk: "^5.6.2",
103
+ chromadb: "^1.10.5",
104
+ "cli-table3": "^0.6.5",
105
+ commander: "^13.1.0",
106
+ zod: "^4.4.3"
107
+ },
108
+ devDependencies: {
109
+ tsup: "^8.0.0",
110
+ tsx: "^4.22.4",
111
+ typescript: "5.7.3"
112
+ }
113
+ };
114
+
115
+ // src/updater.ts
116
+ var VERSION_CHECK_URL = "https://larkup.de/api/version";
117
+ function compareVersions(a, b) {
118
+ const pa = a.replace(/^v/, "").split(".").map(Number);
119
+ const pb = b.replace(/^v/, "").split(".").map(Number);
120
+ for (let i = 0; i < 3; i++) {
121
+ const va = pa[i] || 0;
122
+ const vb = pb[i] || 0;
123
+ if (va > vb) return 1;
124
+ if (va < vb) return -1;
125
+ }
126
+ return 0;
127
+ }
128
+ async function checkUpdate() {
129
+ try {
130
+ const controller = new AbortController();
131
+ const timeout = setTimeout(() => controller.abort(), 1500);
132
+ const res = await fetch(VERSION_CHECK_URL, {
133
+ signal: controller.signal
134
+ });
135
+ clearTimeout(timeout);
136
+ if (!res.ok) return;
137
+ const data = await res.json();
138
+ const currentVersion = package_default.version;
139
+ if (data.version && compareVersions(data.version, currentVersion) > 0) {
140
+ console.log("");
141
+ log.info(`\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E`);
142
+ log.info(
143
+ `\u2502 Update available: ${currentVersion} \u2192 ${data.version}${" ".repeat(25 - currentVersion.length - data.version.length)}\u2502`
144
+ );
145
+ log.info(
146
+ `\u2502 Run: ${log.fmt.cyan("npm install -g @larkup/cli")} \u2502`
147
+ );
148
+ log.info(`\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F`);
149
+ console.log("");
150
+ }
151
+ } catch {
152
+ }
153
+ }
154
+
69
155
  // ../../packages/core/src/workspace.ts
70
156
  import { promises as fs } from "fs";
71
157
  import path from "path";
@@ -245,11 +331,17 @@ async function nextPort(ws) {
245
331
  }
246
332
  return candidate;
247
333
  }
248
- function defaultConfigFor(id, name) {
334
+ function defaultConfigFor(id, name, prevConfig = {}) {
249
335
  return {
250
336
  ...DEFAULT_CONFIG,
251
337
  projectName: name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "my-rag",
252
338
  storeConfig: { mode: "local", dbPath: relServerPath(id, "lancedb") },
339
+ embeddingApiKey: prevConfig.embeddingApiKey ?? DEFAULT_CONFIG.embeddingApiKey,
340
+ customEmbeddings: prevConfig.customEmbeddings ?? DEFAULT_CONFIG.customEmbeddings,
341
+ chatApiKey: prevConfig.chatApiKey ?? DEFAULT_CONFIG.chatApiKey,
342
+ customChatModels: prevConfig.customChatModels ?? DEFAULT_CONFIG.customChatModels,
343
+ serperApiKey: prevConfig.serperApiKey ?? DEFAULT_CONFIG.serperApiKey,
344
+ firecrawlApiKey: prevConfig.firecrawlApiKey ?? DEFAULT_CONFIG.firecrawlApiKey,
253
345
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
254
346
  };
255
347
  }
@@ -267,9 +359,17 @@ function createServer(name) {
267
359
  };
268
360
  const dir = serverDir(id);
269
361
  await fs.mkdir(dir, { recursive: true });
362
+ let prevConfig = {};
363
+ if (ws.activeServerId) {
364
+ try {
365
+ const raw = await fs.readFile(path.join(serverDir(ws.activeServerId), "config.json"), "utf8");
366
+ prevConfig = JSON.parse(raw);
367
+ } catch {
368
+ }
369
+ }
270
370
  await fs.writeFile(
271
371
  path.join(dir, "config.json"),
272
- JSON.stringify(defaultConfigFor(id, meta.name), null, 2),
372
+ JSON.stringify(defaultConfigFor(id, meta.name, prevConfig), null, 2),
273
373
  "utf8"
274
374
  );
275
375
  const next = {
@@ -1529,27 +1629,37 @@ if (IS_SERVERLESS && MODE !== "cloud") {
1529
1629
  console.warn("[LanceDB] Running on serverless \u2014 using /tmp/lancedb. Data will NOT persist between invocations. Use LanceDB Cloud for production.")
1530
1630
  }
1531
1631
 
1632
+ let _conn = null
1532
1633
  let _table = null
1533
1634
 
1534
- async function table() {
1535
- if (_table) return _table
1536
- let conn
1635
+ async function getConn() {
1636
+ if (_conn) return _conn
1537
1637
  if (MODE === "cloud") {
1538
1638
  if (!URI || !API_KEY) {
1539
1639
  throw new Error("LanceDB Cloud needs LANCEDB_URI and LANCEDB_API_KEY.")
1540
1640
  }
1541
- conn = await lancedb.connect(URI, { apiKey: API_KEY })
1641
+ _conn = await lancedb.connect(URI, { apiKey: API_KEY })
1542
1642
  } else {
1543
1643
  const abs = path.isAbsolute(DB_PATH) ? DB_PATH : path.join(process.cwd(), DB_PATH)
1544
- conn = await lancedb.connect(abs)
1644
+ _conn = await lancedb.connect(abs)
1545
1645
  }
1646
+ return _conn
1647
+ }
1546
1648
 
1547
- _table = await conn.openTable(TABLE)
1548
- return _table
1649
+ async function getTable() {
1650
+ if (_table) return _table
1651
+ const conn = await getConn()
1652
+ const names = await conn.tableNames()
1653
+ if (names.includes(TABLE)) {
1654
+ _table = await conn.openTable(TABLE)
1655
+ return _table
1656
+ }
1657
+ return null
1549
1658
  }
1550
1659
 
1551
1660
  export async function query(vector, topK) {
1552
- const t = await table()
1661
+ const t = await getTable()
1662
+ if (!t) return []
1553
1663
  const rows = await t.search(vector).limit(topK).toArray()
1554
1664
  return rows.map((row) => ({
1555
1665
  id: row.id,
@@ -1562,7 +1672,8 @@ export async function query(vector, topK) {
1562
1672
  }
1563
1673
 
1564
1674
  export async function list({ page = 1, limit = 20 } = {}) {
1565
- const t = await table()
1675
+ const t = await getTable()
1676
+ if (!t) return { documents: [], total: 0, page, limit, totalPages: 0 }
1566
1677
  // Fetch all rows to get total count, then slice for the page.
1567
1678
  // We explicitly exclude the "vector" column to avoid memory bloat and Rust panics on full scans.
1568
1679
  const allRows = await t.query().select(["id", "text", "title", "url", "documentId"]).toArray()
@@ -1585,7 +1696,8 @@ export async function list({ page = 1, limit = 20 } = {}) {
1585
1696
  }
1586
1697
 
1587
1698
  export async function get(id) {
1588
- const t = await table()
1699
+ const t = await getTable()
1700
+ if (!t) return null
1589
1701
  const rows = await t.query().filter(\`id = '\${id}'\`).limit(1).toArray()
1590
1702
  if (!rows.length) return null;
1591
1703
  const row = rows[0];
@@ -1593,24 +1705,44 @@ export async function get(id) {
1593
1705
  }
1594
1706
 
1595
1707
  export async function add(docs) {
1596
- const t = await table()
1597
- await t.add(docs)
1708
+ if (docs.length === 0) return { success: true }
1709
+ let t = await getTable()
1710
+ if (t) {
1711
+ await t.add(docs)
1712
+ } else {
1713
+ const conn = await getConn()
1714
+ const names = await conn.tableNames()
1715
+ if (names.includes(TABLE)) {
1716
+ t = await conn.openTable(TABLE)
1717
+ _table = t
1718
+ await t.add(docs)
1719
+ } else {
1720
+ t = await conn.createTable(TABLE, docs)
1721
+ _table = t
1722
+ }
1723
+ }
1598
1724
  return { success: true }
1599
1725
  }
1600
1726
 
1601
1727
  export async function remove(id) {
1602
- const t = await table()
1728
+ const t = await getTable()
1729
+ if (!t) return { success: true }
1603
1730
  await t.delete(\`id = '\${id}'\`)
1604
1731
  return { success: true }
1605
1732
  }
1606
1733
 
1607
1734
  export async function update(id, doc) {
1608
- const t = await table()
1735
+ let t = await getTable()
1736
+ if (!t) {
1737
+ const conn = await getConn()
1738
+ t = await conn.createTable(TABLE, [doc])
1739
+ _table = t
1740
+ return { success: true }
1741
+ }
1609
1742
  await t.delete(\`id = '\${id}'\`)
1610
1743
  await t.add([doc])
1611
1744
  return { success: true }
1612
- }
1613
- `;
1745
+ }`;
1614
1746
  }
1615
1747
  function pineconeStore() {
1616
1748
  return `import { Pinecone } from "@pinecone-database/pinecone"
@@ -1972,9 +2104,10 @@ const server = createServer(async (req, res) => {
1972
2104
  if (!doc.text) return send(res, 400, { error: "Missing text" })
1973
2105
  const vector = await embedQuery(doc.text)
1974
2106
  const id = doc.id || Math.random().toString(36).slice(2)
1975
- await store.add([{ id, vector, text: doc.text, title: doc.title || "Untitled", url: doc.url, documentId: doc.documentId || id }])
2107
+ await store.add([{ id, vector, text: doc.text, title: doc.title || "Untitled", url: doc.url || "", documentId: doc.documentId || id }])
1976
2108
  return send(res, 200, { success: true, id })
1977
2109
  } catch (err) {
2110
+ console.error("[POST /documents] Error:", err)
1978
2111
  return send(res, 500, { error: String(err?.message || err) })
1979
2112
  }
1980
2113
  }
@@ -2027,7 +2160,7 @@ const server = createServer(async (req, res) => {
2027
2160
  for (const [idx, chunk] of chunks.entries()) {
2028
2161
  const id = \`\${documentId}-\${idx}\`
2029
2162
  const vector = await embedQuery(chunk)
2030
- await store.add([{ id, vector, text: chunk, title: \`\${title} (part \${idx + 1})\`, url: targetUrl, documentId }])
2163
+ await store.add([{ id, vector, text: chunk, title: \`\${title} (part \${idx + 1})\`, url: targetUrl || "", documentId }])
2031
2164
  }
2032
2165
 
2033
2166
  return send(res, 200, { success: true, documentId, chunks: chunks.length })
@@ -5180,6 +5313,8 @@ program.command("chat").description("Chat with your knowledge base in the termin
5180
5313
  program.command("settings").description("Configure CLI settings (e.g., chat models)").option("--server <id>", "Target a specific server instead of the active one").action(async (options) => {
5181
5314
  await settingsCommand(options);
5182
5315
  });
5183
- program.parseAsync(process.argv).catch((err) => {
5184
- log.error(err instanceof Error ? err.message : String(err));
5316
+ checkUpdate().then(() => {
5317
+ program.parseAsync(process.argv).catch((err) => {
5318
+ log.error(err instanceof Error ? err.message : String(err));
5319
+ });
5185
5320
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larkup/cli",
3
- "version": "0.1.17",
3
+ "version": "0.1.20",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -28,8 +28,8 @@
28
28
  "cli-table3": "^0.6.5",
29
29
  "commander": "^13.1.0",
30
30
  "zod": "^4.4.3",
31
- "@larkup/core": "0.1.17",
32
- "@larkup/vector-stores": "0.1.17"
31
+ "@larkup/core": "0.1.20",
32
+ "@larkup/vector-stores": "0.1.20"
33
33
  },
34
34
  "devDependencies": {
35
35
  "tsup": "^8.0.0",