@phreshos/cli 0.1.32 → 0.1.34

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
@@ -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 `system` value exposes the same Core contract used
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
@@ -1,13 +1,44 @@
1
- import { Gateway } from "@phreshos/node";
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 gateway = await Gateway.open();
6
- try {
7
- return await gateway.execute(request, signal);
8
- }
9
- finally {
10
- await gateway.close();
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 { Gateway, Project } from "@phreshos/node";
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 attached to the System and present its event stream. */
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 gateway = await Gateway.open();
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 events = mode === "development"
33
- ? gateway.dev(project, { options, signal: controller.signal })
34
- : gateway.start(project, { options, signal: controller.signal });
35
- for await (const event of events)
36
- ended = present(event) ?? ended;
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 gateway.close();
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 { Gateway } from "@phreshos/node";
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
- let installedValue;
6
- let replaced = false;
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 processValue;
9
- const gateway = await Gateway.open();
11
+ let process = null;
12
+ let installationFinished = false;
10
13
  try {
11
- for await (const event of gateway.install(program, options)) {
12
- if (event.event === "output")
13
- writeProgramCommandOutput(event);
14
- else if (event.event === "installed") {
15
- installedValue = event.program;
16
- replaced = event.replaced === true;
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
- else if (event.event === "startupEnabled")
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 gateway.close();
39
+ await system.disconnect();
26
40
  }
27
- if (installedValue === undefined)
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
- }
@@ -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 Gateway project model. */
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
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "phresh",
3
3
  "private": true,
4
- "version": "0.1.18",
4
+ "version": "0.1.19",
5
5
  "description": "The official PhreshOS starter Program.",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -14,15 +14,15 @@
14
14
  "starter"
15
15
  ],
16
16
  "dependencies": {
17
- "@phreshos/client": "^0.1.22",
18
- "@phreshos/core": "^0.1.20",
19
- "@phreshos/react": "^0.1.10",
20
- "@phreshos/server": "^0.1.21",
17
+ "@phreshos/client": "^0.1.23",
18
+ "@phreshos/core": "^0.1.22",
19
+ "@phreshos/react": "^0.1.11",
20
+ "@phreshos/server": "^0.1.23",
21
21
  "react": "^19.2.8",
22
22
  "react-dom": "^19.2.8"
23
23
  },
24
24
  "devDependencies": {
25
- "@phreshos/cli": "^0.1.32",
25
+ "@phreshos/cli": "^0.1.34",
26
26
  "@types/node": "^26.2.0",
27
27
  "@types/react": "^19.2.18",
28
28
  "@types/react-dom": "^19.2.4",
@@ -4,7 +4,7 @@ export default defineConfig({
4
4
  identity: "phresh",
5
5
  name: "Phresh Program",
6
6
  description: "A minimal counter with direct Client and Server endpoints.",
7
- version: "0.1.18",
7
+ version: "0.1.19",
8
8
  icon: "icon.png",
9
9
  categories: ["Development"],
10
10
  keywords: ["example", "counter", "client", "server"],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "repository": "PhreshOS/phresh-program",
3
- "version": "0.1.18",
4
- "sha256": "f612d9c2c55beac0dc618057a68fad177e40564e596047011173e0a5226b65d6",
3
+ "version": "0.1.19",
4
+ "sha256": "e12f0c026b7b6f3e5f77a206442e5dcb6242021902d8f5a51c9886c7d75b0e24",
5
5
  "development": true
6
6
  }
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 gateway = await Gateway.open();
9
+ const system = await (await import("@phreshos/node")).System.connect();
11
10
  try {
12
- for await (const event of gateway.uninstall(identity, { everything: options.everything === true })) {
13
- if (event.event === "output")
14
- return writeProgramCommandOutput(event);
15
- if (event.event !== "uninstalled")
16
- return;
17
- heading(identity, "uninstalled");
18
- console.log(options.everything
19
- ? ` ${dim("Its processes, installed files, stored data, and runtime record were removed.")}\n`
20
- : ` ${dim("Its installed files were removed. Processes, stored data, and runtime state were kept.")}\n`);
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 gateway.close();
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.32",
4
+ "version": "0.1.34",
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.21",
53
- "@phreshos/node": "^0.1.0",
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",