@karakeep/cli 0.27.1 → 0.29.1
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.mjs +166 -17
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as require$$3 from "node:fs";
|
|
3
|
-
import require$$3__default from "node:fs";
|
|
4
2
|
import process$1, { stdout, stdin } from "node:process";
|
|
5
3
|
import os from "node:os";
|
|
6
4
|
import tty from "node:tty";
|
|
7
5
|
import require$$0 from "node:events";
|
|
8
6
|
import require$$1, { spawn } from "node:child_process";
|
|
9
7
|
import path from "node:path";
|
|
8
|
+
import * as require$$3 from "node:fs";
|
|
9
|
+
import require$$3__default from "node:fs";
|
|
10
10
|
import fsp from "node:fs/promises";
|
|
11
11
|
import readline from "node:readline/promises";
|
|
12
12
|
let globalOpts = void 0;
|
|
@@ -11216,6 +11216,70 @@ function requireSrc() {
|
|
|
11216
11216
|
return src;
|
|
11217
11217
|
}
|
|
11218
11218
|
var srcExports = /* @__PURE__ */ requireSrc();
|
|
11219
|
+
const adminCmd = new Command().name("admin").description("admin commands");
|
|
11220
|
+
function toHumanReadableSize(size) {
|
|
11221
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
|
11222
|
+
if (size === 0) return "0 Bytes";
|
|
11223
|
+
const i = Math.floor(Math.log(size) / Math.log(1024));
|
|
11224
|
+
return (size / Math.pow(1024, i)).toFixed(2) + " " + sizes[i];
|
|
11225
|
+
}
|
|
11226
|
+
const usersCmd = new Command().name("users").description("user management commands");
|
|
11227
|
+
usersCmd.command("list").description("list all users").action(async () => {
|
|
11228
|
+
const api2 = getAPIClient();
|
|
11229
|
+
try {
|
|
11230
|
+
const [usersResp, userStats] = await Promise.all([
|
|
11231
|
+
api2.users.list.query(),
|
|
11232
|
+
api2.admin.userStats.query()
|
|
11233
|
+
]);
|
|
11234
|
+
if (getGlobalOptions().json) {
|
|
11235
|
+
printObject({
|
|
11236
|
+
users: usersResp.users.map((u) => ({
|
|
11237
|
+
...u,
|
|
11238
|
+
numBookmarks: userStats[u.id]?.numBookmarks ?? 0,
|
|
11239
|
+
assetSizes: userStats[u.id]?.assetSizes ?? 0
|
|
11240
|
+
}))
|
|
11241
|
+
});
|
|
11242
|
+
} else {
|
|
11243
|
+
const data = [
|
|
11244
|
+
[
|
|
11245
|
+
"Name",
|
|
11246
|
+
"Email",
|
|
11247
|
+
"Num Bookmarks",
|
|
11248
|
+
"Asset Sizes",
|
|
11249
|
+
"Role",
|
|
11250
|
+
"Local User"
|
|
11251
|
+
]
|
|
11252
|
+
];
|
|
11253
|
+
usersResp.users.forEach((user) => {
|
|
11254
|
+
const stats = userStats[user.id] ?? {
|
|
11255
|
+
numBookmarks: 0,
|
|
11256
|
+
assetSizes: 0
|
|
11257
|
+
};
|
|
11258
|
+
const numBookmarksDisplay = `${stats.numBookmarks} / ${user.bookmarkQuota?.toString() ?? "Unlimited"}`;
|
|
11259
|
+
const assetSizesDisplay = `${toHumanReadableSize(stats.assetSizes)} / ${user.storageQuota ? toHumanReadableSize(user.storageQuota) : "Unlimited"}`;
|
|
11260
|
+
data.push([
|
|
11261
|
+
user.name,
|
|
11262
|
+
user.email,
|
|
11263
|
+
numBookmarksDisplay,
|
|
11264
|
+
assetSizesDisplay,
|
|
11265
|
+
user.role ?? "",
|
|
11266
|
+
user.localUser ? "✓" : "✗"
|
|
11267
|
+
]);
|
|
11268
|
+
});
|
|
11269
|
+
console.log(
|
|
11270
|
+
srcExports.table(data, {
|
|
11271
|
+
border: srcExports.getBorderCharacters("ramac"),
|
|
11272
|
+
drawHorizontalLine: (lineIndex, rowCount) => {
|
|
11273
|
+
return lineIndex === 0 || lineIndex === 1 || lineIndex === rowCount;
|
|
11274
|
+
}
|
|
11275
|
+
})
|
|
11276
|
+
);
|
|
11277
|
+
}
|
|
11278
|
+
} catch (error2) {
|
|
11279
|
+
printErrorMessageWithReason("Failed to list all users", error2);
|
|
11280
|
+
}
|
|
11281
|
+
});
|
|
11282
|
+
adminCmd.addCommand(usersCmd);
|
|
11219
11283
|
function listsToTree(lists) {
|
|
11220
11284
|
const idToList = lists.reduce((acc, list) => {
|
|
11221
11285
|
acc[list.id] = list;
|
|
@@ -15408,6 +15472,7 @@ const zCursorV2 = z.object({
|
|
|
15408
15472
|
function normalizeTagName(raw) {
|
|
15409
15473
|
return raw.trim().replace(/^#+/, "");
|
|
15410
15474
|
}
|
|
15475
|
+
const MAX_NUM_TAGS_PER_PAGE = 1e3;
|
|
15411
15476
|
const zTagNameSchemaWithValidation = z.string().transform((s) => normalizeTagName(s).trim()).pipe(z.string().min(1));
|
|
15412
15477
|
z.object({
|
|
15413
15478
|
name: zTagNameSchemaWithValidation
|
|
@@ -15418,7 +15483,7 @@ const zBookmarkTagSchema = z.object({
|
|
|
15418
15483
|
name: z.string(),
|
|
15419
15484
|
attachedBy: zAttachedByEnumSchema
|
|
15420
15485
|
});
|
|
15421
|
-
z.object({
|
|
15486
|
+
const zGetTagResponseSchema = z.object({
|
|
15422
15487
|
id: z.string(),
|
|
15423
15488
|
name: z.string(),
|
|
15424
15489
|
numBookmarks: z.number(),
|
|
@@ -15432,6 +15497,49 @@ z.object({
|
|
|
15432
15497
|
id: z.string(),
|
|
15433
15498
|
name: z.string()
|
|
15434
15499
|
});
|
|
15500
|
+
const zTagCursorSchema = z.object({
|
|
15501
|
+
page: z.number().int().min(0)
|
|
15502
|
+
});
|
|
15503
|
+
const zTagListRequestSchema = z.object({
|
|
15504
|
+
nameContains: z.string().optional(),
|
|
15505
|
+
attachedBy: z.enum([...zAttachedByEnumSchema.options, "none"]).optional(),
|
|
15506
|
+
sortBy: z.enum(["name", "usage", "relevance"]).optional().default("usage"),
|
|
15507
|
+
cursor: zTagCursorSchema.nullish().default({ page: 0 }),
|
|
15508
|
+
// TODO: Remove the optional to enforce a limit after the next release
|
|
15509
|
+
limit: z.number().int().min(1).max(MAX_NUM_TAGS_PER_PAGE).optional()
|
|
15510
|
+
});
|
|
15511
|
+
zTagListRequestSchema.refine(
|
|
15512
|
+
(val) => val.sortBy != "relevance" || val.nameContains !== void 0,
|
|
15513
|
+
{
|
|
15514
|
+
message: "Relevance sorting requires a nameContains filter",
|
|
15515
|
+
path: ["sortBy"]
|
|
15516
|
+
}
|
|
15517
|
+
);
|
|
15518
|
+
const zTagListResponseSchema = z.object({
|
|
15519
|
+
tags: z.array(zGetTagResponseSchema),
|
|
15520
|
+
nextCursor: zTagCursorSchema.nullish()
|
|
15521
|
+
});
|
|
15522
|
+
z.object({
|
|
15523
|
+
nameContains: zTagListRequestSchema.shape.nameContains,
|
|
15524
|
+
sort: zTagListRequestSchema.shape.sortBy,
|
|
15525
|
+
attachedBy: zTagListRequestSchema.shape.attachedBy,
|
|
15526
|
+
cursor: z.string().transform((val, ctx) => {
|
|
15527
|
+
try {
|
|
15528
|
+
return JSON.parse(Buffer.from(val, "base64url").toString("utf8"));
|
|
15529
|
+
} catch {
|
|
15530
|
+
ctx.addIssue({
|
|
15531
|
+
code: "custom",
|
|
15532
|
+
message: "Invalid cursor"
|
|
15533
|
+
});
|
|
15534
|
+
return z.NEVER;
|
|
15535
|
+
}
|
|
15536
|
+
}).optional().pipe(zTagListRequestSchema.shape.cursor),
|
|
15537
|
+
limit: z.coerce.number().optional()
|
|
15538
|
+
});
|
|
15539
|
+
z.object({
|
|
15540
|
+
tags: zTagListResponseSchema.shape.tags,
|
|
15541
|
+
nextCursor: z.string().nullish()
|
|
15542
|
+
});
|
|
15435
15543
|
const MAX_BOOKMARK_TITLE_LENGTH = 1e3;
|
|
15436
15544
|
var BookmarkTypes = /* @__PURE__ */ ((BookmarkTypes2) => {
|
|
15437
15545
|
BookmarkTypes2["LINK"] = "link";
|
|
@@ -15450,11 +15558,13 @@ const zAssetTypesSchema = z.enum([
|
|
|
15450
15558
|
"video",
|
|
15451
15559
|
"bookmarkAsset",
|
|
15452
15560
|
"precrawledArchive",
|
|
15561
|
+
"userUploaded",
|
|
15453
15562
|
"unknown"
|
|
15454
15563
|
]);
|
|
15455
15564
|
const zAssetSchema = z.object({
|
|
15456
15565
|
id: z.string(),
|
|
15457
|
-
assetType: zAssetTypesSchema
|
|
15566
|
+
assetType: zAssetTypesSchema,
|
|
15567
|
+
fileName: z.string().nullish()
|
|
15458
15568
|
});
|
|
15459
15569
|
const zBookmarkedLinkSchema = z.object({
|
|
15460
15570
|
type: z.literal(
|
|
@@ -15508,6 +15618,16 @@ const zBookmarkContentSchema = z.discriminatedUnion("type", [
|
|
|
15508
15618
|
/* UNKNOWN */
|
|
15509
15619
|
) })
|
|
15510
15620
|
]);
|
|
15621
|
+
const zBookmarkSourceSchema = z.enum([
|
|
15622
|
+
"api",
|
|
15623
|
+
"web",
|
|
15624
|
+
"cli",
|
|
15625
|
+
"mobile",
|
|
15626
|
+
"extension",
|
|
15627
|
+
"singlefile",
|
|
15628
|
+
"rss",
|
|
15629
|
+
"import"
|
|
15630
|
+
]);
|
|
15511
15631
|
const zBareBookmarkSchema = z.object({
|
|
15512
15632
|
id: z.string(),
|
|
15513
15633
|
createdAt: z.date(),
|
|
@@ -15518,7 +15638,9 @@ const zBareBookmarkSchema = z.object({
|
|
|
15518
15638
|
taggingStatus: z.enum(["success", "failure", "pending"]).nullable(),
|
|
15519
15639
|
summarizationStatus: z.enum(["success", "failure", "pending"]).nullable(),
|
|
15520
15640
|
note: z.string().nullish(),
|
|
15521
|
-
summary: z.string().nullish()
|
|
15641
|
+
summary: z.string().nullish(),
|
|
15642
|
+
source: zBookmarkSourceSchema.nullish(),
|
|
15643
|
+
userId: z.string()
|
|
15522
15644
|
});
|
|
15523
15645
|
const zBookmarkSchema = zBareBookmarkSchema.merge(
|
|
15524
15646
|
z.object({
|
|
@@ -15557,7 +15679,9 @@ z.object({
|
|
|
15557
15679
|
createdAt: z.coerce.date().optional(),
|
|
15558
15680
|
// A mechanism to prioritize crawling of bookmarks depending on whether
|
|
15559
15681
|
// they were created by a user interaction or by a bulk import.
|
|
15560
|
-
crawlPriority: z.enum(["low", "normal"]).optional()
|
|
15682
|
+
crawlPriority: z.enum(["low", "normal"]).optional(),
|
|
15683
|
+
importSessionId: z.string().optional(),
|
|
15684
|
+
source: zBookmarkSourceSchema.optional()
|
|
15561
15685
|
}).and(
|
|
15562
15686
|
z.discriminatedUnion("type", [
|
|
15563
15687
|
z.object({
|
|
@@ -15734,12 +15858,22 @@ bookmarkCmd.command("add").description("creates a new bookmark").option(
|
|
|
15734
15858
|
const results = [];
|
|
15735
15859
|
const promises = [
|
|
15736
15860
|
...opts.link.map(
|
|
15737
|
-
(url) => api2.bookmarks.createBookmark.mutate({
|
|
15861
|
+
(url) => api2.bookmarks.createBookmark.mutate({
|
|
15862
|
+
type: BookmarkTypes.LINK,
|
|
15863
|
+
url,
|
|
15864
|
+
title: opts.title,
|
|
15865
|
+
source: "cli"
|
|
15866
|
+
}).then((bookmark) => {
|
|
15738
15867
|
results.push(normalizeBookmark(bookmark));
|
|
15739
15868
|
}).catch(printError(`Failed to add a link bookmark for url "${url}"`))
|
|
15740
15869
|
),
|
|
15741
15870
|
...opts.note.map(
|
|
15742
|
-
(text) => api2.bookmarks.createBookmark.mutate({
|
|
15871
|
+
(text) => api2.bookmarks.createBookmark.mutate({
|
|
15872
|
+
type: BookmarkTypes.TEXT,
|
|
15873
|
+
text,
|
|
15874
|
+
title: opts.title,
|
|
15875
|
+
source: "cli"
|
|
15876
|
+
}).then((bookmark) => {
|
|
15743
15877
|
results.push(normalizeBookmark(bookmark));
|
|
15744
15878
|
}).catch(
|
|
15745
15879
|
printError(
|
|
@@ -15751,7 +15885,12 @@ bookmarkCmd.command("add").description("creates a new bookmark").option(
|
|
|
15751
15885
|
if (opts.stdin) {
|
|
15752
15886
|
const text = require$$3.readFileSync(0, "utf-8");
|
|
15753
15887
|
promises.push(
|
|
15754
|
-
api2.bookmarks.createBookmark.mutate({
|
|
15888
|
+
api2.bookmarks.createBookmark.mutate({
|
|
15889
|
+
type: BookmarkTypes.TEXT,
|
|
15890
|
+
text,
|
|
15891
|
+
title: opts.title,
|
|
15892
|
+
source: "cli"
|
|
15893
|
+
}).then((bookmark) => {
|
|
15755
15894
|
results.push(normalizeBookmark(bookmark));
|
|
15756
15895
|
}).catch(
|
|
15757
15896
|
printError(
|
|
@@ -15989,9 +16128,18 @@ const dumpCmd = new Command().name("dump").description("dump all account data an
|
|
|
15989
16128
|
}
|
|
15990
16129
|
if (!opts.excludeTags) {
|
|
15991
16130
|
stepStart$2("Exporting tags");
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
|
|
16131
|
+
let cursor = null;
|
|
16132
|
+
let allTags = [];
|
|
16133
|
+
do {
|
|
16134
|
+
const { tags, nextCursor } = await api2.tags.list.query({
|
|
16135
|
+
limit: MAX_NUM_TAGS_PER_PAGE,
|
|
16136
|
+
cursor
|
|
16137
|
+
});
|
|
16138
|
+
allTags.push(...tags);
|
|
16139
|
+
cursor = nextCursor;
|
|
16140
|
+
} while (cursor);
|
|
16141
|
+
await writeJson(path.join(workRoot, "tags", "index.json"), allTags);
|
|
16142
|
+
manifest.counts.tags = allTags.length;
|
|
15995
16143
|
stepEndSuccess$2();
|
|
15996
16144
|
}
|
|
15997
16145
|
if (!opts.excludeRules) {
|
|
@@ -16539,7 +16687,7 @@ async function migrateWebhooks(src2, dest, onProgress) {
|
|
|
16539
16687
|
}
|
|
16540
16688
|
async function migrateTags(src2, dest, onProgress) {
|
|
16541
16689
|
try {
|
|
16542
|
-
const { tags: srcTags } = await src2.tags.list.query();
|
|
16690
|
+
const { tags: srcTags } = await src2.tags.list.query({});
|
|
16543
16691
|
let ensured = 0;
|
|
16544
16692
|
for (const t of srcTags) {
|
|
16545
16693
|
try {
|
|
@@ -16549,7 +16697,7 @@ async function migrateTags(src2, dest, onProgress) {
|
|
|
16549
16697
|
ensured++;
|
|
16550
16698
|
onProgress?.(ensured, srcTags.length);
|
|
16551
16699
|
}
|
|
16552
|
-
const { tags: destTags } = await dest.tags.list.query();
|
|
16700
|
+
const { tags: destTags } = await dest.tags.list.query({});
|
|
16553
16701
|
const nameToDestId = destTags.reduce((acc, t) => {
|
|
16554
16702
|
acc[t.name] = t.id;
|
|
16555
16703
|
return acc;
|
|
@@ -16802,7 +16950,7 @@ const tagsCmd = new Command().name("tags").description("manipulating tags");
|
|
|
16802
16950
|
tagsCmd.command("list").description("lists all tags").action(async () => {
|
|
16803
16951
|
const api2 = getAPIClient();
|
|
16804
16952
|
try {
|
|
16805
|
-
const tags = (await api2.tags.list.query()).tags;
|
|
16953
|
+
const tags = (await api2.tags.list.query({})).tags;
|
|
16806
16954
|
tags.sort((a, b) => b.numBookmarks - a.numBookmarks);
|
|
16807
16955
|
if (getGlobalOptions().json) {
|
|
16808
16956
|
printObject(tags);
|
|
@@ -17153,7 +17301,7 @@ async function wipeTags(api2) {
|
|
|
17153
17301
|
throw error2;
|
|
17154
17302
|
}
|
|
17155
17303
|
}
|
|
17156
|
-
const __vite_import_meta_env__ = { "BASE_URL": "/", "CLI_VERSION": "0.
|
|
17304
|
+
const __vite_import_meta_env__ = { "BASE_URL": "/", "CLI_VERSION": "0.29.1", "DEV": false, "MODE": "production", "PROD": true, "SSR": true };
|
|
17157
17305
|
const program = new Command().name("karakeep").description("A CLI interface to interact with the karakeep api").addOption(
|
|
17158
17306
|
new Option("--api-key <key>", "the API key to interact with the API").makeOptionMandatory(true).env("KARAKEEP_API_KEY")
|
|
17159
17307
|
).addOption(
|
|
@@ -17162,8 +17310,9 @@ const program = new Command().name("karakeep").description("A CLI interface to i
|
|
|
17162
17310
|
"the address of the server to connect to"
|
|
17163
17311
|
).makeOptionMandatory(true).env("KARAKEEP_SERVER_ADDR")
|
|
17164
17312
|
).addOption(new Option("--json", "to output the result as JSON")).version(
|
|
17165
|
-
__vite_import_meta_env__ && "CLI_VERSION" in __vite_import_meta_env__ ? "0.
|
|
17313
|
+
__vite_import_meta_env__ && "CLI_VERSION" in __vite_import_meta_env__ ? "0.29.1" : "0.0.0"
|
|
17166
17314
|
);
|
|
17315
|
+
program.addCommand(adminCmd);
|
|
17167
17316
|
program.addCommand(bookmarkCmd);
|
|
17168
17317
|
program.addCommand(listsCmd);
|
|
17169
17318
|
program.addCommand(tagsCmd);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@karakeep/cli",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.29.1",
|
|
5
5
|
"description": "Command Line Interface (CLI) for Karakeep",
|
|
6
6
|
"license": "GNU Affero General Public License version 3",
|
|
7
7
|
"type": "module",
|