@phreshos/cli
v0.1.28
Published
The Phresh command-line interface for Program projects and system management.
Readme
@phreshos/cli
The phresh command for creating and operating Programs, and for installing
and managing the PhreshOS System on the current machine.
Package status
This package is one component of a larger architecture that is still under active testing. The architecture's components will be released in stages as their contracts and integrations are verified.
@phreshos/cli is not intended to be used independently of that architecture.
Its Program commands depend on @phreshos/core, and its runtime operations
require a compatible system installation.
phresh create app # create a complete new Program
phresh init # describe this program, once
phresh pack # run the optional build, then package its result
phresh install # lay this program out on this machine
phresh uninstall # remove its installed form
phresh start # run what your build left, and stay with it
phresh dev # run from source, and stay with it
phresh system status # inspect the local System and its background service
phresh program list # inspect authoritative state in the running System
phresh describe endpoint ask # read one capability's exact contractphresh --help lists them, phresh <command> --help explains one, and
phresh --version says which CLI you have. phresh describe [path...] exposes
the same complete command tree as machine-readable contracts. Nothing is guessed: an
unknown command, an unknown flag and a malformed option are each refused
and named.
Program authoring commands act on the current project when no Program name is
provided. install and uninstall accept a direct Program name. Running-System
capabilities live in the top-level program, process, endpoint, and window
namespaces. System execution and native lifecycle remain isolated under
phresh system.
System lifecycle
Run a System project or prepared release directly while selecting an independent absolute state home:
PHRESHOS_HOME=/absolute/state phresh system .When PHRESHOS_HOME is omitted here, the CLI passes ~/.phreshos. Running the
System project itself without the CLI instead defaults to that project's
storage/ directory.
phresh system install
phresh system uninstall
phresh system status
phresh system version
phresh system start
phresh system stop
phresh system enable
phresh system disableinstall resolves the newest compatible stable release from the official
PhreshOS/system GitHub Releases. It
downloads the production archive and adjacent checksum, verifies every byte,
installs production dependencies into a staged version directory, atomically
points the stable current path at it, then registers, enables, and starts the
native per-user service. The selected release and the service entry therefore
cannot become two competing sources of truth if installation is interrupted.
It never reads a source checkout and never requires Bun or TypeScript.
The System runs under launchd on macOS, a real systemd --user manager on
Linux, and a least-privilege per-user scheduled task on Windows. In Linux
containers with no init manager, it runs as a detached user-owned background
process that survives the terminal but ends with the container. Automatic
startup is unavailable there rather than being reported as enabled. start
and stop change current execution only; where a native manager exists,
enable and disable change automatic startup only. Successful installation
and startup show the desktop address. status reports that same address with
the installed version, service readiness, and automatic startup without
changing them; version reports only the installed System release.
Installation files and persistent System state have separate homes. Removing
the System unregisters its service and removes its release files while keeping
~/.phreshos, including Programs and owner data. The owner-local gateway uses an
owner-only socket file on POSIX and an owner-created duplex named pipe on
Windows; neither becomes a network endpoint or introduces a bearer secret.
create
phresh create <directory> creates a complete Server and Client Program from
the maintained PhreshOS/phresh-program repository. The CLI build downloads
the newest complete stable release, validates its source identity and version,
removes repository-only material, and bundles that exact authoring project.
The resolved source digest is recorded with the bundle. The installed CLI
therefore creates projects offline without reading a live branch or maintaining
a release pin or second template by hand.
The directory name becomes the stable kebab-case Program identity. In a
terminal, create asks for the directory when it is omitted, the readable
Program name, and the package manager. With no terminal, the directory is the
first argument and every optional choice is named:
phresh create status-board \
--name "Status Board" \
--package-manager npmDependencies are installed by default. --no-install creates the same valid
project and leaves installation as the first reported next step. Generated and
initialized Programs always use the published package ranges embedded in the
CLI; repository layout never changes dependency meaning.
Saying something to a program you start
phresh dev --run-option-path=/notes.md --run-option-line=42Read back by name, on either half:
const path = await current.option("path")Options are text, all of them. An option must mean the same thing
however the process was started, and a command line can only hand over
text — a number made here would be a guess about your program's meaning
by the one party with no way to know it. Is --run-option-id=007 seven,
or a string with two noughts in front? Only your program knows, so your
program decides: Number(...), once, where the meaning is.
Which is what argv and the environment have always been, for the same
reason. The prefix is long because these share a line with the tool's
own flags, and a program wanting an option called client should not
have to fight the CLI for the word.
phresh.config is not a program's configuration
A program's configuration is derived from it — three times, and the derivations differ only in where each half is said to be. That is the rule everything else here follows from.
It follows that every field which lands in a program.json is spelled
the way the contract spells it and crosses untouched: size, not a
width and a height; startCommand, not a command.
An optional top-level buildCommand is authoring metadata. phresh start,
phresh install, and phresh pack run it from this project before consuming
the production files. It never crosses into program.json or the system.
phresh dev uses the development declarations and does not build.
Everything else is yours: where each half is left.
import { defineConfig } from "@phreshos/core"
export default defineConfig({
identity: "file-manager", // kebab-case: the program's stable address
name: "File Manager", // what a person reads
version: "0.1.0",
description: "A file manager",
icon: "icon.png",
categories: ["Utilities"],
keywords: ["files", "storage"],
website: "https://example.com/file-manager",
buildCommand: "bun run build",
server: {
location: "build/server",
installCommand: "npm ci",
uninstallCommand: "npm run clean:external",
startCommand: "node main.js",
development: {
startCommand: "tsx server/main.ts"
}
},
client: {
location: "dist",
size: { width: "1/2", height: 440 },
position: { x: 60, y: 40 },
development: {
url: "http://localhost:5173",
startCommand: "bun run dev"
}
}
})identity identifies; name is read. The identity is kebab-case
because it is also the directory the system lays your program out in, so
it is a path component before anything else. The name is free-form,
identifies nothing, and absent means the identity serves for both.
identity, version and description begin from your package.json
during init; the readable name is asked for. They are written into
the config rather than read from the manifest later. pack says so if
the two versions have drifted apart.
A window's size and position are finite pixel numbers or linear
expressions. Fractions and percentages are equivalent relative terms, so
"1/2" and "50%" mean the same thing; pixel offsets may be combined with
them, as in "50% + 10". Every value survives derivation unchanged.
development — what phresh dev needs
phresh init offers to configure development for each declared half. It uses
the project's dev script as a suggested command when one exists, but records
nothing unless the author chooses it. If development is left unconfigured,
phresh dev refuses and names the declarations it needs:
Nothing here says how this program is developed.
Say how the server runs or where the client is served:
server: { …, development: { startCommand: "tsx source/server/main.ts" } }
client: { …, development: { url: "http://localhost:5173", startCommand: "bun run dev" } }Each half may carry a development block, but the two shapes are deliberately
different. A server block requires startCommand; phresh dev runs it from the
directory containing phresh.config.ts, and that directory becomes the derived
server location. A client block requires an HTTP(S) url; development clients
are never resolved from filesystem paths.
The client development shape may also declare startCommand. phresh dev
runs it from the project directory as a foreground development tool; the
command is never derived into the Program sent to the system. The tool and
the attached Program share one lifetime, so ending either ends the other.
Before launching the Program, phresh dev waits up to 15 seconds for the client
development URL to respond. While it remains unavailable, the URL is printed
every two seconds. A command that exits first is reported immediately. This
means the window is never deliberately opened onto a client that the authoring
tool already knows is unavailable.
init
phresh init turns an existing package into a Program project. It reads the
identity, version, and description from package.json, ensures the project has
the matching @phreshos/core development dependency, and writes the typed
phresh.config.ts authoring description.
In a terminal, init asks for the production locations and commands needed by
start and install, including whether a package build should prepare those
locations. It then offers the development command and URL needed by dev.
Existing build and dev package scripts become editable suggestions, never
silent assumptions. A single Endpoint defaults to dist; when both Endpoints
exist, their defaults are dist/server and dist/client. API documentation is
opt-in and defaults to disabled even when a likely document already exists.
Outside a terminal it never waits for input; the same values are supplied as named options:
phresh init --client \
--client-location dist \
--build-command "bun run build" \
--client-development-url http://localhost:5173 \
--client-development-start-command "bun run dev"
phresh init --server \
--server-location dist \
--server-start-command "node main.js"Use phresh init --help for the complete option list. An existing config is
never replaced silently: a terminal asks, while automation must say --force.
A Program must declare a Server endpoint, a Client endpoint, or both. Neither is refused during the interview rather than at the border, which is the earliest place it can be refused.
The final Next line is derived from the resulting config. It always shows
phresh start and phresh install; it shows phresh dev only when at least
one Endpoint received a development declaration.
Visual and advanced runtime defaults remain for the author to add deliberately:
icon, size, position, installCommand, start, layers, and minimization
are not guessed. An omitted start is true; only a default-off Endpoint needs to
say start: false.
start and dev
Both run your program without installing it, and stay attached. They
print the program.json it will be declared as, hand that to the system
through the socket below the selected system home. With no override, that is
~/.phreshos/gateway.sock. Set PHRESHOS_HOME to an absolute system home to
address another system instance; the CLI derives
<PHRESHOS_HOME>/gateway.sock from it. The socket is not selected
separately from its instance. Only your account can open it, so nothing is
sent to prove anything, and then the command holds.
The connection is the tether, in both directions. Ctrl-C and your
program stops. Close the window it opened and the command returns, with
your program's own exit status as its own. Its stdout and stderr
arrive in your terminal as well as its system log. The system always
drains a server process; attachment adds the terminal as an audience
rather than changing how the process starts.
Nothing has to promise to clean up, which is the point — a promise would
not survive kill -9, a closed terminal, or a dropped ssh session. All
three end the command without running a line of it, and all three still
close the socket, which is what the system is watching.
Attached means not installed; installed means persistent. A program meant to outlive your terminal is installed rather than run.
The run is registered as an ordinary uninstalled Program under the identity
declared by this project. Before registration, the system ends and forgets any
runtime Program already using that identity, whether it was installed or
uninstalled. Forgetting never uninstalls: installed files and storage remain
untouched while the attached Program becomes the sole runtime occupant. Its
root process tethers the whole Program to this command; when it exits, remaining
processes end and the runtime record disappears. A later phresh install can
replace the preserved installed files and immediately register the identity as
installed again. If no system is listening, the gateway says so plainly rather
than exposing ENOENT.
An attached Program still owns persistent project storage. The authoring tool
declares <project>/storage explicitly, so start and dev keep the same
database, store, data, cache, and logs as any other runtime form without
inventing a path from the system's working directory. Installation changes
where Program files are laid out; it does not change the logging contract.
They are one derivation over one config, differing only in where each half is said to be:
| | locations from | derived form |
|---|---|---|
| pack | location | none — the system lays an installed program out |
| start | location | absolute |
| dev | project root for a declared server development block; development.url for a declared client block | server absolute, client URL |
Every derived filesystem path is absolute because relative paths resolve
against the program.json they were read from, and a derived one does not live
beside your source. A client development URL remains the URL the author wrote.
install
A program has two ways of being used: run it, or install it.
phresh install # this project, laid out on this machine
phresh install flambo # the official Flambo ProgramWithout a name, what is sent is
the description this directory derives, and the system copies what it
names into place — your program's parts are already on this disk at the
locations it names, so there is nothing an archive would carry that the
description does not already point at. phresh pack is for when you have
somewhere to send a program; installing here is a different act.
With a name, the CLI resolves and verifies that official Program's production release directly; the current directory is irrelevant.
If buildCommand is declared, it completes successfully before anything is
sent to the system. Without it, install uses the production files exactly as
they stand.
Installing is the persistent one — laid out under ~/.phreshos/programs/<identity>,
marked installed, and reconstructed after a restart. Running is the other one:
phresh start / phresh dev register it under its declared identity and
attach its whole lifetime to your terminal.
The command installs through the machine's local gateway. A running Program may also install itself through the server SDK; installation is not a client capability.
When a Server declares installCommand, phresh install writes that command's
stdout and stderr chunks as the System emits them. The final installed
confirmation appears only after the output stream completes successfully.
That is also why it names paths rather than sending bytes: install used to want an upload because the installer was a browser, which has bytes and no path. You have the paths.
uninstall
phresh uninstall
phresh uninstall flambo
phresh uninstall --everythingOrdinary uninstall removes the installed Program files while preserving its
running Processes, stored data, and runtime Program. --everything explicitly
ends those Processes, removes everything the system owns for the Program, and
forgets its runtime record.
When the installed Server declares uninstallCommand, the System runs it from
that Server directory before removing files. phresh uninstall writes its
ordered stdout and stderr chunks as they arrive. A failed cleanup command
aborts removal and reports the failure.
Without a name, the identity comes from this project's phresh.config.ts.
With a name, the installed Program is addressed directly and the current
directory is irrelevant.
What pack produces
pack takes what is at each half's location and writes both program.json
and <identity>@<version>.zip. The exact generated declaration is also stored
inside the archive. Your program may leave its halves anywhere; the
package always keeps them in the same places, so the artifact's shape
belongs to the contract rather than to your project. That is why the
program.json it writes names server and client explicitly — those
are the canonical locations the package just created. An explicit
start: false crosses with its half; an omitted value remains omitted
and means true. At least one declared half must resolve to true.
There is no wrapping directory: program.json, server/, client/,
optional icon.png, and optional agent.md sit at the package's root. The
system names the directory it installs into from your program's identity.
An authored agent document may use any project-relative path, while packaging
normalizes it to agent.md.
Nothing about the system moves because this exists. program.json is
still the only declaration the system and release catalog read. Root
categories, keywords, and website values are optional and cross into it
without affecting execution. The file is generated output; phresh.config.ts
remains the authored source.
