@phreshos/cli 0.1.32 → 0.1.33
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 +2 -2
- package/dist/control-client.js +39 -8
- package/dist/launch.js +9 -23
- package/dist/program-installation.js +32 -37
- package/dist/program-release.js +1 -0
- package/dist/project.js +1 -1
- package/dist/template/package.json +1 -1
- package/dist/uninstall.js +11 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ their contracts and integrations are verified.
|
|
|
11
11
|
|
|
12
12
|
`@phreshos/cli` is not intended to be used independently of that architecture.
|
|
13
13
|
Its Program commands use `@phreshos/node`, whose `Project` model owns local
|
|
14
|
-
project behavior and whose `
|
|
15
|
-
inside Server Programs. Runtime operations require a compatible System.
|
|
14
|
+
project behavior and whose `System.connect()` provides the same Core contract
|
|
15
|
+
used inside Server Programs. Runtime operations require a compatible System.
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
phresh create app # create a complete new Program
|
package/dist/control-client.js
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { gatewayAddress, resolveHome } from "@phreshos/node";
|
|
2
|
+
import { connect } from "node:net";
|
|
2
3
|
/** Owner-local client for the authoritative running-System contract. */
|
|
3
4
|
export default class LocalSystemControl {
|
|
4
5
|
async execute(request, signal) {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const address = gatewayAddress(resolveHome());
|
|
7
|
+
return await new Promise((resolve, reject) => {
|
|
8
|
+
const socket = connect(address);
|
|
9
|
+
let buffer = "";
|
|
10
|
+
let settled = false;
|
|
11
|
+
const finish = (work) => {
|
|
12
|
+
if (settled)
|
|
13
|
+
return;
|
|
14
|
+
settled = true;
|
|
15
|
+
signal?.removeEventListener("abort", cancel);
|
|
16
|
+
socket.destroy();
|
|
17
|
+
work();
|
|
18
|
+
};
|
|
19
|
+
const cancel = () => finish(() => reject(signal?.reason instanceof Error ? signal.reason : new Error("The request was cancelled")));
|
|
20
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
21
|
+
socket.on("connect", () => socket.write(`${JSON.stringify({ target: "system", request })}\n`));
|
|
22
|
+
socket.on("data", chunk => {
|
|
23
|
+
buffer += String(chunk);
|
|
24
|
+
const boundary = buffer.indexOf("\n");
|
|
25
|
+
if (boundary < 0)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
const outcome = JSON.parse(buffer.slice(0, boundary));
|
|
29
|
+
if (outcome.success)
|
|
30
|
+
finish(() => resolve(outcome.result));
|
|
31
|
+
else
|
|
32
|
+
finish(() => reject(new Error(outcome.error)));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
finish(() => reject(new Error("The System returned an invalid response")));
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
socket.on("error", () => finish(() => reject(new Error(`No System is listening at ${address} — start PhreshOS first`))));
|
|
39
|
+
socket.on("close", () => finish(() => reject(new Error("The System closed the request without an answer"))));
|
|
40
|
+
if (signal?.aborted)
|
|
41
|
+
cancel();
|
|
42
|
+
});
|
|
12
43
|
}
|
|
13
44
|
}
|
package/dist/launch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Project } from "@phreshos/node";
|
|
2
2
|
import { relative } from "node:path";
|
|
3
3
|
import { dim, heading, line } from "./style.js";
|
|
4
|
-
/** Run the current project
|
|
4
|
+
/** Run the current project through one connected System. */
|
|
5
5
|
export default async function launch(mode, directory = process.cwd(), options = {}) {
|
|
6
6
|
const project = await Project.open(directory);
|
|
7
7
|
const program = project.description(mode);
|
|
@@ -16,7 +16,7 @@ export default async function launch(mode, directory = process.cwd(), options =
|
|
|
16
16
|
if (Object.keys(options).length)
|
|
17
17
|
line("options", Object.entries(options).map(([name, value]) => `${name}=${value}`).join(" "));
|
|
18
18
|
console.log("");
|
|
19
|
-
const
|
|
19
|
+
const system = await (await import("@phreshos/node")).System.connect();
|
|
20
20
|
const controller = new AbortController();
|
|
21
21
|
let interrupted = false;
|
|
22
22
|
let ended = null;
|
|
@@ -29,11 +29,11 @@ export default async function launch(mode, directory = process.cwd(), options =
|
|
|
29
29
|
for (const signal of ["SIGINT", "SIGTERM"])
|
|
30
30
|
process.on(signal, stop);
|
|
31
31
|
try {
|
|
32
|
-
const
|
|
33
|
-
?
|
|
34
|
-
:
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const result = mode === "development"
|
|
33
|
+
? await project.dev(system, { options, signal: controller.signal })
|
|
34
|
+
: await project.start(system, { options, signal: controller.signal });
|
|
35
|
+
console.log(`\n ${dim("ran as")} ${result.process.identity}`);
|
|
36
|
+
ended = { code: result.exit.code, signal: result.exit.signal };
|
|
37
37
|
}
|
|
38
38
|
catch (error) {
|
|
39
39
|
if (!interrupted)
|
|
@@ -42,7 +42,7 @@ export default async function launch(mode, directory = process.cwd(), options =
|
|
|
42
42
|
finally {
|
|
43
43
|
for (const signal of ["SIGINT", "SIGTERM"])
|
|
44
44
|
process.off(signal, stop);
|
|
45
|
-
await
|
|
45
|
+
await system.disconnect();
|
|
46
46
|
}
|
|
47
47
|
if (interrupted)
|
|
48
48
|
process.exit(130);
|
|
@@ -51,20 +51,6 @@ export default async function launch(mode, directory = process.cwd(), options =
|
|
|
51
51
|
console.log(`\n ${dim(ended.signal ? `ended on ${ended.signal}` : `ended with ${ended.code ?? 0}`)}\n`);
|
|
52
52
|
process.exit(ended.signal ? 128 : ended.code ?? 0);
|
|
53
53
|
}
|
|
54
|
-
function present(event) {
|
|
55
|
-
if (event.event === "started")
|
|
56
|
-
console.log(` ${dim("running as")} ${String(event.process)}\n`);
|
|
57
|
-
else if (event.event === "waiting")
|
|
58
|
-
line("waiting for", String(event.url));
|
|
59
|
-
else if (event.event === "output")
|
|
60
|
-
(event.stream === "err" ? process.stderr : process.stdout).write(String(event.text));
|
|
61
|
-
else if (event.event === "exited")
|
|
62
|
-
return {
|
|
63
|
-
code: typeof event.code === "number" ? event.code : null,
|
|
64
|
-
signal: typeof event.signal === "string" ? event.signal : null
|
|
65
|
-
};
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
54
|
function place(directory, location) {
|
|
69
55
|
return /^https?:\/\//i.test(location) ? location : `./${relative(directory, location)}`;
|
|
70
56
|
}
|
|
@@ -1,58 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Project } from "@phreshos/node";
|
|
2
2
|
import writeProgramCommandOutput from "./program-command-output.js";
|
|
3
3
|
/** Install one prepared Program and await every explicitly requested outcome. */
|
|
4
4
|
export default async function installProgram(program, options = {}) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const system = await (await import("@phreshos/node")).System.connect();
|
|
6
|
+
const identity = program instanceof Project ? program.config.identity : program.identity;
|
|
7
|
+
const current = await system.program.find(identity);
|
|
8
|
+
const replaced = await current?.installed() ?? false;
|
|
9
|
+
let installed = null;
|
|
7
10
|
let startupEnabled = false;
|
|
8
|
-
let
|
|
9
|
-
|
|
11
|
+
let process = null;
|
|
12
|
+
let installationFinished = false;
|
|
10
13
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (program instanceof Project)
|
|
15
|
+
installed = await program.install(system);
|
|
16
|
+
else {
|
|
17
|
+
installed = await system.forceCreateProgram(program);
|
|
18
|
+
for await (const chunk of installed.install())
|
|
19
|
+
writeProgramCommandOutput(chunk);
|
|
20
|
+
}
|
|
21
|
+
installationFinished = true;
|
|
22
|
+
if (options.startup) {
|
|
23
|
+
await installed.startup.enable();
|
|
24
|
+
startupEnabled = true;
|
|
25
|
+
}
|
|
26
|
+
if (options.run)
|
|
27
|
+
process = (await installed.process.create()).identity;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (installed && !installationFinished) {
|
|
31
|
+
try {
|
|
32
|
+
await installed.forget();
|
|
17
33
|
}
|
|
18
|
-
|
|
19
|
-
startupEnabled = true;
|
|
20
|
-
else if (event.event === "running")
|
|
21
|
-
processValue = event.process;
|
|
34
|
+
catch { /* Preserve the installation failure. */ }
|
|
22
35
|
}
|
|
36
|
+
throw error;
|
|
23
37
|
}
|
|
24
38
|
finally {
|
|
25
|
-
await
|
|
39
|
+
await system.disconnect();
|
|
26
40
|
}
|
|
27
|
-
if (
|
|
41
|
+
if (!installed)
|
|
28
42
|
throw new Error("The System ended Program installation without confirming it");
|
|
29
|
-
const installed = installedProgram(installedValue);
|
|
30
43
|
if (options.startup && !startupEnabled)
|
|
31
44
|
throw new Error("The System installed the Program without confirming startup");
|
|
32
|
-
const process = processValue === undefined ? null : processIdentity(processValue);
|
|
33
45
|
if (options.run && !process)
|
|
34
46
|
throw new Error("The System installed the Program without confirming that it is running");
|
|
35
47
|
return {
|
|
36
|
-
program: installed,
|
|
48
|
+
program: { identity: installed.identity, name: installed.name, version: installed.version },
|
|
37
49
|
replaced,
|
|
38
50
|
startupEnabled,
|
|
39
51
|
process
|
|
40
52
|
};
|
|
41
53
|
}
|
|
42
|
-
function processIdentity(value) {
|
|
43
|
-
if (typeof value !== "string" || !value)
|
|
44
|
-
throw new Error("The System returned an invalid Process identity");
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
function installedProgram(value) {
|
|
48
|
-
if (!record(value)
|
|
49
|
-
|| typeof value.identity !== "string"
|
|
50
|
-
|| typeof value.name !== "string"
|
|
51
|
-
|| value.version !== null && typeof value.version !== "string") {
|
|
52
|
-
throw new Error("The System returned an invalid installed Program");
|
|
53
|
-
}
|
|
54
|
-
return { identity: value.identity, name: value.name, version: value.version };
|
|
55
|
-
}
|
|
56
|
-
function record(value) {
|
|
57
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
58
|
-
}
|
package/dist/program-release.js
CHANGED
|
@@ -85,6 +85,7 @@ async function readProgram(directory, release) {
|
|
|
85
85
|
throw new Error("A published Program package cannot choose its installed storage");
|
|
86
86
|
return {
|
|
87
87
|
...value,
|
|
88
|
+
storage: join(directory, "storage"),
|
|
88
89
|
...pathField(value, directory, "icon", "icon.png"),
|
|
89
90
|
...pathField(value, directory, "agent", "agent.md"),
|
|
90
91
|
...half(value, directory, "server"),
|
package/dist/project.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { isAbsolute, normalize, resolve, sep } from "node:path";
|
|
5
5
|
export const configFile = "phresh.config.ts";
|
|
6
|
-
/** Read the current Project through the reusable
|
|
6
|
+
/** Read the current Project through the reusable Node project model. */
|
|
7
7
|
export async function readConfig(directory = process.cwd()) {
|
|
8
8
|
return (await Project.open(directory)).config;
|
|
9
9
|
}
|
package/dist/uninstall.js
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import { readConfig } from "./project.js";
|
|
2
2
|
import { dim, heading } from "./style.js";
|
|
3
|
-
import { Gateway } from "@phreshos/node";
|
|
4
3
|
import writeProgramCommandOutput from "./program-command-output.js";
|
|
5
4
|
/** Uninstall an installed Program by name or by the current project's identity. */
|
|
6
5
|
export default async function uninstall(options = {}) {
|
|
7
6
|
const identity = options.name ?? (await readConfig(options.directory ?? process.cwd())).identity;
|
|
8
7
|
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(identity))
|
|
9
8
|
throw new Error(`The Program name "${identity}" is invalid`);
|
|
10
|
-
const
|
|
9
|
+
const system = await (await import("@phreshos/node")).System.connect();
|
|
11
10
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
11
|
+
const program = await system.program.find(identity);
|
|
12
|
+
if (!program)
|
|
13
|
+
throw new Error("The System does not know this Program");
|
|
14
|
+
for await (const chunk of program.uninstall(options.everything === true))
|
|
15
|
+
writeProgramCommandOutput(chunk);
|
|
16
|
+
heading(identity, "uninstalled");
|
|
17
|
+
console.log(options.everything
|
|
18
|
+
? ` ${dim("Its processes, installed files, stored data, and runtime record were removed.")}\n`
|
|
19
|
+
: ` ${dim("Its installed files were removed. Processes, stored data, and runtime state were kept.")}\n`);
|
|
22
20
|
}
|
|
23
21
|
finally {
|
|
24
|
-
await
|
|
22
|
+
await system.disconnect();
|
|
25
23
|
}
|
|
26
24
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.33",
|
|
5
5
|
"description": "The Phresh command-line interface for Program projects and system management.",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.10"
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"packageManager": "bun@1.3.14",
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@clack/prompts": "^1.7.0",
|
|
52
|
-
"@phreshos/core": "^0.1.
|
|
53
|
-
"@phreshos/node": "^0.1.
|
|
52
|
+
"@phreshos/core": "^0.1.22",
|
|
53
|
+
"@phreshos/node": "^0.1.1",
|
|
54
54
|
"adm-zip": "^0.6.0",
|
|
55
55
|
"commander": "^15.0.0",
|
|
56
56
|
"jiti": "^2.7.0",
|