@phreshos/cli 0.1.61 → 0.1.63
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 +6 -5
- package/dist/commands/access.js +4 -0
- package/dist/commands/endpoint.js +52 -55
- package/dist/commands/execute.js +30 -0
- package/dist/commands/execution.js +44 -0
- package/dist/commands/input.js +4 -1
- package/dist/commands/operation.js +65 -0
- package/dist/commands/process.js +49 -52
- package/dist/commands/program.js +75 -52
- package/dist/commands/project.js +9 -9
- package/dist/commands/projection.js +3 -2
- package/dist/commands/schemas.js +11 -10
- package/dist/commands/window.js +44 -58
- package/dist/init.js +48 -29
- package/dist/launch.js +8 -2
- package/dist/output/format.js +35 -0
- package/dist/output/write.js +6 -5
- package/dist/template/package.json +6 -6
- package/dist/template/phresh.config.ts +3 -3
- package/dist/template/tests/build.test.ts +2 -2
- package/dist/template/vite.client.ts +1 -2
- package/dist/template.json +2 -2
- package/package.json +4 -6
- package/dist/commands/observation.js +0 -4
- package/dist/output/table.js +0 -88
package/dist/commands/program.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { defineCommand } from "../contract/command.js";
|
|
2
2
|
import { value } from "../contract/schema.js";
|
|
3
|
-
import { option, timeoutOption, withJson } from "./options.js";
|
|
3
|
+
import { launchOptions, option, timeoutOption, withJson } from "./options.js";
|
|
4
4
|
import { dataOutput, eventOutput, eventPresentation, pageOutput, programListPresentation, programOutput, programPresentation } from "./schemas.js";
|
|
5
|
-
import { connected
|
|
6
|
-
import { bounded, integer, page } from "./input.js";
|
|
7
|
-
import {
|
|
8
|
-
import { processView, programView } from "./projection.js";
|
|
5
|
+
import { connected } from "./connection.js";
|
|
6
|
+
import { bounded, integer, json, launch, page } from "./input.js";
|
|
7
|
+
import { executeDescription } from "./execution.js";
|
|
9
8
|
export default function programCommands(root, connect) {
|
|
10
9
|
const programs = defineCommand(root, {
|
|
11
10
|
name: "program",
|
|
@@ -14,29 +13,36 @@ export default function programCommands(root, connect) {
|
|
|
14
13
|
});
|
|
15
14
|
defineCommand(programs, {
|
|
16
15
|
name: "list",
|
|
17
|
-
description: "
|
|
16
|
+
description: executeDescription("program", "list"),
|
|
18
17
|
requiresSystem: true,
|
|
19
18
|
options: withJson(option("--installed-only", "return only installed Programs"), option("--search <text>", "case-insensitive identity, name, or description search"), option("--limit <count>", "maximum returned Programs", { parse: value => integer(value), default: 30 }), option("--offset <count>", "number of matching Programs to skip", { parse: value => integer(value), default: 0 })),
|
|
20
19
|
output: dataOutput(pageOutput(programOutput, "matching Programs"), "A bounded page of Programs", programListPresentation),
|
|
21
20
|
examples: ["phresh program list", "phresh program list --installed-only --json"]
|
|
22
21
|
}, async ({ options }) => connected(connect, async (system) => {
|
|
23
|
-
const programs = await system.
|
|
22
|
+
const programs = await system.execute({
|
|
23
|
+
$domain: "program",
|
|
24
|
+
$operation: "list",
|
|
25
|
+
installedOnly: options.installedOnly === true
|
|
26
|
+
});
|
|
24
27
|
const selected = page(programs, options.search, bounded(options.offset, "--offset", 0), bounded(options.limit, "--limit", 1, 100), current => `${current.identity}\n${current.name}\n${current.description ?? ""}`);
|
|
25
|
-
return
|
|
28
|
+
return selected;
|
|
26
29
|
}));
|
|
27
30
|
defineCommand(programs, {
|
|
28
31
|
name: "inspect",
|
|
29
|
-
description: "
|
|
32
|
+
description: executeDescription("program", "find"),
|
|
30
33
|
requiresSystem: true,
|
|
31
34
|
options: withJson(option("--program <identity>", "Program identity", { mandatory: true })),
|
|
32
35
|
output: dataOutput(programOutput, "The selected Program", programPresentation),
|
|
33
36
|
examples: ["phresh program inspect --program terminal"]
|
|
34
37
|
}, async ({ options }) => connected(connect, async (system) => {
|
|
35
|
-
|
|
38
|
+
const program = await system.execute({ $domain: "program", $operation: "find", identity: options.program });
|
|
39
|
+
if (!program)
|
|
40
|
+
throw new Error(`Unknown Program "${options.program}"`);
|
|
41
|
+
return program;
|
|
36
42
|
}));
|
|
37
43
|
defineCommand(programs, {
|
|
38
44
|
name: "agent",
|
|
39
|
-
description: "
|
|
45
|
+
description: executeDescription("program", "agent"),
|
|
40
46
|
requiresSystem: true,
|
|
41
47
|
options: withJson(option("--program <identity>", "Program identity", { mandatory: true })),
|
|
42
48
|
output: dataOutput(value.object({
|
|
@@ -48,15 +54,60 @@ export default function programCommands(root, connect) {
|
|
|
48
54
|
}),
|
|
49
55
|
examples: ["phresh program agent --program terminal --json"]
|
|
50
56
|
}, async ({ options }) => connected(connect, async (system) => {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
const result = await system.execute({ $domain: "program", $operation: "agent", identity: options.program });
|
|
58
|
+
if (result.content === null)
|
|
59
|
+
throw new Error(`Program "${result.program}" has no agent documentation`);
|
|
60
|
+
return result;
|
|
61
|
+
}));
|
|
62
|
+
defineCommand(programs, {
|
|
63
|
+
name: "getLaunch",
|
|
64
|
+
aliases: ["get-launch"],
|
|
65
|
+
description: executeDescription("program", "getLaunch"),
|
|
66
|
+
requiresSystem: true,
|
|
67
|
+
options: withJson(option("--program <identity>", "Program identity", { mandatory: true })),
|
|
68
|
+
output: dataOutput(value.any("Saved Process launch or null"), "The saved Program launch", { format: "value" }),
|
|
69
|
+
examples: ["phresh program get-launch --program terminal --json"]
|
|
70
|
+
}, ({ options }) => connected(connect, system => system.execute({
|
|
71
|
+
$domain: "program",
|
|
72
|
+
$operation: "getLaunch",
|
|
73
|
+
identity: options.program
|
|
74
|
+
})));
|
|
75
|
+
defineCommand(programs, {
|
|
76
|
+
name: "setLaunch",
|
|
77
|
+
aliases: ["set-launch"],
|
|
78
|
+
description: executeDescription("program", "setLaunch"),
|
|
79
|
+
requiresSystem: true,
|
|
80
|
+
options: withJson(option("--program <identity>", "Program identity", { mandatory: true }), ...launchOptions),
|
|
81
|
+
output: dataOutput(value.any("Saved Process launch"), "The saved Program launch", { format: "value" }),
|
|
82
|
+
examples: ["phresh program set-launch --program terminal --client --name main --json"]
|
|
83
|
+
}, ({ options }) => connected(connect, system => system.execute({
|
|
84
|
+
$domain: "program",
|
|
85
|
+
$operation: "setLaunch",
|
|
86
|
+
identity: options.program,
|
|
87
|
+
launch: launch(options)
|
|
88
|
+
})));
|
|
89
|
+
defineCommand(programs, {
|
|
90
|
+
name: "logs",
|
|
91
|
+
description: executeDescription("program", "logs"),
|
|
92
|
+
requiresSystem: true,
|
|
93
|
+
options: withJson(option("--program <identity>", "Program identity", { mandatory: true }), option("--statement <sql>", "read-only SQL statement", { mandatory: true }), option("--values <json>", "bound statement values encoded as a JSON array")),
|
|
94
|
+
output: dataOutput(value.array(value.any("log query row"), "log query rows"), "Program log query result", { format: "value" }),
|
|
95
|
+
examples: ["phresh program logs --program terminal --statement 'select createdAt, process, source, kind, content from logs order by createdAt desc limit 100' --json"]
|
|
96
|
+
}, ({ options }) => connected(connect, system => {
|
|
97
|
+
const parsed = options.values === undefined ? undefined : json(options.values, "--values");
|
|
98
|
+
if (parsed !== undefined && !Array.isArray(parsed))
|
|
99
|
+
throw new Error("--values must be a JSON array");
|
|
100
|
+
return system.execute({
|
|
101
|
+
$domain: "program",
|
|
102
|
+
$operation: "logs",
|
|
103
|
+
identity: options.program,
|
|
104
|
+
statement: options.statement,
|
|
105
|
+
...(parsed === undefined ? {} : { values: parsed })
|
|
106
|
+
});
|
|
56
107
|
}));
|
|
57
108
|
defineCommand(programs, {
|
|
58
109
|
name: "wait",
|
|
59
|
-
description: "
|
|
110
|
+
description: executeDescription("program", "wait"),
|
|
60
111
|
requiresSystem: true,
|
|
61
112
|
options: withJson(option("--event <event>", "Program lifecycle event", {
|
|
62
113
|
mandatory: true,
|
|
@@ -64,39 +115,11 @@ export default function programCommands(root, connect) {
|
|
|
64
115
|
}), option("--program <identity>", "observe events belonging to one Program"), timeoutOption),
|
|
65
116
|
output: dataOutput(eventOutput("The observed Program event", value.any("Program state or uninstall result")), "One Program event", eventPresentation),
|
|
66
117
|
examples: ["phresh program wait --event create", "phresh program wait --event uninstall --program terminal --json"]
|
|
67
|
-
}, async ({ options }) => connected(connect,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const target = options.program ? await requireProgram(system, options.program) : system.program;
|
|
75
|
-
const message = await wait(target, options.event, timeout(options.timeout));
|
|
76
|
-
return {
|
|
77
|
-
scope: options.program ? `program:${options.program}` : "program",
|
|
78
|
-
event: options.event,
|
|
79
|
-
payload: await eventView(options.event, message, options.program ? target : undefined)
|
|
80
|
-
};
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
async function eventView(event, message, scoped) {
|
|
84
|
-
if (event === "uninstall") {
|
|
85
|
-
if (scoped)
|
|
86
|
-
return { program: await programView(scoped), purge: message.purge };
|
|
87
|
-
const current = message;
|
|
88
|
-
return { program: await programView(current.program), purge: current.purge };
|
|
89
|
-
}
|
|
90
|
-
if (event === "processCreate")
|
|
91
|
-
return processView(message);
|
|
92
|
-
if (event === "processExit") {
|
|
93
|
-
const current = message;
|
|
94
|
-
return { process: await processView(current.process), status: current.status, code: current.code, signal: current.signal };
|
|
95
|
-
}
|
|
96
|
-
if (scoped)
|
|
97
|
-
return programView(scoped);
|
|
98
|
-
return programView(message);
|
|
99
|
-
}
|
|
100
|
-
function timeout(value) {
|
|
101
|
-
return value === undefined ? undefined : bounded(value, "--timeout", 1);
|
|
118
|
+
}, async ({ options }) => connected(connect, system => system.execute({
|
|
119
|
+
$domain: "program",
|
|
120
|
+
$operation: "wait",
|
|
121
|
+
event: options.event,
|
|
122
|
+
...(options.program ? { program: options.program } : {}),
|
|
123
|
+
...(options.timeout === undefined ? {} : { timeout: bounded(options.timeout, "--timeout", 1) })
|
|
124
|
+
})));
|
|
102
125
|
}
|
package/dist/commands/project.js
CHANGED
|
@@ -36,10 +36,10 @@ export default function projectCommands(program, coreRange) {
|
|
|
36
36
|
option("--build-command <command>", "prepare production files before use"),
|
|
37
37
|
option("--server", "include a Server endpoint"),
|
|
38
38
|
option("--server-location <path>", "production Server directory"),
|
|
39
|
-
option("--server-
|
|
40
|
-
option("--server-
|
|
41
|
-
option("--server-
|
|
42
|
-
option("--server-development-
|
|
39
|
+
option("--server-command <command>", "production Server host command"),
|
|
40
|
+
option("--server-worker <path>", "production Server Node Worker entry"),
|
|
41
|
+
option("--server-sandbox <path>", "production Server Sandbox entry"),
|
|
42
|
+
option("--server-development-command <command>", "development Server command"),
|
|
43
43
|
option("--client", "include a Client endpoint"),
|
|
44
44
|
option("--client-location <path>", "production Client directory"),
|
|
45
45
|
option("--client-development-url <url>", "fixed or external development Client URL"),
|
|
@@ -56,12 +56,12 @@ export default function projectCommands(program, coreRange) {
|
|
|
56
56
|
await init({
|
|
57
57
|
name: options.name,
|
|
58
58
|
buildCommand: options.buildCommand,
|
|
59
|
-
server: options.server === true || options.serverLocation !== undefined || options.
|
|
59
|
+
server: options.server === true || options.serverLocation !== undefined || options.serverCommand !== undefined || options.serverWorker !== undefined || options.serverSandbox !== undefined,
|
|
60
60
|
serverLocation: options.serverLocation,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
serverCommand: options.serverCommand,
|
|
62
|
+
serverWorker: options.serverWorker,
|
|
63
|
+
serverSandbox: options.serverSandbox,
|
|
64
|
+
serverDevelopmentCommand: options.serverDevelopmentCommand,
|
|
65
65
|
client: options.client === true
|
|
66
66
|
|| options.clientLocation !== undefined
|
|
67
67
|
|| options.clientDevelopmentUrl !== undefined
|
|
@@ -46,8 +46,9 @@ export async function endpointView(process, name) {
|
|
|
46
46
|
}
|
|
47
47
|
export async function windowView(process) {
|
|
48
48
|
const window = process.client.window;
|
|
49
|
-
const [title, position, size, minimized, maximized, front, layer] = await Promise.all([
|
|
49
|
+
const [title, header, position, size, minimized, maximized, front, layer] = await Promise.all([
|
|
50
50
|
window.title(),
|
|
51
|
+
window.header(),
|
|
51
52
|
window.position(),
|
|
52
53
|
window.size(),
|
|
53
54
|
window.minimized(),
|
|
@@ -55,5 +56,5 @@ export async function windowView(process) {
|
|
|
55
56
|
window.front(),
|
|
56
57
|
window.layer()
|
|
57
58
|
]);
|
|
58
|
-
return { process: process.identity, title, position, size, minimized, maximized, front, layer };
|
|
59
|
+
return { process: process.identity, title, header, position, size, minimized, maximized, front, layer };
|
|
59
60
|
}
|
package/dist/commands/schemas.js
CHANGED
|
@@ -55,31 +55,32 @@ export const windowOutput = value.object({
|
|
|
55
55
|
layer: value.enumeration(layers, "Window layer")
|
|
56
56
|
}, ["process", "title", "position", "size", "minimized", "maximized", "front", "layer"], "Window state");
|
|
57
57
|
export const programPresentation = fields(["Identity", "identity"], ["Asset", "assetId"], ["Name", "name"], ["Version", "version"], ["Description", "description"], ["Installed", "installed"], ["Agent", "hasAgent"], ["Server", "server"], ["Client", "client"]);
|
|
58
|
-
export const programListPresentation =
|
|
59
|
-
{ label: "Name", path: "name"
|
|
60
|
-
{ label: "Identity", path: "identity"
|
|
58
|
+
export const programListPresentation = list("data", "Program", "Programs", "No matching Programs", [
|
|
59
|
+
{ label: "Name", path: "name" },
|
|
60
|
+
{ label: "Identity", path: "identity" },
|
|
61
61
|
{ label: "Version", path: "version" },
|
|
62
62
|
{ label: "Installed", path: "installed" }
|
|
63
63
|
]);
|
|
64
64
|
export const processPresentation = fields(["Identity", "identity"], ["Name", "name"], ["Program", "program"], ["Started", "startedAt"], ["Server declared", "server.declared"], ["Server running", "server.running"], ["Server service", "server.service"], ["Client declared", "client.declared"], ["Client running", "client.running"], ["Client service", "client.service"]);
|
|
65
65
|
export const processActionPresentation = fields(["Process", "identity"], ["Name", "name"], ["Program", "program"], ["Server", "server.running"], ["Client", "client.running"]);
|
|
66
66
|
export const processIdentityPresentation = fields(["Process", "identity"], ["Program", "program"]);
|
|
67
|
-
export const processListPresentation =
|
|
68
|
-
{ label: "Name", path: "name"
|
|
69
|
-
{ label: "Identity", path: "identity"
|
|
70
|
-
{ label: "Program", path: "program"
|
|
67
|
+
export const processListPresentation = list("data", "Process", "Processes", "No matching Processes", [
|
|
68
|
+
{ label: "Name", path: "name" },
|
|
69
|
+
{ label: "Identity", path: "identity" },
|
|
70
|
+
{ label: "Program", path: "program" },
|
|
71
71
|
{ label: "Server", path: "server.running" },
|
|
72
72
|
{ label: "Client", path: "client.running" }
|
|
73
73
|
]);
|
|
74
74
|
export const endpointPresentation = fields(["Process", "process"], ["Program", "program"], ["Endpoint", "endpoint"], ["Declared", "declared"], ["Running", "running"], ["Service", "service"]);
|
|
75
75
|
export const endpointActionPresentation = fields(["Process", "process"], ["Endpoint", "endpoint"], ["Running", "running"], ["Service", "service"]);
|
|
76
|
-
export const windowPresentation = fields(["Process", "process"], ["Title", "title"], ["Position", "position"], ["Size", "size"], ["Minimized", "minimized"], ["Maximized", "maximized"], ["Front", "front"], ["Layer", "layer"], ["Location", "location"]);
|
|
76
|
+
export const windowPresentation = fields(["Process", "process"], ["Title", "title"], ["Header", "header"], ["Position", "position"], ["Size", "size"], ["Minimized", "minimized"], ["Maximized", "maximized"], ["Front", "front"], ["Layer", "layer"], ["Location", "location"]);
|
|
77
77
|
export const windowPositionPresentation = fields(["Process", "process"], ["Position", "position"]);
|
|
78
78
|
export const windowSizePresentation = fields(["Process", "process"], ["Size", "size"]);
|
|
79
79
|
export const windowGeometryPresentation = fields(["Process", "process"], ["Position", "position"], ["Size", "size"]);
|
|
80
80
|
export const windowMinimizePresentation = fields(["Process", "process"], ["Minimized", "minimized"]);
|
|
81
81
|
export const windowMaximizePresentation = fields(["Process", "process"], ["Maximized", "maximized"]);
|
|
82
82
|
export const windowTitlePresentation = fields(["Process", "process"], ["Title", "title"]);
|
|
83
|
+
export const windowHeaderPresentation = fields(["Process", "process"], ["Header", "header"]);
|
|
83
84
|
export const windowRaisePresentation = fields(["Process", "process"], ["Front", "front"]);
|
|
84
85
|
export const eventPresentation = fields(["Scope", "scope"], ["Event", "event"], ["Payload", "payload"]);
|
|
85
86
|
export const lifecyclePresentation = fields(["Scope", "scope"], ["Event", "event"]);
|
|
@@ -118,6 +119,6 @@ function fields(...values) {
|
|
|
118
119
|
fields: values.map(([label, path]) => ({ label, path }))
|
|
119
120
|
};
|
|
120
121
|
}
|
|
121
|
-
function
|
|
122
|
-
return { format: "
|
|
122
|
+
function list(rows, item, items, empty, fields) {
|
|
123
|
+
return { format: "list", rows, fields, item, items, empty, total: "total", truncated: "truncated" };
|
|
123
124
|
}
|
package/dist/commands/window.js
CHANGED
|
@@ -1,110 +1,96 @@
|
|
|
1
|
+
import { parseExecuteRequest } from "@phreshos/core";
|
|
1
2
|
import { defineCommand } from "../contract/command.js";
|
|
2
3
|
import { option, processOptions, timeoutOption, withJson } from "./options.js";
|
|
3
|
-
import { dataOutput, eventOutput, eventPresentation, windowGeometryPresentation, windowMinimizePresentation, windowMaximizePresentation, windowOutput, windowPositionPresentation, windowPresentation, windowRaisePresentation, windowSizePresentation, windowTitlePresentation } from "./schemas.js";
|
|
4
|
-
import { connected
|
|
4
|
+
import { dataOutput, eventOutput, eventPresentation, windowGeometryPresentation, windowHeaderPresentation, windowMinimizePresentation, windowMaximizePresentation, windowOutput, windowPositionPresentation, windowPresentation, windowRaisePresentation, windowSizePresentation, windowTitlePresentation } from "./schemas.js";
|
|
5
|
+
import { connected } from "./connection.js";
|
|
5
6
|
import { bounded, position, size } from "./input.js";
|
|
6
|
-
import {
|
|
7
|
-
import { windowOf, windowView } from "./projection.js";
|
|
7
|
+
import { executeDescription } from "./execution.js";
|
|
8
8
|
export default function windowCommands(root, connect) {
|
|
9
9
|
const windows = defineCommand(root, {
|
|
10
10
|
name: "window",
|
|
11
11
|
description: "inspect and control authoritative Client Windows",
|
|
12
12
|
guidance: ["A Window belongs to the Client Endpoint of one exact Process."]
|
|
13
13
|
});
|
|
14
|
-
defineCommand(windows, state("inspect",
|
|
15
|
-
return
|
|
14
|
+
defineCommand(windows, state("inspect", windowPresentation), async ({ options }) => {
|
|
15
|
+
return executeWindow(connect, options, { $operation: "inspect" });
|
|
16
16
|
});
|
|
17
17
|
defineCommand(windows, {
|
|
18
|
-
...state("move",
|
|
18
|
+
...state("move", windowPositionPresentation),
|
|
19
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
20
|
examples: ["phresh window move --process main --program terminal --x 50% --y 0"]
|
|
21
|
-
}, async ({ options }) =>
|
|
22
|
-
await windowOf(process).move(position(options.x, options.y));
|
|
23
|
-
return await windowView(process);
|
|
24
|
-
}));
|
|
21
|
+
}, async ({ options }) => executeWindow(connect, options, { $operation: "move", position: position(options.x, options.y) }));
|
|
25
22
|
defineCommand(windows, {
|
|
26
|
-
...state("resize",
|
|
23
|
+
...state("resize", windowSizePresentation),
|
|
27
24
|
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
25
|
examples: ["phresh window resize --process main --program terminal --width 800 --height 600"]
|
|
29
|
-
}, async ({ options }) =>
|
|
30
|
-
await windowOf(process).resize(size(options.width, options.height));
|
|
31
|
-
return await windowView(process);
|
|
32
|
-
}));
|
|
26
|
+
}, async ({ options }) => executeWindow(connect, options, { $operation: "resize", size: size(options.width, options.height) }));
|
|
33
27
|
defineCommand(windows, {
|
|
34
|
-
...state("setGeometry",
|
|
28
|
+
...state("setGeometry", windowGeometryPresentation),
|
|
35
29
|
aliases: ["set-geometry"],
|
|
36
30
|
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
31
|
examples: ["phresh window set-geometry --process main --program terminal --x 0 --y 0 --width 100% --height 100%"]
|
|
38
|
-
}, async ({ options }) =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
return await windowView(process);
|
|
32
|
+
}, async ({ options }) => executeWindow(connect, options, {
|
|
33
|
+
$operation: "setGeometry",
|
|
34
|
+
position: position(options.x, options.y),
|
|
35
|
+
size: size(options.width, options.height)
|
|
44
36
|
}));
|
|
45
37
|
defineCommand(windows, {
|
|
46
|
-
...state("minimize",
|
|
38
|
+
...state("minimize", windowMinimizePresentation),
|
|
47
39
|
options: withJson(...processOptions, option("--restore", "restore rather than minimize the Window")),
|
|
48
40
|
examples: ["phresh window minimize --process main --program terminal", "phresh window minimize --process main --program terminal --restore"]
|
|
49
|
-
}, async ({ options }) =>
|
|
50
|
-
await windowOf(process).minimize(options.restore !== true);
|
|
51
|
-
return await windowView(process);
|
|
52
|
-
}));
|
|
41
|
+
}, async ({ options }) => executeWindow(connect, options, { $operation: "minimize", minimized: options.restore !== true }));
|
|
53
42
|
defineCommand(windows, {
|
|
54
|
-
...state("maximize",
|
|
43
|
+
...state("maximize", windowMaximizePresentation),
|
|
55
44
|
options: withJson(...processOptions, option("--restore", "restore rather than maximize the Window")),
|
|
56
45
|
examples: ["phresh window maximize --process main --program terminal", "phresh window maximize --process main --program terminal --restore"]
|
|
57
|
-
}, async ({ options }) =>
|
|
58
|
-
await windowOf(process).maximize(options.restore !== true);
|
|
59
|
-
return await windowView(process);
|
|
60
|
-
}));
|
|
46
|
+
}, async ({ options }) => executeWindow(connect, options, { $operation: "maximize", maximized: options.restore !== true }));
|
|
61
47
|
defineCommand(windows, {
|
|
62
|
-
...state("changeTitle",
|
|
48
|
+
...state("changeTitle", windowTitlePresentation),
|
|
63
49
|
aliases: ["change-title"],
|
|
64
50
|
options: withJson(...processOptions, option("--title <title>", "new Window title", { mandatory: true })),
|
|
65
51
|
examples: ["phresh window change-title --process main --program terminal --title Shell"]
|
|
66
|
-
}, async ({ options }) =>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
52
|
+
}, async ({ options }) => executeWindow(connect, options, { $operation: "changeTitle", title: options.title }));
|
|
53
|
+
defineCommand(windows, {
|
|
54
|
+
...state("changeHeader", windowHeaderPresentation),
|
|
55
|
+
aliases: ["change-header"],
|
|
56
|
+
options: withJson(...processOptions, option("--hide", "hide rather than show the Window header")),
|
|
57
|
+
examples: ["phresh window change-header --process main --program terminal --hide", "phresh window change-header --process main --program terminal"]
|
|
58
|
+
}, async ({ options }) => executeWindow(connect, options, { $operation: "changeHeader", header: options.hide !== true }));
|
|
70
59
|
defineCommand(windows, {
|
|
71
|
-
...state("raise",
|
|
60
|
+
...state("raise", windowRaisePresentation),
|
|
72
61
|
examples: ["phresh window raise --process main --program terminal"]
|
|
73
|
-
}, async ({ options }) =>
|
|
74
|
-
await windowOf(process).raise();
|
|
75
|
-
return await windowView(process);
|
|
76
|
-
}));
|
|
62
|
+
}, async ({ options }) => executeWindow(connect, options, { $operation: "raise" }));
|
|
77
63
|
defineCommand(windows, {
|
|
78
64
|
name: "wait",
|
|
79
|
-
description: "
|
|
65
|
+
description: executeDescription("window", "wait"),
|
|
80
66
|
requiresSystem: true,
|
|
81
67
|
options: withJson(...processOptions, option("--event <event>", "Window event", {
|
|
82
68
|
mandatory: true,
|
|
83
|
-
choices: ["move", "resize", "geometry", "minimize", "maximize", "changeTitle", "front"]
|
|
69
|
+
choices: ["move", "resize", "geometry", "minimize", "maximize", "changeTitle", "changeHeader", "front"]
|
|
84
70
|
}), timeoutOption),
|
|
85
71
|
output: dataOutput(eventOutput("The observed Window event"), "One Window event", eventPresentation),
|
|
86
72
|
examples: ["phresh window wait --process main --program terminal --event geometry --json"]
|
|
87
|
-
}, async ({ options }) =>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
payload: await wait(windowOf(process), options.event, options.timeout === undefined
|
|
92
|
-
? undefined
|
|
93
|
-
: bounded(options.timeout, "--timeout", 1))
|
|
94
|
-
};
|
|
73
|
+
}, async ({ options }) => executeWindow(connect, options, {
|
|
74
|
+
$operation: "wait",
|
|
75
|
+
event: options.event,
|
|
76
|
+
...(options.timeout === undefined ? {} : { timeout: bounded(options.timeout, "--timeout", 1) })
|
|
95
77
|
}));
|
|
96
78
|
}
|
|
97
|
-
function state(name,
|
|
79
|
+
function state(name, presentation) {
|
|
98
80
|
return {
|
|
99
81
|
name,
|
|
100
|
-
description,
|
|
82
|
+
description: executeDescription("window", name),
|
|
101
83
|
requiresSystem: true,
|
|
102
84
|
options: withJson(...processOptions),
|
|
103
85
|
output: dataOutput(windowOutput, "The current Window state", presentation)
|
|
104
86
|
};
|
|
105
87
|
}
|
|
106
|
-
async function
|
|
107
|
-
|
|
108
|
-
|
|
88
|
+
async function executeWindow(connect, options, operation) {
|
|
89
|
+
const request = parseExecuteRequest({
|
|
90
|
+
$domain: "window",
|
|
91
|
+
...operation,
|
|
92
|
+
process: options.process,
|
|
93
|
+
...(options.program ? { program: options.program } : {})
|
|
109
94
|
});
|
|
95
|
+
return connected(connect, system => system.execute(request));
|
|
110
96
|
}
|
package/dist/init.js
CHANGED
|
@@ -61,14 +61,16 @@ export default async function init(options = {}, directory = process.cwd(), core
|
|
|
61
61
|
const location = options.serverLocation ?? (interactive ? await ask("The system runs the production Server from this project-relative directory.", "Where are the production Server files?", clientSelected ? "dist/server" : "dist") : "");
|
|
62
62
|
if (!location)
|
|
63
63
|
throw new Error("--server-location is required without a terminal");
|
|
64
|
-
const production = options.
|
|
64
|
+
const production = options.serverCommand === undefined && options.serverWorker === undefined && options.serverSandbox === undefined && interactive
|
|
65
65
|
? await askExecution(interaction, "production", "node main.js")
|
|
66
|
-
: execution(options.
|
|
67
|
-
let development =
|
|
66
|
+
: execution(options.serverCommand, options.serverWorker, options.serverSandbox, "production Server");
|
|
67
|
+
let development = developmentExecution(options.serverDevelopmentCommand, "development Server");
|
|
68
68
|
if (interactive && development === undefined) {
|
|
69
69
|
const suggested = manifest.scripts?.dev && projectScript(directory, manifest.packageManager, "dev");
|
|
70
70
|
if (await yes("Development mode can run the Server directly from the project source.", "Run the Server from source during development?", Boolean(suggested && !clientSelected))) {
|
|
71
|
-
development =
|
|
71
|
+
development = {
|
|
72
|
+
command: await ask("This command runs from the project directory with access to development tooling.", "What command starts the development Server?", suggested || undefined)
|
|
73
|
+
};
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
server = { location, ...production, ...development && { development } };
|
|
@@ -119,7 +121,7 @@ export default async function init(options = {}, directory = process.cwd(), core
|
|
|
119
121
|
await ensureProjectDependency("@phreshos/core", coreRange, directory);
|
|
120
122
|
writeFileSync(path, compose(config));
|
|
121
123
|
if (config.server)
|
|
122
|
-
interaction.detail(
|
|
124
|
+
interaction.detail(`server ${serverExecution(config.server)[0]}`, serverExecution(config.server)[1], `./${config.server.location}`);
|
|
123
125
|
if (config.client)
|
|
124
126
|
interaction.detail("client", `./${config.client.location}`);
|
|
125
127
|
const next = [config.server?.development || config.client?.development ? "phresh dev" : null, "phresh start", "phresh install"].filter(Boolean);
|
|
@@ -140,9 +142,7 @@ function compose(config) {
|
|
|
140
142
|
field("buildCommand", config.buildCommand),
|
|
141
143
|
half("server", config.server && {
|
|
142
144
|
location: config.server.location,
|
|
143
|
-
|
|
144
|
-
? { startCommand: config.server.startCommand }
|
|
145
|
-
: { entryFile: config.server.entryFile }),
|
|
145
|
+
[serverExecution(config.server)[0]]: serverExecution(config.server)[1],
|
|
146
146
|
...config.server.development && { development: config.server.development }
|
|
147
147
|
}),
|
|
148
148
|
half("client", config.client && {
|
|
@@ -182,35 +182,54 @@ function httpUrl(value) {
|
|
|
182
182
|
return false;
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
|
-
function execution(
|
|
186
|
-
const selected =
|
|
187
|
-
if (
|
|
188
|
-
throw new Error(`Choose exactly one --server-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
function explicitExecution(startCommand, entryFile, owner) {
|
|
192
|
-
if (startCommand !== undefined && entryFile !== undefined)
|
|
193
|
-
throw new Error(`The ${owner} cannot declare both a start command and an entry file`);
|
|
194
|
-
if (startCommand !== undefined) {
|
|
195
|
-
if (startCommand.trim().length === 0)
|
|
185
|
+
function execution(command, worker, sandbox, owner) {
|
|
186
|
+
const selected = [command, worker, sandbox].filter(value => value !== undefined);
|
|
187
|
+
if (selected.length !== 1)
|
|
188
|
+
throw new Error(`Choose exactly one --server-command, --server-worker, or --server-sandbox for the ${owner}`);
|
|
189
|
+
if (command !== undefined) {
|
|
190
|
+
if (!command.trim())
|
|
196
191
|
throw new Error(`The ${owner} command must not be empty`);
|
|
197
|
-
return {
|
|
192
|
+
return { command };
|
|
198
193
|
}
|
|
199
|
-
if (
|
|
200
|
-
if (
|
|
201
|
-
throw new Error(`The ${owner}
|
|
202
|
-
if (!containedServerEntry(
|
|
203
|
-
throw new Error(`The ${owner} entry
|
|
204
|
-
return {
|
|
194
|
+
if (worker !== undefined) {
|
|
195
|
+
if (!worker.trim())
|
|
196
|
+
throw new Error(`The ${owner} worker must not be empty`);
|
|
197
|
+
if (!containedServerEntry(worker))
|
|
198
|
+
throw new Error(`The ${owner} worker entry must remain inside its Server directory`);
|
|
199
|
+
return { worker };
|
|
205
200
|
}
|
|
201
|
+
if (!sandbox?.trim())
|
|
202
|
+
throw new Error(`The ${owner} sandbox must not be empty`);
|
|
203
|
+
if (!containedServerEntry(sandbox))
|
|
204
|
+
throw new Error(`The ${owner} sandbox entry must remain inside its Server directory`);
|
|
205
|
+
return { sandbox };
|
|
206
|
+
}
|
|
207
|
+
function developmentExecution(command, owner) {
|
|
208
|
+
if (command === undefined)
|
|
209
|
+
return undefined;
|
|
210
|
+
if (!command.trim())
|
|
211
|
+
throw new Error(`The ${owner} command must not be empty`);
|
|
212
|
+
return { command };
|
|
206
213
|
}
|
|
207
214
|
async function askExecution(interaction, mode, suggestedCommand) {
|
|
208
|
-
const
|
|
215
|
+
const sandbox = await interaction.yes("A Sandbox provides only JavaScript and the permission-constrained System API.", `Run the ${mode} Server in a Sandbox?`, false);
|
|
216
|
+
if (sandbox)
|
|
217
|
+
return {
|
|
218
|
+
sandbox: await interaction.ask("This bundled JavaScript module remains inside the Server files.", `What is the ${mode} Server Sandbox entry?`, "main.js")
|
|
219
|
+
};
|
|
220
|
+
const worker = await interaction.yes("A Worker uses Node.js host capabilities in an isolated thread.", `Run the ${mode} Server as a Worker?`, false);
|
|
209
221
|
if (worker)
|
|
210
222
|
return {
|
|
211
|
-
|
|
223
|
+
worker: await interaction.ask("This JavaScript module remains inside the Server files.", `What is the ${mode} Server Worker entry?`, "main.js")
|
|
212
224
|
};
|
|
213
225
|
return {
|
|
214
|
-
|
|
226
|
+
command: await interaction.ask("This command runs from the production Server directory.", `What command starts the ${mode} Server?`, suggestedCommand)
|
|
215
227
|
};
|
|
216
228
|
}
|
|
229
|
+
function serverExecution(server) {
|
|
230
|
+
if (server.command !== undefined)
|
|
231
|
+
return ["command", server.command];
|
|
232
|
+
if (server.worker !== undefined)
|
|
233
|
+
return ["worker", server.worker];
|
|
234
|
+
return ["sandbox", server.sandbox];
|
|
235
|
+
}
|
package/dist/launch.js
CHANGED
|
@@ -8,8 +8,14 @@ export default async function launch(mode, directory = process.cwd(), options =
|
|
|
8
8
|
heading(`${definition.name ?? definition.identity}${definition.version ? ` ${definition.version}` : ""}`, mode);
|
|
9
9
|
if (mode === "production" && project.config.buildCommand)
|
|
10
10
|
line("build", project.config.buildCommand);
|
|
11
|
-
if (definition.server)
|
|
12
|
-
|
|
11
|
+
if (definition.server) {
|
|
12
|
+
const [execution, value] = definition.server.command !== undefined
|
|
13
|
+
? ["command", definition.server.command]
|
|
14
|
+
: definition.server.worker !== undefined
|
|
15
|
+
? ["worker", definition.server.worker]
|
|
16
|
+
: ["sandbox", definition.server.sandbox];
|
|
17
|
+
line(`server ${execution}`, value, place(project.directory, definition.server.location));
|
|
18
|
+
}
|
|
13
19
|
if (definition.client)
|
|
14
20
|
line("client", project.config.client?.development?.startCommand ?? place(project.directory, definition.client.location));
|
|
15
21
|
line("storage", place(project.directory, String(definition.storage)));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Render records as readable field blocks without terminal-table layout work. */
|
|
2
|
+
export function renderList(fields, rows) {
|
|
3
|
+
return rows.map(row => renderFields(fields, row)).join("\n\n");
|
|
4
|
+
}
|
|
5
|
+
/** Render one value as aligned labels with indented multiline values. */
|
|
6
|
+
export function renderFields(fields, source) {
|
|
7
|
+
const width = Math.max(...fields.map(field => field.label.length), 0);
|
|
8
|
+
return fields.map(field => {
|
|
9
|
+
const value = display(readPath(source, field.path), true);
|
|
10
|
+
const lines = value.split("\n");
|
|
11
|
+
const prefix = `${field.label.padEnd(width)} `;
|
|
12
|
+
const continuation = " ".repeat(prefix.length);
|
|
13
|
+
return [prefix + lines[0], ...lines.slice(1).map(line => continuation + line)].join("\n");
|
|
14
|
+
}).join("\n");
|
|
15
|
+
}
|
|
16
|
+
export function readPath(source, path) {
|
|
17
|
+
if (!path)
|
|
18
|
+
return source;
|
|
19
|
+
return path.split(".").reduce((current, key) => {
|
|
20
|
+
if (typeof current !== "object" || current === null)
|
|
21
|
+
return undefined;
|
|
22
|
+
return current[key];
|
|
23
|
+
}, source);
|
|
24
|
+
}
|
|
25
|
+
export function display(value, expanded = false) {
|
|
26
|
+
if (value === null || value === undefined || value === "")
|
|
27
|
+
return "—";
|
|
28
|
+
if (typeof value === "boolean")
|
|
29
|
+
return value ? "yes" : "no";
|
|
30
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "bigint")
|
|
31
|
+
return String(value);
|
|
32
|
+
if (Array.isArray(value) && !expanded)
|
|
33
|
+
return value.length ? value.map(item => display(item)).join(", ") : "—";
|
|
34
|
+
return JSON.stringify(value, null, expanded ? 2 : undefined);
|
|
35
|
+
}
|