@phreshos/cli 0.1.6 → 0.1.7
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 +4 -3
- package/dist/cli.js +13 -2
- package/dist/create.js +1 -0
- package/dist/prompts.js +18 -8
- package/dist/style.js +10 -0
- package/dist/system/command.js +45 -19
- package/dist/system/lifecycle.js +3 -1
- package/dist/system/service/linux.js +8 -3
- package/dist/template/package.json +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ System.
|
|
|
41
41
|
phresh system install
|
|
42
42
|
phresh system uninstall
|
|
43
43
|
phresh system status
|
|
44
|
+
phresh system version
|
|
44
45
|
phresh system start
|
|
45
46
|
phresh system stop
|
|
46
47
|
phresh system enable
|
|
@@ -59,9 +60,9 @@ It never reads a source checkout and never requires Bun or TypeScript.
|
|
|
59
60
|
The System runs under `launchd` on macOS and `systemd --user` on Linux. The
|
|
60
61
|
native manager owns it after the CLI exits and restarts a failed active
|
|
61
62
|
service. `start` and `stop` change current execution only; `enable` and
|
|
62
|
-
`disable` change automatic startup only. `status`
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
`disable` change automatic startup only. `status` reports the installed version,
|
|
64
|
+
service readiness, and automatic startup without changing them; `version`
|
|
65
|
+
reports only the installed System release.
|
|
65
66
|
|
|
66
67
|
Installation files and persistent System state have separate homes. Removing
|
|
67
68
|
the System unregisters its service and removes its release files while keeping
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command, Option } from "commander";
|
|
3
3
|
import metadata from "../package.json" with { type: "json" };
|
|
4
|
-
import { PromptCancelled } from "./prompts.js";
|
|
4
|
+
import { PromptCancelled, ReportedFailure } from "./prompts.js";
|
|
5
5
|
import create from "./create.js";
|
|
6
6
|
import install from "./install.js";
|
|
7
7
|
import launch from "./launch.js";
|
|
@@ -17,7 +17,11 @@ const program = new Command()
|
|
|
17
17
|
.description("Create Programs and manage PhreshOS")
|
|
18
18
|
.version(version, "-v, --version")
|
|
19
19
|
.showHelpAfterError()
|
|
20
|
-
.showSuggestionAfterError()
|
|
20
|
+
.showSuggestionAfterError()
|
|
21
|
+
.configureOutput({
|
|
22
|
+
writeOut: value => process.stdout.write(spaced(value)),
|
|
23
|
+
writeErr: value => process.stderr.write(spaced(value))
|
|
24
|
+
});
|
|
21
25
|
program.addHelpText("after", "\nRun phresh <command> --help for detailed command guidance.\n");
|
|
22
26
|
describe(program.command("create")
|
|
23
27
|
.description("create a new Program project")
|
|
@@ -119,11 +123,18 @@ try {
|
|
|
119
123
|
catch (error) {
|
|
120
124
|
if (error instanceof PromptCancelled)
|
|
121
125
|
process.exitCode = 0;
|
|
126
|
+
else if (error instanceof ReportedFailure)
|
|
127
|
+
process.exitCode = 1;
|
|
122
128
|
else {
|
|
123
129
|
console.error(`\n phresh: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
124
130
|
process.exitCode = 1;
|
|
125
131
|
}
|
|
126
132
|
}
|
|
133
|
+
function spaced(value) {
|
|
134
|
+
if (value.endsWith("\n\n"))
|
|
135
|
+
return value;
|
|
136
|
+
return value.endsWith("\n") ? `${value}\n` : `${value}\n\n`;
|
|
137
|
+
}
|
|
127
138
|
function attached(name, summary, detail, mode) {
|
|
128
139
|
describe(program.command(name)
|
|
129
140
|
.description(summary)
|
package/dist/create.js
CHANGED
|
@@ -60,6 +60,7 @@ export default async function create(options = {}, directory = process.cwd()) {
|
|
|
60
60
|
console.log(bold(`\n${bundled.development ? "Run Development Program" : "Run Program"}`));
|
|
61
61
|
console.log(accent(script));
|
|
62
62
|
console.log(bold("\nYou can now open the project and start building your Program"));
|
|
63
|
+
console.log("");
|
|
63
64
|
}
|
|
64
65
|
function template() {
|
|
65
66
|
const candidates = [
|
package/dist/prompts.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
|
|
2
2
|
import colors from "picocolors";
|
|
3
|
-
import { column,
|
|
3
|
+
import { caution, column, ending, line, section } from "./style.js";
|
|
4
4
|
/** Signals an ordinary interactive cancellation rather than an operation failure. */
|
|
5
5
|
export class PromptCancelled extends Error {
|
|
6
6
|
}
|
|
7
|
+
/** An unsuccessful command whose complete explanation has already been shown. */
|
|
8
|
+
export class ReportedFailure extends Error {
|
|
9
|
+
}
|
|
7
10
|
/**
|
|
8
11
|
* One interaction language shared by commands that can ask questions.
|
|
9
12
|
*
|
|
@@ -16,26 +19,32 @@ export default function prompts() {
|
|
|
16
19
|
if (interactive)
|
|
17
20
|
intro(`${colors.bold(title)}${context ? ` ${colors.dim(`· ${context}`)}` : ""}`);
|
|
18
21
|
else
|
|
19
|
-
|
|
22
|
+
section(title, context);
|
|
20
23
|
}
|
|
21
24
|
function finish(message) {
|
|
22
25
|
if (interactive)
|
|
23
26
|
outro(colors.bold(message));
|
|
24
27
|
else
|
|
25
|
-
|
|
28
|
+
ending(message);
|
|
26
29
|
}
|
|
27
30
|
function detail(label, value, source) {
|
|
28
31
|
if (interactive)
|
|
29
|
-
log.message(`${colors.dim(column(label))}${value}${source ? ` ${colors.dim(source)}` : ""}
|
|
32
|
+
log.message(`${colors.dim(column(label))}${value}${source ? ` ${colors.dim(source)}` : ""}`, { spacing: 0 });
|
|
30
33
|
else
|
|
31
34
|
line(label, value, source);
|
|
32
35
|
}
|
|
33
36
|
function message(value = "") {
|
|
34
37
|
if (interactive)
|
|
35
|
-
log.message(value);
|
|
38
|
+
log.message(value, { spacing: 0 });
|
|
36
39
|
else
|
|
37
40
|
console.log(value ? ` ${value}` : "");
|
|
38
41
|
}
|
|
42
|
+
function warning(value) {
|
|
43
|
+
if (interactive)
|
|
44
|
+
log.warning(value, { spacing: 0 });
|
|
45
|
+
else
|
|
46
|
+
line("warning", caution(value));
|
|
47
|
+
}
|
|
39
48
|
async function progress(message, completed, work) {
|
|
40
49
|
if (!interactive)
|
|
41
50
|
return await work();
|
|
@@ -55,7 +64,7 @@ export default function prompts() {
|
|
|
55
64
|
async function ask(explanation, question, fallback) {
|
|
56
65
|
if (!interactive)
|
|
57
66
|
throw new Error(`${question} Supply the corresponding option when no terminal is attached`);
|
|
58
|
-
log.message(colors.dim(explanation));
|
|
67
|
+
log.message(colors.dim(explanation), { spacing: 0 });
|
|
59
68
|
const value = await text({
|
|
60
69
|
message: question,
|
|
61
70
|
placeholder: fallback,
|
|
@@ -68,7 +77,7 @@ export default function prompts() {
|
|
|
68
77
|
async function yes(explanation, question, fallback) {
|
|
69
78
|
if (!interactive)
|
|
70
79
|
throw new Error(`${question} Supply the corresponding option when no terminal is attached`);
|
|
71
|
-
log.message(colors.dim(explanation));
|
|
80
|
+
log.message(colors.dim(explanation), { spacing: 0 });
|
|
72
81
|
const value = await confirm({ message: question, initialValue: fallback });
|
|
73
82
|
if (isCancel(value))
|
|
74
83
|
stop();
|
|
@@ -77,7 +86,7 @@ export default function prompts() {
|
|
|
77
86
|
async function choose(explanation, question, values, fallback) {
|
|
78
87
|
if (!interactive)
|
|
79
88
|
throw new Error(`${question} Supply the corresponding option when no terminal is attached`);
|
|
80
|
-
log.message(colors.dim(explanation));
|
|
89
|
+
log.message(colors.dim(explanation), { spacing: 0 });
|
|
81
90
|
const value = await select({
|
|
82
91
|
message: question,
|
|
83
92
|
options: values.map(value => ({ value, label: value })),
|
|
@@ -97,6 +106,7 @@ export default function prompts() {
|
|
|
97
106
|
finish,
|
|
98
107
|
detail,
|
|
99
108
|
message,
|
|
109
|
+
warning,
|
|
100
110
|
progress,
|
|
101
111
|
ask,
|
|
102
112
|
yes,
|
package/dist/style.js
CHANGED
|
@@ -3,6 +3,9 @@ import colors from "picocolors";
|
|
|
3
3
|
export const dim = colors.dim;
|
|
4
4
|
export const bold = colors.bold;
|
|
5
5
|
export const accent = colors.cyan;
|
|
6
|
+
export const positive = colors.green;
|
|
7
|
+
export const caution = colors.yellow;
|
|
8
|
+
export const negative = colors.red;
|
|
6
9
|
// A label, what it says, and where that came from. The label is quiet
|
|
7
10
|
// and the value is not, because the value is the thing being reported.
|
|
8
11
|
//
|
|
@@ -17,6 +20,13 @@ export function column(label) {
|
|
|
17
20
|
}
|
|
18
21
|
export function heading(title, note) {
|
|
19
22
|
console.log("");
|
|
23
|
+
section(title, note);
|
|
24
|
+
console.log("");
|
|
25
|
+
}
|
|
26
|
+
export function section(title, note) {
|
|
20
27
|
console.log(` ${bold(title)}${note ? ` ${dim("·")} ${dim(note)}` : ""}`);
|
|
28
|
+
}
|
|
29
|
+
export function ending(message) {
|
|
30
|
+
console.log(` ${bold(message)}`);
|
|
21
31
|
console.log("");
|
|
22
32
|
}
|
package/dist/system/command.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import SystemLifecycle from "./lifecycle.js";
|
|
2
|
-
import prompts from "../prompts.js";
|
|
2
|
+
import prompts, { ReportedFailure } from "../prompts.js";
|
|
3
|
+
import { accent, caution, dim, negative, positive } from "../style.js";
|
|
3
4
|
/** Attach the System lifecycle without mixing it with Program commands. */
|
|
4
5
|
export default function systemCommands(program, provided) {
|
|
5
6
|
let lifecycle = provided;
|
|
@@ -11,8 +12,7 @@ export default function systemCommands(program, provided) {
|
|
|
11
12
|
const interaction = prompts();
|
|
12
13
|
interaction.begin("Install System", "official stable release");
|
|
13
14
|
const status = await interaction.progress("Installing PhreshOS", "PhreshOS installed", () => current().install());
|
|
14
|
-
|
|
15
|
-
interaction.finish("System installed");
|
|
15
|
+
interaction.finish(`PhreshOS ${accent(status.installed?.version ?? "")} installed`);
|
|
16
16
|
});
|
|
17
17
|
system.command("uninstall")
|
|
18
18
|
.description("remove the System installation and service")
|
|
@@ -24,37 +24,63 @@ export default function systemCommands(program, provided) {
|
|
|
24
24
|
interaction.finish("System uninstalled");
|
|
25
25
|
});
|
|
26
26
|
system.command("status")
|
|
27
|
-
.description("show
|
|
27
|
+
.description("show the System version and operating state")
|
|
28
28
|
.action(async function () {
|
|
29
|
+
const status = await installed(current());
|
|
29
30
|
const interaction = prompts();
|
|
30
31
|
interaction.begin("System Status");
|
|
31
|
-
report(interaction,
|
|
32
|
+
report(interaction, status);
|
|
33
|
+
interaction.finish(status.ready ? positive("System ready") : caution("System not ready"));
|
|
34
|
+
});
|
|
35
|
+
system.command("version")
|
|
36
|
+
.description("show the installed System version")
|
|
37
|
+
.action(async function () {
|
|
38
|
+
const status = await installed(current());
|
|
39
|
+
const interaction = prompts();
|
|
40
|
+
interaction.begin("System Version");
|
|
41
|
+
interaction.finish(`PhreshOS ${accent(status.installed.version)}`);
|
|
32
42
|
});
|
|
33
|
-
action(system, "start", "start the background service",
|
|
34
|
-
action(system, "stop", "stop the background service",
|
|
35
|
-
action(system, "enable", "enable automatic startup",
|
|
36
|
-
action(system, "disable", "disable automatic startup",
|
|
43
|
+
action(system, "start", "start the background service", current, lifecycle => lifecycle.start());
|
|
44
|
+
action(system, "stop", "stop the background service", current, lifecycle => lifecycle.stop());
|
|
45
|
+
action(system, "enable", "enable automatic startup", current, lifecycle => lifecycle.enable());
|
|
46
|
+
action(system, "disable", "disable automatic startup", current, lifecycle => lifecycle.disable());
|
|
37
47
|
return system;
|
|
38
48
|
}
|
|
39
|
-
function action(system, name, description, work) {
|
|
49
|
+
function action(system, name, description, current, work) {
|
|
40
50
|
system.command(name)
|
|
41
51
|
.description(description)
|
|
42
52
|
.action(async function () {
|
|
53
|
+
const lifecycle = current();
|
|
54
|
+
await installed(lifecycle);
|
|
43
55
|
const interaction = prompts();
|
|
44
56
|
interaction.begin(`System ${title(name)}`);
|
|
45
|
-
|
|
46
|
-
report(interaction, status);
|
|
57
|
+
await interaction.progress(`${title(name)}ing PhreshOS`, `PhreshOS ${past(name)}`, () => work(lifecycle));
|
|
47
58
|
interaction.finish(`System ${past(name)}`);
|
|
48
59
|
});
|
|
49
60
|
}
|
|
50
61
|
function report(interaction, status) {
|
|
51
|
-
interaction.detail("
|
|
52
|
-
interaction.detail("service", status
|
|
53
|
-
interaction.detail("startup", status.enabled ? "enabled" : "disabled");
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
interaction.detail("version", accent(status.installed?.version ?? "unknown"));
|
|
63
|
+
interaction.detail("service", service(status));
|
|
64
|
+
interaction.detail("startup", status.enabled ? positive("enabled") : dim("disabled"));
|
|
65
|
+
}
|
|
66
|
+
async function installed(lifecycle) {
|
|
67
|
+
const status = await lifecycle.status();
|
|
68
|
+
if (status.installed)
|
|
69
|
+
return { ...status, installed: status.installed };
|
|
70
|
+
const interaction = prompts();
|
|
71
|
+
interaction.begin("PhreshOS System");
|
|
72
|
+
interaction.warning("PhreshOS System is not installed");
|
|
73
|
+
interaction.finish(`${dim("Install it with")} ${accent("phresh system install")}`);
|
|
74
|
+
throw new ReportedFailure();
|
|
75
|
+
}
|
|
76
|
+
function service(status) {
|
|
77
|
+
if (status.ready)
|
|
78
|
+
return positive("ready");
|
|
79
|
+
if (status.running)
|
|
80
|
+
return caution("starting");
|
|
81
|
+
if (status.registered)
|
|
82
|
+
return caution("stopped");
|
|
83
|
+
return negative("not registered");
|
|
58
84
|
}
|
|
59
85
|
function title(value) {
|
|
60
86
|
return `${value[0]?.toUpperCase()}${value.slice(1)}`;
|
package/dist/system/lifecycle.js
CHANGED
|
@@ -4,6 +4,7 @@ import { intakeReady, waitForIntake } from "./readiness.js";
|
|
|
4
4
|
import systemPaths from "./paths.js";
|
|
5
5
|
import systemService from "./service/index.js";
|
|
6
6
|
import nodeExecutable from "./node.js";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
7
8
|
import { join } from "node:path";
|
|
8
9
|
/** Coordinates acquisition, immutable files, and the native service as one transaction. */
|
|
9
10
|
export default class SystemLifecycle {
|
|
@@ -123,7 +124,8 @@ export default class SystemLifecycle {
|
|
|
123
124
|
}
|
|
124
125
|
catch (error) {
|
|
125
126
|
const message = error instanceof Error ? error.message : String(error);
|
|
126
|
-
|
|
127
|
+
const log = existsSync(installation.paths.log) ? `. Service log: ${installation.paths.log}` : "";
|
|
128
|
+
throw new Error(`${message}${log}`, { cause: error });
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
async activate(prepared, previous, state) {
|
|
@@ -79,11 +79,11 @@ Description=PhreshOS System
|
|
|
79
79
|
[Service]
|
|
80
80
|
Type=simple
|
|
81
81
|
ExecStart=${quote(definition.executable)} ${quote(definition.entry)}
|
|
82
|
-
WorkingDirectory=${
|
|
82
|
+
WorkingDirectory=${setting(definition.directory)}
|
|
83
83
|
Restart=on-failure
|
|
84
84
|
RestartSec=2
|
|
85
|
-
StandardOutput=append:${
|
|
86
|
-
StandardError=append:${
|
|
85
|
+
StandardOutput=append:${setting(definition.output)}
|
|
86
|
+
StandardError=append:${setting(definition.output)}
|
|
87
87
|
|
|
88
88
|
[Install]
|
|
89
89
|
WantedBy=default.target
|
|
@@ -92,3 +92,8 @@ WantedBy=default.target
|
|
|
92
92
|
function quote(value) {
|
|
93
93
|
return JSON.stringify(value);
|
|
94
94
|
}
|
|
95
|
+
function setting(value) {
|
|
96
|
+
if (value.includes("\n") || value.includes("\r"))
|
|
97
|
+
throw new Error("A systemd service path cannot contain a line break");
|
|
98
|
+
return value.replaceAll("%", "%%");
|
|
99
|
+
}
|
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.7",
|
|
5
5
|
"description": "The Phresh command-line interface for Program projects and system management.",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.10"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"test": "node --run compile && node --test tests/*.test.mjs",
|
|
14
14
|
"verify": "node --run build && node --run test && node scripts/verify-package.mjs",
|
|
15
15
|
"verify:system-release": "node --run compile && node scripts/verify-system-release.mjs",
|
|
16
|
+
"verify:linux-service": "node --run compile && node scripts/verify-linux-service.mjs",
|
|
16
17
|
"verify:macos-service": "node --run compile && node scripts/verify-macos-service.mjs",
|
|
17
18
|
"prepack": "node --run build"
|
|
18
19
|
},
|