@fedify/cli 2.1.9 → 2.2.0-dev.898

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/cache.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import process from "node:process";
5
2
  import { mkdir } from "node:fs/promises";
3
+ import process from "node:process";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
6
  //#region src/cache.ts
7
7
  /**
8
8
  * Returns the default cache directory path.
package/dist/config.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import "@js-temporal/polyfill";
2
- import { message } from "@optique/core";
3
2
  import { printError } from "@optique/run";
3
+ import { message } from "@optique/core";
4
+ import { createConfigContext } from "@optique/config";
4
5
  import { readFileSync } from "node:fs";
5
6
  import { parse } from "smol-toml";
6
- import { createConfigContext } from "@optique/config";
7
7
  import { array, boolean, check, forward, integer as integer$1, minValue, number, object as object$1, optional as optional$1, picklist, pipe, string as string$1 } from "valibot";
8
8
  //#region src/config.ts
9
9
  /**
@@ -23,8 +23,10 @@ const lookupSchema = pipe(object$1({
23
23
  traverse: optional$1(boolean()),
24
24
  recurse: optional$1(picklist([
25
25
  "replyTarget",
26
+ "quote",
26
27
  "quoteUrl",
27
28
  "https://www.w3.org/ns/activitystreams#inReplyTo",
29
+ "https://w3id.org/fep/044f#quote",
28
30
  "https://www.w3.org/ns/activitystreams#quoteUrl",
29
31
  "https://misskey-hub.net/ns#_misskey_quote",
30
32
  "http://fedibird.com/ns#quoteUri"
package/dist/deno.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  //#region deno.json
3
- var version = "2.1.9";
3
+ var version = "2.2.0-dev.898+44ea8f2f";
4
4
  //#endregion
5
5
  export { version };
@@ -1,9 +1,9 @@
1
1
  import "@js-temporal/polyfill";
2
- import { printError } from "@optique/run";
3
- import process from "node:process";
4
2
  import { generateVocab } from "@fedify/vocab-tools";
5
3
  import { message } from "@optique/core/message";
4
+ import { printError } from "@optique/run";
6
5
  import { stat } from "node:fs/promises";
6
+ import process from "node:process";
7
7
  //#region src/generate-vocab/action.ts
8
8
  async function runGenerateVocab({ schemaDir, generatedPath }) {
9
9
  if (!(await stat(schemaDir)).isDirectory()) {
@@ -1,6 +1,6 @@
1
1
  import "@js-temporal/polyfill";
2
- import { argument, command, constant, message, object, option, withDefault } from "@optique/core";
3
2
  import { path } from "@optique/run";
3
+ import { argument, command, constant, message, object, option, withDefault } from "@optique/core";
4
4
  //#region src/generate-vocab/command.ts
5
5
  const schemaDir = withDefault(option("-i", "--input", path({
6
6
  metavar: "DIR",
@@ -1,11 +1,11 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { Jimp } from "./nodeinfo.js";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import process from "node:process";
6
3
  import fs from "node:fs/promises";
4
+ import process from "node:process";
7
5
  import { validatePublicUrl } from "@fedify/vocab-runtime";
6
+ import path from "node:path";
8
7
  import { encodeBase64 } from "byte-encodings/base64";
8
+ import os from "node:os";
9
9
  //#region src/imagerenderer.ts
10
10
  const KITTY_IDENTIFIERS = [
11
11
  "kitty",
@@ -0,0 +1,41 @@
1
+ import "@js-temporal/polyfill";
2
+ import { configContext } from "../config.js";
3
+ import { createTunnelOption } from "../options.js";
4
+ import { command, constant, group, merge, message, multiple, object, option, string } from "@optique/core";
5
+ import { bindConfig } from "@optique/config";
6
+ //#region src/inbox/command.ts
7
+ const DEFAULT_EPHEMERAL_INBOX_NAME = "Fedify Ephemeral Inbox";
8
+ const DEFAULT_EPHEMERAL_INBOX_SUMMARY = "An ephemeral ActivityPub inbox for testing purposes.";
9
+ const inboxCommand = command("inbox", merge(object("Inbox options", {
10
+ command: constant("inbox"),
11
+ follow: bindConfig(multiple(option("-f", "--follow", string({ metavar: "URI" }), { description: message`Follow the given actor. The argument can be either an actor URI or a handle. Can be specified multiple times.` })), {
12
+ context: configContext,
13
+ key: (config) => config.inbox?.follow ?? [],
14
+ default: []
15
+ }),
16
+ acceptFollow: bindConfig(multiple(option("-a", "--accept-follow", string({ metavar: "URI" }), { description: message`Accept follow requests from the given actor. The argument can be either an actor URI or a handle, or a wildcard (${"*"}). Can be specified multiple times. If a wildcard is specified, all follow requests will be accepted.` })), {
17
+ context: configContext,
18
+ key: (config) => config.inbox?.acceptFollow ?? [],
19
+ default: []
20
+ }),
21
+ actorName: bindConfig(option("--actor-name", string({ metavar: "NAME" }), { description: message`Customize the actor display name.` }), {
22
+ context: configContext,
23
+ key: (config) => config.inbox?.actorName ?? DEFAULT_EPHEMERAL_INBOX_NAME,
24
+ default: DEFAULT_EPHEMERAL_INBOX_NAME
25
+ }),
26
+ actorSummary: bindConfig(option("--actor-summary", string({ metavar: "SUMMARY" }), { description: message`Customize the actor description.` }), {
27
+ context: configContext,
28
+ key: (config) => config.inbox?.actorSummary ?? DEFAULT_EPHEMERAL_INBOX_SUMMARY,
29
+ default: DEFAULT_EPHEMERAL_INBOX_SUMMARY
30
+ }),
31
+ authorizedFetch: bindConfig(option("-A", "--authorized-fetch", { description: message`Enable authorized fetch mode. Incoming requests without valid HTTP signatures will be rejected with 401 Unauthorized.` }), {
32
+ context: configContext,
33
+ key: (config) => config.inbox?.authorizedFetch ?? false,
34
+ default: false
35
+ })
36
+ }), group("Tunnel options", createTunnelOption("inbox"))), {
37
+ brief: message`Run an ephemeral ActivityPub inbox server.`,
38
+ description: message`Spins up an ephemeral server that serves the ActivityPub inbox with a one-time actor, through a short-lived public DNS with HTTPS. You can monitor the incoming activities in real-time.`
39
+ });
40
+ //#endregion
41
+ export { inboxCommand };
package/dist/inbox.js CHANGED
@@ -1,16 +1,12 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
- import { configContext } from "./config.js";
3
2
  import { version } from "./deno.js";
4
3
  import { getDocumentLoader } from "./docloader.js";
5
4
  import { ActivityEntryPage, ActivityListPage } from "./inbox/view.js";
6
5
  import { configureLogging, recordingSink } from "./log.js";
7
- import { createTunnelOption } from "./options.js";
8
6
  import { tableStyle } from "./table.js";
9
7
  import { spawnTemporaryServer } from "./tempserver.js";
10
8
  import { colors, matchesActor } from "./utils.js";
11
- import { command, constant, group, merge, message, multiple, object, option, string } from "@optique/core";
12
9
  import process from "node:process";
13
- import { bindConfig } from "@optique/config";
14
10
  import { MemoryKvStore, createFederation, generateCryptoKeyPair } from "@fedify/fedify";
15
11
  import { Accept, Activity, Application, Delete, Endpoints, Follow, Image, PUBLIC_COLLECTION, isActor, lookupObject } from "@fedify/vocab";
16
12
  import { getLogger } from "@logtape/logtape";
@@ -26,37 +22,6 @@ const logger = getLogger([
26
22
  "cli",
27
23
  "inbox"
28
24
  ]);
29
- const inboxCommand = command("inbox", merge(object("Inbox options", {
30
- command: constant("inbox"),
31
- follow: bindConfig(multiple(option("-f", "--follow", string({ metavar: "URI" }), { description: message`Follow the given actor. The argument can be either an actor URI or a handle. Can be specified multiple times.` })), {
32
- context: configContext,
33
- key: (config) => config.inbox?.follow ?? [],
34
- default: []
35
- }),
36
- acceptFollow: bindConfig(multiple(option("-a", "--accept-follow", string({ metavar: "URI" }), { description: message`Accept follow requests from the given actor. The argument can be either an actor URI or a handle, or a wildcard (${"*"}). Can be specified multiple times. If a wildcard is specified, all follow requests will be accepted.` })), {
37
- context: configContext,
38
- key: (config) => config.inbox?.acceptFollow ?? [],
39
- default: []
40
- }),
41
- actorName: bindConfig(option("--actor-name", string({ metavar: "NAME" }), { description: message`Customize the actor display name.` }), {
42
- context: configContext,
43
- key: (config) => config.inbox?.actorName ?? "Fedify Ephemeral Inbox",
44
- default: "Fedify Ephemeral Inbox"
45
- }),
46
- actorSummary: bindConfig(option("--actor-summary", string({ metavar: "SUMMARY" }), { description: message`Customize the actor description.` }), {
47
- context: configContext,
48
- key: (config) => config.inbox?.actorSummary ?? "An ephemeral ActivityPub inbox for testing purposes.",
49
- default: "An ephemeral ActivityPub inbox for testing purposes."
50
- }),
51
- authorizedFetch: bindConfig(option("-A", "--authorized-fetch", { description: message`Enable authorized fetch mode. Incoming requests without valid HTTP signatures will be rejected with 401 Unauthorized.` }), {
52
- context: configContext,
53
- key: (config) => config.inbox?.authorizedFetch ?? false,
54
- default: false
55
- })
56
- }), group("Tunnel options", createTunnelOption("inbox"))), {
57
- brief: message`Run an ephemeral ActivityPub inbox server.`,
58
- description: message`Spins up an ephemeral server that serves the ActivityPub inbox with an one-time actor, through a short-lived public DNS with HTTPS. You can monitor the incoming activities in real-time.`
59
- });
60
25
  const activities = [];
61
26
  const acceptFollows = [];
62
27
  const peers = {};
@@ -341,4 +306,4 @@ function createFetchHandler(federation, actorOptions) {
341
306
  };
342
307
  }
343
308
  //#endregion
344
- export { inboxCommand, runInbox };
309
+ export { runInbox };
package/dist/log.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import "@js-temporal/polyfill";
2
- import { dirname } from "node:path";
3
- import process from "node:process";
4
2
  import { mkdir } from "node:fs/promises";
3
+ import process from "node:process";
5
4
  import { configure, getConsoleSink } from "@logtape/logtape";
6
5
  import { getFileSink } from "@logtape/file";
7
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
+ import { dirname } from "node:path";
8
8
  //#region src/log.ts
9
9
  function getRecordingSink() {
10
10
  let records = [];
package/dist/lookup.js CHANGED
@@ -1,21 +1,21 @@
1
1
  import "@js-temporal/polyfill";
2
- import { configContext } from "./config.js";
3
2
  import { getContextLoader, getDocumentLoader as getDocumentLoader$1 } from "./docloader.js";
4
3
  import { configureLogging } from "./log.js";
5
- import { createTunnelServiceOption, userAgentOption } from "./options.js";
6
4
  import { spawnTemporaryServer } from "./tempserver.js";
7
5
  import { colorEnabled, colors, formatObject } from "./utils.js";
6
+ import { configContext } from "./config.js";
7
+ import { createTunnelServiceOption, userAgentOption } from "./options.js";
8
8
  import { renderImages } from "./imagerenderer.js";
9
- import { argument, choice, command, constant, flag, float, integer, map, merge, message, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
10
9
  import { path, printError } from "@optique/run";
11
- import { createWriteStream } from "node:fs";
12
10
  import process from "node:process";
13
- import { bindConfig } from "@optique/config";
11
+ import { argument, choice, command, constant, flag, float, integer, map, merge, message, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
14
12
  import { generateCryptoKeyPair, getAuthenticatedDocumentLoader, respondWithObject } from "@fedify/fedify";
15
13
  import { Application, Collection, CryptographicKey, Object as Object$1, lookupObject, traverseCollection } from "@fedify/vocab";
16
14
  import { getLogger } from "@logtape/logtape";
17
15
  import ora from "ora";
18
16
  import { UrlError } from "@fedify/vocab-runtime";
17
+ import { bindConfig } from "@optique/config";
18
+ import { createWriteStream } from "node:fs";
19
19
  //#region src/lookup.ts
20
20
  const logger = getLogger([
21
21
  "fedify",
@@ -23,13 +23,16 @@ const logger = getLogger([
23
23
  "lookup"
24
24
  ]);
25
25
  const IN_REPLY_TO_IRI = "https://www.w3.org/ns/activitystreams#inReplyTo";
26
+ const QUOTE_IRI = "https://w3id.org/fep/044f#quote";
26
27
  const QUOTE_URL_IRI = "https://www.w3.org/ns/activitystreams#quoteUrl";
27
28
  const MISSKEY_QUOTE_IRI = "https://misskey-hub.net/ns#_misskey_quote";
28
29
  const FEDIBIRD_QUOTE_IRI = "http://fedibird.com/ns#quoteUri";
29
30
  const recurseProperties = [
30
31
  "replyTarget",
32
+ "quote",
31
33
  "quoteUrl",
32
34
  IN_REPLY_TO_IRI,
35
+ QUOTE_IRI,
33
36
  QUOTE_URL_IRI,
34
37
  MISSKEY_QUOTE_IRI,
35
38
  FEDIBIRD_QUOTE_IRI
@@ -59,7 +62,7 @@ const authorizedFetchOption = withDefault(object("Authorized fetch options", {
59
62
  key: (config) => config.lookup?.firstKnock ?? "draft-cavage-http-signatures-12",
60
63
  default: "draft-cavage-http-signatures-12"
61
64
  }),
62
- tunnelService: createTunnelServiceOption()
65
+ tunnelService: optional(createTunnelServiceOption())
63
66
  }), {
64
67
  authorizedFetch: false,
65
68
  firstKnock: void 0,
@@ -317,6 +320,11 @@ function getRecursiveTargetId(object, recurseProperty) {
317
320
  switch (recurseProperty) {
318
321
  case "replyTarget":
319
322
  case IN_REPLY_TO_IRI: return object.replyTargetId;
323
+ case "quote":
324
+ case QUOTE_IRI: {
325
+ const quote = object.quoteId;
326
+ return quote instanceof URL ? quote : null;
327
+ }
320
328
  case "quoteUrl":
321
329
  case QUOTE_URL_IRI:
322
330
  case MISSKEY_QUOTE_IRI:
package/dist/mod.js CHANGED
@@ -1,99 +1,25 @@
1
1
  #!/usr/bin/env -S node --disable-warning=ExperimentalWarning
2
2
  import "@js-temporal/polyfill";
3
- import { configContext, tryLoadToml } from "./config.js";
4
3
  import runGenerateVocab from "./generate-vocab/action.js";
5
- import generateVocabCommand from "./generate-vocab/command.js";
6
4
  import "./generate-vocab/mod.js";
7
- import { version } from "./deno.js";
8
- import { globalOptions } from "./options.js";
9
- import { inboxCommand, runInbox } from "./inbox.js";
10
- import { initCommand, runInit } from "./init/mod.js";
11
- import { nodeInfoCommand, runNodeInfo } from "./nodeinfo.js";
12
- import { lookupCommand, runLookup } from "./lookup.js";
13
- import { relayCommand, runRelay } from "./relay.js";
14
- import { runTunnel, tunnelCommand } from "./tunnel.js";
5
+ import { runInbox } from "./inbox.js";
6
+ import { runInit } from "./init/mod.js";
7
+ import { runNodeInfo } from "./nodeinfo.js";
8
+ import { runLookup } from "./lookup.js";
9
+ import { runRelay } from "./relay.js";
10
+ import { runTunnel } from "./tunnel.js";
15
11
  import runWebFinger from "./webfinger/action.js";
16
- import { webFingerCommand } from "./webfinger/command.js";
17
12
  import "./webfinger/mod.js";
18
- import { runWithConfig } from "@optique/config/run";
19
- import { group, merge, message, or } from "@optique/core";
20
- import { printError } from "@optique/run";
21
- import { merge as merge$1 } from "es-toolkit";
22
- import { readFileSync } from "node:fs";
23
- import { homedir } from "node:os";
24
- import { join } from "node:path";
13
+ import { runCli } from "./runner.js";
25
14
  import process from "node:process";
26
- import { parse } from "smol-toml";
27
15
  //#region src/mod.ts
28
- /**
29
- * Returns the system-wide configuration file paths.
30
- * - Linux/macOS: Searches `$XDG_CONFIG_DIRS` (default: /etc/xdg)
31
- * - Windows: Uses `%ProgramData%` (default: C:\ProgramData)
32
- */
33
- function getSystemConfigPaths() {
34
- if (process.platform === "win32") return [join(process.env.ProgramData || "C:\\ProgramData", "fedify", "config.toml")];
35
- return (process.env.XDG_CONFIG_DIRS || "/etc/xdg").split(":").map((dir) => join(dir, "fedify", "config.toml"));
36
- }
37
- /**
38
- * Returns the user-level configuration file path.
39
- * - Linux/macOS: `$XDG_CONFIG_HOME/fedify/config.toml` (default: ~/.config)
40
- * - Windows: `%APPDATA%\fedify\config.toml`
41
- */
42
- function getUserConfigPath() {
43
- if (process.platform === "win32") return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), "fedify", "config.toml");
44
- return join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "fedify", "config.toml");
45
- }
46
- const command$1 = merge(or(group("Generating code", or(initCommand, generateVocabCommand)), group("ActivityPub tools", or(webFingerCommand, lookupCommand, inboxCommand, nodeInfoCommand, relayCommand)), group("Network tools", tunnelCommand)), globalOptions);
47
16
  async function main() {
48
- const result = await runWithConfig(command$1, configContext, {
49
- programName: "fedify",
50
- load: (parsed) => {
51
- if (parsed.ignoreConfig) return {};
52
- const system = getSystemConfigPaths().map(tryLoadToml).reduce((acc, config) => merge$1(acc, config), {});
53
- const user = tryLoadToml(getUserConfigPath());
54
- const project = tryLoadToml(join(process.cwd(), ".fedify.toml"));
55
- let custom = {};
56
- if (parsed.configPath) try {
57
- custom = parse(readFileSync(parsed.configPath, "utf-8"));
58
- } catch (error) {
59
- printError(message`Could not load config file at ${parsed.configPath}: ${error instanceof Error ? error.message : String(error)}`);
60
- process.exit(1);
61
- }
62
- return [
63
- system,
64
- user,
65
- project,
66
- custom
67
- ].reduce((acc, config) => merge$1(acc, config), {});
68
- },
69
- args: process.argv.slice(2),
70
- help: {
71
- mode: "both",
72
- onShow: () => process.exit(0),
73
- group: "Meta commands"
74
- },
75
- version: {
76
- mode: "both",
77
- value: version,
78
- group: "Meta commands"
79
- },
80
- completion: {
81
- mode: "command",
82
- name: "both",
83
- helpVisibility: "plural",
84
- group: "Meta commands"
85
- },
86
- onError: () => process.exit(1),
87
- colors: process.stdout.isTTY && (process.env.NO_COLOR == null || process.env.NO_COLOR === ""),
88
- maxWidth: process.stdout.columns,
89
- showDefault: true,
90
- showChoices: true
91
- });
17
+ const result = await runCli(process.argv.slice(2));
92
18
  if (result.command === "init") await runInit(result);
93
19
  else if (result.command === "lookup") await runLookup(result);
94
20
  else if (result.command === "webfinger") await runWebFinger(result);
95
- else if (result.command === "inbox") runInbox(result);
96
- else if (result.command === "nodeinfo") runNodeInfo(result);
21
+ else if (result.command === "inbox") await runInbox(result);
22
+ else if (result.command === "nodeinfo") await runNodeInfo(result);
97
23
  else if (result.command === "tunnel") await runTunnel(result);
98
24
  else if (result.command === "generate-vocab") await runGenerateVocab(result);
99
25
  else if (result.command === "relay") await runRelay(result);
package/dist/nodeinfo.js CHANGED
@@ -1,16 +1,16 @@
1
1
  import "@js-temporal/polyfill";
2
+ import { colors, formatObject } from "./utils.js";
2
3
  import { configContext } from "./config.js";
3
4
  import { userAgentOption } from "./options.js";
4
- import { colors, formatObject } from "./utils.js";
5
- import { argument, command, constant, flag, group, merge, message, object, string, text } from "@optique/core";
6
5
  import { print, printError } from "@optique/run";
7
- import os from "node:os";
8
6
  import process from "node:process";
9
- import { bindConfig } from "@optique/config";
7
+ import { argument, command, constant, flag, group, merge, message, object, string, text } from "@optique/core";
10
8
  import { getNodeInfo } from "@fedify/fedify";
11
9
  import { getLogger } from "@logtape/logtape";
12
10
  import ora from "ora";
13
11
  import { getUserAgent } from "@fedify/vocab-runtime";
12
+ import { bindConfig } from "@optique/config";
13
+ import os from "node:os";
14
14
  import { createJimp } from "@jimp/core";
15
15
  import webp from "@jimp/wasm-webp";
16
16
  import { isICO, parseICO } from "icojs";
package/dist/options.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { configContext } from "./config.js";
3
3
  import { choice, constant, flag, map, merge, message, object, option, or, string, withDefault } from "@optique/core";
4
- import { bindConfig } from "@optique/config";
5
4
  import { getUserAgent } from "@fedify/vocab-runtime";
5
+ import { bindConfig } from "@optique/config";
6
6
  //#region src/options.ts
7
7
  /**
8
8
  * Available tunneling services for exposing local servers to the public internet.
@@ -16,7 +16,8 @@ const TUNNEL_SERVICES = [
16
16
  * Creates a tunnel service option with customizable option names.
17
17
  */
18
18
  function createTunnelServiceOption(optionNames = ["--tunnel-service"]) {
19
- return withDefault(bindConfig(option(...optionNames, choice(TUNNEL_SERVICES, { metavar: "SERVICE" }), { description: message`The tunneling service to use.
19
+ const [firstOptionName, ...restOptionNames] = optionNames;
20
+ return withDefault(bindConfig(option(firstOptionName, ...restOptionNames, choice(TUNNEL_SERVICES, { metavar: "SERVICE" }), { description: message`The tunneling service to use.
20
21
  By default, any of the supported tunneling services will be used
21
22
  (randomly selected for each tunnel).` }), {
22
23
  context: configContext,
@@ -0,0 +1,50 @@
1
+ import "@js-temporal/polyfill";
2
+ import { configContext } from "../config.js";
3
+ import { createTunnelOption } from "../options.js";
4
+ import { command, constant, group, integer, merge, message, multiple, object, option, optionName, optional, string, value } from "@optique/core";
5
+ import { bindConfig } from "@optique/config";
6
+ import { choice as choice$1 } from "@optique/core/valueparser";
7
+ //#region src/relay/command.ts
8
+ const relayCommand = command("relay", merge(object("Relay options", {
9
+ command: constant("relay"),
10
+ protocol: bindConfig(option("-p", "--protocol", choice$1(["mastodon", "litepub"], { metavar: "TYPE" }), { description: message`The relay protocol to use. ${value("mastodon")} for Mastodon-compatible relay, ${value("litepub")} for LitePub-compatible relay.` }), {
11
+ context: configContext,
12
+ key: (config) => config.relay?.protocol ?? "mastodon",
13
+ default: "mastodon"
14
+ }),
15
+ persistent: optional(bindConfig(option("--persistent", string({ metavar: "PATH" }), { description: message`Path to SQLite database file for persistent storage. If not specified, uses in-memory storage which is lost when the server stops.` }), {
16
+ context: configContext,
17
+ key: (config) => config.relay?.persistent
18
+ })),
19
+ port: bindConfig(option("-P", "--port", integer({
20
+ min: 0,
21
+ max: 65535,
22
+ metavar: "PORT"
23
+ }), { description: message`The local port to listen on.` }), {
24
+ context: configContext,
25
+ key: (config) => config.relay?.port ?? 8e3,
26
+ default: 8e3
27
+ }),
28
+ name: bindConfig(option("-n", "--name", string({ metavar: "NAME" }), { description: message`The relay display name.` }), {
29
+ context: configContext,
30
+ key: (config) => config.relay?.name ?? "Fedify Relay",
31
+ default: "Fedify Relay"
32
+ }),
33
+ acceptFollow: bindConfig(multiple(option("-a", "--accept-follow", string({ metavar: "URI" }), { description: message`Accept follow requests from the given actor. The argument can be either an actor URI or a handle, or a wildcard (${"*"}). Can be specified multiple times. If a wildcard is specified, all follow requests will be accepted.` })), {
34
+ context: configContext,
35
+ key: (config) => config.relay?.acceptFollow ?? [],
36
+ default: []
37
+ }),
38
+ rejectFollow: bindConfig(multiple(option("-r", "--reject-follow", string({ metavar: "URI" }), { description: message`Reject follow requests from the given actor. The argument can be either an actor URI or a handle, or a wildcard (${"*"}). Can be specified multiple times. If a wildcard is specified, all follow requests will be rejected.` })), {
39
+ context: configContext,
40
+ key: (config) => config.relay?.rejectFollow ?? [],
41
+ default: []
42
+ })
43
+ }), group("Tunnel options", createTunnelOption("relay"))), {
44
+ brief: message`Run an ephemeral ActivityPub relay server.`,
45
+ description: message`Spins up an ActivityPub relay server that forwards activities between federated instances. The server can use either Mastodon or LitePub compatible relay protocol.
46
+
47
+ By default, the server is tunneled to the public internet for external access. Use ${optionName("--no-tunnel")} to run locally only.`
48
+ });
49
+ //#endregion
50
+ export { relayCommand };
package/dist/relay.js CHANGED
@@ -1,20 +1,15 @@
1
1
  import "@js-temporal/polyfill";
2
- import { configContext } from "./config.js";
3
2
  import { configureLogging } from "./log.js";
4
- import { createTunnelOption } from "./options.js";
5
3
  import { tableStyle } from "./table.js";
6
4
  import { spawnTemporaryServer } from "./tempserver.js";
7
5
  import { colors, matchesActor } from "./utils.js";
8
- import { command, constant, group, integer, merge, message, multiple, object, option, optionName, optional, string, value } from "@optique/core";
9
6
  import process from "node:process";
10
- import { bindConfig } from "@optique/config";
11
7
  import { MemoryKvStore } from "@fedify/fedify";
12
8
  import { getLogger } from "@logtape/logtape";
13
9
  import Table from "cli-table3";
14
10
  import ora from "ora";
15
11
  import { createRelay } from "@fedify/relay";
16
12
  import { SqliteKvStore } from "@fedify/sqlite";
17
- import { choice as choice$1 } from "@optique/core/valueparser";
18
13
  import { DatabaseSync } from "node:sqlite";
19
14
  //#region src/relay.ts
20
15
  const logger = getLogger([
@@ -22,47 +17,6 @@ const logger = getLogger([
22
17
  "cli",
23
18
  "relay"
24
19
  ]);
25
- const relayCommand = command("relay", merge(object("Relay options", {
26
- command: constant("relay"),
27
- protocol: bindConfig(option("-p", "--protocol", choice$1(["mastodon", "litepub"], { metavar: "TYPE" }), { description: message`The relay protocol to use. ${value("mastodon")} for Mastodon-compatible relay, ${value("litepub")} for LitePub-compatible relay.` }), {
28
- context: configContext,
29
- key: (config) => config.relay?.protocol ?? "mastodon",
30
- default: "mastodon"
31
- }),
32
- persistent: optional(bindConfig(option("--persistent", string({ metavar: "PATH" }), { description: message`Path to SQLite database file for persistent storage. If not specified, uses in-memory storage which is lost when the server stops.` }), {
33
- context: configContext,
34
- key: (config) => config.relay?.persistent
35
- })),
36
- port: bindConfig(option("-P", "--port", integer({
37
- min: 0,
38
- max: 65535,
39
- metavar: "PORT"
40
- }), { description: message`The local port to listen on.` }), {
41
- context: configContext,
42
- key: (config) => config.relay?.port ?? 8e3,
43
- default: 8e3
44
- }),
45
- name: bindConfig(option("-n", "--name", string({ metavar: "NAME" }), { description: message`The relay display name.` }), {
46
- context: configContext,
47
- key: (config) => config.relay?.name ?? "Fedify Relay",
48
- default: "Fedify Relay"
49
- }),
50
- acceptFollow: bindConfig(multiple(option("-a", "--accept-follow", string({ metavar: "URI" }), { description: message`Accept follow requests from the given actor. The argument can be either an actor URI or a handle, or a wildcard (${"*"}). Can be specified multiple times. If a wildcard is specified, all follow requests will be accepted.` })), {
51
- context: configContext,
52
- key: (config) => config.relay?.acceptFollow ?? [],
53
- default: []
54
- }),
55
- rejectFollow: bindConfig(multiple(option("-r", "--reject-follow", string({ metavar: "URI" }), { description: message`Reject follow requests from the given actor. The argument can be either an actor URI or a handle, or a wildcard (${"*"}). Can be specified multiple times. If a wildcard is specified, all follow requests will be rejected.` })), {
56
- context: configContext,
57
- key: (config) => config.relay?.rejectFollow ?? [],
58
- default: []
59
- })
60
- }), group("Tunnel options", createTunnelOption("relay"))), {
61
- brief: message`Run an ephemeral ActivityPub relay server.`,
62
- description: message`Spins up an ActivityPub relay server that forwards activities between federated instances. The server can use either Mastodon or LitePub compatible relay protocol.
63
-
64
- By default, the server is tunneled to the public internet for external access. Use ${optionName("--no-tunnel")} to run locally only.`
65
- });
66
20
  async function runRelay(command) {
67
21
  if (command.debug) await configureLogging();
68
22
  const spinner = ora({
@@ -128,4 +82,4 @@ async function printRelayInfo(relay, options) {
128
82
  console.log("\nPress ^C to stop the relay server.");
129
83
  }
130
84
  //#endregion
131
- export { relayCommand, runRelay };
85
+ export { runRelay };
package/dist/runner.js ADDED
@@ -0,0 +1,96 @@
1
+ import "@js-temporal/polyfill";
2
+ import generateVocabCommand from "./generate-vocab/command.js";
3
+ import "./generate-vocab/mod.js";
4
+ import { version } from "./deno.js";
5
+ import { initCommand } from "./init/mod.js";
6
+ import { configContext, tryLoadToml } from "./config.js";
7
+ import { globalOptions } from "./options.js";
8
+ import { nodeInfoCommand } from "./nodeinfo.js";
9
+ import { lookupCommand } from "./lookup.js";
10
+ import { inboxCommand } from "./inbox/command.js";
11
+ import { relayCommand } from "./relay/command.js";
12
+ import { tunnelCommand } from "./tunnel.js";
13
+ import { webFingerCommand } from "./webfinger/command.js";
14
+ import "./webfinger/mod.js";
15
+ import { printError, run } from "@optique/run";
16
+ import process from "node:process";
17
+ import { group, merge, message, or } from "@optique/core";
18
+ import { join } from "node:path";
19
+ import { merge as merge$1 } from "es-toolkit";
20
+ import { readFileSync } from "node:fs";
21
+ import { parse } from "smol-toml";
22
+ import { homedir } from "node:os";
23
+ //#region src/runner.ts
24
+ /**
25
+ * Returns the system-wide configuration file paths.
26
+ * - Linux/macOS: Searches `$XDG_CONFIG_DIRS` (default: /etc/xdg)
27
+ * - Windows: Uses `%ProgramData%` (default: C:\ProgramData)
28
+ */
29
+ function getSystemConfigPaths() {
30
+ if (process.platform === "win32") return [join(process.env.ProgramData || "C:\\ProgramData", "fedify", "config.toml")];
31
+ return (process.env.XDG_CONFIG_DIRS || "/etc/xdg").split(":").map((dir) => join(dir, "fedify", "config.toml"));
32
+ }
33
+ /**
34
+ * Returns the user-level configuration file path.
35
+ * - Linux/macOS: `$XDG_CONFIG_HOME/fedify/config.toml` (default: ~/.config)
36
+ * - Windows: `%APPDATA%\fedify\config.toml`
37
+ */
38
+ function getUserConfigPath() {
39
+ if (process.platform === "win32") return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), "fedify", "config.toml");
40
+ return join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "fedify", "config.toml");
41
+ }
42
+ const command$1 = merge(or(group("Generating code", or(initCommand, generateVocabCommand)), group("ActivityPub tools", or(webFingerCommand, lookupCommand, inboxCommand, nodeInfoCommand, relayCommand)), group("Network tools", tunnelCommand)), globalOptions);
43
+ function loadConfig(parsed) {
44
+ if (parsed.ignoreConfig) return void 0;
45
+ const system = getSystemConfigPaths().map(tryLoadToml).reduce((acc, config) => merge$1(acc, config), {});
46
+ const user = tryLoadToml(getUserConfigPath());
47
+ const project = tryLoadToml(join(process.cwd(), ".fedify.toml"));
48
+ let custom = {};
49
+ if (parsed.configPath) try {
50
+ custom = parse(readFileSync(parsed.configPath, "utf-8"));
51
+ } catch (error) {
52
+ printError(message`Could not load config file at ${parsed.configPath}: ${error instanceof Error ? error.message : String(error)}`);
53
+ process.exit(1);
54
+ }
55
+ return {
56
+ config: [
57
+ system,
58
+ user,
59
+ project,
60
+ custom
61
+ ].reduce((acc, config) => merge$1(acc, config), {}),
62
+ meta: void 0
63
+ };
64
+ }
65
+ /**
66
+ * Runs the Fedify CLI with the given command-line arguments.
67
+ * @param args Command-line arguments, usually `process.argv.slice(2)`.
68
+ * @returns The parsed command result from Optique's runner.
69
+ */
70
+ function runCli(args) {
71
+ return run(command$1, {
72
+ contexts: [configContext],
73
+ contextOptions: { load: loadConfig },
74
+ programName: "fedify",
75
+ args,
76
+ help: {
77
+ command: { group: "Meta commands" },
78
+ option: { group: "Meta commands" }
79
+ },
80
+ version: {
81
+ value: version,
82
+ command: { group: "Meta commands" },
83
+ option: { group: "Meta commands" }
84
+ },
85
+ completion: { command: {
86
+ names: ["completions", "completion"],
87
+ group: "Meta commands"
88
+ } },
89
+ colors: process.stdout.isTTY && (process.env.NO_COLOR == null || process.env.NO_COLOR === ""),
90
+ maxWidth: process.stdout.columns,
91
+ showDefault: true,
92
+ showChoices: true
93
+ });
94
+ }
95
+ //#endregion
96
+ export { runCli };
package/dist/tunnel.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { configureLogging } from "./log.js";
3
3
  import { createTunnelServiceOption } from "./options.js";
4
- import { argument, command, constant, integer, merge, message, object } from "@optique/core";
5
4
  import { print, printError } from "@optique/run";
6
5
  import process from "node:process";
6
+ import { argument, command, constant, integer, merge, message, object } from "@optique/core";
7
7
  import ora from "ora";
8
8
  import { openTunnel } from "@hongminhee/localtunnel";
9
9
  //#region src/tunnel.ts
package/dist/utils.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import "@js-temporal/polyfill";
2
- import { message } from "@optique/core";
3
2
  import { print, printError } from "@optique/run";
4
- import { flow } from "es-toolkit";
5
3
  import process from "node:process";
4
+ import { message } from "@optique/core";
6
5
  import { getActorHandle } from "@fedify/vocab";
7
6
  import util from "node:util";
8
7
  import "@fxts/core";
9
8
  import { Chalk } from "chalk";
10
9
  import { highlight } from "cli-highlight";
10
+ import { flow } from "es-toolkit";
11
11
  //#region src/utils.ts
12
12
  const colorEnabled = process.stdout.isTTY && !("NO_COLOR" in process.env && process.env.NO_COLOR !== "");
13
13
  const colors = new Chalk(colorEnabled ? {} : { level: 0 });
@@ -2,8 +2,8 @@ import "@js-temporal/polyfill";
2
2
  import { formatObject } from "../utils.js";
3
3
  import { NotFoundError, getErrorMessage } from "./error.js";
4
4
  import { convertUrlIfHandle } from "./lib.js";
5
- import { print } from "@optique/run";
6
5
  import { formatMessage, message } from "@optique/core/message";
6
+ import { print } from "@optique/run";
7
7
  import ora from "ora";
8
8
  import { lookupWebFinger } from "@fedify/webfinger";
9
9
  //#region src/webfinger/action.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/cli",
3
- "version": "2.1.9",
3
+ "version": "2.2.0-dev.898+44ea8f2f",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "README.md",
@@ -58,9 +58,9 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@fxts/core": "^1.20.0",
61
- "@optique/config": "^0.10.7",
62
- "@optique/core": "^0.10.7",
63
- "@optique/run": "^0.10.7",
61
+ "@optique/config": "^1.0.0",
62
+ "@optique/core": "^1.0.0",
63
+ "@optique/run": "^1.0.0",
64
64
  "@hongminhee/localtunnel": "^0.3.0",
65
65
  "@inquirer/prompts": "^7.8.4",
66
66
  "@jimp/core": "^1.6.0",
@@ -86,14 +86,14 @@
86
86
  "smol-toml": "^1.6.0",
87
87
  "srvx": "^0.8.7",
88
88
  "valibot": "^1.2.0",
89
- "@fedify/fedify": "2.1.9",
90
- "@fedify/init": "2.1.9",
91
- "@fedify/relay": "2.1.9",
92
- "@fedify/sqlite": "2.1.9",
93
- "@fedify/vocab-runtime": "2.1.9",
94
- "@fedify/vocab-tools": "2.1.9",
95
- "@fedify/vocab": "2.1.9",
96
- "@fedify/webfinger": "2.1.9"
89
+ "@fedify/fedify": "2.2.0-dev.898+44ea8f2f",
90
+ "@fedify/relay": "2.2.0-dev.898+44ea8f2f",
91
+ "@fedify/sqlite": "2.2.0-dev.898+44ea8f2f",
92
+ "@fedify/vocab": "2.2.0-dev.898+44ea8f2f",
93
+ "@fedify/init": "2.2.0-dev.898+44ea8f2f",
94
+ "@fedify/vocab-tools": "2.2.0-dev.898+44ea8f2f",
95
+ "@fedify/webfinger": "2.2.0-dev.898+44ea8f2f",
96
+ "@fedify/vocab-runtime": "2.2.0-dev.898+44ea8f2f"
97
97
  },
98
98
  "devDependencies": {
99
99
  "@types/bun": "^1.2.23",