@bitsocial/bitsocial-cli 0.19.39

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 (80) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +706 -0
  3. package/bin/dev +20 -0
  4. package/bin/dev.cmd +3 -0
  5. package/bin/postinstall.js +125 -0
  6. package/bin/run +13 -0
  7. package/bin/run.cmd +3 -0
  8. package/dist/challenge-packages/challenge-utils.d.ts +24 -0
  9. package/dist/challenge-packages/challenge-utils.js +304 -0
  10. package/dist/cli/base-command.d.ts +11 -0
  11. package/dist/cli/base-command.js +45 -0
  12. package/dist/cli/commands/challenge/install.d.ts +12 -0
  13. package/dist/cli/commands/challenge/install.js +131 -0
  14. package/dist/cli/commands/challenge/list.d.ts +10 -0
  15. package/dist/cli/commands/challenge/list.js +37 -0
  16. package/dist/cli/commands/challenge/remove.d.ts +12 -0
  17. package/dist/cli/commands/challenge/remove.js +60 -0
  18. package/dist/cli/commands/community/create.d.ts +12 -0
  19. package/dist/cli/commands/community/create.js +54 -0
  20. package/dist/cli/commands/community/delete.d.ts +10 -0
  21. package/dist/cli/commands/community/delete.js +44 -0
  22. package/dist/cli/commands/community/edit.d.ts +12 -0
  23. package/dist/cli/commands/community/edit.js +74 -0
  24. package/dist/cli/commands/community/get.d.ts +9 -0
  25. package/dist/cli/commands/community/get.js +32 -0
  26. package/dist/cli/commands/community/list.d.ts +9 -0
  27. package/dist/cli/commands/community/list.js +30 -0
  28. package/dist/cli/commands/community/start.d.ts +13 -0
  29. package/dist/cli/commands/community/start.js +46 -0
  30. package/dist/cli/commands/community/stop.d.ts +10 -0
  31. package/dist/cli/commands/community/stop.js +44 -0
  32. package/dist/cli/commands/daemon.d.ts +14 -0
  33. package/dist/cli/commands/daemon.js +484 -0
  34. package/dist/cli/commands/logs.d.ts +24 -0
  35. package/dist/cli/commands/logs.js +199 -0
  36. package/dist/cli/commands/subplebbit/create.d.ts +12 -0
  37. package/dist/cli/commands/subplebbit/create.js +54 -0
  38. package/dist/cli/commands/subplebbit/edit.d.ts +12 -0
  39. package/dist/cli/commands/subplebbit/edit.js +73 -0
  40. package/dist/cli/commands/subplebbit/get.d.ts +9 -0
  41. package/dist/cli/commands/subplebbit/get.js +32 -0
  42. package/dist/cli/commands/subplebbit/list.d.ts +9 -0
  43. package/dist/cli/commands/subplebbit/list.js +30 -0
  44. package/dist/cli/commands/subplebbit/start.d.ts +10 -0
  45. package/dist/cli/commands/subplebbit/start.js +41 -0
  46. package/dist/cli/commands/subplebbit/stop.d.ts +10 -0
  47. package/dist/cli/commands/subplebbit/stop.js +43 -0
  48. package/dist/cli/commands/update/check.d.ts +6 -0
  49. package/dist/cli/commands/update/check.js +28 -0
  50. package/dist/cli/commands/update/install.d.ts +12 -0
  51. package/dist/cli/commands/update/install.js +63 -0
  52. package/dist/cli/commands/update/versions.d.ts +9 -0
  53. package/dist/cli/commands/update/versions.js +29 -0
  54. package/dist/cli/hooks/init/version-hook.d.ts +3 -0
  55. package/dist/cli/hooks/init/version-hook.js +43 -0
  56. package/dist/cli/hooks/prerun/parse-dynamic-flags-hook.d.ts +3 -0
  57. package/dist/cli/hooks/prerun/parse-dynamic-flags-hook.js +94 -0
  58. package/dist/cli/types.d.ts +4 -0
  59. package/dist/cli/types.js +1 -0
  60. package/dist/common-utils/data-migration.d.ts +1 -0
  61. package/dist/common-utils/data-migration.js +27 -0
  62. package/dist/common-utils/defaults.d.ts +9 -0
  63. package/dist/common-utils/defaults.js +10 -0
  64. package/dist/common-utils/resolvers.d.ts +2 -0
  65. package/dist/common-utils/resolvers.js +6 -0
  66. package/dist/index.d.ts +1 -0
  67. package/dist/index.js +1 -0
  68. package/dist/ipfs/startIpfs.d.ts +3 -0
  69. package/dist/ipfs/startIpfs.js +304 -0
  70. package/dist/seeder.d.ts +1 -0
  71. package/dist/seeder.js +83 -0
  72. package/dist/update/npm-registry.d.ts +6 -0
  73. package/dist/update/npm-registry.js +66 -0
  74. package/dist/update/semver.d.ts +5 -0
  75. package/dist/update/semver.js +29 -0
  76. package/dist/util.d.ts +31 -0
  77. package/dist/util.js +157 -0
  78. package/dist/webui/daemon-server.d.ts +10 -0
  79. package/dist/webui/daemon-server.js +140 -0
  80. package/package.json +143 -0
