@phreshos/cli 0.1.4 → 0.1.6
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/LICENSE +21 -0
- package/README.md +63 -26
- package/dist/build-template.js +73 -42
- package/dist/cli.js +135 -241
- package/dist/create.js +73 -80
- package/dist/derive.js +4 -4
- package/dist/init.js +113 -122
- package/dist/pack.js +9 -8
- package/dist/program-intake.js +10 -2
- package/dist/project-dependency.js +13 -44
- package/dist/project.js +2 -3
- package/dist/prompts.js +105 -0
- package/dist/style.js +9 -14
- package/dist/system/command.js +70 -0
- package/dist/system/installation.js +241 -0
- package/dist/system/lifecycle.js +174 -0
- package/dist/system/node.js +13 -0
- package/dist/system/paths.js +28 -0
- package/dist/system/process.js +23 -0
- package/dist/system/readiness.js +29 -0
- package/dist/system/release.js +86 -0
- package/dist/system/service/index.js +11 -0
- package/dist/system/service/linux.js +94 -0
- package/dist/system/service/macos.js +123 -0
- package/dist/system/types.js +0 -0
- package/dist/template/README.md +1 -1
- package/dist/template/icon.png +0 -0
- package/dist/template/package.json +8 -7
- package/dist/template/phresh.config.ts +65 -25
- package/dist/template/source/build.ts +3 -3
- package/dist/template/source/client/app.tsx +3 -8
- package/dist/template/source/server/main.ts +1 -1
- package/dist/template/vite.client.ts +22 -0
- package/dist/template/vite.server.ts +27 -0
- package/dist/template.json +5 -0
- package/package.json +36 -4
- package/dist/questions.js +0 -25
- package/dist/relative-value.js +0 -118
- package/dist/template/icons/128x128.png +0 -0
- package/dist/template/vite.config.ts +0 -38
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zohayr SLILEH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @phreshos/cli
|
|
2
2
|
|
|
3
|
-
The `phresh` command for creating
|
|
4
|
-
|
|
3
|
+
The `phresh` command for creating and operating Programs, and for installing
|
|
4
|
+
and managing the PhreshOS System on the current machine.
|
|
5
5
|
|
|
6
6
|
## Package status
|
|
7
7
|
|
|
@@ -21,6 +21,7 @@ phresh install # lay this program out on this machine
|
|
|
21
21
|
phresh uninstall # remove its installed form
|
|
22
22
|
phresh start # run what your build left, and stay with it
|
|
23
23
|
phresh dev # run from source, and stay with it
|
|
24
|
+
phresh system status # inspect the local System and its background service
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
`phresh --help` lists them, `phresh <command> --help` explains one, and
|
|
@@ -28,20 +29,53 @@ phresh dev # run from source, and stay with it
|
|
|
28
29
|
unknown command, an unknown flag and a malformed option are each refused
|
|
29
30
|
and named.
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
Every top-level Program command acts on the current project, and that list ends
|
|
33
|
+
there. It accepts no arbitrary Program identity and has no word for a Process,
|
|
34
|
+
Window, store, or setting. Machine lifecycle is isolated under `phresh system`;
|
|
35
|
+
it manages the System installation and native service, not the state inside the
|
|
36
|
+
System.
|
|
37
|
+
|
|
38
|
+
## System lifecycle
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
phresh system install
|
|
42
|
+
phresh system uninstall
|
|
43
|
+
phresh system status
|
|
44
|
+
phresh system start
|
|
45
|
+
phresh system stop
|
|
46
|
+
phresh system enable
|
|
47
|
+
phresh system disable
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`install` resolves the newest compatible stable release from the official
|
|
51
|
+
[`PhreshOS/system`](https://github.com/PhreshOS/system) GitHub Releases. It
|
|
52
|
+
downloads the production archive and adjacent checksum, verifies every byte,
|
|
53
|
+
installs production dependencies into a staged version directory, atomically
|
|
54
|
+
points the stable `current` path at it, then registers, enables, and starts the
|
|
55
|
+
native per-user service. The selected release and the service entry therefore
|
|
56
|
+
cannot become two competing sources of truth if installation is interrupted.
|
|
57
|
+
It never reads a source checkout and never requires Bun or TypeScript.
|
|
58
|
+
|
|
59
|
+
The System runs under `launchd` on macOS and `systemd --user` on Linux. The
|
|
60
|
+
native manager owns it after the CLI exits and restarts a failed active
|
|
61
|
+
service. `start` and `stop` change current execution only; `enable` and
|
|
62
|
+
`disable` change automatic startup only. `status` reads installation,
|
|
63
|
+
registration, startup, process, and local-intake readiness without changing
|
|
64
|
+
any of them.
|
|
65
|
+
|
|
66
|
+
Installation files and persistent System state have separate homes. Removing
|
|
67
|
+
the System unregisters its service and removes its release files while keeping
|
|
68
|
+
`~/.phreshos`, including Programs and owner data. Windows remains unsupported
|
|
69
|
+
until the System has an equally strong local-intake authorization model there.
|
|
36
70
|
|
|
37
71
|
## create
|
|
38
72
|
|
|
39
73
|
`phresh create <directory>` creates a complete Server and Client Program from
|
|
40
|
-
the maintained
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
therefore creates projects offline without
|
|
44
|
-
a second template by hand.
|
|
74
|
+
the maintained `PhreshOS/phresh-program` repository. The CLI build downloads
|
|
75
|
+
one exact tagged source release, verifies its SHA-256 checksum, removes
|
|
76
|
+
repository-only material, and bundles the resulting authoring project. The
|
|
77
|
+
installed CLI therefore creates projects offline without reading a live branch
|
|
78
|
+
or maintaining a second template by hand.
|
|
45
79
|
|
|
46
80
|
The directory name becomes the stable kebab-case Program identity. In a
|
|
47
81
|
terminal, `create` asks for the directory when it is omitted, the readable
|
|
@@ -55,11 +89,9 @@ phresh create status-board \
|
|
|
55
89
|
```
|
|
56
90
|
|
|
57
91
|
Dependencies are installed by default. `--no-install` creates the same valid
|
|
58
|
-
project and leaves installation as the first reported next step.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
template. Both choices pass through the same dependency resolver used by
|
|
62
|
-
`init`.
|
|
92
|
+
project and leaves installation as the first reported next step. Generated and
|
|
93
|
+
initialized Programs always use the published package ranges embedded in the
|
|
94
|
+
CLI; repository layout never changes dependency meaning.
|
|
63
95
|
|
|
64
96
|
## Saying something to a program you start
|
|
65
97
|
|
|
@@ -117,7 +149,7 @@ export default defineConfig({
|
|
|
117
149
|
|
|
118
150
|
apiDocs: "api.md",
|
|
119
151
|
|
|
120
|
-
|
|
152
|
+
icon: "icon.png",
|
|
121
153
|
|
|
122
154
|
buildCommand: "bun run build",
|
|
123
155
|
|
|
@@ -212,7 +244,9 @@ In a terminal, `init` asks for the production locations and commands needed by
|
|
|
212
244
|
`start` and `install`, including whether a package build should prepare those
|
|
213
245
|
locations. It then offers the development command and URL needed by `dev`.
|
|
214
246
|
Existing `build` and `dev` package scripts become editable suggestions, never
|
|
215
|
-
silent assumptions.
|
|
247
|
+
silent assumptions. A single Endpoint defaults to `dist`; when both Endpoints
|
|
248
|
+
exist, their defaults are `dist/server` and `dist/client`. API documentation is
|
|
249
|
+
opt-in and defaults to disabled even when a likely document already exists.
|
|
216
250
|
|
|
217
251
|
Outside a terminal it never waits for input; the same values are supplied as
|
|
218
252
|
named options:
|
|
@@ -225,7 +259,7 @@ phresh init --client \
|
|
|
225
259
|
--client-development-start-command "bun run dev"
|
|
226
260
|
|
|
227
261
|
phresh init --server \
|
|
228
|
-
--server-location
|
|
262
|
+
--server-location dist \
|
|
229
263
|
--server-start-command "node main.js"
|
|
230
264
|
```
|
|
231
265
|
|
|
@@ -241,7 +275,7 @@ The final `Next` line is derived from the resulting config. It always shows
|
|
|
241
275
|
one Endpoint received a development declaration.
|
|
242
276
|
|
|
243
277
|
Visual and advanced runtime defaults remain for the author to add deliberately:
|
|
244
|
-
`
|
|
278
|
+
`icon`, `size`, `position`, `installCommand`, `start`, layers, and minimization
|
|
245
279
|
are not guessed. An omitted `start` is `true`; only a default-off Endpoint needs to
|
|
246
280
|
say `start: false`.
|
|
247
281
|
|
|
@@ -249,9 +283,12 @@ say `start: false`.
|
|
|
249
283
|
|
|
250
284
|
Both **run your program without installing it, and stay attached.** They
|
|
251
285
|
print the `program.json` it will be declared as, hand that to the system
|
|
252
|
-
through the socket
|
|
253
|
-
|
|
254
|
-
|
|
286
|
+
through the socket below the selected system home. With no override, that is
|
|
287
|
+
`~/.phreshos/intake.sock`. Set `PHRESHOS_HOME` to an absolute system home to
|
|
288
|
+
address another system instance; the CLI derives
|
|
289
|
+
`<PHRESHOS_HOME>/intake.sock` from it. The socket is not selected
|
|
290
|
+
separately from its instance. Only your account can open it, so nothing is
|
|
291
|
+
sent to prove anything, and then the command holds.
|
|
255
292
|
|
|
256
293
|
**The connection is the tether, in both directions.** Ctrl-C and your
|
|
257
294
|
program stops. Close the window it opened and the command returns, with
|
|
@@ -367,8 +404,8 @@ register a subscription. Those are system contracts documented once by the
|
|
|
367
404
|
SDKs; `api-docs.md` supplies only the Program-specific meaning carried through
|
|
368
405
|
them.
|
|
369
406
|
|
|
370
|
-
There is **no wrapping directory**: `program.json`, `server/`, `client
|
|
371
|
-
`
|
|
407
|
+
There is **no wrapping directory**: `program.json`, `server/`, `client/`,
|
|
408
|
+
optional `icon.png`, and optional `api-docs.md` sit at the package's root, and the system names the
|
|
372
409
|
directory it installs into from your program's `identity`.
|
|
373
410
|
|
|
374
411
|
Nothing about the system moves because this exists. `program.json` is
|
package/dist/build-template.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import metadata from "../package.json" with { type: "json" };
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, resolve, sep } from "node:path";
|
|
5
|
+
import AdmZip from "adm-zip";
|
|
6
|
+
import { readConfig } from "./project.js";
|
|
7
|
+
const repository = "PhreshOS/phresh-program";
|
|
8
|
+
const release = {
|
|
9
|
+
version: "0.1.0",
|
|
10
|
+
sha256: "6e1a87a037a6ac5f82ff4c7d420757f972098387d73a8b4165aab2ef331cec36"
|
|
11
|
+
};
|
|
12
|
+
const archiveUrl = `https://github.com/${repository}/archive/refs/tags/v${release.version}.zip`;
|
|
4
13
|
const output = resolve(import.meta.dirname, "template");
|
|
5
|
-
const
|
|
14
|
+
const descriptionOutput = resolve(import.meta.dirname, "template.json");
|
|
15
|
+
const excludedDirectories = new Set([".github", "dist", "node_modules", "scripts", "storage"]);
|
|
16
|
+
const excludedRootFiles = new Set(["CONTRIBUTING.md", "LICENSE", "SECURITY.md"]);
|
|
6
17
|
const excludedFiles = new Set([
|
|
7
18
|
".DS_Store",
|
|
8
19
|
"bun.lock",
|
|
@@ -11,49 +22,69 @@ const excludedFiles = new Set([
|
|
|
11
22
|
"pnpm-lock.yaml",
|
|
12
23
|
"yarn.lock"
|
|
13
24
|
]);
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
const response = await fetch(archiveUrl);
|
|
26
|
+
if (!response.ok)
|
|
27
|
+
throw new Error(`Could not download ${repository} v${release.version}: ${response.status} ${response.statusText}`);
|
|
28
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
29
|
+
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
30
|
+
if (digest !== release.sha256)
|
|
31
|
+
throw new Error(`The ${repository} v${release.version} source archive failed integrity verification`);
|
|
32
|
+
const archive = new AdmZip(bytes);
|
|
33
|
+
const entries = archive.getEntries();
|
|
34
|
+
const roots = new Set(entries.map(entry => entry.entryName.split("/")[0]).filter(Boolean));
|
|
35
|
+
if (roots.size !== 1)
|
|
36
|
+
throw new Error(`The ${repository} v${release.version} source archive has an invalid root`);
|
|
37
|
+
const root = [...roots][0];
|
|
16
38
|
rmSync(output, { recursive: true, force: true });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
if (!dependencies)
|
|
39
|
+
rmSync(descriptionOutput, { force: true });
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
if (entry.isDirectory || !entry.entryName.startsWith(`${root}/`))
|
|
42
|
+
continue;
|
|
43
|
+
const local = entry.entryName.slice(root.length + 1);
|
|
44
|
+
if (!included(local))
|
|
24
45
|
continue;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
dependencies[name] = `^${version}`;
|
|
32
|
-
}
|
|
46
|
+
const target = local === ".gitignore" ? "gitignore" : local;
|
|
47
|
+
const path = resolve(output, target);
|
|
48
|
+
if (!path.startsWith(`${output}${sep}`))
|
|
49
|
+
throw new Error(`The ${repository} source archive contains an unsafe path: ${local}`);
|
|
50
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
51
|
+
writeFileSync(path, entry.getData());
|
|
33
52
|
}
|
|
53
|
+
const manifestPath = resolve(output, "package.json");
|
|
54
|
+
const manifestSource = archive.readFile(`${root}/package.json`);
|
|
55
|
+
if (!manifestSource)
|
|
56
|
+
throw new Error(`The ${repository} v${release.version} source archive has no package.json`);
|
|
57
|
+
const manifest = JSON.parse(manifestSource.toString("utf8"));
|
|
58
|
+
manifest.scripts = select(manifest.scripts, ["dev", "start"]);
|
|
59
|
+
manifest.devDependencies = {
|
|
60
|
+
...manifest.devDependencies,
|
|
61
|
+
"@phreshos/cli": `^${metadata.version}`
|
|
62
|
+
};
|
|
63
|
+
delete manifest.author;
|
|
64
|
+
delete manifest.license;
|
|
65
|
+
delete manifest.repository;
|
|
66
|
+
delete manifest.bugs;
|
|
67
|
+
delete manifest.homepage;
|
|
68
|
+
delete manifest.packageManager;
|
|
34
69
|
writeFileSync(manifestPath, JSON.stringify(manifest, null, 4) + "\n");
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
70
|
+
const config = await readConfig(output);
|
|
71
|
+
if (config.identity !== "phresh-program" || config.name !== "Phresh Program")
|
|
72
|
+
throw new Error(`The ${repository} release is not Phresh Program`);
|
|
73
|
+
writeFileSync(descriptionOutput, JSON.stringify({
|
|
74
|
+
repository,
|
|
75
|
+
version: release.version,
|
|
76
|
+
development: Boolean(config.server?.development || config.client?.development)
|
|
77
|
+
}, null, 4) + "\n");
|
|
78
|
+
function included(local) {
|
|
79
|
+
const parts = local.split("/");
|
|
40
80
|
if (parts.some(part => excludedDirectories.has(part)))
|
|
41
81
|
return false;
|
|
42
|
-
|
|
43
|
-
|
|
82
|
+
if (parts.length === 1 && excludedRootFiles.has(local))
|
|
83
|
+
return false;
|
|
84
|
+
if (excludedFiles.has(parts.at(-1)))
|
|
85
|
+
return false;
|
|
86
|
+
return !local.endsWith(".zip");
|
|
44
87
|
}
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
const devKit = resolve(source, "..");
|
|
48
|
-
for (const entry of readdirSync(devKit, { withFileTypes: true })) {
|
|
49
|
-
if (!entry.isDirectory())
|
|
50
|
-
continue;
|
|
51
|
-
const path = resolve(devKit, entry.name, "package.json");
|
|
52
|
-
if (!existsSync(path))
|
|
53
|
-
continue;
|
|
54
|
-
const manifest = JSON.parse(readFileSync(path, "utf-8"));
|
|
55
|
-
if (manifest.name && manifest.version)
|
|
56
|
-
versions.set(manifest.name, manifest.version);
|
|
57
|
-
}
|
|
58
|
-
return versions;
|
|
88
|
+
function select(source, names) {
|
|
89
|
+
return Object.fromEntries(names.flatMap(name => source?.[name] === undefined ? [] : [[name, source[name]]]));
|
|
59
90
|
}
|