@profullstack/readm3 0.3.0 → 0.5.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # readm3
2
2
 
3
- A terminal markdown reader and editor. File browser on the left, the rendered
3
+ Markdown editing and sharing for people and teams, with a terminal reader and editor. File browser on the left, the rendered
4
4
  document on the right, and nothing else in the way. Press `e` to edit the file
5
5
  you are looking at, `esc` to see it rendered again.
6
6
 
@@ -113,8 +113,8 @@ and add a paragraph, not a replacement for your editor. `$EDITOR` is still
113
113
 
114
114
  ## What it renders
115
115
 
116
- Parsing is [marked](https://github.com/markedjs/marked) — CommonMark and GFM,
117
- zero dependencies of its own — so readm3 has two dependencies in total.
116
+ Parsing is [marked](https://github.com/markedjs/marked) — CommonMark and GFM.
117
+ The terminal UI uses HQTUI; the cloud tools use the official MCP SDK.
118
118
 
119
119
  Headings, **bold**, *italic*, `inline code`, links, reference links, bare URLs,
120
120
  fenced and indented code blocks with a language label, ordered and unordered
@@ -215,14 +215,106 @@ Windows Terminal.
215
215
  ```bash
216
216
  bun install
217
217
  bun src/cli.ts . # run it against its own repo
218
- bun test test/ # 68 tests, no TTY needed
218
+ bun test ./test # 68 tests, no TTY needed
219
219
  npm test # the same tests on node
220
220
  bun run typecheck
221
221
  bun run build
222
222
  ```
223
223
 
224
+ ## Shared documents, organizations, and teams
225
+
226
+ Read and edit online at https://readm3.com/viewer, and manage your account at
227
+ https://readm3.com/admin. Create private Markdown files, share view or edit links,
228
+ add named collaborators, and grant access to an organization or team. Every link
229
+ opens in read mode. File owners manage permissions and deletion; editors save new
230
+ versions; the super administrator can manage every user's documents.
231
+
232
+ Every online save creates an immutable version with a short ID and SHA-256 checksum.
233
+ Stale saves are rejected so concurrent edits cannot silently overwrite each other.
234
+ Owners can restore prior versions, pin links to a version, and revoke links.
235
+
236
+ ```sh
237
+ readm3 login --token-stdin # token from /admin → API tokens
238
+ readm3 orgs list
239
+ readm3 share README.md --org ORG_ID # view link by default
240
+ readm3 docs update DOC_ID notes.md --base VERSION_ID
241
+ readm3 docs history DOC_ID
242
+ readm3 mcp # stdio MCP, same account permissions
243
+ ```
244
+
245
+ The REST API is at `/api/v1`, with all operations also available through
246
+ `POST /api/v1/actions` and `readm3 cloud OPERATION --args JSON`.
247
+ Full permissions, accounts, CLI, API, and MCP documentation: https://readm3.com/sharing.
248
+
249
+ ## Web reader
250
+
251
+ Open https://readm3.com/viewer for the same two-pane Markdown reader in your browser.
252
+ Open local files or a folder, drop Markdown files into the window, open a raw URL or
253
+ GitHub file link, or start a new note. The explorer filters files, prunes hidden and
254
+ build directories, and sorts README first. All nine themes and Markdown flavors use
255
+ the same renderer as the terminal.
256
+
257
+ Press `/` to filter, `e` to edit, `Escape` to return to reading, and `?` for shortcuts.
258
+ `Ctrl+S` / `⌘S` downloads the current Markdown. Files and edits are saved in IndexedDB
259
+ on this device; originals on disk are never overwritten. Reopening a file adds a copy
260
+ so an existing draft is preserved. Clearing browser site data removes the workspace.
261
+
262
+ Install through your browser’s app menu, or Share → Add to Home Screen on iOS.
263
+ After the first visit, the application and saved workspace work offline. Remote URLs
264
+ need a connection and a host that permits browser access (CORS); files are fetched
265
+ directly with no credentials or server proxy. Local files are uploaded only when you explicitly choose Share → Save online.
266
+
267
+ Imports are limited to 4 MB per file, 20 MB per workspace, and 1,000 files.
268
+
224
269
  ## The website
225
270
 
271
+ ### Accounts
272
+
273
+ Open https://readm3.com/account to create an email-verified account or sign in.
274
+ The email link expires after 15 minutes and works once. A confirmation button
275
+ prevents email scanners from consuming links. Accounts are created only after
276
+ verification; returning users sign in to the same identity. Account settings
277
+ include display name, sign out, and sign out on all devices.
278
+
279
+ The server stores accounts, hashed sessions, verification challenges, and rate
280
+ limits in SQLite. Set `READM3_DB` to a persistent volume path (production uses
281
+ `/data/readm3.sqlite`), `READM3_URL` to the canonical public origin,
282
+ `READM3_MAIL_FROM` to a verified sender, and `RESEND_API_KEY` to an email-sending key.
283
+ Missing or failed mail delivery returns an error; verification links are never
284
+ printed to logs. Production cookies are Secure, HttpOnly, SameSite=Lax, and
285
+ host-scoped. The account page and APIs are excluded from offline caches.
286
+
287
+ On Railway, attach a volume at `/data`, keep a single replica, and set
288
+ `RAILWAY_RUN_UID=0` for the root-owned volume. Back up the volume before changing
289
+ the account schema. The local reader remains usable without an account.
290
+
291
+ ```bash
292
+ bun run test:accounts # account security tests and browser signup flow
293
+ ```
294
+
295
+ The account service is `server/accounts.ts`. It can accept an existing Bun SQLite
296
+ `Database` so workspace APIs share the same `users` and `sessions` tables.
297
+ `account_emails` holds the verified identity associated with each user ID.
298
+ Use `Accounts.authenticate(token)` or `Accounts.requireAccount(request)` when
299
+ authorizing organization, team, role, or API-token operations: these methods
300
+ reject sessions whose user has no verified email. Browser sessions use
301
+ `__Host-readm3_session` over HTTPS and `readm3_session` on localhost.
302
+ Do not enable a separate unverified registration or password-recovery path.
303
+
304
+ | Endpoint | Purpose |
305
+ | --- | --- |
306
+ | `POST /api/auth/email` | Send a sign-in link for an email address |
307
+ | `POST /api/auth/preview` | Show the email associated with an unconsumed link |
308
+ | `POST /api/auth/verify` | Consume the link and set a browser session |
309
+ | `GET /api/auth/session` | Return the verified account, or `null` |
310
+ | `POST /api/auth/profile` | Update the current account's display name |
311
+ | `POST /api/auth/logout` | Revoke the current session |
312
+ | `POST /api/auth/logout-all` | Revoke all sessions belonging to the account |
313
+
314
+ POST requests require JSON and an `Origin` matching `READM3_URL`.
315
+
316
+ ### Building and serving
317
+
226
318
  [readm3.com](https://readm3.com) is built from this repository by `site/build.ts`,
227
319
  which renders every document on the page through `renderMarkdown` and takes every
228
320
  color from a real HQTUI theme. There is no second implementation to keep in sync:
@@ -231,8 +323,111 @@ change the parser and the site changes with it.
231
323
  ```bash
232
324
  bun run site:build # writes site/dist
233
325
  bun run site:start # serves it on $PORT, default 3000
326
+ bun x playwright install chromium # once, for browser checks
327
+ bun run test:site # imports, editing, persistence, offline PWA, and mobile layout
234
328
  ```
235
329
 
330
+ The site build bundles `site/viewer.ts` for browsers, generates content-addressed
331
+ assets, and precaches the viewer shell through a service worker scoped to `/viewer`.
332
+ Updates wait until the reader chooses **Update available**; drafts are saved before
333
+ reloading. The web viewer does not load the marketing site’s third-party scripts.
334
+
335
+ ## Hosting shared documents
336
+
337
+ The web server uses Bun and SQLite. Set `READM3_DB` to a database file on persistent
338
+ storage, and `READM3_URL` to the public HTTPS origin. The Docker image uses
339
+ `/data/readm3.sqlite`; mount a volume at `/data`. On Railway set `RAILWAY_RUN_UID=0`
340
+ for access to the mounted volume. Use one service replica for SQLite and configure
341
+ volume backups in the hosting platform.
342
+
343
+ Set `READM3_ADMIN_BOOTSTRAP_SECRET` to a random secret. Sign in at `/account` as
344
+ the intended super administrator, then open `/admin#claim=YOUR_SECRET`. This is a
345
+ one-time claim stored in the database. Never publish this URL; remove the environment
346
+ secret after claiming. Email sign-in never grants administrator access by itself.
347
+
348
+ Accounts use verified email sign-in and HttpOnly cookies. API tokens and share
349
+ capabilities are stored as SHA-256 hashes. Shared documents are not cached in the
350
+ PWA or saved in the browser's local workspace, so revocation applies to future access.
351
+
352
+ ```sh
353
+ bun run test:server # auth, roles, version history, persistence, CLI and MCP
354
+ bun run test:site # local/offline reader plus browser sharing and admin workflows
355
+ ```
356
+
357
+ ## Workspace and settings sync
358
+
359
+ Sign in with verified email at `/account`, then create a personal API token in `/admin`. The PWA's **Sync** button saves or loads
360
+ your local Markdown workspace and reader preferences on that same account. Shared
361
+ documents continue to use their own document permissions and version history.
362
+ Local files are uploaded only when you choose **Save to account**, `readm3 save`,
363
+ or a sharing action. Sync requires a connection; the local reader still works offline.
364
+
365
+ The CLI, document commands, and MCP use one credential file, `cloud.json`, under
366
+ `$XDG_CONFIG_HOME/readm3` (normally `~/.config/readm3`). It is written with mode
367
+ 0600. Login verifies the account; logout removes the local credential. Revoke a token
368
+ in `/admin` to invalidate remote copies. Tokens can also be created and revoked from the account page.
369
+
370
+ ```sh
371
+ readm3 login --token-stdin # paste a token, then Ctrl+D
372
+ readm3 whoami
373
+ readm3 settings --theme nord --flavor github
374
+ readm3 save ./notes # explicitly import and save Markdown
375
+ readm3 sync status
376
+ readm3 sync revisions
377
+ readm3 load --dry-run # inspect the incoming changes
378
+ readm3 load ./downloaded-notes # load and export into a new directory
379
+ readm3 load --force # replace local conflicts, with backups
380
+ readm3 logout
381
+ ```
382
+
383
+ For self-hosting, set `READM3_URL` to the server origin, or set `READM3_API_URL` to
384
+ its API base such as `http://127.0.0.1:3000/api/v1`. `READM3_TOKEN` supplies a token
385
+ without writing it to disk. `READM3_CONFIG_DIR` selects an isolated client directory.
386
+ HTTPS is required except on localhost. A saved token is never sent to a different
387
+ server merely because an environment variable changed.
388
+
389
+ Sync uses `@profullstack/synconfig`, the same package used by moshcode. Only
390
+ `settings.json` (theme and flavor) and `workspace.json` (the explicitly imported
391
+ workspace) are allowed. Credentials, local revision markers, and backups are excluded.
392
+ Running `save` without a path saves those local files; it does not scan the current
393
+ directory. Running `load` without an output directory updates those files, and loaded
394
+ preferences apply to subsequent terminal reader invocations.
395
+
396
+ Each account keeps ten revisions. Saves carry the last loaded revision and reject
397
+ concurrent changes; a new device starts at revision 0. Loads refuse to replace
398
+ conflicting local edits, including on a device that has never synced. `--force`
399
+ explicitly overrides a conflict. Replaced local files receive numbered `.bak-NNN`
400
+ backups. Browser replacements receive backups in IndexedDB, accessible through
401
+ **Sync → Local backups** for download or restoration. Clearing browser site data
402
+ also removes those local backups. Sync supports the browser's workspace limits:
403
+ 1,000 Markdown files, 4 MB per file, and 20 MB of source in total.
404
+
405
+ The authenticated API exposes:
406
+
407
+ | Endpoint | Purpose |
408
+ | --- | --- |
409
+ | `GET /api/v1/me` | Current account; `user: null` when signed out |
410
+ | `GET /api/v1/settings` | Latest snapshot, digest, and revision; 404 when empty |
411
+ | `PUT /api/v1/settings` | Save `{ snapshot, ifRevision }`; 409 on a conflict |
412
+ | `GET /api/v1/settings/revisions` | The last ten revision summaries |
413
+
414
+ `/api/v1/synconfig` is an alias for the settings endpoints. A snapshot is
415
+ `{ version: 1, host, app, files: { "settings.json": { content: "{...}" } } }`.
416
+ `workspace.json` uses `{ name, active, documents: [{ path, source }] }` inside its
417
+ JSON content string. Send `ifRevision: 0` for a first save, the revision last loaded
418
+ for subsequent saves, or `null` for an explicitly forced save. API callers use
419
+ `Authorization: Bearer TOKEN`; browser callers use the same account's HTTP-only
420
+ session cookie and same-origin requests. Account responses are never cached offline.
421
+
422
+ Start MCP with `readm3 mcp`. Alongside the document and organization tools, it exposes
423
+ `settings_get`, `settings_save`, and `settings_revisions`. `settings_save` requires
424
+ a nonnegative `ifRevision` and uses the same validation and conflict checks as the API.
425
+
426
+ Run `bun run test:server` for account/sync integration checks and `bun run test:site`
427
+ for browser checks. The server needs Bun and a persistent `READM3_DB` SQLite path;
428
+ in the container this defaults to `/data/readm3.sqlite`, so mount persistent storage
429
+ at `/data`. Set `READM3_URL` to the public origin for browser origin checks.
430
+
236
431
  ## License
237
432
 
238
433
  MIT.
@@ -0,0 +1,27 @@
1
+ export interface AccountUser {
2
+ id: string;
3
+ username: string;
4
+ displayName: string;
5
+ }
6
+ export interface Credentials {
7
+ api: string;
8
+ token: string;
9
+ user: AccountUser;
10
+ expiresAt?: string;
11
+ }
12
+ export declare const configDir: () => string;
13
+ export declare function apiUrl(value?: string): string;
14
+ export declare function writeConfig(name: string, value: unknown, root?: string): void;
15
+ export declare function storedCredentials(root?: string): Credentials | undefined;
16
+ export declare class ApiError extends Error {
17
+ status: number;
18
+ body: Record<string, unknown>;
19
+ constructor(status: number, message: string, body: Record<string, unknown>);
20
+ }
21
+ export declare function accountClient(options: {
22
+ api: string;
23
+ token?: string;
24
+ fetchImpl?: typeof fetch;
25
+ }): <T = Record<string, unknown>>(path: string, method?: string, body?: unknown) => Promise<T>;
26
+ export declare function credentials(root?: string): Promise<Credentials>;
27
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE;AAClF,MAAM,WAAW,WAAW;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AAClG,eAAO,MAAM,SAAS,cAAmH,CAAC;AAC1I,wBAAgB,MAAM,CAAC,KAAK,SAAgJ,GAAG,MAAM,CAIpL;AACD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,SAAc,QAO3E;AACD,wBAAgB,iBAAiB,CAAC,IAAI,SAAc,GAAG,WAAW,GAAG,SAAS,CAK7E;AACD,qBAAa,QAAS,SAAQ,KAAK;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAClB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAC3E;AACD,wBAAgB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAE,IAEhE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,MAAM,EAAE,eAAc,EAAE,OAAO,OAAO,KAAG,OAAO,CAAC,CAAC,CAAC,CAUrH;AACD,wBAAsB,WAAW,CAAC,IAAI,SAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAU1E"}
@@ -0,0 +1,59 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ export const configDir = () => process.env.READM3_CONFIG_DIR || join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "readm3");
5
+ export function apiUrl(value = process.env.READM3_API_URL || (process.env.READM3_URL ? `${process.env.READM3_URL.replace(/\/+$/, "")}/api/v1` : "https://readm3.com/api/v1")) {
6
+ const url = new URL(value);
7
+ if (url.username || url.password || url.search || url.hash || !(url.protocol === "https:" || (url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))))
8
+ throw new Error("Use an HTTPS API URL (HTTP is allowed on localhost).");
9
+ return url.href.replace(/\/+$/, "");
10
+ }
11
+ export function writeConfig(name, value, root = configDir()) {
12
+ mkdirSync(root, { recursive: true, mode: 0o700 });
13
+ const path = join(root, name);
14
+ const temp = `${path}.${process.pid}.${crypto.randomUUID()}.tmp`;
15
+ writeFileSync(temp, JSON.stringify(value, null, 2) + "\n", { mode: 0o600 });
16
+ renameSync(temp, path);
17
+ chmodSync(path, 0o600);
18
+ }
19
+ export function storedCredentials(root = configDir()) {
20
+ const path = join(root, "cloud.json");
21
+ if (!existsSync(path))
22
+ return;
23
+ const value = JSON.parse(readFileSync(path, "utf8"));
24
+ return { ...value, api: apiUrl(value.api || (value.url ? `${value.url}/api/v1` : undefined)) };
25
+ }
26
+ export class ApiError extends Error {
27
+ status;
28
+ body;
29
+ constructor(status, message, body) { super(message); this.status = status; this.body = body; }
30
+ }
31
+ export function accountClient(options) {
32
+ const base = apiUrl(options.api);
33
+ return async function request(path, method = "GET", body) {
34
+ const response = await (options.fetchImpl || fetch)(`${base}${path}`, {
35
+ method, redirect: "error", signal: AbortSignal.timeout(30_000),
36
+ headers: { accept: "application/json", ...(options.token ? { authorization: `Bearer ${options.token}` } : {}), ...(body === undefined ? {} : { "content-type": "application/json" }) },
37
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
38
+ });
39
+ const data = await response.json();
40
+ if (!response.ok)
41
+ throw new ApiError(response.status, String(data.error || `HTTP ${response.status}`), data);
42
+ return data;
43
+ };
44
+ }
45
+ export async function credentials(root = configDir()) {
46
+ const saved = storedCredentials(root);
47
+ const token = process.env.READM3_TOKEN || saved?.token;
48
+ if (!token)
49
+ throw new Error("Sign in with `readm3 login` first, or set READM3_TOKEN and READM3_API_URL.");
50
+ // Never send a saved token to a different server because an environment variable changed.
51
+ const api = apiUrl(process.env.READM3_API_URL || (process.env.READM3_URL ? `${process.env.READM3_URL.replace(/\/+$/, "")}/api/v1` : saved?.api));
52
+ if (!process.env.READM3_TOKEN && saved && api !== saved.api)
53
+ throw new Error("The API changed. Sign in to this server first.");
54
+ const { user } = await accountClient({ api, token })("/me");
55
+ if (!user)
56
+ throw new Error("Session expired or revoked. Run `readm3 login`.");
57
+ return { api, token, user };
58
+ }
59
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type ColorMode } from "./print.ts";
2
2
  import { type ViewerOptions } from "./viewer.ts";
