upload, list, get, share, rename, delete, and configure. Run factrove --help for the short list, and factrove <command> --help for that command’s flags. The generated pages reprint those help texts word for word, then list flags the help text omits.
Install
The published npm package isfactrove. Node.js must be at least 24.7.0 and below 25.
ftrv is an alias for factrove. npm rejected a separate ftrv package, so both names are bins of factrove. npx factrove runs the same program without a global install.
With no profile and no API URL, the CLI calls https://api.factrove.app. A self-hosted API needs --api-url, FACTROVE_API_URL, or a profile api_url. Release the CLI publishes a version. It does not run on push.
From a checkout of this repository, pnpm factrove runs the built CLI. pnpm prints the script line it runs to stderr. The program help uses the name factrove. apps/cli/.env can point that command at http://localhost:3001. The published commands do not read that file.
Help text uses synopsis placeholders such as <artifact-id>. In a shell, < starts a redirection. Executable examples below use quoted variables.
Agent workflow
Plain upload prints only the artifact id on stdout. pnpm writes the script line it runs to stderr, so a substitution captures the id. Plain get prints the content bytes. Plain share prints the public URL and then a status line, so agents should use--json when they need one machine-readable line.
--password prompts twice and needs a terminal. --password-stdin reads a pipe and removes one trailing newline. Set LINK_PASSWORD in the environment, then:
pnpm factrove. After npm install --global factrove, drop the pnpm prefix, or use ftrv in its place. pnpm prints the script line it runs to stderr. The program help uses the name factrove.
Commands
upload <file> sends one HTML file and creates a new artifact. Plain success prints only the new id and a newline. --json prints the { artifact } envelope on one line. --access private|org_shared chooses who can see a new artifact. The default is private. org_shared requires a current organization membership. Signup does not create one. See Organization membership.
--name <name> sets the name shown in the library, the viewer, and a public share. The name is one line of 1 to 120 characters. Omit --name and the stored name is the file name without .html or .htm. A stem that is empty, longer than 120 characters, or not one line stores null, and the upload still succeeds. Those screens then show {artifactId}.html. There is no flag to clear a name.
upload <file> --artifactId <id> replaces that artifact’s content and prints the same id. The API keeps the current access, the current name, and the current share token, so an existing public link serves the new HTML. Pass --name with --artifactId to set a new name in that same replace. Omit --name and the current name stays, including null. --access together with --artifactId is an error. A replace writes a new object and points the artifact at it. Another replace of the same revision receives 409 CONTENT_CONFLICT and does not change the name. A replace that returns 500 because the database acknowledgement was lost can still land the new HTML and, when --name was sent, the new name together. Factrove currently exposes only the current content. Replacement provides no version-history or undo interface and normally removes the previous object. A replace can rarely return 500 after the new object is already committed. GET the artifact before retrying.
get <id> prints the artifact’s content. The default is HTML. --format markdown asks the API to convert the stored HTML on the way out. Markdown is not saved. The conversion does not run JavaScript and drops scripts, styles, SVG, and MathML. Limits and errors are in Markdown conversion. --json prints { format, mediaType, content } on one line.
list prints the caller’s artifacts, one per line: the id, private or org_shared, then the library name. A null name uses {id}.html. An empty list prints nothing. --view org lists organization-shared artifacts in the caller’s current organization, and is an empty list when the caller has no current organization. --json prints the { items } envelope on one line.
share <id> creates a public link, or prints the existing one. --password prompts twice on a terminal. --password-stdin reads a pipe and removes one trailing newline. --remove-password keeps the link and clears its password. --revoke removes the link and its password. A Factrove link is the public base origin plus /share/{id}/{token} and lasts until revoke. It can also carry a password.
rename <id> --name <name> changes the library name and leaves the stored HTML, access, and any public link in place. Quote a name that contains spaces. Plain success prints Renamed {id}. --json prints the { artifact } envelope. There is no way to clear a name. Only the owner can rename. Anyone else, including an organization admin, gets 404 ARTIFACT_NOT_FOUND.
delete <id> removes the artifact, its current HTML object, and any public link. A terminal asks for confirmation and continues only for y or yes. --yes and --force skip that prompt. They are the same flag. Use one of them when stdin is not a terminal, which is the usual case for an agent. Plain success prints Deleted {id}. --json prints {"deleted":true,"id":"…"}. The HTTP response is empty. Only the owner can delete. Anyone else, including an organization admin, gets 404 ARTIFACT_NOT_FOUND. A storage failure keeps the artifact row. The stored HTML may already be gone, so retry the delete. If concurrent replaces prevent deletion after eight attempts, the API returns 409 CONTENT_CONFLICT. Retry the delete. The CLI does not keep a local index such as factrove.md, so delete and rename change only the artifact on the server.
configure saves an API key and API URL for a profile. --api-key prompts when omitted, so the key stays out of shell history. --api-url sets the origin, and --endpoint-url is the same option. --profile selects the profile. The default profile name is default. Create the key in the web app, or with the session recipe. If configure cannot reach the API, see Troubleshooting.
Flags
--api-url <origin> and --endpoint-url <origin> set the API origin for one command. They must be the same value when both are present. configure stores that origin as api_url.
--json and --output json print one JSON line. --output text is the plain output. --json together with --output text is an error in either order.
--profile <name> is accepted on every command. Help lists it on configure. An explicit --profile uses that profile’s key and ignores FACTROVE_API_KEY.
--quiet is accepted on upload, get, share, list, rename, and delete. Help does not list it, and it does not hide their output. configure rejects it.
Success exits 0. Failure exits 1.
Config precedence
The API origin is the first value that is set:--api-urlor--endpoint-urlFACTROVE_API_URLFACTROVE_ENDPOINT_URL, only whenFACTROVE_API_URLis unsetapi_urlin the selected profileendpoint_urlin that profile, whenapi_urlis absent- a URL from the dev env file, only for
pnpm factrove https://api.factrove.app
FACTROVE_API_KEY, then factrove_api_key in the selected profile. An explicit --profile skips FACTROVE_API_KEY and uses that profile’s key. The profile name is --profile, then FACTROVE_PROFILE, then default.
configure writes ~/.factrove/credentials and ~/.factrove/config with mode 0600. Directories Factrove creates for those files are mode 0700. FACTROVE_CONFIG_DIR, FACTROVE_SHARED_CREDENTIALS_FILE, and FACTROVE_CONFIG_FILE override the paths. The command does not print the key.
Dev env file
pnpm factrove reads apps/cli/.env. node apps/cli/dist/main.js and the published factrove command do not. FACTROVE_CLI_ENV_FILE selects a different file, and that name comes from the process environment. The file may set FACTROVE_API_URL and FACTROVE_PROFILE when those variables are unset. It does not set an API key or FACTROVE_ENDPOINT_URL. A URL from the file is used only when the process environment and the selected profile have no URL. The example sets FACTROVE_API_URL=http://localhost:3001 so pnpm factrove talks to a local API. Without that file, the built-in origin is https://api.factrove.app.
Differences from aws s3
Factrove stores an artifact id, a human-readable name, access, a share link, and the current HTML object key with its size. HTTP responses and the CLI omit the object key. There is no etag and no caller-supplied object metadata. New objects use{artifactId}/{contentId}.html. Older rows keep {artifactId}.html. The public filename is {artifactId}.html. The name is a separate field.
aws s3 commands such as cp, ls, rm, mv, sync, mb, and rb are not Factrove commands. Factrove uses list, rename, and delete for those jobs. aws s3api commands such as put-object and get-object take a caller-supplied object key. The CLI addresses an artifact by id and has no s3api surface. It uploads a local HTML file, lists artifacts, and prints or downloads one artifact. It does not copy between two remote locations, and it does not read an upload from stdin.
share is the public-link command. An aws s3 presign URL is a temporary signed GET and has no password. A Factrove share lasts until --revoke and can take a password. The password flags stay on share.
--endpoint-url is the API origin, the same option as --api-url. It is not an S3 endpoint. The API’s S3_ENDPOINT is server configuration and is not a CLI flag.