@karakeep/cli 0.27.0 → 0.29.0

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.mjs +472 -306
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -15408,6 +15408,7 @@ const zCursorV2 = z.object({
15408
15408
  function normalizeTagName(raw) {
15409
15409
  return raw.trim().replace(/^#+/, "");
15410
15410
  }
15411
+ const MAX_NUM_TAGS_PER_PAGE = 1e3;
15411
15412
  const zTagNameSchemaWithValidation = z.string().transform((s) => normalizeTagName(s).trim()).pipe(z.string().min(1));
15412
15413
  z.object({
15413
15414
  name: zTagNameSchemaWithValidation
@@ -15418,7 +15419,7 @@ const zBookmarkTagSchema = z.object({
15418
15419
  name: z.string(),
15419
15420
  attachedBy: zAttachedByEnumSchema
15420
15421
  });
15421
- z.object({
15422
+ const zGetTagResponseSchema = z.object({
15422
15423
  id: z.string(),
15423
15424
  name: z.string(),
15424
15425
  numBookmarks: z.number(),
@@ -15432,6 +15433,49 @@ z.object({
15432
15433
  id: z.string(),
15433
15434
  name: z.string()
15434
15435
  });
15436
+ const zTagCursorSchema = z.object({
15437
+ page: z.number().int().min(0)
15438
+ });
15439
+ const zTagListRequestSchema = z.object({
15440
+ nameContains: z.string().optional(),
15441
+ attachedBy: z.enum([...zAttachedByEnumSchema.options, "none"]).optional(),
15442
+ sortBy: z.enum(["name", "usage", "relevance"]).optional().default("usage"),
15443
+ cursor: zTagCursorSchema.nullish().default({ page: 0 }),
15444
+ // TODO: Remove the optional to enforce a limit after the next release
15445
+ limit: z.number().int().min(1).max(MAX_NUM_TAGS_PER_PAGE).optional()
15446
+ });
15447
+ zTagListRequestSchema.refine(
15448
+ (val) => val.sortBy != "relevance" || val.nameContains !== void 0,
15449
+ {
15450
+ message: "Relevance sorting requires a nameContains filter",
15451
+ path: ["sortBy"]
15452
+ }
15453
+ );
15454
+ const zTagListResponseSchema = z.object({
15455
+ tags: z.array(zGetTagResponseSchema),
15456
+ nextCursor: zTagCursorSchema.nullish()
15457
+ });
15458
+ z.object({
15459
+ nameContains: zTagListRequestSchema.shape.nameContains,
15460
+ sort: zTagListRequestSchema.shape.sortBy,
15461
+ attachedBy: zTagListRequestSchema.shape.attachedBy,
15462
+ cursor: z.string().transform((val, ctx) => {
15463
+ try {
15464
+ return JSON.parse(Buffer.from(val, "base64url").toString("utf8"));
15465
+ } catch {
15466
+ ctx.addIssue({
15467
+ code: "custom",
15468
+ message: "Invalid cursor"
15469
+ });
15470
+ return z.NEVER;
15471
+ }
15472
+ }).optional().pipe(zTagListRequestSchema.shape.cursor),
15473
+ limit: z.coerce.number().optional()
15474
+ });
15475
+ z.object({
15476
+ tags: zTagListResponseSchema.shape.tags,
15477
+ nextCursor: z.string().nullish()
15478
+ });
15435
15479
  const MAX_BOOKMARK_TITLE_LENGTH = 1e3;
15436
15480
  var BookmarkTypes = /* @__PURE__ */ ((BookmarkTypes2) => {
15437
15481
  BookmarkTypes2["LINK"] = "link";
@@ -15450,11 +15494,13 @@ const zAssetTypesSchema = z.enum([
15450
15494
  "video",
15451
15495
  "bookmarkAsset",
15452
15496
  "precrawledArchive",
15497
+ "userUploaded",
15453
15498
  "unknown"
15454
15499
  ]);
15455
15500
  const zAssetSchema = z.object({
15456
15501
  id: z.string(),
15457
- assetType: zAssetTypesSchema
15502
+ assetType: zAssetTypesSchema,
15503
+ fileName: z.string().nullish()
15458
15504
  });
15459
15505
  const zBookmarkedLinkSchema = z.object({
15460
15506
  type: z.literal(
@@ -15508,6 +15554,16 @@ const zBookmarkContentSchema = z.discriminatedUnion("type", [
15508
15554
  /* UNKNOWN */
15509
15555
  ) })
15510
15556
  ]);
15557
+ const zBookmarkSourceSchema = z.enum([
15558
+ "api",
15559
+ "web",
15560
+ "cli",
15561
+ "mobile",
15562
+ "extension",
15563
+ "singlefile",
15564
+ "rss",
15565
+ "import"
15566
+ ]);
15511
15567
  const zBareBookmarkSchema = z.object({
15512
15568
  id: z.string(),
15513
15569
  createdAt: z.date(),
@@ -15518,7 +15574,9 @@ const zBareBookmarkSchema = z.object({
15518
15574
  taggingStatus: z.enum(["success", "failure", "pending"]).nullable(),
15519
15575
  summarizationStatus: z.enum(["success", "failure", "pending"]).nullable(),
15520
15576
  note: z.string().nullish(),
15521
- summary: z.string().nullish()
15577
+ summary: z.string().nullish(),
15578
+ source: zBookmarkSourceSchema.nullish(),
15579
+ userId: z.string()
15522
15580
  });
15523
15581
  const zBookmarkSchema = zBareBookmarkSchema.merge(
15524
15582
  z.object({
@@ -15557,7 +15615,9 @@ z.object({
15557
15615
  createdAt: z.coerce.date().optional(),
15558
15616
  // A mechanism to prioritize crawling of bookmarks depending on whether
15559
15617
  // they were created by a user interaction or by a bulk import.
15560
- crawlPriority: z.enum(["low", "normal"]).optional()
15618
+ crawlPriority: z.enum(["low", "normal"]).optional(),
15619
+ importSessionId: z.string().optional(),
15620
+ source: zBookmarkSourceSchema.optional()
15561
15621
  }).and(
15562
15622
  z.discriminatedUnion("type", [
15563
15623
  z.object({
@@ -15734,12 +15794,22 @@ bookmarkCmd.command("add").description("creates a new bookmark").option(
15734
15794
  const results = [];
15735
15795
  const promises = [
15736
15796
  ...opts.link.map(
15737
- (url) => api2.bookmarks.createBookmark.mutate({ type: BookmarkTypes.LINK, url, title: opts.title }).then((bookmark) => {
15797
+ (url) => api2.bookmarks.createBookmark.mutate({
15798
+ type: BookmarkTypes.LINK,
15799
+ url,
15800
+ title: opts.title,
15801
+ source: "cli"
15802
+ }).then((bookmark) => {
15738
15803
  results.push(normalizeBookmark(bookmark));
15739
15804
  }).catch(printError(`Failed to add a link bookmark for url "${url}"`))
15740
15805
  ),
15741
15806
  ...opts.note.map(
15742
- (text) => api2.bookmarks.createBookmark.mutate({ type: BookmarkTypes.TEXT, text, title: opts.title }).then((bookmark) => {
15807
+ (text) => api2.bookmarks.createBookmark.mutate({
15808
+ type: BookmarkTypes.TEXT,
15809
+ text,
15810
+ title: opts.title,
15811
+ source: "cli"
15812
+ }).then((bookmark) => {
15743
15813
  results.push(normalizeBookmark(bookmark));
15744
15814
  }).catch(
15745
15815
  printError(
@@ -15751,7 +15821,12 @@ bookmarkCmd.command("add").description("creates a new bookmark").option(
15751
15821
  if (opts.stdin) {
15752
15822
  const text = require$$3.readFileSync(0, "utf-8");
15753
15823
  promises.push(
15754
- api2.bookmarks.createBookmark.mutate({ type: BookmarkTypes.TEXT, text, title: opts.title }).then((bookmark) => {
15824
+ api2.bookmarks.createBookmark.mutate({
15825
+ type: BookmarkTypes.TEXT,
15826
+ text,
15827
+ title: opts.title,
15828
+ source: "cli"
15829
+ }).then((bookmark) => {
15755
15830
  results.push(normalizeBookmark(bookmark));
15756
15831
  }).catch(
15757
15832
  printError(
@@ -15926,6 +16001,9 @@ async function createTarGz(srcDir, outFile) {
15926
16001
  });
15927
16002
  }
15928
16003
  const dumpCmd = new Command().name("dump").description("dump all account data and assets into an archive").option("--output <file>", "output archive path (.tar.gz)").option(
16004
+ "--exclude-assets",
16005
+ "exclude binary assets (skip assets index and files)"
16006
+ ).option("--exclude-bookmarks", "exclude bookmarks (metadata/content)").option("--exclude-lists", "exclude lists and list membership").option("--exclude-tags", "exclude tags").option("--exclude-ai-prompts", "exclude AI prompts").option("--exclude-rules", "exclude rule engine rules").option("--exclude-feeds", "exclude RSS feeds").option("--exclude-webhooks", "exclude webhooks").option("--exclude-user-settings", "exclude user settings").option("--exclude-link-content", "exclude link content").option(
15929
16007
  "--batch-size <n>",
15930
16008
  `number of bookmarks per page (max ${MAX_NUM_BOOKMARKS_PER_PAGE})`,
15931
16009
  (v) => Math.min(Number(v || 50), MAX_NUM_BOOKMARKS_PER_PAGE),
@@ -15966,126 +16044,163 @@ const dumpCmd = new Command().name("dump").description("dump all account data an
15966
16044
  prompts: 0
15967
16045
  }
15968
16046
  };
15969
- stepStart$2("Exporting user settings");
15970
- const settings = await api2.users.settings.query();
15971
- await writeJson(path.join(workRoot, "users", "settings.json"), settings);
15972
- stepEndSuccess$2();
15973
- stepStart$2("Exporting lists");
15974
- const { lists } = await api2.lists.list.query();
15975
- await writeJson(path.join(workRoot, "lists", "index.json"), lists);
15976
- manifest.counts.lists = lists.length;
15977
- stepEndSuccess$2();
15978
- stepStart$2("Exporting tags");
15979
- const { tags } = await api2.tags.list.query();
15980
- await writeJson(path.join(workRoot, "tags", "index.json"), tags);
15981
- manifest.counts.tags = tags.length;
15982
- stepEndSuccess$2();
15983
- stepStart$2("Exporting rules");
15984
- const { rules } = await api2.rules.list.query();
15985
- await writeJson(path.join(workRoot, "rules", "index.json"), rules);
15986
- manifest.counts.rules = rules.length;
15987
- stepEndSuccess$2();
15988
- stepStart$2("Exporting feeds");
15989
- const { feeds } = await api2.feeds.list.query();
15990
- await writeJson(path.join(workRoot, "feeds", "index.json"), feeds);
15991
- manifest.counts.feeds = feeds.length;
15992
- stepEndSuccess$2();
15993
- stepStart$2("Exporting AI prompts");
15994
- const prompts = await api2.prompts.list.query();
15995
- await writeJson(path.join(workRoot, "prompts", "index.json"), prompts);
15996
- manifest.counts.prompts = prompts.length;
15997
- stepEndSuccess$2();
15998
- stepStart$2("Exporting webhooks");
15999
- const webhooks = await api2.webhooks.list.query();
16000
- await writeJson(
16001
- path.join(workRoot, "webhooks", "index.json"),
16002
- webhooks.webhooks
16003
- );
16004
- manifest.counts.webhooks = webhooks.webhooks.length;
16005
- stepEndSuccess$2();
16006
- stepStart$2("Exporting bookmarks (metadata/content)");
16007
- const bookmarkJsonl = path.join(workRoot, "bookmarks", "index.jsonl");
16008
- let bookmarksExported = 0;
16009
- const bookmarkIterator = async function* () {
16047
+ if (!opts.excludeUserSettings) {
16048
+ stepStart$2("Exporting user settings");
16049
+ const settings = await api2.users.settings.query();
16050
+ await writeJson(
16051
+ path.join(workRoot, "users", "settings.json"),
16052
+ settings
16053
+ );
16054
+ stepEndSuccess$2();
16055
+ }
16056
+ let lists;
16057
+ if (!opts.excludeLists) {
16058
+ stepStart$2("Exporting lists");
16059
+ const resp = await api2.lists.list.query();
16060
+ lists = resp.lists;
16061
+ await writeJson(path.join(workRoot, "lists", "index.json"), lists);
16062
+ manifest.counts.lists = lists.length;
16063
+ stepEndSuccess$2();
16064
+ }
16065
+ if (!opts.excludeTags) {
16066
+ stepStart$2("Exporting tags");
16010
16067
  let cursor = null;
16068
+ let allTags = [];
16011
16069
  do {
16012
- const resp = await api2.bookmarks.getBookmarks.query({
16013
- includeContent: true,
16014
- limit: Number(opts.batchSize) || 50,
16015
- cursor,
16016
- useCursorV2: true
16070
+ const { tags, nextCursor } = await api2.tags.list.query({
16071
+ limit: MAX_NUM_TAGS_PER_PAGE,
16072
+ cursor
16017
16073
  });
16018
- for (const b of resp.bookmarks) {
16019
- yield b;
16020
- bookmarksExported++;
16021
- progressUpdate$2("Bookmarks", bookmarksExported);
16022
- }
16023
- cursor = resp.nextCursor;
16074
+ allTags.push(...tags);
16075
+ cursor = nextCursor;
16024
16076
  } while (cursor);
16025
- };
16026
- await writeJsonl(bookmarkJsonl, bookmarkIterator());
16027
- progressDone$2();
16028
- manifest.counts.bookmarks = bookmarksExported;
16029
- stepEndSuccess$2();
16030
- stepStart$2("Exporting list membership");
16031
- const membership = await buildListMembership(
16032
- api2,
16033
- lists,
16034
- (p, t) => progressUpdate$2("Lists scanned", p, t)
16035
- );
16036
- progressDone$2();
16037
- await writeJson(
16038
- path.join(workRoot, "lists", "membership.json"),
16039
- Object.fromEntries(membership.entries())
16040
- );
16041
- stepEndSuccess$2();
16042
- stepStart$2("Exporting assets (binary files)");
16043
- const assetsDir = path.join(workRoot, "assets", "files");
16044
- await ensureDir(assetsDir);
16045
- const assetsIndex = [];
16046
- let assetPageCursor = null;
16047
- let downloaded = 0;
16048
- let totalAssets = void 0;
16049
- do {
16050
- const resp = await api2.assets.list.query({
16051
- limit: 50,
16052
- cursor: assetPageCursor ?? void 0
16053
- });
16054
- if (totalAssets == null) totalAssets = resp.totalCount;
16055
- for (const a of resp.assets) {
16056
- const relPath = path.join("assets", "files", a.id);
16057
- const absPath = path.join(workRoot, relPath);
16058
- try {
16059
- await downloadAsset(
16060
- globals.serverAddr,
16061
- globals.apiKey,
16062
- a.id,
16063
- absPath
16064
- );
16065
- assetsIndex.push({
16066
- id: a.id,
16067
- assetType: a.assetType,
16068
- size: a.size,
16069
- contentType: a.contentType,
16070
- fileName: a.fileName,
16071
- bookmarkId: a.bookmarkId,
16072
- filePath: relPath.replace(/\\/g, "/")
16077
+ await writeJson(path.join(workRoot, "tags", "index.json"), allTags);
16078
+ manifest.counts.tags = allTags.length;
16079
+ stepEndSuccess$2();
16080
+ }
16081
+ if (!opts.excludeRules) {
16082
+ stepStart$2("Exporting rules");
16083
+ const { rules } = await api2.rules.list.query();
16084
+ await writeJson(path.join(workRoot, "rules", "index.json"), rules);
16085
+ manifest.counts.rules = rules.length;
16086
+ stepEndSuccess$2();
16087
+ }
16088
+ if (!opts.excludeFeeds) {
16089
+ stepStart$2("Exporting feeds");
16090
+ const { feeds } = await api2.feeds.list.query();
16091
+ await writeJson(path.join(workRoot, "feeds", "index.json"), feeds);
16092
+ manifest.counts.feeds = feeds.length;
16093
+ stepEndSuccess$2();
16094
+ }
16095
+ if (!opts.excludeAiPrompts) {
16096
+ stepStart$2("Exporting AI prompts");
16097
+ const prompts = await api2.prompts.list.query();
16098
+ await writeJson(path.join(workRoot, "prompts", "index.json"), prompts);
16099
+ manifest.counts.prompts = prompts.length;
16100
+ stepEndSuccess$2();
16101
+ }
16102
+ if (!opts.excludeWebhooks) {
16103
+ stepStart$2("Exporting webhooks");
16104
+ const webhooks = await api2.webhooks.list.query();
16105
+ await writeJson(
16106
+ path.join(workRoot, "webhooks", "index.json"),
16107
+ webhooks.webhooks
16108
+ );
16109
+ manifest.counts.webhooks = webhooks.webhooks.length;
16110
+ stepEndSuccess$2();
16111
+ }
16112
+ if (!opts.excludeBookmarks) {
16113
+ stepStart$2("Exporting bookmarks (metadata/content)");
16114
+ const bookmarkJsonl = path.join(workRoot, "bookmarks", "index.jsonl");
16115
+ let bookmarksExported = 0;
16116
+ const bookmarkIterator = async function* () {
16117
+ let cursor = null;
16118
+ do {
16119
+ const resp = await api2.bookmarks.getBookmarks.query({
16120
+ includeContent: !opts.excludeLinkContent,
16121
+ limit: Number(opts.batchSize) || 50,
16122
+ cursor,
16123
+ useCursorV2: true
16073
16124
  });
16074
- downloaded++;
16075
- progressUpdate$2("Assets", downloaded, totalAssets);
16076
- } catch (e) {
16077
- printErrorMessageWithReason(
16078
- `Failed to download asset "${a.id}"`,
16079
- e
16080
- );
16125
+ for (const b of resp.bookmarks) {
16126
+ yield b;
16127
+ bookmarksExported++;
16128
+ progressUpdate$2("Bookmarks", bookmarksExported);
16129
+ }
16130
+ cursor = resp.nextCursor;
16131
+ } while (cursor);
16132
+ };
16133
+ await writeJsonl(bookmarkJsonl, bookmarkIterator());
16134
+ progressDone$2();
16135
+ manifest.counts.bookmarks = bookmarksExported;
16136
+ stepEndSuccess$2();
16137
+ }
16138
+ if (!opts.excludeLists && !opts.excludeBookmarks && lists) {
16139
+ stepStart$2("Exporting list membership");
16140
+ const membership = await buildListMembership(
16141
+ api2,
16142
+ lists,
16143
+ (p, t) => progressUpdate$2("Lists scanned", p, t)
16144
+ );
16145
+ progressDone$2();
16146
+ await writeJson(
16147
+ path.join(workRoot, "lists", "membership.json"),
16148
+ Object.fromEntries(membership.entries())
16149
+ );
16150
+ stepEndSuccess$2();
16151
+ }
16152
+ if (!opts.excludeAssets) {
16153
+ stepStart$2("Exporting assets (binary files)");
16154
+ const assetsDir = path.join(workRoot, "assets", "files");
16155
+ await ensureDir(assetsDir);
16156
+ const assetsIndex = [];
16157
+ let assetPageCursor = null;
16158
+ let downloaded = 0;
16159
+ let totalAssets = void 0;
16160
+ do {
16161
+ const resp = await api2.assets.list.query({
16162
+ limit: 50,
16163
+ cursor: assetPageCursor ?? void 0
16164
+ });
16165
+ if (totalAssets == null) totalAssets = resp.totalCount;
16166
+ for (const a of resp.assets) {
16167
+ const relPath = path.join("assets", "files", a.id);
16168
+ const absPath = path.join(workRoot, relPath);
16169
+ try {
16170
+ await downloadAsset(
16171
+ globals.serverAddr,
16172
+ globals.apiKey,
16173
+ a.id,
16174
+ absPath
16175
+ );
16176
+ assetsIndex.push({
16177
+ id: a.id,
16178
+ assetType: a.assetType,
16179
+ size: a.size,
16180
+ contentType: a.contentType,
16181
+ fileName: a.fileName,
16182
+ bookmarkId: a.bookmarkId,
16183
+ filePath: relPath.replace(/\\/g, "/")
16184
+ });
16185
+ downloaded++;
16186
+ progressUpdate$2("Assets", downloaded, totalAssets);
16187
+ } catch (e) {
16188
+ printErrorMessageWithReason(
16189
+ `Failed to download asset "${a.id}"`,
16190
+ e
16191
+ );
16192
+ }
16081
16193
  }
16082
- }
16083
- assetPageCursor = resp.nextCursor;
16084
- } while (assetPageCursor);
16085
- progressDone$2();
16086
- manifest.counts.assets = downloaded;
16087
- await writeJson(path.join(workRoot, "assets", "index.json"), assetsIndex);
16088
- stepEndSuccess$2();
16194
+ assetPageCursor = resp.nextCursor;
16195
+ } while (assetPageCursor);
16196
+ progressDone$2();
16197
+ manifest.counts.assets = downloaded;
16198
+ await writeJson(
16199
+ path.join(workRoot, "assets", "index.json"),
16200
+ assetsIndex
16201
+ );
16202
+ stepEndSuccess$2();
16203
+ }
16089
16204
  stepStart$2("Writing manifest");
16090
16205
  await writeJson(path.join(workRoot, "manifest.json"), manifest);
16091
16206
  stepEndSuccess$2();
@@ -16181,7 +16296,7 @@ function progressDone$1() {
16181
16296
  const migrateCmd = new Command().name("migrate").description("migrate data from a source server to a destination server").requiredOption(
16182
16297
  "--dest-server <url>",
16183
16298
  "destination server base URL, e.g. https://dest.example.com"
16184
- ).requiredOption("--dest-api-key <key>", "API key for the destination server").option("-y, --yes", "skip confirmation prompt").option(
16299
+ ).requiredOption("--dest-api-key <key>", "API key for the destination server").option("-y, --yes", "skip confirmation prompt").option("--exclude-assets", "exclude assets (skip asset bookmarks)").option("--exclude-lists", "exclude lists and list membership").option("--exclude-ai-prompts", "exclude AI prompts").option("--exclude-rules", "exclude rule engine rules").option("--exclude-feeds", "exclude RSS feeds").option("--exclude-webhooks", "exclude webhooks").option("--exclude-bookmarks", "exclude bookmarks migration").option("--exclude-tags", "exclude tags migration").option("--exclude-user-settings", "exclude user settings migration").option(
16185
16300
  "--batch-size <n>",
16186
16301
  `number of bookmarks per page (max ${MAX_NUM_BOOKMARKS_PER_PAGE})`,
16187
16302
  (v) => Math.min(Number(v || 50), MAX_NUM_BOOKMARKS_PER_PAGE),
@@ -16216,113 +16331,143 @@ const migrateCmd = new Command().name("migrate").description("migrate data from
16216
16331
  totalBookmarks = stats.numBookmarks;
16217
16332
  } catch {
16218
16333
  }
16219
- stepStart$1("Migrating user settings");
16220
- await migrateUserSettings(src2, dest);
16221
- stepEndSuccess$1();
16222
- stepStart$1("Migrating lists");
16223
- const listsStart = Date.now();
16224
- const {
16225
- lists,
16226
- listIdMap,
16227
- createdCount: listsCreated
16228
- } = await migrateLists(src2, dest, (created, alreadyExists, total) => {
16229
- progressUpdate$1("Lists (created)", created + alreadyExists, total);
16230
- });
16231
- progressDone$1();
16232
- stepEndSuccess$1(
16233
- `${listsCreated} created in ${Math.round((Date.now() - listsStart) / 1e3)}s`
16234
- );
16235
- stepStart$1("Migrating feeds");
16236
- const feedsStart = Date.now();
16237
- const { idMap: feedIdMap, count: feedsCount } = await migrateFeeds(
16238
- src2,
16239
- dest,
16240
- (created, total) => {
16334
+ if (!opts.excludeUserSettings) {
16335
+ stepStart$1("Migrating user settings");
16336
+ await migrateUserSettings(src2, dest);
16337
+ stepEndSuccess$1();
16338
+ }
16339
+ let lists = [];
16340
+ let listIdMap = /* @__PURE__ */ new Map();
16341
+ if (!opts.excludeLists) {
16342
+ stepStart$1("Migrating lists");
16343
+ const listsStart = Date.now();
16344
+ const listsRes = await migrateLists(
16345
+ src2,
16346
+ dest,
16347
+ (created, alreadyExists, total) => {
16348
+ progressUpdate$1("Lists (created)", created + alreadyExists, total);
16349
+ }
16350
+ );
16351
+ lists = listsRes.lists;
16352
+ listIdMap = listsRes.listIdMap;
16353
+ progressDone$1();
16354
+ stepEndSuccess$1(
16355
+ `${listsRes.createdCount} created in ${Math.round((Date.now() - listsStart) / 1e3)}s`
16356
+ );
16357
+ }
16358
+ let feedIdMap = /* @__PURE__ */ new Map();
16359
+ if (!opts.excludeFeeds) {
16360
+ stepStart$1("Migrating feeds");
16361
+ const feedsStart = Date.now();
16362
+ const res = await migrateFeeds(src2, dest, (created, total) => {
16241
16363
  progressUpdate$1("Feeds", created, total);
16242
- }
16243
- );
16244
- progressDone$1();
16245
- stepEndSuccess$1(
16246
- `${feedsCount} migrated in ${Math.round((Date.now() - feedsStart) / 1e3)}s`
16247
- );
16248
- stepStart$1("Migrating AI prompts");
16249
- const promptsStart = Date.now();
16250
- const promptsCount = await migratePrompts(src2, dest, (created, total) => {
16251
- progressUpdate$1("Prompts", created, total);
16252
- });
16253
- progressDone$1();
16254
- stepEndSuccess$1(
16255
- `${promptsCount} migrated in ${Math.round((Date.now() - promptsStart) / 1e3)}s`
16256
- );
16257
- stepStart$1("Migrating webhooks");
16258
- const webhooksStart = Date.now();
16259
- const webhooksCount = await migrateWebhooks(
16260
- src2,
16261
- dest,
16262
- (created, total) => {
16263
- progressUpdate$1("Webhooks", created, total);
16264
- }
16265
- );
16266
- progressDone$1();
16267
- stepEndSuccess$1(
16268
- `${webhooksCount} migrated in ${Math.round((Date.now() - webhooksStart) / 1e3)}s`
16269
- );
16270
- stepStart$1("Ensuring tags on destination");
16271
- const tagsStart = Date.now();
16272
- const { idMap: tagIdMap, count: tagsCount } = await migrateTags(
16273
- src2,
16274
- dest,
16275
- (ensured, total) => {
16364
+ });
16365
+ feedIdMap = res.idMap;
16366
+ progressDone$1();
16367
+ stepEndSuccess$1(
16368
+ `${res.count} migrated in ${Math.round((Date.now() - feedsStart) / 1e3)}s`
16369
+ );
16370
+ }
16371
+ if (!opts.excludeAiPrompts) {
16372
+ stepStart$1("Migrating AI prompts");
16373
+ const promptsStart = Date.now();
16374
+ const promptsCount = await migratePrompts(
16375
+ src2,
16376
+ dest,
16377
+ (created, total) => {
16378
+ progressUpdate$1("Prompts", created, total);
16379
+ }
16380
+ );
16381
+ progressDone$1();
16382
+ stepEndSuccess$1(
16383
+ `${promptsCount} migrated in ${Math.round((Date.now() - promptsStart) / 1e3)}s`
16384
+ );
16385
+ }
16386
+ if (!opts.excludeWebhooks) {
16387
+ stepStart$1("Migrating webhooks");
16388
+ const webhooksStart = Date.now();
16389
+ const webhooksCount = await migrateWebhooks(
16390
+ src2,
16391
+ dest,
16392
+ (created, total) => {
16393
+ progressUpdate$1("Webhooks", created, total);
16394
+ }
16395
+ );
16396
+ progressDone$1();
16397
+ stepEndSuccess$1(
16398
+ `${webhooksCount} migrated in ${Math.round((Date.now() - webhooksStart) / 1e3)}s`
16399
+ );
16400
+ }
16401
+ let tagIdMap = /* @__PURE__ */ new Map();
16402
+ if (!opts.excludeTags) {
16403
+ stepStart$1("Ensuring tags on destination");
16404
+ const tagsStart = Date.now();
16405
+ const res = await migrateTags(src2, dest, (ensured, total) => {
16276
16406
  progressUpdate$1("Tags", ensured, total);
16277
- }
16278
- );
16279
- progressDone$1();
16280
- stepEndSuccess$1(
16281
- `${tagsCount} ensured in ${Math.round((Date.now() - tagsStart) / 1e3)}s`
16282
- );
16283
- stepStart$1("Migrating rule engine rules");
16284
- const rulesStart = Date.now();
16285
- const rulesCount = await migrateRules(
16286
- src2,
16287
- dest,
16288
- { tagIdMap, listIdMap, feedIdMap },
16289
- (created, total) => {
16290
- progressUpdate$1("Rules", created, total);
16291
- }
16292
- );
16293
- progressDone$1();
16294
- stepEndSuccess$1(
16295
- `${rulesCount} migrated in ${Math.round((Date.now() - rulesStart) / 1e3)}s`
16296
- );
16297
- stepStart$1("Building list membership for bookmarks");
16298
- const blmStart = Date.now();
16299
- const { bookmarkListsMap, scannedLists } = await buildBookmarkListMembership(src2, lists, (processed, total) => {
16300
- progressUpdate$1("Scanning lists", processed, total);
16301
- });
16302
- progressDone$1();
16303
- stepEndSuccess$1(
16304
- `${scannedLists} lists scanned in ${Math.round((Date.now() - blmStart) / 1e3)}s`
16305
- );
16306
- stepStart$1("Migrating bookmarks");
16307
- const bmStart = Date.now();
16308
- const res = await migrateBookmarks(src2, dest, {
16309
- pageSize: Number(opts.batchSize) || 50,
16310
- listIdMap,
16311
- bookmarkListsMap,
16312
- total: totalBookmarks,
16313
- onProgress: (migrated, skipped, total) => {
16314
- const suffix = skipped > 0 ? `(skipped ${skipped} assets)` : void 0;
16315
- progressUpdate$1("Bookmarks", migrated, total, suffix);
16316
- },
16317
- srcServer: globals.serverAddr,
16318
- srcApiKey: globals.apiKey,
16319
- destServer: opts.destServer,
16320
- destApiKey: opts.destApiKey
16321
- });
16322
- progressDone$1();
16323
- stepEndSuccess$1(
16324
- `${res.migrated} migrated${res.skippedAssets ? `, ${res.skippedAssets} skipped` : ""} in ${Math.round((Date.now() - bmStart) / 1e3)}s`
16325
- );
16407
+ });
16408
+ tagIdMap = res.idMap;
16409
+ progressDone$1();
16410
+ stepEndSuccess$1(
16411
+ `${res.count} ensured in ${Math.round((Date.now() - tagsStart) / 1e3)}s`
16412
+ );
16413
+ }
16414
+ if (!opts.excludeRules && !opts.excludeLists && !opts.excludeFeeds && !opts.excludeTags) {
16415
+ stepStart$1("Migrating rule engine rules");
16416
+ const rulesStart = Date.now();
16417
+ const rulesCount = await migrateRules(
16418
+ src2,
16419
+ dest,
16420
+ { tagIdMap, listIdMap, feedIdMap },
16421
+ (created, total) => {
16422
+ progressUpdate$1("Rules", created, total);
16423
+ }
16424
+ );
16425
+ progressDone$1();
16426
+ stepEndSuccess$1(
16427
+ `${rulesCount} migrated in ${Math.round((Date.now() - rulesStart) / 1e3)}s`
16428
+ );
16429
+ }
16430
+ let bookmarkListsMap = /* @__PURE__ */ new Map();
16431
+ if (!opts.excludeLists && !opts.excludeBookmarks) {
16432
+ stepStart$1("Building list membership for bookmarks");
16433
+ const blmStart = Date.now();
16434
+ const res = await buildBookmarkListMembership(
16435
+ src2,
16436
+ lists,
16437
+ (processed, total) => {
16438
+ progressUpdate$1("Scanning lists", processed, total);
16439
+ }
16440
+ );
16441
+ bookmarkListsMap = res.bookmarkListsMap;
16442
+ progressDone$1();
16443
+ stepEndSuccess$1(
16444
+ `${res.scannedLists} lists scanned in ${Math.round((Date.now() - blmStart) / 1e3)}s`
16445
+ );
16446
+ }
16447
+ if (!opts.excludeBookmarks) {
16448
+ stepStart$1("Migrating bookmarks");
16449
+ const bmStart = Date.now();
16450
+ const res = await migrateBookmarks(src2, dest, {
16451
+ pageSize: Number(opts.batchSize) || 50,
16452
+ listIdMap,
16453
+ bookmarkListsMap,
16454
+ total: totalBookmarks,
16455
+ onProgress: (migrated, skipped, total) => {
16456
+ const suffix = skipped > 0 ? `(skipped ${skipped} assets)` : void 0;
16457
+ progressUpdate$1("Bookmarks", migrated, total, suffix);
16458
+ },
16459
+ srcServer: globals.serverAddr,
16460
+ srcApiKey: globals.apiKey,
16461
+ destServer: opts.destServer,
16462
+ destApiKey: opts.destApiKey,
16463
+ excludeAssets: !!opts.excludeAssets,
16464
+ excludeLists: !!opts.excludeLists
16465
+ });
16466
+ progressDone$1();
16467
+ stepEndSuccess$1(
16468
+ `${res.migrated} migrated${res.skippedAssets ? `, ${res.skippedAssets} skipped` : ""} in ${Math.round((Date.now() - bmStart) / 1e3)}s`
16469
+ );
16470
+ }
16326
16471
  printStatusMessage(true, "Migration completed successfully");
16327
16472
  } catch (error2) {
16328
16473
  stepEndFail$1();
@@ -16478,7 +16623,7 @@ async function migrateWebhooks(src2, dest, onProgress) {
16478
16623
  }
16479
16624
  async function migrateTags(src2, dest, onProgress) {
16480
16625
  try {
16481
- const { tags: srcTags } = await src2.tags.list.query();
16626
+ const { tags: srcTags } = await src2.tags.list.query({});
16482
16627
  let ensured = 0;
16483
16628
  for (const t of srcTags) {
16484
16629
  try {
@@ -16488,7 +16633,7 @@ async function migrateTags(src2, dest, onProgress) {
16488
16633
  ensured++;
16489
16634
  onProgress?.(ensured, srcTags.length);
16490
16635
  }
16491
- const { tags: destTags } = await dest.tags.list.query();
16636
+ const { tags: destTags } = await dest.tags.list.query({});
16492
16637
  const nameToDestId = destTags.reduce((acc, t) => {
16493
16638
  acc[t.name] = t.id;
16494
16639
  return acc;
@@ -16651,6 +16796,10 @@ async function migrateBookmarks(src2, dest, opts) {
16651
16796
  break;
16652
16797
  }
16653
16798
  case BookmarkTypes.ASSET: {
16799
+ if (opts.excludeAssets) {
16800
+ skippedAssets++;
16801
+ continue;
16802
+ }
16654
16803
  try {
16655
16804
  const downloadResp = await fetch(
16656
16805
  `${opts.srcServer}/api/assets/${b.content.assetId}`,
@@ -16706,14 +16855,16 @@ async function migrateBookmarks(src2, dest, opts) {
16706
16855
  detach: []
16707
16856
  });
16708
16857
  }
16709
- const srcListIds = opts.bookmarkListsMap.get(b.id) ?? [];
16710
- for (const srcListId of srcListIds) {
16711
- const destListId = opts.listIdMap.get(srcListId);
16712
- if (destListId) {
16713
- await dest.lists.addToList.mutate({
16714
- listId: destListId,
16715
- bookmarkId: createdId
16716
- });
16858
+ if (!opts.excludeLists) {
16859
+ const srcListIds = opts.bookmarkListsMap.get(b.id) ?? [];
16860
+ for (const srcListId of srcListIds) {
16861
+ const destListId = opts.listIdMap.get(srcListId);
16862
+ if (destListId) {
16863
+ await dest.lists.addToList.mutate({
16864
+ listId: destListId,
16865
+ bookmarkId: createdId
16866
+ });
16867
+ }
16717
16868
  }
16718
16869
  }
16719
16870
  migrated++;
@@ -16735,7 +16886,7 @@ const tagsCmd = new Command().name("tags").description("manipulating tags");
16735
16886
  tagsCmd.command("list").description("lists all tags").action(async () => {
16736
16887
  const api2 = getAPIClient();
16737
16888
  try {
16738
- const tags = (await api2.tags.list.query()).tags;
16889
+ const tags = (await api2.tags.list.query({})).tags;
16739
16890
  tags.sort((a, b) => b.numBookmarks - a.numBookmarks);
16740
16891
  if (getGlobalOptions().json) {
16741
16892
  printObject(tags);
@@ -16802,7 +16953,7 @@ function progressUpdate(prefix, current, total, suffix) {
16802
16953
  function progressDone() {
16803
16954
  process.stdout.write("\n");
16804
16955
  }
16805
- const wipeCmd = new Command().name("wipe").description("wipe all data for the current user from the server").option("-y, --yes", "skip confirmation prompt").option(
16956
+ const wipeCmd = new Command().name("wipe").description("wipe all data for the current user from the server").option("-y, --yes", "skip confirmation prompt").option("--exclude-lists", "exclude lists from deletion").option("--exclude-ai-prompts", "exclude AI prompts from deletion").option("--exclude-rules", "exclude rules from deletion").option("--exclude-feeds", "exclude RSS feeds from deletion").option("--exclude-webhooks", "exclude webhooks from deletion").option("--exclude-bookmarks", "exclude bookmarks from deletion").option("--exclude-tags", "exclude tags cleanup from deletion").option("--exclude-user-settings", "exclude user settings (no-op)").option(
16806
16957
  "--batch-size <n>",
16807
16958
  `number of bookmarks per page (max ${MAX_NUM_BOOKMARKS_PER_PAGE})`,
16808
16959
  (v) => Math.min(Number(v || 50), MAX_NUM_BOOKMARKS_PER_PAGE),
@@ -16832,70 +16983,84 @@ const wipeCmd = new Command().name("wipe").description("wipe all data for the cu
16832
16983
  totalBookmarks = stats.numBookmarks;
16833
16984
  } catch {
16834
16985
  }
16835
- stepStart("Deleting rule engine rules");
16836
- const rulesStart = Date.now();
16837
- const rulesDeleted = await wipeRules(api2, (deleted, total) => {
16838
- progressUpdate("Rules", deleted, total);
16839
- });
16840
- progressDone();
16841
- stepEndSuccess(
16842
- `${rulesDeleted} deleted in ${Math.round((Date.now() - rulesStart) / 1e3)}s`
16843
- );
16844
- stepStart("Deleting feeds");
16845
- const feedsStart = Date.now();
16846
- const feedsDeleted = await wipeFeeds(api2, (deleted, total) => {
16847
- progressUpdate("Feeds", deleted, total);
16848
- });
16849
- progressDone();
16850
- stepEndSuccess(
16851
- `${feedsDeleted} deleted in ${Math.round((Date.now() - feedsStart) / 1e3)}s`
16852
- );
16853
- stepStart("Deleting webhooks");
16854
- const webhooksStart = Date.now();
16855
- const webhooksDeleted = await wipeWebhooks(api2, (deleted, total) => {
16856
- progressUpdate("Webhooks", deleted, total);
16857
- });
16858
- progressDone();
16859
- stepEndSuccess(
16860
- `${webhooksDeleted} deleted in ${Math.round((Date.now() - webhooksStart) / 1e3)}s`
16861
- );
16862
- stepStart("Deleting AI prompts");
16863
- const promptsStart = Date.now();
16864
- const promptsDeleted = await wipePrompts(api2, (deleted, total) => {
16865
- progressUpdate("Prompts", deleted, total);
16866
- });
16867
- progressDone();
16868
- stepEndSuccess(
16869
- `${promptsDeleted} deleted in ${Math.round((Date.now() - promptsStart) / 1e3)}s`
16870
- );
16871
- stepStart("Deleting bookmarks");
16872
- const bmStart = Date.now();
16873
- const bookmarksDeleted = await wipeBookmarks(api2, {
16874
- pageSize: Number(opts.batchSize) || 50,
16875
- total: totalBookmarks,
16876
- onProgress: (deleted, total) => {
16877
- progressUpdate("Bookmarks", deleted, total);
16878
- }
16879
- });
16880
- progressDone();
16881
- stepEndSuccess(
16882
- `${bookmarksDeleted} deleted in ${Math.round((Date.now() - bmStart) / 1e3)}s`
16883
- );
16884
- stepStart("Deleting lists");
16885
- const listsStart = Date.now();
16886
- const listsDeleted = await wipeLists(api2, (deleted, total) => {
16887
- progressUpdate("Lists", deleted, total);
16888
- });
16889
- progressDone();
16890
- stepEndSuccess(
16891
- `${listsDeleted} deleted in ${Math.round((Date.now() - listsStart) / 1e3)}s`
16892
- );
16893
- stepStart("Deleting unused tags");
16894
- const tagsStart = Date.now();
16895
- const deletedTags = await wipeTags(api2);
16896
- stepEndSuccess(
16897
- `${deletedTags} deleted in ${Math.round((Date.now() - tagsStart) / 1e3)}s`
16898
- );
16986
+ if (!opts.excludeRules) {
16987
+ stepStart("Deleting rule engine rules");
16988
+ const rulesStart = Date.now();
16989
+ const rulesDeleted = await wipeRules(api2, (deleted, total) => {
16990
+ progressUpdate("Rules", deleted, total);
16991
+ });
16992
+ progressDone();
16993
+ stepEndSuccess(
16994
+ `${rulesDeleted} deleted in ${Math.round((Date.now() - rulesStart) / 1e3)}s`
16995
+ );
16996
+ }
16997
+ if (!opts.excludeFeeds) {
16998
+ stepStart("Deleting feeds");
16999
+ const feedsStart = Date.now();
17000
+ const feedsDeleted = await wipeFeeds(api2, (deleted, total) => {
17001
+ progressUpdate("Feeds", deleted, total);
17002
+ });
17003
+ progressDone();
17004
+ stepEndSuccess(
17005
+ `${feedsDeleted} deleted in ${Math.round((Date.now() - feedsStart) / 1e3)}s`
17006
+ );
17007
+ }
17008
+ if (!opts.excludeWebhooks) {
17009
+ stepStart("Deleting webhooks");
17010
+ const webhooksStart = Date.now();
17011
+ const webhooksDeleted = await wipeWebhooks(api2, (deleted, total) => {
17012
+ progressUpdate("Webhooks", deleted, total);
17013
+ });
17014
+ progressDone();
17015
+ stepEndSuccess(
17016
+ `${webhooksDeleted} deleted in ${Math.round((Date.now() - webhooksStart) / 1e3)}s`
17017
+ );
17018
+ }
17019
+ if (!opts.excludeAiPrompts) {
17020
+ stepStart("Deleting AI prompts");
17021
+ const promptsStart = Date.now();
17022
+ const promptsDeleted = await wipePrompts(api2, (deleted, total) => {
17023
+ progressUpdate("Prompts", deleted, total);
17024
+ });
17025
+ progressDone();
17026
+ stepEndSuccess(
17027
+ `${promptsDeleted} deleted in ${Math.round((Date.now() - promptsStart) / 1e3)}s`
17028
+ );
17029
+ }
17030
+ if (!opts.excludeBookmarks) {
17031
+ stepStart("Deleting bookmarks");
17032
+ const bmStart = Date.now();
17033
+ const bookmarksDeleted = await wipeBookmarks(api2, {
17034
+ pageSize: Number(opts.batchSize) || 50,
17035
+ total: totalBookmarks,
17036
+ onProgress: (deleted, total) => {
17037
+ progressUpdate("Bookmarks", deleted, total);
17038
+ }
17039
+ });
17040
+ progressDone();
17041
+ stepEndSuccess(
17042
+ `${bookmarksDeleted} deleted in ${Math.round((Date.now() - bmStart) / 1e3)}s`
17043
+ );
17044
+ }
17045
+ if (!opts.excludeLists) {
17046
+ stepStart("Deleting lists");
17047
+ const listsStart = Date.now();
17048
+ const listsDeleted = await wipeLists(api2, (deleted, total) => {
17049
+ progressUpdate("Lists", deleted, total);
17050
+ });
17051
+ progressDone();
17052
+ stepEndSuccess(
17053
+ `${listsDeleted} deleted in ${Math.round((Date.now() - listsStart) / 1e3)}s`
17054
+ );
17055
+ }
17056
+ if (!opts.excludeTags) {
17057
+ stepStart("Deleting unused tags");
17058
+ const tagsStart = Date.now();
17059
+ const deletedTags = await wipeTags(api2);
17060
+ stepEndSuccess(
17061
+ `${deletedTags} deleted in ${Math.round((Date.now() - tagsStart) / 1e3)}s`
17062
+ );
17063
+ }
16899
17064
  printStatusMessage(true, "Wipe completed successfully");
16900
17065
  } catch (error2) {
16901
17066
  stepEndFail();
@@ -16998,7 +17163,8 @@ async function wipeBookmarks(api2, opts) {
16998
17163
  const resp = await api2.bookmarks.getBookmarks.query({
16999
17164
  limit: opts.pageSize,
17000
17165
  cursor,
17001
- useCursorV2: true
17166
+ useCursorV2: true,
17167
+ includeContent: false
17002
17168
  });
17003
17169
  for (const b of resp.bookmarks) {
17004
17170
  try {
@@ -17071,7 +17237,7 @@ async function wipeTags(api2) {
17071
17237
  throw error2;
17072
17238
  }
17073
17239
  }
17074
- const __vite_import_meta_env__ = { "BASE_URL": "/", "CLI_VERSION": "0.27.0", "DEV": false, "MODE": "production", "PROD": true, "SSR": true };
17240
+ const __vite_import_meta_env__ = { "BASE_URL": "/", "CLI_VERSION": "0.29.0", "DEV": false, "MODE": "production", "PROD": true, "SSR": true };
17075
17241
  const program = new Command().name("karakeep").description("A CLI interface to interact with the karakeep api").addOption(
17076
17242
  new Option("--api-key <key>", "the API key to interact with the API").makeOptionMandatory(true).env("KARAKEEP_API_KEY")
17077
17243
  ).addOption(
@@ -17080,7 +17246,7 @@ const program = new Command().name("karakeep").description("A CLI interface to i
17080
17246
  "the address of the server to connect to"
17081
17247
  ).makeOptionMandatory(true).env("KARAKEEP_SERVER_ADDR")
17082
17248
  ).addOption(new Option("--json", "to output the result as JSON")).version(
17083
- __vite_import_meta_env__ && "CLI_VERSION" in __vite_import_meta_env__ ? "0.27.0" : "0.0.0"
17249
+ __vite_import_meta_env__ && "CLI_VERSION" in __vite_import_meta_env__ ? "0.29.0" : "0.0.0"
17084
17250
  );
17085
17251
  program.addCommand(bookmarkCmd);
17086
17252
  program.addCommand(listsCmd);
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.27.0",
4
+ "version": "0.29.0",
5
5
  "description": "Command Line Interface (CLI) for Karakeep",
6
6
  "license": "GNU Affero General Public License version 3",
7
7
  "type": "module",