diff --git a/README.md b/README.md index 800e290..ed464be 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,9 @@ on the machine it fetches a private official Node first: curl -fsSL https://readm3.com/install.sh | sh ``` -Run it again to update; remove with `sh ~/.local/share/readm3/uninstall.sh`. +Update with `readm3 update` and remove with `readm3 uninstall`; both work the way readm3 +was installed (this installer, npm, bun, pnpm or yarn), through +[@profullstack/cli](https://github.com/profullstack/cli). Add `--desktop` to install the desktop editor as well (Linux x64; macOS and Windows download it, see [Desktop app](#desktop-app)): diff --git a/bun.lock b/bun.lock index 2951266..a3d22c7 100644 --- a/bun.lock +++ b/bun.lock @@ -7,6 +7,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", "@profullstack/auth-system": "^0.8.3", + "@profullstack/cli": "^0.1.0", "@profullstack/footer": "latest", "@profullstack/hqtui": "^0.5.0", "@profullstack/synconfig": "^0.1.3", @@ -144,6 +145,8 @@ "@profullstack/auth-system": ["@profullstack/auth-system@0.8.3", "", { "dependencies": { "bcryptjs": "^2.4.3", "jsonwebtoken": "^9.0.2", "uuid": "^11.1.0" }, "peerDependencies": { "@supabase/supabase-js": "^2.49.4", "mongodb": "^6.5.0", "mysql2": "^3.9.2", "pg": "^8.11.3", "pocketbase": "^0.21.1" }, "optionalPeers": ["@supabase/supabase-js", "mongodb", "mysql2", "pg", "pocketbase"] }, "sha512-goafDEaq8XDDCChfAfi8wvUxn1xMeGWXpr7XEH9czFU/eIq9PkehGRTy3d/YP2BGzO1iIxrTAb2m2eOMTMdYUA=="], + "@profullstack/cli": ["@profullstack/cli@0.1.0", "", {}, "sha512-kX2mMvxsfsz89u3RIj2cGRIb89TUVUgwNO1rlTowPuHQrsStsOCNtz8KQ/VqPgJ2FjpfAj0CCQwCRgXaehPL5w=="], + "@profullstack/footer": ["@profullstack/footer@0.2.4", "", { "peerDependencies": { "react": ">=18" }, "optionalPeers": ["react"], "bin": { "profullstack-footer": "bin/profullstack-footer.js" } }, "sha512-2ErIzHJCDA2baP8aK6f7rCHohW4EX+wnp7D1mrVMQTUxRcyZwKzmho96hhEFJndXRBGgxT3H/78BHOeBfLxTzg=="], "@profullstack/hqtui": ["@profullstack/hqtui@0.5.0", "", { "bin": { "hqtui": "bin/hqtui.mjs" } }, "sha512-jDwILBmdQx8pQA0ao/tzzty8u5nFNS4XhvU9xXhZvdIaMeiF3HBJIVtP6cS6kigS4lXmHR7sztbb9QJpaWwhig=="], diff --git a/desktop/package.json b/desktop/package.json index c5b054f..83f1150 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,7 +1,7 @@ { "name": "readm3-desktop", "productName": "readm3", - "version": "0.14.0", + "version": "0.14.1", "private": true, "description": "readm3 as a desktop app: a plain, fast text editor for Markdown and code.", "author": "Profullstack, Inc. ", diff --git a/package.json b/package.json index 7f71d5d..206f882 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@profullstack/readm3", - "version": "0.14.0", + "version": "0.14.1", "description": "Markdown editing and sharing for people and teams. Web/PWA, CLI, API and MCP with version history, view/edit links, and a terminal reader.", "type": "module", "license": "MIT", @@ -44,6 +44,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", "@profullstack/auth-system": "^0.8.3", + "@profullstack/cli": "^0.1.0", "@profullstack/footer": "latest", "@profullstack/hqtui": "^0.5.0", "@profullstack/synconfig": "^0.1.3", diff --git a/src/cli.ts b/src/cli.ts index 6dd2fdb..8b38eb6 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,8 +1,9 @@ /** * Argument parsing and entry point. `readm3 --help` is the contract. */ -import { statSync } from "node:fs"; -import { dirname, resolve } from "node:path"; +import { existsSync, realpathSync, statSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { detectInstall, lifecycle, lifecycleUsage } from "@profullstack/cli"; import { pathToFileURL } from "node:url"; import { themeList } from "@profullstack/hqtui"; import { FLAVORS, isFlavor, type Flavor } from "./flavors.ts"; @@ -11,7 +12,7 @@ import { run, type ViewerOptions } from "./viewer.ts"; import { isCloudCommand, cloudMain, CLOUD_USAGE } from "./cloud-cli.ts"; import { isDesktopCommand, desktopMain, installedDesktop, prefersDesktop, DESKTOP_USAGE } from "./desktop-cli.ts"; -export const VERSION = "0.14.0"; +export const VERSION = "0.14.1"; /** The help text, exported so a test can hold it to the flags it documents. */ export const USAGE = `readm3 — a plain text editor for Markdown and code @@ -61,6 +62,7 @@ Editing ctrl+g keys for editing tab indent two spaces ${DESKTOP_USAGE} +${lifecycleUsage("readm3")} ${CLOUD_USAGE}`; export interface ParsedArgs extends ViewerOptions { @@ -186,7 +188,17 @@ export function parseArgs(argv: string[]): ParsedArgs { return parsed; } +/** An installer copy that also installed the desktop app keeps it on update. */ +function keepDesktop(): string[] { + let script: string | undefined; + try { script = process.argv[1] && realpathSync(process.argv[1]); } catch { return []; } + const { share } = detectInstall({ name: "readm3", script }); + return share && (existsSync(join(share, "desktop")) || existsSync(join(share, "readm3.AppImage"))) ? ["--desktop"] : []; +} + export async function main(argv: string[] = process.argv.slice(2)): Promise { + // readm3 update / uninstall (remove): done the way this copy was installed. + if (await lifecycle(argv, { name: "readm3", package: "@profullstack/readm3", version: VERSION, installer: "https://readm3.com/install.sh", installerArgs: keepDesktop })) return; const { accountCommand } = await import("./sync-cli.ts"); if (await accountCommand(argv)) return; if (isCloudCommand(argv[0])) { await cloudMain(argv); return; } diff --git a/test/cli.test.ts b/test/cli.test.ts index 662bce0..e2e8102 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -7,6 +7,11 @@ import { readFile } from "node:fs/promises"; import { parseArgs, USAGE, VERSION } from "../src/cli.ts"; import { FLAVORS } from "../src/flavors.ts"; +test("help names update and uninstall", () => { + assert.match(USAGE, /readm3 update \[--check\]/); + assert.match(USAGE, /readm3 uninstall \[--yes\]/); +}); + test("no arguments browse the working directory", () => { const args = parseArgs([]); assert.equal(args.root, process.cwd());