3
- export declare const VERSION = "0.3.0";
3
+ export declare const VERSION = "0.5.0";
4
4
  /** The help text, exported so a test can hold it to the flags it documents. */
5
5
  export declare const USAGE: string;
6
6
  export interface ParsedArgs extends ViewerOptions {
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAO,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,KAAK,QA0CjB,CAAC;AAEF,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,uDAAuD;IACvD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA6GpD;AAED,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAwChF"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAO,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGtD,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,KAAK,QA2CH,CAAC;AAEhB,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,uDAAuD;IACvD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA6GpD;AAED,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAoDhF"}
package/dist/cli.js CHANGED
@@ -8,7 +8,8 @@ import { themeList } from "@profullstack/hqtui";
8
8
  import { FLAVORS, isFlavor } from "./flavors.js";
9
9
  import { printDocument, COLOR_MODES } from "./print.js";
10
10
  import { run } from "./viewer.js";
11
- export const VERSION = "0.3.0";
11
+ import { isCloudCommand, cloudMain, CLOUD_USAGE } from "./cloud-cli.js";
12
+ export const VERSION = "0.5.0";
12
13
  /** The help text, exported so a test can hold it to the flags it documents. */
13
14
  export const USAGE = `readm3 — a terminal markdown reader and editor
14
15
 
@@ -52,7 +53,8 @@ Editing
52
53
  ctrl+a ctrl+e line start / end ctrl+u kill to start of line
53
54
  ctrl+arrow move by word enter split, continuing a list
54
55
  ctrl+g keys for editing tab indent two spaces
55
- `;
56
+
57
+ ${CLOUD_USAGE}`;
56
58
  class UsageError extends Error {
57
59
  }
58
60
  export function parseArgs(argv) {
@@ -169,6 +171,13 @@ export function parseArgs(argv) {
169
171
  return parsed;
170
172
  }
171
173
  export async function main(argv = process.argv.slice(2)) {
174
+ const { accountCommand } = await import("./sync-cli.js");
175
+ if (await accountCommand(argv))
176
+ return;
177
+ if (isCloudCommand(argv[0])) {
178
+ await cloudMain(argv);
179
+ return;
180
+ }
172
181
  let args;
173
182
  try {
174
183
  args = parseArgs(argv);
@@ -182,7 +191,8 @@ export async function main(argv = process.argv.slice(2)) {
182
191
  throw error;
183
192
  }
184
193
  if (args.help) {
185
- process.stdout.write(USAGE);
194
+ const { ACCOUNT_USAGE } = await import("./sync-cli.js");
195
+ process.stdout.write(USAGE + "\n" + ACCOUNT_USAGE);
186
196
  return;
187
197
  }
188
198
  if (args.version) {
@@ -190,6 +200,10 @@ export async function main(argv = process.argv.slice(2)) {
190
200
  return;
191
201
  }
192
202
  if (args.print) {
203
+ const { localSettings } = await import("./settings-sync.js");
204
+ const settings = localSettings();
205
+ args.theme ??= settings.theme;
206
+ args.flavor ??= settings.flavor;
193
207
  printDocument({
194
208
  path: args.open,
195
209
  width: args.sidebar,
@@ -205,6 +219,10 @@ export async function main(argv = process.argv.slice(2)) {
205
219
  process.exitCode = 1;
206
220
  return;
207
221
  }
222
+ const { localSettings } = await import("./settings-sync.js");
223
+ const settings = localSettings();
224
+ args.theme ??= settings.theme;
225
+ args.flavor ??= settings.flavor;
208
226
  await run(args);
209
227
  }
210
228
  // Running the source directly (`bun src/cli.ts`, `node src/cli.ts`) should start
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare const CLOUD_USAGE = "Shared Markdown\n readm3 login --token-stdin Save a personal API token securely\n readm3 logout Remove the local token\n readm3 share FILE --org ID [--role view|edit]\n readm3 docs list [--org ID] [--search TEXT] [--limit N] [--offset N]\n readm3 docs get ID [--raw]\n readm3 docs create FILE --org ID [--title NAME]\n readm3 docs update ID FILE --base VERSION\n readm3 docs delete ID\n readm3 docs history ID\n readm3 docs restore ID --version VERSION --base CURRENT\n readm3 docs share ID [--role view|edit] [--version VERSION]\n readm3 docs revoke ID --share SHARE_ID\n readm3 docs transfer ID --username USERNAME\n readm3 shared get URL [--raw]\n readm3 shared update URL FILE --base VERSION\n readm3 orgs list | create --name NAME | update ID --name NAME | delete ID\n readm3 teams list --org ID | create --org ID --name NAME\n readm3 members list --org ID\n readm3 cloud OPERATION --args '{\"key\":\"value\"}'\n readm3 mcp Start the stdio MCP server\n\nFILE may be - for stdin. --raw prints Markdown; other results are JSON.\nREADM3_URL selects a self-hosted server; READM3_TOKEN supplies an API token.\nView links cannot write. Edit links can save versions. Owners administer files.\nUse readm3 cloud for the full API, including team membership and permissions.\n";
2
+ export declare function isCloudCommand(command: string | undefined): boolean;
3
+ export declare function cloudMain(argv: string[]): Promise<void>;
4
+ //# sourceMappingURL=cloud-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-cli.d.ts","sourceRoot":"","sources":["../src/cloud-cli.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,WAAW,m1CA0BvB,CAAC;AAaF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,WAEzD;AACD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,iBAiJ7C"}
@@ -0,0 +1,192 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { basename } from "node:path";
3
+ import { cloudAction, cloudConfig, cloudRequest, clearCloudConfig, saveCloudConfig, sharedLocation, } from "./cloud-client.js";
4
+ export const CLOUD_USAGE = `Shared Markdown
5
+ readm3 login --token-stdin Save a personal API token securely
6
+ readm3 logout Remove the local token
7
+ readm3 share FILE --org ID [--role view|edit]
8
+ readm3 docs list [--org ID] [--search TEXT] [--limit N] [--offset N]
9
+ readm3 docs get ID [--raw]
10
+ readm3 docs create FILE --org ID [--title NAME]
11
+ readm3 docs update ID FILE --base VERSION
12
+ readm3 docs delete ID
13
+ readm3 docs history ID
14
+ readm3 docs restore ID --version VERSION --base CURRENT
15
+ readm3 docs share ID [--role view|edit] [--version VERSION]
16
+ readm3 docs revoke ID --share SHARE_ID
17
+ readm3 docs transfer ID --username USERNAME
18
+ readm3 shared get URL [--raw]
19
+ readm3 shared update URL FILE --base VERSION
20
+ readm3 orgs list | create --name NAME | update ID --name NAME | delete ID
21
+ readm3 teams list --org ID | create --org ID --name NAME
22
+ readm3 members list --org ID
23
+ readm3 cloud OPERATION --args '{"key":"value"}'
24
+ readm3 mcp Start the stdio MCP server
25
+
26
+ FILE may be - for stdin. --raw prints Markdown; other results are JSON.
27
+ READM3_URL selects a self-hosted server; READM3_TOKEN supplies an API token.
28
+ View links cannot write. Edit links can save versions. Owners administer files.
29
+ Use readm3 cloud for the full API, including team membership and permissions.
30
+ `;
31
+ const commands = new Set([
32
+ "login",
33
+ "logout",
34
+ "share",
35
+ "docs",
36
+ "shared",
37
+ "orgs",
38
+ "teams",
39
+ "members",
40
+ "cloud",
41
+ "mcp",
42
+ ]);
43
+ export function isCloudCommand(command) {
44
+ return !!command && commands.has(command);
45
+ }
46
+ export async function cloudMain(argv) {
47
+ if (argv.includes("--help") || argv.includes("-h")) {
48
+ process.stdout.write(CLOUD_USAGE);
49
+ return;
50
+ }
51
+ const command = argv[0];
52
+ if (command === "mcp") {
53
+ const { runMcp } = await import("./mcp.js");
54
+ await runMcp();
55
+ return;
56
+ }
57
+ const flags = {};
58
+ const positional = [];
59
+ for (let i = 1; i < argv.length; i++) {
60
+ const value = argv[i];
61
+ if (value.startsWith("--")) {
62
+ const name = value.slice(2);
63
+ if (["raw", "token-stdin", "json"].includes(name))
64
+ flags[name] = "true";
65
+ else {
66
+ if (!argv[i + 1] || argv[i + 1].startsWith("--"))
67
+ throw new Error(`${value} needs a value.`);
68
+ flags[name] = argv[++i];
69
+ }
70
+ }
71
+ else
72
+ positional.push(value);
73
+ }
74
+ const output = (value) => process.stdout.write(JSON.stringify(value, null, 2) + "\n");
75
+ const fileSource = (file) => {
76
+ if (!file)
77
+ throw new Error("A Markdown file is required.");
78
+ const value = readFileSync(file === "-" ? 0 : file, "utf8");
79
+ if (Buffer.byteLength(value) > 1024 * 1024)
80
+ throw new Error("Shared documents are limited to 1 MB.");
81
+ return value;
82
+ };
83
+ if (command === "login") {
84
+ if (!flags["token-stdin"])
85
+ throw new Error("Use --token-stdin and paste the API token, then end input (Ctrl+D). Tokens are created at /admin.");
86
+ const token = readFileSync(0, "utf8").trim();
87
+ if (!token)
88
+ throw new Error("No token supplied.");
89
+ const config = cloudConfig(token);
90
+ const user = await cloudAction("account_me", {}, config);
91
+ saveCloudConfig(config);
92
+ output({ signedIn: true, user });
93
+ return;
94
+ }
95
+ if (command === "logout") {
96
+ clearCloudConfig();
97
+ output({
98
+ signedOut: true,
99
+ note: "Revoke the token in /admin to invalidate other copies.",
100
+ });
101
+ return;
102
+ }
103
+ if (command === "cloud") {
104
+ if (!positional[0])
105
+ throw new Error(CLOUD_USAGE);
106
+ const args = flags.args ? JSON.parse(flags.args) : {};
107
+ output(await cloudAction(positional[0], args));
108
+ return;
109
+ }
110
+ if (command === "shared") {
111
+ const [verb, url, file] = positional;
112
+ if (!["get", "update"].includes(verb))
113
+ throw new Error("Use shared get or shared update.");
114
+ if (!url)
115
+ throw new Error("A shared document URL is required.");
116
+ const { token, config } = sharedLocation(url);
117
+ const result = await cloudRequest(`shared/${token}`, verb === "update" ? "PATCH" : "GET", verb === "update"
118
+ ? { source: fileSource(file), baseVersion: flags.base }
119
+ : undefined, config);
120
+ if (!["get", "update"].includes(verb))
121
+ throw new Error("Use shared get or shared update.");
122
+ if (flags.raw)
123
+ process.stdout.write(result.version.source);
124
+ else
125
+ output(result);
126
+ return;
127
+ }
128
+ const args = {};
129
+ const names = {
130
+ org: "orgId",
131
+ team: "teamId",
132
+ base: "baseVersion",
133
+ version: "versionId",
134
+ share: "shareId",
135
+ user: "userId",
136
+ };
137
+ for (const [name, value] of Object.entries(flags))
138
+ if (!["raw", "json"].includes(name))
139
+ args[names[name] || name] = value;
140
+ if (command === "share") {
141
+ const file = positional[0];
142
+ const doc = await cloudAction("documents_create", {
143
+ ...args,
144
+ title: flags.title || basename(file || "Document.md"),
145
+ source: fileSource(file),
146
+ });
147
+ output(await cloudAction("shares_create", {
148
+ documentId: doc.id,
149
+ role: flags.role || "view",
150
+ }));
151
+ return;
152
+ }
153
+ const [verb, identifier, file] = positional;
154
+ if (command === "docs") {
155
+ const operations = {
156
+ list: "documents_list",
157
+ get: "documents_get",
158
+ create: "documents_create",
159
+ update: "documents_update",
160
+ delete: "documents_delete",
161
+ history: "versions_list",
162
+ restore: "versions_restore",
163
+ share: "shares_create",
164
+ revoke: "shares_revoke",
165
+ transfer: "documents_transfer",
166
+ };
167
+ const op = operations[verb];
168
+ if (!op)
169
+ throw new Error(CLOUD_USAGE);
170
+ if (verb === "create") {
171
+ args.source = fileSource(identifier);
172
+ args.title = flags.title || basename(identifier || "Document.md");
173
+ }
174
+ else if (verb !== "list")
175
+ args.documentId = identifier;
176
+ if (verb === "update")
177
+ args.source = fileSource(file);
178
+ const result = await cloudAction(op, args);
179
+ if (flags.raw && result.version)
180
+ process.stdout.write(result.version.source);
181
+ else
182
+ output(result);
183
+ return;
184
+ }
185
+ const prefix = command === "orgs" ? "organizations" : command;
186
+ if (!["list", "create", "update", "delete", "remove"].includes(verb))
187
+ throw new Error(CLOUD_USAGE);
188
+ if (identifier)
189
+ args[command === "orgs" ? "orgId" : command === "teams" ? "teamId" : "userId"] = identifier;
190
+ output(await cloudAction(`${prefix}_${verb}`, args));
191
+ }
192
+ //# sourceMappingURL=cloud-cli.js.map