runAnnounce

tcmts.cli.run.runAnnounce · #foshdpxa · term · runs on the server

uses node:path

Runs tcm announce: lists this codebase as the signed-in account (its URL, name and description kept for next time), says whether people can read it there, or with --withdraw stops listing it.

import { atprotoSession } from "#codebase/tcmts/atproto";
import { ANNOUNCE_SCOPE, announceCodebase, CODEBASE_SETTING, withdrawCodebase } from "#codebase/tcmts/cli/announce";
import { routeAnnounce } from "#codebase/tcmts/cli/commands";
import { execute } from "#codebase/tcmts/cli/main";
import type { Context } from "#codebase/tcmts/cli/run";
import { bold, dim, green } from "#codebase/tcmts/cli/style";
import { HttpRemote } from "#codebase/tcmts/cli/sync";
import type { RemoteConfig } from "#codebase/tcmts/cli/sync";
import { UsageError } from "#codebase/tcmts/cli/ui";
import { codebaseLexicon, codebaseRecord, isCodebaseName } from "#codebase/tcmts/records";
import { parse } from "@bomb.sh/router";
import path_1 from "node:path";

export async function runAnnounce({ cb, cmd, rest }: Context): Promise<number> {
  const m = execute(cmd, parse(routeAnnounce(), { argv: rest })).model;
  const keptSetting = cb.store.getSetting(CODEBASE_SETTING);
  const kept = keptSetting
    ? (JSON.parse(keptSetting) as { name: string; url: string; description: string })
    : undefined;
  const name =
    m.name ??
    kept?.name ??
    path_1
      .basename(cb.lay.root)
      .replace(/[^A-Za-z0-9._-]+/g, "-")
      .replace(/^[._-]+/, "")
      .slice(0, 64);
  if (!isCodebaseName(name))
    throw new UsageError(
      `--name ${name}: a codebase's name is letters, digits, '.', '_' and '-' (64 at most), from a letter or digit`,
    );
  const sign = (why: string) => atprotoSession({ who: m.as, scope: ANNOUNCE_SCOPE, why });
  if (m.withdraw) {
    if (m.url !== undefined || m.description !== undefined)
      throw new UsageError("--withdraw stops listing it: give no URL or --description with it");
    const session = await sign(
      `withdrawing ${name} deletes a record from your AT Protocol repository`,
    );
    const who = session.handle ? `@${session.handle}` : session.did;
    if (!(await withdrawCodebase(session, name))) {
      console.log(`${bold(name)} isn't listed as ${who}`);
      return 1;
    }
    console.log(`${green("withdrawn")} ${bold(name)}: registries stop listing it as ${who}'s`);
    return 0;
  }
  const origin = cb.store.getSetting("remote.origin");
  const originUrl = origin && (JSON.parse(origin) as RemoteConfig).url;
  const url =
    m.url ?? kept?.url ?? (originUrl && /^https?:\/\//.test(originUrl) ? originUrl : undefined);
  if (!url)
    throw new UsageError(
      "listing this codebase needs the URL it's served at: tcm announce <url> (a `tcm serve --public` server; kept for next time)",
    );
  const most = codebaseLexicon.defs.main.record.properties;
  if (!/^https?:\/\/[^/]/i.test(url) || url.length > most.url.maxLength)
    throw new UsageError(
      `${url}: a codebase is listed at a server's URL (http or https), as \`tcm serve\` serves it`,
    );
  const description = (m.description ?? kept?.description ?? "").trim();
  const graphemes = [...new Intl.Segmenter().segment(description)].length;
  if (
    graphemes > most.description.maxGraphemes ||
    Buffer.byteLength(description) > most.description.maxLength
  )
    throw new UsageError(
      `--description is ${graphemes} characters: a listing says what it is in ${most.description.maxGraphemes} at most`,
    );
  const session = await sign(`listing ${name} writes a record to your AT Protocol repository`);
  const { uri, was } = await announceCodebase(session, codebaseRecord({ name, url, description }));
  cb.store.setSetting(CODEBASE_SETTING, JSON.stringify({ name, url, description }));
  const who = session.handle ? `@${session.handle}` : session.did;
  console.log(
    was === "same"
      ? `${bold(name)} is already listed as ${who}, as it is: ${uri}`
      : `${green(was === "new" ? "listed" : "listed again")} ${bold(name)} as ${who}${was === "updated" ? " (as it is now)" : ""}: ${uri}`,
  );
  console.log(dim(`  served at ${url}`));
  // (whether people can read it there: asked as anyone would, with no token)
  const why = await new HttpRemote(url).whoami().then(
    () => undefined,
    (e: unknown) => (e as Error).message.split("\n")[0],
  );
  if (why)
    console.log(
      dim(
        `  ${url} didn't answer without a token (${why}): people can't browse it until it does (\`tcm serve --public\`)`,
      ),
    );
  return 0;
}

uses

used by