@@ -0,0 +1,131 @@
1
+ import { Args, Flags, Command } from "@oclif/core";
2
+ import path from "path";
3
+ import fs from "fs/promises";
4
+ import decompress from "decompress";
5
+ import defaults from "../../../common-utils/defaults.js";
6
+ import { ensureNpmAvailable, ensureChallengesDir, challengeNameToDir, readChallengePackageJson, runNpmPack, runNpmInstall, verifyNativeModuleAbi } from "../../../challenge-packages/challenge-utils.js";
7
+ export default class Install extends Command {
8
+ static description = "Install a challenge package (npm package name, git URL, tarball URL, or local path)";
9
+ static args = {
10
+ package: Args.string({
11
+ description: "Package specifier — anything npm can install (name, name@version, git URL, tarball URL, local path)",
12
+ required: true
13
+ })
14
+ };
15
+ static flags = {
16
+ "pkcOptions.dataPath": Flags.directory({
17
+ description: "Data path to install the challenge into",
18
+ required: false
19
+ })
20
+ };
21
+ static examples = [
22
+ "bitsocial challenge install @bitsocial/mintpass-challenge",
23
+ "bitsocial challenge install @bitsocial/mintpass-challenge@1.0.0",
24
+ "bitsocial challenge install github:user/repo",
25
+ "bitsocial challenge install https://example.com/my-challenge-1.0.0.tar.gz",
26
+ "bitsocial challenge install ./my-local-challenge"
27
+ ];
28
+ async run() {
29
+ const { args, flags } = await this.parse(Install);
30
+ const dataPath = flags["pkcOptions.dataPath"] || defaults.PKC_DATA_PATH;
31
+ this.log("Installing challenge package — this may take a few minutes...");
32
+ // 1. Check npm is available
33
+ await ensureNpmAvailable();
34
+ // 2. Use npm pack to download the package as a tarball
35
+ const tmpDir = path.join(dataPath, ".challenge-install-tmp-" + Date.now());
36
+ await fs.mkdir(tmpDir, { recursive: true });
37
+ try {
38
+ const archivePath = await runNpmPack(args.package, tmpDir);
39
+ // 3. Extract the archive
40
+ const extractDir = path.join(tmpDir, "extracted");
41
+ await decompress(archivePath, extractDir);
42
+ process.stderr.write("[challenge-install] archive extracted\n");
43
+ // 4. Find package.json (npm pack tarballs have a package/ root dir)
44
+ let pkgDir = extractDir;
45
+ try {
46
+ await readChallengePackageJson(pkgDir);
47
+ }
48
+ catch {
49
+ // Check one level deep
50
+ const entries = await fs.readdir(extractDir, { withFileTypes: true });
51
+ const dirs = entries.filter((e) => e.isDirectory());
52
+ let found = false;
53
+ for (const dir of dirs) {
54
+ const candidate = path.join(extractDir, dir.name);
55
+ try {
56
+ await readChallengePackageJson(candidate);
57
+ pkgDir = candidate;
58
+ found = true;
59
+ break;
60
+ }
61
+ catch {
62
+ // continue
63
+ }
64
+ }
65
+ if (!found) {
66
+ this.error("No valid package.json found in the archive. The archive must contain a package.json with a \"name\" field.");
67
+ }
68
+ }
69
+ // 5. Read package info
70
+ const pkg = await readChallengePackageJson(pkgDir);
71
+ // 6. Check not already installed
72
+ const challengesDir = await ensureChallengesDir(dataPath);
73
+ const destDir = challengeNameToDir(challengesDir, pkg.name);
74
+ let alreadyExists = true;
75
+ try {
76
+ await fs.access(destDir);
77
+ }
78
+ catch {
79
+ alreadyExists = false;
80
+ }
81
+ if (alreadyExists) {
82
+ this.error(`Challenge "${pkg.name}" is already installed. Remove it first with: bitsocial challenge remove ${pkg.name}`);
83
+ }
84
+ // 7. Move to challenges dir
85
+ if (pkg.name.startsWith("@")) {
86
+ // Ensure scope dir exists for scoped packages
87
+ const scopeDir = path.dirname(destDir);
88
+ await fs.mkdir(scopeDir, { recursive: true });
89
+ }
90
+ await fs.rename(pkgDir, destDir);
91
+ // 8. Run npm install
92
+ process.stderr.write(`[challenge-install] starting npm install in ${destDir}\n`);
93
+ await runNpmInstall(destDir);
94
+ process.stderr.write("[challenge-install] npm install completed\n");
95
+ // 9. Verify native modules are ABI-compatible
96
+ try {
97
+ await verifyNativeModuleAbi(destDir);
98
+ }
99
+ catch (err) {
100
+ // Roll back the installation on ABI mismatch
101
+ await fs.rm(destDir, { recursive: true, force: true });
102
+ if (pkg.name.startsWith("@")) {
103
+ const scopeDir = path.dirname(destDir);
104
+ try {
105
+ const entries = await fs.readdir(scopeDir);
106
+ if (entries.length === 0)
107
+ await fs.rmdir(scopeDir);
108
+ }
109
+ catch {
110
+ // ignore
111
+ }
112
+ }
113
+ this.error(err instanceof Error ? err.message : String(err));
114
+ }
115
+ // 10. Print success
116
+ const version = pkg.version ? `@${pkg.version}` : "";
117
+ this.log(`Installed challenge '${pkg.name}${version}'`);
118
+ // 11. Best-effort reload via daemon
119
+ try {
120
+ await fetch("http://localhost:9138/api/challenges/reload", { method: "POST" });
121
+ }
122
+ catch {
123
+ // daemon not running, that's fine
124
+ }
125
+ }
126
+ finally {
127
+ // 12. Clean up temp dir
128
+ await fs.rm(tmpDir, { recursive: true, force: true });
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class List extends Command {
3
+ static description: string;
4
+ static flags: {
5
+ quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ "pkcOptions.dataPath": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ };
8
+ static examples: string[];
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,37 @@
1
+ import { Flags, Command } from "@oclif/core";
2
+ import { EOL } from "os";
3
+ import { printTable } from "@oclif/table";
4
+ import defaults from "../../../common-utils/defaults.js";
5
+ import { listInstalledChallenges } from "../../../challenge-packages/challenge-utils.js";
6
+ export default class List extends Command {
7
+ static description = "List installed challenge packages";
8
+ static flags = {
9
+ quiet: Flags.boolean({ char: "q", summary: "Only display challenge names" }),
10
+ "pkcOptions.dataPath": Flags.directory({
11
+ description: "Data path where challenges are installed",
12
+ required: false
13
+ })
14
+ };
15
+ static examples = ["bitsocial challenge list", "bitsocial challenge list -q"];
16
+ async run() {
17
+ const { flags } = await this.parse(List);
18
+ const dataPath = flags["pkcOptions.dataPath"] || defaults.PKC_DATA_PATH;
19
+ const challenges = await listInstalledChallenges(dataPath);
20
+ if (challenges.length === 0) {
21
+ this.log("No challenge packages installed.");
22
+ return;
23
+ }
24
+ if (flags.quiet) {
25
+ this.log(challenges.map((c) => c.name).join(EOL));
26
+ }
27
+ else {
28
+ printTable({
29
+ data: challenges.map((c) => ({
30
+ name: c.name,
31
+ version: c.version,
32
+ description: c.description
33
+ }))
34
+ });
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class Remove extends Command {
3
+ static description: string;
4
+ static args: {
5
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ "pkcOptions.dataPath": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static examples: string[];
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,60 @@
1
+ import { Args, Flags, Command } from "@oclif/core";
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ import defaults from "../../../common-utils/defaults.js";
5
+ import { getChallengesDir, challengeNameToDir } from "../../../challenge-packages/challenge-utils.js";
6
+ export default class Remove extends Command {
7
+ static description = "Remove an installed challenge package";
8
+ static args = {
9
+ name: Args.string({
10
+ description: "The challenge package name (e.g., my-challenge or @scope/my-challenge)",
11
+ required: true
12
+ })
13
+ };
14
+ static flags = {
15
+ "pkcOptions.dataPath": Flags.directory({
16
+ description: "Data path where challenges are installed",
17
+ required: false
18
+ })
19
+ };
20
+ static examples = [
21
+ "bitsocial challenge remove my-challenge",
22
+ "bitsocial challenge remove @scope/my-challenge"
23
+ ];
24
+ async run() {
25
+ const { args, flags } = await this.parse(Remove);
26
+ const dataPath = flags["pkcOptions.dataPath"] || defaults.PKC_DATA_PATH;
27
+ const challengesDir = getChallengesDir(dataPath);
28
+ const challengeDir = challengeNameToDir(challengesDir, args.name);
29
+ // Verify the challenge exists
30
+ try {
31
+ await fs.access(challengeDir);
32
+ }
33
+ catch {
34
+ this.error(`Challenge "${args.name}" is not installed.`);
35
+ }
36
+ // Remove the challenge directory
37
+ await fs.rm(challengeDir, { recursive: true, force: true });
38
+ // Clean up empty @scope/ dir for scoped packages
39
+ if (args.name.startsWith("@")) {
40
+ const scopeDir = path.dirname(challengeDir);
41
+ try {
42
+ const entries = await fs.readdir(scopeDir);
43
+ if (entries.length === 0) {
44
+ await fs.rmdir(scopeDir);
45
+ }
46
+ }
47
+ catch {
48
+ // ignore
49
+ }
50
+ }
51
+ this.log(`Removed challenge '${args.name}'`);
52
+ // Best-effort reload via daemon
53
+ try {
54
+ await fetch("http://localhost:9138/api/challenges/reload", { method: "POST" });
55
+ }
56
+ catch {
57
+ // daemon not running, that's fine
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from "../../base-command.js";
2
+ export default class Create extends BaseCommand {
3
+ static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
8
+ static flags: {
9
+ privateKeyPath: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,54 @@
1
+ import { Flags } from "@oclif/core";
2
+ //@ts-ignore
3
+ import DataObjectParser from "dataobject-parser";
4
+ import fs from "fs";
5
+ import { BaseCommand } from "../../base-command.js";
6
+ import { getPKCLogger } from "../../../util.js";
7
+ import * as remeda from "remeda";
8
+ export default class Create extends BaseCommand {
9
+ static description = "Create a community with specific properties. A newly created community will be started after creation and be able to receive publications. For a list of properties, visit https://github.com/pkcprotocol/pkc-js";
10
+ static examples = [
11
+ {
12
+ description: "Create a community with title 'Hello Plebs' and description 'Welcome'",
13
+ command: "<%= config.bin %> <%= command.id %> --title 'Hello Plebs' --description 'Welcome'"
14
+ }
15
+ ];
16
+ static flags = {
17
+ privateKeyPath: Flags.file({
18
+ exists: true,
19
+ description: "Private key (PEM) of the community signer that will be used to determine address (if address is not a domain). If it's not provided then PKC will generate a private key"
20
+ })
21
+ };
22
+ async run() {
23
+ const { flags } = await this.parse(Create);
24
+ const log = (await getPKCLogger())("bitsocial-cli:commands:community:create");
25
+ log(`flags: `, flags);
26
+ const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
27
+ const createOptions = DataObjectParser.transpose(remeda.omit(flags, ["pkcRpcUrl", "privateKeyPath"]))["_data"];
28
+ if (flags.privateKeyPath)
29
+ try {
30
+ //@ts-expect-error
31
+ createOptions.signer = { privateKey: (await fs.promises.readFile(flags.privateKeyPath)).toString(), type: "ed25519" };
32
+ }
33
+ catch (e) {
34
+ const error = e instanceof Error ? e : new Error(typeof e === "string" ? e : JSON.stringify(e));
35
+ //@ts-expect-error
36
+ error.details = { ...error.details, privateKeyPath: flags.privateKeyPath };
37
+ await pkc.destroy();
38
+ this.error(error);
39
+ }
40
+ try {
41
+ const createdCommunity = await pkc.createCommunity(createOptions);
42
+ await createdCommunity.start();
43
+ this.log(createdCommunity.address);
44
+ }
45
+ catch (e) {
46
+ const error = e instanceof Error ? e : new Error(typeof e === "string" ? e : JSON.stringify(e));
47
+ //@ts-expect-error
48
+ error.details = { ...error.details, createOptions };
49
+ await pkc.destroy();
50
+ this.error(error);
51
+ }
52
+ await pkc.destroy();
53
+ }
54
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from "../../base-command.js";
2
+ export default class Delete extends BaseCommand {
3
+ static description: string;
4
+ static strict: boolean;
5
+ static args: {
6
+ addresses: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static examples: string[];
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,44 @@
1
+ import { getPKCLogger } from "../../../util.js";
2
+ import { BaseCommand } from "../../base-command.js";
3
+ import { Args } from "@oclif/core";
4
+ export default class Delete extends BaseCommand {
5
+ static description = "Delete a community permanently.";
6
+ static strict = false; // To allow for variable length arguments
7
+ static args = {
8
+ addresses: Args.string({
9
+ name: "addresses",
10
+ required: true,
11
+ description: "Addresses of communities to delete. Separated by space"
12
+ })
13
+ };
14
+ static examples = [
15
+ "bitsocial community delete plebbit.bso",
16
+ "bitsocial community delete 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu"
17
+ ];
18
+ async run() {
19
+ const { argv, flags } = await this.parse(Delete);
20
+ const log = (await getPKCLogger())("bitsocial-cli:commands:community:delete");
21
+ log(`addresses: `, argv);
22
+ log(`flags: `, flags);
23
+ const addresses = argv;
24
+ if (!Array.isArray(addresses))
25
+ this.error(`Failed to parse addresses correctly (${addresses})`);
26
+ const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
27
+ for (const address of addresses) {
28
+ try {
29
+ const community = await pkc.createCommunity({ address });
30
+ await community.delete();
31
+ this.log(address);
32
+ }
33
+ catch (e) {
34
+ const error = e instanceof Error ? e : new Error(typeof e === "string" ? e : JSON.stringify(e));
35
+ //@ts-expect-error
36
+ error.details = { ...error.details, address };
37
+ console.error(error);
38
+ await pkc.destroy();
39
+ this.exit(1);
40
+ }
41
+ }
42
+ await pkc.destroy();
43
+ }
44
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from "../../base-command.js";
2
+ export default class Edit extends BaseCommand {
3
+ static description: string;
4
+ static args: {
5
+ address: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static examples: {
8
+ description: string;
9
+ command: string;
10
+ }[];
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,74 @@
1
+ //@ts-expect-error
2
+ import DataObjectParser from "dataobject-parser";
3
+ import { Args } from "@oclif/core";
4
+ import { BaseCommand } from "../../base-command.js";
5
+ import { getPKCLogger, mergeDeep } from "../../../util.js";
6
+ import * as remeda from "remeda";
7
+ export default class Edit extends BaseCommand {
8
+ static description = "Edit a community's properties. For a list of properties, visit https://github.com/pkcprotocol/pkc-js";
9
+ static args = {
10
+ address: Args.string({
11
+ name: "address",
12
+ required: true,
13
+ description: "Address of the community to edit"
14
+ })
15
+ };
16
+ static examples = [
17
+ {
18
+ description: "Change the address of the community to a new domain address",
19
+ command: "bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --address newAddress.bso"
20
+ },
21
+ {
22
+ description: "Add the author address 'esteban.bso' as an admin on the community",
23
+ command: `bitsocial community edit mysub.bso '--roles["esteban.bso"].role' admin`
24
+ },
25
+ {
26
+ description: "Add two challenges to the community. The first challenge will be a question and answer, and the second will be an image captcha",
27
+ command: `bitsocial community edit mysub.bso --settings.challenges[0].name question --settings.challenges[0].options.question "what is the password?" --settings.challenges[0].options.answer thepassword --settings.challenges[1].name captcha-canvas-v3`
28
+ },
29
+ {
30
+ description: "Change the title and description",
31
+ command: `bitsocial community edit mysub.bso --title "This is the new title" --description "This is the new description" `
32
+ },
33
+ {
34
+ description: "Remove a role from a moderator/admin/owner",
35
+ command: "bitsocial community edit plebbit.bso --roles['rinse12.bso'] null"
36
+ },
37
+ {
38
+ description: "Enable settings.fetchThumbnailUrls to fetch the thumbnail of url submitted by authors",
39
+ command: "bitsocial community edit plebbit.bso --settings.fetchThumbnailUrls"
40
+ },
41
+ {
42
+ description: "disable settings.fetchThumbnailUrls",
43
+ command: "bitsocial community edit plebbit.bso --settings.fetchThumbnailUrls=false"
44
+ }
45
+ ];
46
+ async run() {
47
+ const { flags, args } = await this.parse(Edit);
48
+ const log = (await getPKCLogger())("bitsocial-cli:commands:community:edit");
49
+ log(`flags: `, flags);
50
+ const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
51
+ const editOptions = DataObjectParser.transpose(remeda.omit(flags, ["pkcRpcUrl"]))["_data"];
52
+ log("Edit options parsed:", editOptions);
53
+ const localCommunities = pkc.communities;
54
+ if (!localCommunities.includes(args.address))
55
+ this.error("Can't edit a remote community, make sure you're editing a local community");
56
+ try {
57
+ const community = await pkc.createCommunity({ address: args.address });
58
+ const mergedState = remeda.pick(community, remeda.keys.strict(editOptions));
59
+ const finalMergedState = mergeDeep(mergedState, editOptions);
60
+ log("Internal community state after merge:", finalMergedState);
61
+ await community.edit(finalMergedState);
62
+ this.log(community.address);
63
+ }
64
+ catch (e) {
65
+ const error = e instanceof Error ? e : new Error(typeof e === "string" ? e : JSON.stringify(e));
66
+ //@ts-expect-error
67
+ error.details = { ...error.details, editOptions, address: args.address };
68
+ console.error(error);
69
+ await pkc.destroy();
70
+ this.exit(1);
71
+ }
72
+ await pkc.destroy();
73
+ }
74
+ }
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from "../../base-command.js";
2
+ export default class Get extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ address: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,32 @@
1
+ import { Args } from "@oclif/core";
2
+ import { BaseCommand } from "../../base-command.js";
3
+ import * as remeda from "remeda";
4
+ export default class Get extends BaseCommand {
5
+ static description = "Fetch a local or remote community, and print its json in the terminal";
6
+ static examples = [
7
+ "bitsocial community get plebmusic.bso",
8
+ "bitsocial community get 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu"
9
+ ];
10
+ static args = {
11
+ address: Args.string({
12
+ name: "address",
13
+ required: true,
14
+ description: "Address of the community to fetch"
15
+ })
16
+ };
17
+ async run() {
18
+ const { args, flags } = await this.parse(Get);
19
+ const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
20
+ try {
21
+ const community = await pkc.getCommunity({ address: args.address });
22
+ const communityJson = JSON.parse(JSON.stringify(community));
23
+ this.logJson({ posts: communityJson.posts, ...remeda.omit(communityJson, ["posts"]) }); // make sure posts is printed first, because most users won't look at it
24
+ }
25
+ catch (e) {
26
+ console.error(e);
27
+ await pkc.destroy();
28
+ this.exit(1);
29
+ }
30
+ await pkc.destroy();
31
+ }
32
+ }
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from "../../base-command.js";
2
+ export default class List extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,30 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { BaseCommand } from "../../base-command.js";
3
+ import { EOL } from "os";
4
+ import { getPKCLogger } from "../../../util.js";
5
+ import { printTable } from "@oclif/table";
6
+ export default class List extends BaseCommand {
7
+ static description = "List your communities";
8
+ static examples = ["bitsocial community list -q", "bitsocial community list"];
9
+ static flags = {
10
+ quiet: Flags.boolean({ char: "q", summary: "Only display community addresses" })
11
+ };
12
+ async run() {
13
+ const { flags } = await this.parse(List);
14
+ const log = (await getPKCLogger())("bitsocial-cli:commands:community:list");
15
+ log(`flags: `, flags);
16
+ const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
17
+ const communities = pkc.communities;
18
+ if (flags.quiet) {
19
+ this.log(communities.join(EOL));
20
+ }
21
+ else {
22
+ const communitiesWithStarted = await Promise.all(communities.map(async (address) => {
23
+ const community = await pkc.createCommunity({ address });
24
+ return { address: community.address, started: community.started };
25
+ }));
26
+ printTable({ data: communitiesWithStarted, sort: { started: "desc" } });
27
+ }
28
+ await pkc.destroy();
29
+ }
30
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from "../../base-command.js";
2
+ export default class Start extends BaseCommand {
3
+ static description: string;
4
+ static strict: boolean;
5
+ static args: {
6
+ addresses: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static examples: (string | {
9
+ description: string;
10
+ command: string;
11
+ })[];
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,46 @@
1
+ import { getPKCLogger } from "../../../util.js";
2
+ import { BaseCommand } from "../../base-command.js";
3
+ import { Args } from "@oclif/core";
4
+ export default class Start extends BaseCommand {
5
+ static description = "Start a community";
6
+ static strict = false; // To allow for variable length arguments
7
+ static args = {
8
+ addresses: Args.string({
9
+ name: "addresses", // name of arg to show in help and reference with args[name]
10
+ required: true,
11
+ description: "Addresses of communities to start. Separated by space"
12
+ })
13
+ };
14
+ static examples = [
15
+ "bitsocial community start plebbit.bso",
16
+ "bitsocial community start 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu",
17
+ {
18
+ description: "Start all communities in your data path",
19
+ command: "bitsocial community start $(bitsocial community list -q)"
20
+ }
21
+ ];
22
+ async run() {
23
+ const { argv, flags } = await this.parse(Start);
24
+ const addresses = argv;
25
+ const log = (await getPKCLogger())("bitsocial-cli:commands:community:start");
26
+ log(`addresses: `, addresses);
27
+ log(`flags: `, flags);
28
+ const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
29
+ for (const address of addresses) {
30
+ try {
31
+ const community = await pkc.createCommunity({ address });
32
+ await community.start();
33
+ this.log(address);
34
+ }
35
+ catch (e) {
36
+ const error = e instanceof Error ? e : new Error(typeof e === "string" ? e : JSON.stringify(e));
37
+ //@ts-expect-error
38
+ error.details = { ...error.details, address };
39
+ console.error(error);
40
+ await pkc.destroy();
41
+ this.exit(1);
42
+ }
43
+ }
44
+ await pkc.destroy();
45
+ }
46
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from "../../base-command.js";
2
+ export default class Stop extends BaseCommand {
3
+ static description: string;
4
+ static strict: boolean;
5
+ static args: {
6
+ addresses: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static examples: string[];
9
+ run(): Promise<void>;
10
+ }