@phreshos/cli 0.1.48 → 0.1.49

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.
@@ -0,0 +1,102 @@
1
+ import { defineCommand } from "../contract/command.js";
2
+ import { option, processOptions, timeoutOption, withJson } from "./options.js";
3
+ import { eventOutput, jsonOutput, windowOutput } from "./schemas.js";
4
+ import { connected, requireProcess } from "./connection.js";
5
+ import { bounded, position, size } from "./input.js";
6
+ import { wait } from "./observation.js";
7
+ import { windowOf, windowView } from "./projection.js";
8
+ export default function windowCommands(root, connect) {
9
+ const windows = defineCommand(root, {
10
+ name: "window",
11
+ description: "inspect and control authoritative Client Windows",
12
+ guidance: ["A Window belongs to the Client Endpoint of one exact Process."]
13
+ });
14
+ defineCommand(windows, state("inspect", "read the complete current Window state"), async ({ options }) => {
15
+ return await withWindow(connect, options, windowView);
16
+ });
17
+ defineCommand(windows, {
18
+ ...state("move", "change Window position"),
19
+ options: withJson(...processOptions, option("--x <value>", "horizontal pixels or workspace-relative expression", { mandatory: true }), option("--y <value>", "vertical pixels or workspace-relative expression", { mandatory: true })),
20
+ examples: ["phresh window move --process main --program terminal --x 50% --y 0"]
21
+ }, async ({ options }) => withWindow(connect, options, async (process) => {
22
+ await windowOf(process).move(position(options.x, options.y));
23
+ return await windowView(process);
24
+ }));
25
+ defineCommand(windows, {
26
+ ...state("resize", "change Window size"),
27
+ options: withJson(...processOptions, option("--width <value>", "width in pixels or a workspace-relative expression", { mandatory: true }), option("--height <value>", "height in pixels or a workspace-relative expression", { mandatory: true })),
28
+ examples: ["phresh window resize --process main --program terminal --width 800 --height 600"]
29
+ }, async ({ options }) => withWindow(connect, options, async (process) => {
30
+ await windowOf(process).resize(size(options.width, options.height));
31
+ return await windowView(process);
32
+ }));
33
+ defineCommand(windows, {
34
+ ...state("setGeometry", "change Window position and size atomically"),
35
+ aliases: ["set-geometry"],
36
+ options: withJson(...processOptions, option("--x <value>", "horizontal pixels or workspace-relative expression", { mandatory: true }), option("--y <value>", "vertical pixels or workspace-relative expression", { mandatory: true }), option("--width <value>", "width in pixels or a workspace-relative expression", { mandatory: true }), option("--height <value>", "height in pixels or a workspace-relative expression", { mandatory: true })),
37
+ examples: ["phresh window set-geometry --process main --program terminal --x 0 --y 0 --width 100% --height 100%"]
38
+ }, async ({ options }) => withWindow(connect, options, async (process) => {
39
+ await windowOf(process).setGeometry({
40
+ position: position(options.x, options.y),
41
+ size: size(options.width, options.height)
42
+ });
43
+ return await windowView(process);
44
+ }));
45
+ defineCommand(windows, {
46
+ ...state("minimize", "set Window visibility without changing its order"),
47
+ options: withJson(...processOptions, option("--restore", "restore rather than minimize the Window")),
48
+ examples: ["phresh window minimize --process main --program terminal", "phresh window minimize --process main --program terminal --restore"]
49
+ }, async ({ options }) => withWindow(connect, options, async (process) => {
50
+ await windowOf(process).minimize(options.restore !== true);
51
+ return await windowView(process);
52
+ }));
53
+ defineCommand(windows, {
54
+ ...state("changeTitle", "change the human-readable Window title"),
55
+ aliases: ["change-title"],
56
+ options: withJson(...processOptions, option("--title <title>", "new Window title", { mandatory: true })),
57
+ examples: ["phresh window change-title --process main --program terminal --title Shell"]
58
+ }, async ({ options }) => withWindow(connect, options, async (process) => {
59
+ await windowOf(process).changeTitle(options.title);
60
+ return await windowView(process);
61
+ }));
62
+ defineCommand(windows, {
63
+ ...state("raise", "raise the Window within its own layer"),
64
+ examples: ["phresh window raise --process main --program terminal"]
65
+ }, async ({ options }) => withWindow(connect, options, async (process) => {
66
+ await windowOf(process).raise();
67
+ return await windowView(process);
68
+ }));
69
+ defineCommand(windows, {
70
+ name: "wait",
71
+ description: "wait for one authoritative Window change",
72
+ requiresSystem: true,
73
+ options: withJson(...processOptions, option("--event <event>", "Window event", {
74
+ mandatory: true,
75
+ choices: ["move", "resize", "geometry", "minimize", "changeTitle", "front"]
76
+ }), timeoutOption),
77
+ output: jsonOutput(eventOutput("The observed Window event"), "One Window event"),
78
+ examples: ["phresh window wait --process main --program terminal --event geometry --json"]
79
+ }, async ({ options }) => withWindow(connect, options, async (process) => {
80
+ return {
81
+ scope: `window:${process.identity}`,
82
+ event: options.event,
83
+ payload: await wait(windowOf(process), options.event, options.timeout === undefined
84
+ ? undefined
85
+ : bounded(options.timeout, "--timeout", 1))
86
+ };
87
+ }));
88
+ }
89
+ function state(name, description) {
90
+ return {
91
+ name,
92
+ description,
93
+ requiresSystem: true,
94
+ options: withJson(...processOptions),
95
+ output: jsonOutput(windowOutput, "The current Window state")
96
+ };
97
+ }
98
+ async function withWindow(connect, options, action) {
99
+ return await connected(connect, async (system) => {
100
+ return await action(await requireProcess(system, options.process, options.program));
101
+ });
102
+ }
@@ -0,0 +1,92 @@
1
+ import { Argument, Command, Option } from "commander";
2
+ import { assertValue } from "./schema.js";
3
+ const contracts = new WeakMap();
4
+ /** Register one command from the contract used by help, discovery, and execution. */
5
+ export function defineCommand(parent, contract, execute) {
6
+ const command = parent.command(contract.name);
7
+ attachCommandContract(command, contract);
8
+ for (const alias of contract.aliases ?? [])
9
+ command.alias(alias);
10
+ for (const argument of contract.arguments ?? []) {
11
+ const configured = new Argument(argument.syntax, argument.description);
12
+ if (argument.choices)
13
+ configured.choices([...argument.choices]);
14
+ if (Object.hasOwn(argument, "default"))
15
+ configured.default(argument.default);
16
+ command.addArgument(configured);
17
+ }
18
+ for (const option of contract.options ?? [])
19
+ command.addOption(configureOption(option));
20
+ if (contract.allowUnknownOptions)
21
+ command.allowUnknownOption();
22
+ if (execute)
23
+ command.action(async (...received) => {
24
+ const owner = received.pop();
25
+ const options = received.pop();
26
+ const result = await execute({ arguments: received, options, command: owner });
27
+ if (contract.output?.format === "json")
28
+ writeJson(result, contract.output, options.json === true);
29
+ });
30
+ return command;
31
+ }
32
+ /** Associate the root command with the same public contract as its descendants. */
33
+ export function attachCommandContract(command, contract) {
34
+ command.description(contract.description);
35
+ contracts.set(command, freezeContract(contract));
36
+ const detail = [...contract.guidance ?? [], ...(contract.examples?.length ? ["", "Examples:", ...contract.examples.map(example => ` ${example}`)] : [])];
37
+ if (detail.length)
38
+ command.addHelpText("after", `\n${detail.map(line => line ? ` ${line}` : "").join("\n")}\n`);
39
+ return command;
40
+ }
41
+ export function readCommandContract(command) {
42
+ return contracts.get(command);
43
+ }
44
+ /** Reject any Commander node that bypassed the authoritative CLI contract. */
45
+ export function assertCommandContracts(root) {
46
+ for (const command of descendants(root)) {
47
+ if (!contracts.has(command))
48
+ throw new Error(`Command "${pathOf(command).join(" ")}" has no CLI contract`);
49
+ }
50
+ }
51
+ function configureOption(contract) {
52
+ const option = new Option(contract.flags, contract.description);
53
+ if (contract.mandatory)
54
+ option.makeOptionMandatory();
55
+ if (contract.choices)
56
+ option.choices([...contract.choices]);
57
+ if (contract.parse)
58
+ option.argParser(contract.parse);
59
+ if (Object.hasOwn(contract, "default"))
60
+ option.default(contract.default);
61
+ return option;
62
+ }
63
+ function freezeContract(contract) {
64
+ return Object.freeze({
65
+ ...contract,
66
+ ...(contract.aliases ? { aliases: Object.freeze([...contract.aliases]) } : {}),
67
+ ...(contract.arguments ? { arguments: Object.freeze(contract.arguments.map(value => Object.freeze({ ...value }))) } : {}),
68
+ ...(contract.options ? { options: Object.freeze(contract.options.map(value => Object.freeze({ ...value }))) } : {}),
69
+ ...(contract.guidance ? { guidance: Object.freeze([...contract.guidance]) } : {}),
70
+ ...(contract.examples ? { examples: Object.freeze([...contract.examples]) } : {})
71
+ });
72
+ }
73
+ function descendants(command) {
74
+ return [command, ...command.commands.flatMap(descendants)];
75
+ }
76
+ function pathOf(command) {
77
+ const path = [];
78
+ let current = command;
79
+ while (current) {
80
+ path.unshift(current.name());
81
+ current = current.parent;
82
+ }
83
+ return path;
84
+ }
85
+ function writeJson(result, contract, compact) {
86
+ const normalized = result ?? null;
87
+ if (contract.value)
88
+ assertValue(normalized, contract.value);
89
+ console.log(JSON.stringify(normalized, null, compact ? undefined : 2));
90
+ if (!compact)
91
+ console.log();
92
+ }
@@ -0,0 +1,80 @@
1
+ export const value = Object.freeze({
2
+ any: (description) => ({ description }),
3
+ string: (description) => ({ type: "string", description }),
4
+ number: (description) => ({ type: "number", description }),
5
+ integer: (description) => ({ type: "integer", description }),
6
+ boolean: (description) => ({ type: "boolean", description }),
7
+ literal: (literal, description) => ({
8
+ ...(literal === null ? { type: "null" } : { type: typeof literal }),
9
+ enum: [literal],
10
+ description
11
+ }),
12
+ enumeration: (values, description) => ({
13
+ type: "string",
14
+ enum: values,
15
+ description
16
+ }),
17
+ array: (items, description) => ({ type: "array", items, description }),
18
+ object: (properties, required, description, additionalProperties = false) => ({ type: "object", properties, required, additionalProperties, description }),
19
+ nullable: (schema) => ({ anyOf: [schema, { type: "null" }] })
20
+ });
21
+ /** Validate one emitted JSON value against its declared CLI output contract. */
22
+ export function assertValue(value, contract, path = "result") {
23
+ if (contract.anyOf) {
24
+ if (contract.anyOf.some(candidate => accepts(value, candidate)))
25
+ return;
26
+ throw new Error(`${path} does not match its CLI contract`);
27
+ }
28
+ if (contract.enum && !contract.enum.some(candidate => Object.is(candidate, value))) {
29
+ throw new Error(`${path} is not one of its declared values`);
30
+ }
31
+ if (!contract.type)
32
+ return;
33
+ if (contract.type === "null") {
34
+ if (value !== null)
35
+ throw new Error(`${path} must be null`);
36
+ return;
37
+ }
38
+ if (contract.type === "array") {
39
+ if (!Array.isArray(value))
40
+ throw new Error(`${path} must be an array`);
41
+ if (contract.items)
42
+ value.forEach((item, index) => assertValue(item, contract.items, `${path}[${index}]`));
43
+ return;
44
+ }
45
+ if (contract.type === "object") {
46
+ if (!value || typeof value !== "object" || Array.isArray(value))
47
+ throw new Error(`${path} must be an object`);
48
+ const record = value;
49
+ for (const name of contract.required ?? []) {
50
+ if (!Object.hasOwn(record, name))
51
+ throw new Error(`${path}.${name} is required by its CLI contract`);
52
+ }
53
+ for (const [name, item] of Object.entries(record)) {
54
+ const property = contract.properties?.[name];
55
+ if (property)
56
+ assertValue(item, property, `${path}.${name}`);
57
+ else if (contract.additionalProperties === false)
58
+ throw new Error(`${path}.${name} is not declared by its CLI contract`);
59
+ else if (typeof contract.additionalProperties === "object")
60
+ assertValue(item, contract.additionalProperties, `${path}.${name}`);
61
+ }
62
+ return;
63
+ }
64
+ if (contract.type === "integer") {
65
+ if (!Number.isInteger(value))
66
+ throw new Error(`${path} must be an integer`);
67
+ return;
68
+ }
69
+ if (typeof value !== contract.type)
70
+ throw new Error(`${path} must be ${contract.type}`);
71
+ }
72
+ function accepts(value, contract) {
73
+ try {
74
+ assertValue(value, contract);
75
+ return true;
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ }
@@ -1,48 +1,42 @@
1
1
  import SystemLifecycle from "./lifecycle.js";
2
2
  import prompts, { ReportedFailure } from "../prompts.js";
3
3
  import { accent, caution, dim, negative, positive } from "../style.js";
4
- import { commandContract } from "../command-contract.js";
4
+ import { defineCommand } from "../contract/command.js";
5
+ import { textOutput } from "../commands/schemas.js";
5
6
  /** Attach the System lifecycle without mixing it with Program commands. */
6
7
  export default function systemCommands(program, provided) {
7
8
  let lifecycle = provided;
8
9
  const current = () => lifecycle ?? (lifecycle = new SystemLifecycle());
9
- const system = commandContract(program.command("system")
10
- .description("install and manage the PhreshOS System"), {
10
+ const system = defineCommand(program, {
11
+ name: "system",
12
+ description: "install and manage the PhreshOS System",
11
13
  guidance: [
12
14
  "Choose one native System lifecycle operation.",
13
15
  "Running-System Programs, Processes, Endpoints, and Windows are top-level capabilities, not System subcommands."
14
16
  ]
15
17
  });
16
- system.command("install")
17
- .description("install or update the System and start its service")
18
- .action(async function () {
18
+ defineCommand(system, lifecycleContract("install", "install or update the System and start its service"), async function () {
19
19
  const interaction = prompts();
20
20
  interaction.begin("Install System", "official stable release");
21
21
  const status = await interaction.progress("Installing PhreshOS", "PhreshOS installed", () => current().install());
22
22
  interaction.detail("desktop", accent(status.desktop));
23
23
  interaction.finish(`PhreshOS ${accent(status.installed?.version ?? "")} installed`);
24
24
  });
25
- system.command("uninstall")
26
- .description("remove the System installation and service")
27
- .action(async function () {
25
+ defineCommand(system, lifecycleContract("uninstall", "remove the System installation and service"), async function () {
28
26
  const interaction = prompts();
29
27
  interaction.begin("Uninstall System");
30
28
  await interaction.progress("Removing PhreshOS", "PhreshOS removed", () => current().uninstall());
31
29
  interaction.message("Persistent System data was kept.");
32
30
  interaction.finish("System uninstalled");
33
31
  });
34
- system.command("status")
35
- .description("show the System version and operating state")
36
- .action(async function () {
32
+ defineCommand(system, lifecycleContract("status", "show the System version and operating state"), async function () {
37
33
  const status = await installed(current());
38
34
  const interaction = prompts();
39
35
  interaction.begin("System Status");
40
36
  report(interaction, status);
41
37
  interaction.finish(status.ready ? positive("System ready") : caution("System not ready"));
42
38
  });
43
- system.command("version")
44
- .description("show the installed System version")
45
- .action(async function () {
39
+ defineCommand(system, lifecycleContract("version", "show the installed System version"), async function () {
46
40
  const status = await installed(current());
47
41
  const interaction = prompts();
48
42
  interaction.begin("System Version");
@@ -55,9 +49,7 @@ export default function systemCommands(program, provided) {
55
49
  return system;
56
50
  }
57
51
  function action(system, name, description, current, work) {
58
- system.command(name)
59
- .description(description)
60
- .action(async function () {
52
+ defineCommand(system, lifecycleContract(name, description), async function () {
61
53
  const lifecycle = current();
62
54
  await installed(lifecycle);
63
55
  const interaction = prompts();
@@ -68,6 +60,13 @@ function action(system, name, description, current, work) {
68
60
  interaction.finish(`System ${past(name)}`);
69
61
  });
70
62
  }
63
+ function lifecycleContract(name, description) {
64
+ return {
65
+ name,
66
+ description,
67
+ output: textOutput(description)
68
+ };
69
+ }
71
70
  function report(interaction, status) {
72
71
  interaction.detail("version", accent(status.installed?.version ?? "unknown"));
73
72
  interaction.detail("desktop", accent(status.desktop));
@@ -22,7 +22,7 @@
22
22
  "react-dom": "^19.2.8"
23
23
  },
24
24
  "devDependencies": {
25
- "@phreshos/cli": "^0.1.48",
25
+ "@phreshos/cli": "^0.1.49",
26
26
  "@types/node": "^26.4.0",
27
27
  "@types/react": "^19.2.18",
28
28
  "@types/react-dom": "^19.2.5",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@phreshos/cli",
3
3
  "type": "module",
4
- "version": "0.1.48",
4
+ "version": "0.1.49",
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.37",
53
- "@phreshos/node": "^0.1.13",
52
+ "@phreshos/core": "^0.1.39",
53
+ "@phreshos/node": "^0.1.14",
54
54
  "adm-zip": "^0.6.0",
55
55
  "commander": "^15.0.0",
56
56
  "picocolors": "^1.1.1"
@@ -1,12 +0,0 @@
1
- const contracts = new WeakMap();
2
- /** Attach machine-readable policy to the same Commander node that owns syntax. */
3
- export function commandContract(command, contract = {}) {
4
- contracts.set(command, Object.freeze({
5
- ...contract,
6
- ...(contract.guidance ? { guidance: Object.freeze([...contract.guidance]) } : {})
7
- }));
8
- return command;
9
- }
10
- export function readCommandContract(command) {
11
- return contracts.get(command);
12
- }
@@ -1,99 +0,0 @@
1
- import { Option } from "commander";
2
- import { commandContract } from "../command-contract.js";
3
- import { clientOverrideOptions, endpointOptions, outputOptions, serverOverrideOptions } from "./options.js";
4
- import { bounded, clientLaunch, connected, endpoint, endpointView, integer, output, payload, requireProcess, serverLaunch, wait } from "./shared.js";
5
- export default function endpointCommands(root, connect) {
6
- const endpoints = commandContract(root.command("endpoint")
7
- .description("inspect, control, and communicate with Process Endpoints"));
8
- outputOptions(endpointOptions(endpoints.command("inspect")
9
- .description("read whether one Endpoint is declared and running")))
10
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
11
- output(await endpointView(process, name), options.compact);
12
- }));
13
- outputOptions(serverOverrideOptions(clientOverrideOptions(endpointOptions(endpoints.command("start")
14
- .description("start a fresh Endpoint incarnation")))))
15
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
16
- const client = clientLaunch(options);
17
- const server = serverLaunch(options);
18
- if (name === "server" && client !== undefined)
19
- throw new Error("Client overrides require --endpoint client");
20
- if (name === "client" && server !== undefined)
21
- throw new Error("Server overrides require --endpoint server");
22
- if (name === "client")
23
- await process.client.start(typeof client === "object" ? client : undefined);
24
- else
25
- await process.server.start(typeof server === "object" ? server : undefined);
26
- output(await endpointView(process, name), options.compact);
27
- }));
28
- outputOptions(endpointOptions(endpoints.command("stop")
29
- .description("stop one Endpoint")))
30
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
31
- await endpoint(process, name).stop();
32
- output(await endpointView(process, name), options.compact);
33
- }));
34
- outputOptions(endpointOptions(endpoints.command("waitReady")
35
- .alias("wait-ready")
36
- .description("wait until the Server Endpoint reports readiness")
37
- .option("--timeout <milliseconds>", "maximum wait in milliseconds", integer)))
38
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
39
- if (name !== "server")
40
- throw new Error("waitReady requires --endpoint server");
41
- await process.server.waitReady(timeout(options.timeout));
42
- output(await endpointView(process, "server"), options.compact);
43
- }));
44
- outputOptions(endpointOptions(endpoints.command("waitLifecycle")
45
- .alias("wait-lifecycle")
46
- .description("wait for one lifecycle transition of an exact Endpoint")
47
- .addOption(new Option("--event <event>", "Endpoint lifecycle event")
48
- .choices(["start", "stop"])
49
- .makeOptionMandatory())
50
- .option("--timeout <milliseconds>", "maximum wait in milliseconds", integer)))
51
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
52
- await wait(endpoint(process, name).lifecycle, options.event, timeout(options.timeout));
53
- output({
54
- scope: `endpoint:${process.identity}:${name}:lifecycle`,
55
- event: options.event
56
- }, options.compact);
57
- }));
58
- outputOptions(endpointOptions(endpoints.command("ask")
59
- .description("ask a Server event and return its answer")
60
- .requiredOption("--event <event>", "event name")
61
- .option("--payload <json>", "arbitrary event payload as JSON")
62
- .option("--timeout <milliseconds>", "maximum wait in milliseconds", integer)))
63
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
64
- if (name !== "server")
65
- throw new Error("ask requires --endpoint server");
66
- const value = payload(options.payload);
67
- const answer = options.timeout === undefined
68
- ? await process.server.ask(options.event, value)
69
- : await process.server.timeout(timeout(options.timeout)).ask(options.event, value);
70
- output(answer, options.compact);
71
- }));
72
- outputOptions(endpointOptions(endpoints.command("publish")
73
- .description("publish one event without waiting for an answer")
74
- .requiredOption("--event <event>", "event name")
75
- .option("--payload <json>", "arbitrary event payload as JSON")))
76
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
77
- endpoint(process, name).publish(options.event, payload(options.payload));
78
- output(await endpointView(process, name), options.compact);
79
- }));
80
- outputOptions(endpointOptions(endpoints.command("wait")
81
- .description("wait for the next event emitted by one live Endpoint")
82
- .requiredOption("--event <event>", "event name")
83
- .option("--timeout <milliseconds>", "maximum wait in milliseconds", integer)))
84
- .action(async (options) => withEndpoint(connect, options, async (process, name) => {
85
- output({
86
- scope: `endpoint:${process.identity}:${name}`,
87
- event: options.event,
88
- payload: await wait(endpoint(process, name), options.event, timeout(options.timeout))
89
- }, options.compact);
90
- }));
91
- }
92
- async function withEndpoint(connect, options, action) {
93
- await connected(connect, async (system) => {
94
- await action(await requireProcess(system, options.process, options.program), options.endpoint);
95
- });
96
- }
97
- function timeout(value) {
98
- return value === undefined ? undefined : bounded(value, "--timeout", 1);
99
- }
@@ -1,44 +0,0 @@
1
- import { Option } from "commander";
2
- import { collect } from "./shared.js";
3
- export function outputOptions(command) {
4
- return command.option("--compact", "write machine-readable JSON on one line");
5
- }
6
- export function processOptions(command) {
7
- return command
8
- .requiredOption("--process <identity>", "Process identity or Program-local name")
9
- .option("--program <identity>", "owning Program identity when resolving a Process name");
10
- }
11
- export function endpointOptions(command) {
12
- return processOptions(command)
13
- .addOption(new Option("--endpoint <endpoint>", "Endpoint kind").choices(["server", "client"]).makeOptionMandatory());
14
- }
15
- export function clientOptions(command) {
16
- return clientOverrideOptions(command)
17
- .option("--client", "start the Client Endpoint")
18
- .option("--no-client", "do not start the Client Endpoint");
19
- }
20
- export function clientOverrideOptions(command) {
21
- return command
22
- .option("--client-service", "address this Client incarnation through system.service()")
23
- .option("--no-client-service", "do not address this Client incarnation through system.service()")
24
- .option("--client-title <title>", "initial Window title")
25
- .option("--client-width <value>", "initial Window width")
26
- .option("--client-height <value>", "initial Window height")
27
- .option("--client-x <value>", "initial Window horizontal position")
28
- .option("--client-y <value>", "initial Window vertical position")
29
- .addOption(new Option("--client-layer <layer>", "initial Window layer").choices(["window", "under", "over"]))
30
- .option("--client-location <location>", "initial page beneath the Client location")
31
- .option("--client-minimized", "open the Window minimized");
32
- }
33
- export function serverOverrideOptions(command) {
34
- return command
35
- .option("--server-service", "address this Server incarnation through system.service()")
36
- .option("--no-server-service", "do not address this Server incarnation through system.service()");
37
- }
38
- export function launchOptions(command) {
39
- return serverOverrideOptions(clientOptions(command))
40
- .option("--name <name>", "stable Program-local Process name")
41
- .option("--server", "start the Server Endpoint")
42
- .option("--no-server", "do not start the Server Endpoint")
43
- .option("--option <name=value>", "immutable Process option; repeat for more values", collect, []);
44
- }
@@ -1,86 +0,0 @@
1
- import { Option } from "commander";
2
- import { commandContract } from "../command-contract.js";
3
- import { launchOptions, outputOptions, processOptions } from "./options.js";
4
- import { bounded, connected, integer, launch, output, page, processView, requireProcess, requireProgram, wait } from "./shared.js";
5
- export default function processCommands(root, connect) {
6
- const process = commandContract(root.command("process")
7
- .description("discover and control live Program executions"));
8
- outputOptions(process.command("list")
9
- .description("list live Processes with bounded filtering")
10
- .option("--program <identity>", "restrict results to one Program")
11
- .option("--search <text>", "case-insensitive identity or name search")
12
- .option("--limit <count>", "maximum returned Processes", integer, 30)
13
- .option("--offset <count>", "number of matching Processes to skip", integer, 0))
14
- .action(async (options) => connected(connect, async (system) => {
15
- const processes = options.program
16
- ? await (await requireProgram(system, options.program)).process.list()
17
- : await system.process.list();
18
- const selected = page(processes, options.search, bounded(options.offset, "--offset", 0), bounded(options.limit, "--limit", 1, 100), value => `${value.identity}\n${value.name ?? ""}`);
19
- output({ ...selected, data: await Promise.all(selected.data.map(processView)) }, options.compact);
20
- }));
21
- outputOptions(processOptions(process.command("inspect")
22
- .description("read one live Process and its Endpoint state")))
23
- .action(async (options) => connected(connect, async (system) => {
24
- output(await processView(await requireProcess(system, options.process, options.program)), options.compact);
25
- }));
26
- outputOptions(launchOptions(process.command("create")
27
- .description("create a Process")
28
- .requiredOption("--program <identity>", "owning Program identity")))
29
- .action(async (options) => connected(connect, async (system) => {
30
- const program = await requireProgram(system, options.program);
31
- output(await processView(await program.process.create(launch(options))), options.compact);
32
- }));
33
- outputOptions(launchOptions(process.command("findOrCreate")
34
- .alias("find-or-create")
35
- .description("find the named Process or create it atomically")
36
- .requiredOption("--program <identity>", "owning Program identity")))
37
- .action(async (options) => connected(connect, async (system) => {
38
- const program = await requireProgram(system, options.program);
39
- output(await processView(await program.process.findOrCreate(launch(options, true))), options.compact);
40
- }));
41
- outputOptions(processOptions(process.command("exit")
42
- .description("exit one Process and all of its live Endpoints")))
43
- .action(async (options) => connected(connect, async (system) => {
44
- const process = await requireProcess(system, options.process, options.program);
45
- const snapshot = await processView(process);
46
- await process.exit();
47
- output(snapshot, options.compact);
48
- }));
49
- outputOptions(process.command("wait")
50
- .description("wait for one Process lifecycle event")
51
- .addOption(new Option("--event <event>", "Process lifecycle event")
52
- .choices(["create", "exit"])
53
- .makeOptionMandatory())
54
- .option("--process <identity>", "scope the event to one Process")
55
- .option("--program <identity>", "scope the event to one Program")
56
- .option("--timeout <milliseconds>", "maximum wait in milliseconds", integer))
57
- .action(async (options) => connected(connect, async (system) => {
58
- if (options.process && options.event === "create")
59
- throw new Error("An individual Process does not emit create");
60
- const target = options.process
61
- ? await requireProcess(system, options.process, options.program)
62
- : options.program
63
- ? (await requireProgram(system, options.program)).process
64
- : system.process;
65
- const message = await wait(target, options.event, options.timeout === undefined
66
- ? undefined
67
- : bounded(options.timeout, "--timeout", 1));
68
- output({
69
- scope: options.process ? `process:${options.process}` : options.program ? `program:${options.program}` : "process",
70
- event: options.event,
71
- payload: await eventView(options.event, message, options.process ? target : undefined)
72
- }, options.compact);
73
- }));
74
- }
75
- async function eventView(event, message, scoped) {
76
- if (event === "create")
77
- return processView(message);
78
- const exit = message;
79
- const process = exit.process ?? scoped;
80
- return {
81
- ...(process ? { process: await processView(process) } : {}),
82
- status: exit.status,
83
- code: exit.code,
84
- signal: exit.signal
85
- };
86
- }