aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/cli/commands/bookmarks.ts23
-rw-r--r--apps/cli/commands/lists.ts74
-rw-r--r--apps/cli/commands/tags.ts40
-rw-r--r--apps/cli/index.ts4
-rw-r--r--apps/cli/package.json9
-rw-r--r--pnpm-lock.yaml40
6 files changed, 186 insertions, 4 deletions
diff --git a/apps/cli/commands/bookmarks.ts b/apps/cli/commands/bookmarks.ts
index 55c87b05..0da3dd71 100644
--- a/apps/cli/commands/bookmarks.ts
+++ b/apps/cli/commands/bookmarks.ts
@@ -86,6 +86,27 @@ bookmarkCmd
});
bookmarkCmd
+ .command("update")
+ .description("Archive a bookmark")
+ .option("--title <title>", "If set, the bookmark's title will be updated")
+ .option("--note <note>", "If set, the bookmark's note will be updated")
+ .option("--archive", "If set, the bookmark will be archived")
+ .option("--no-archive", "If set, the bookmark will be unarchived")
+ .option("--favourite", "If set, the bookmark will be favourited")
+ .option("--no-favourite", "If set, the bookmark will be unfavourited")
+ .argument("<id>", "The id of the bookmark to get")
+ .action(async (id, opts) => {
+ const api = getAPIClient();
+ const resp = await api.bookmarks.updateBookmark.mutate({
+ bookmarkId: id,
+ archived: opts.archive,
+ favourited: opts.favourite,
+ title: opts.title,
+ });
+ console.log(resp);
+ });
+
+bookmarkCmd
.command("list")
.description("list all bookmarks")
.option(
@@ -93,10 +114,12 @@ bookmarkCmd
"If set, archived bookmarks will be fetched as well",
false,
)
+ .option("--list-id <id>", "If set, only items from that list will be fetched")
.action(async (opts) => {
const api = getAPIClient();
const resp = await api.bookmarks.getBookmarks.query({
archived: opts.includeArchived ? undefined : false,
+ listId: opts.listId,
});
console.log(resp.bookmarks.map(normalizeBookmark));
});
diff --git a/apps/cli/commands/lists.ts b/apps/cli/commands/lists.ts
new file mode 100644
index 00000000..099b7869
--- /dev/null
+++ b/apps/cli/commands/lists.ts
@@ -0,0 +1,74 @@
+import { Command } from "@commander-js/extra-typings";
+import { getAPIClient } from "lib/trpc";
+import { getBorderCharacters, table } from "table";
+
+import { listsToTree } from "@hoarder/shared/utils/listUtils";
+
+export const listsCmd = new Command()
+ .name("lists")
+ .description("Manipulating lists");
+
+listsCmd
+ .command("list")
+ .description("Lists all lists")
+ .action(async () => {
+ const api = getAPIClient();
+
+ const resp = await api.lists.list.query();
+ const { allPaths } = listsToTree(resp.lists);
+
+ const data: string[][] = [["Id", "Name"]];
+
+ allPaths.forEach((path) => {
+ const name = path.map((p) => `${p.icon} ${p.name}`).join(" / ");
+ const id = path[path.length - 1].id;
+ data.push([id, name]);
+ });
+ console.log(
+ table(data, { border: getBorderCharacters("ramac"), singleLine: true }),
+ );
+ });
+
+listsCmd
+ .command("delete")
+ .description("Deletes a list")
+ .argument("<id>", "The id of the list")
+ .action(async (id) => {
+ const api = getAPIClient();
+
+ await api.lists.delete.mutate({
+ listId: id,
+ });
+ console.log("Successfully deleted list with id:", id);
+ });
+
+listsCmd
+ .command("add-bookmark")
+ .description("Add a bookmark to list")
+ .requiredOption("--list <id>", "The id of the list")
+ .requiredOption("--bookmark <bookmark>", "The id of the bookmark")
+ .action(async (opts) => {
+ const api = getAPIClient();
+
+ await api.lists.addToList.mutate({
+ listId: opts.list,
+ bookmarkId: opts.bookmark,
+ });
+ console.log("Successfully added bookmark from list");
+ });
+
+listsCmd
+ .command("remove-bookmark")
+ .description("Remove a bookmark from list")
+ .requiredOption("--list <id>", "The id of the list")
+ .requiredOption("--bookmark <bookmark>", "The id of the bookmark")
+ .action(async (opts) => {
+ const api = getAPIClient();
+
+ await api.lists.removeFromList.mutate({
+ listId: opts.list,
+ bookmarkId: opts.bookmark,
+ });
+
+ console.log("Successfully removed bookmark from list");
+ });
diff --git a/apps/cli/commands/tags.ts b/apps/cli/commands/tags.ts
new file mode 100644
index 00000000..f9df83cd
--- /dev/null
+++ b/apps/cli/commands/tags.ts
@@ -0,0 +1,40 @@
+import { Command } from "@commander-js/extra-typings";
+import { getAPIClient } from "lib/trpc";
+import { getBorderCharacters, table } from "table";
+
+export const tagsCmd = new Command()
+ .name("tags")
+ .description("Manipulating tags");
+
+tagsCmd
+ .command("list")
+ .description("Lists all tags")
+ .action(async () => {
+ const api = getAPIClient();
+
+ const tags = (await api.tags.list.query()).tags;
+ tags.sort((a, b) => b.count - a.count);
+
+ const data: string[][] = [["Id", "Name", "Num bookmarks"]];
+
+ tags.forEach((tag) => {
+ data.push([tag.id, tag.name, tag.count.toString()]);
+ });
+ console.log(
+ table(data, { border: getBorderCharacters("ramac"), singleLine: true }),
+ );
+ });
+
+tagsCmd
+ .command("delete")
+ .description("Delete a tag")
+ .argument("<id>", "The id of the tag")
+ .action(async (id) => {
+ const api = getAPIClient();
+
+ await api.tags.delete.mutate({
+ tagId: id,
+ });
+
+ console.log("Successfully delete the tag with id:", id);
+ });
diff --git a/apps/cli/index.ts b/apps/cli/index.ts
index 4d0adafb..5971b811 100644
--- a/apps/cli/index.ts
+++ b/apps/cli/index.ts
@@ -1,5 +1,7 @@
import { Command, Option } from "@commander-js/extra-typings";
import { bookmarkCmd } from "commands/bookmarks";
+import { listsCmd } from "commands/lists";
+import { tagsCmd } from "commands/tags";
import { whoamiCmd } from "commands/whoami";
import { setGlobalOptions } from "lib/globals";
@@ -22,6 +24,8 @@ const program = new Command()
.version(process.env.SERVER_VERSION ?? "nightly");
program.addCommand(bookmarkCmd);
+program.addCommand(listsCmd);
+program.addCommand(tagsCmd);
program.addCommand(whoamiCmd);
setGlobalOptions(program.opts());
diff --git a/apps/cli/package.json b/apps/cli/package.json
index 05ab0d0f..87c02c3d 100644
--- a/apps/cli/package.json
+++ b/apps/cli/package.json
@@ -5,16 +5,17 @@
"private": true,
"dependencies": {
"@commander-js/extra-typings": "^12.0.1",
- "@hoarder/trpc": "workspace:^0.1.0",
"@hoarder/shared": "workspace:^0.1.0",
+ "@hoarder/trpc": "workspace:^0.1.0",
"@hoarder/tsconfig": "workspace:^0.1.0",
- "@tsconfig/node21": "^21.0.1",
- "tsx": "^4.7.1",
"@trpc/client": "11.0.0-next-beta.308",
"@trpc/server": "11.0.0-next-beta.308",
+ "@tsconfig/node21": "^21.0.1",
"chalk": "^5.3.0",
"commander": "^12.0.0",
- "superjson": "^2.2.1"
+ "superjson": "^2.2.1",
+ "table": "^6.8.2",
+ "tsx": "^4.7.1"
},
"devDependencies": {
"@hoarder/eslint-config": "workspace:^0.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 754fe314..a1010473 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -200,6 +200,9 @@ importers:
superjson:
specifier: ^2.2.1
version: 2.2.1
+ table:
+ specifier: ^6.8.2
+ version: 6.8.2
tsx:
specifier: ^4.7.1
version: 4.7.1
@@ -4784,6 +4787,10 @@ packages:
resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==}
engines: {node: '>=4'}
+ astral-regex@2.0.0:
+ resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+ engines: {node: '>=8'}
+
astring@1.8.6:
resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
hasBin: true
@@ -8223,6 +8230,9 @@ packages:
lodash.throttle@4.1.1:
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
+ lodash.truncate@4.4.2:
+ resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
+
lodash.uniq@4.5.0:
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
@@ -10830,6 +10840,10 @@ packages:
resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==}
engines: {node: '>=6'}
+ slice-ansi@4.0.0:
+ resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
+ engines: {node: '>=10'}
+
slugify@1.6.6:
resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==}
engines: {node: '>=8.0.0'}
@@ -11175,6 +11189,10 @@ packages:
resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==}
engines: {node: ^14.18.0 || >=16.0.0}
+ table@6.8.2:
+ resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==}
+ engines: {node: '>=10.0.0'}
+
tailwind-merge@2.2.1:
resolution: {integrity: sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==}
@@ -18822,6 +18840,9 @@ snapshots:
astral-regex@1.0.0:
dev: false
+ astral-regex@2.0.0:
+ dev: false
+
astring@1.8.6: {}
async-limiter@1.0.1:
@@ -23488,6 +23509,9 @@ snapshots:
lodash.throttle@4.1.1: {}
+ lodash.truncate@4.4.2:
+ dev: false
+
lodash.uniq@4.5.0:
dev: false
@@ -27348,6 +27372,13 @@ snapshots:
is-fullwidth-code-point: 2.0.0
dev: false
+ slice-ansi@4.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+ dev: false
+
slugify@1.6.6:
dev: false
@@ -27759,6 +27790,15 @@ snapshots:
tslib: 2.6.2
dev: true
+ table@6.8.2:
+ dependencies:
+ ajv: 8.12.0
+ lodash.truncate: 4.4.2
+ slice-ansi: 4.0.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ dev: false
+
tailwind-merge@2.2.1:
dependencies:
'@babel/runtime': 7.23.9